diff --git a/README.md b/README.md index b8f7578..d076a66 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # wwdc2022-subtitles wwdc2022 substitles, include download links and srt files +The downloadlinks are in the downloadlinks file, and each line is a subtitle file. +Example: + +`ffmpeg -i "https://devstreaming-cdn.apple.com/videos/wwdc/2022/10028/3/F2DB18FC-F40C-4FB9-A080-5202CE2794CA/subtitles/zho/prog_index.m3u8" wwdc2022-10028.srt` diff --git a/downloadlinks.txt b/downloadlinks.txt new file mode 100644 index 0000000..5b1080e --- /dev/null +++ b/downloadlinks.txt @@ -0,0 +1,180 @@ +https://devstreaming-cdn.apple.com/videos/wwdc/2022/102/3/29AB52D7-09CF-4A22-8C26-84AEB4A90097/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/103/3/AFC3701A-AC3C-4F5D-9AB3-1CDE42CB5763/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/112/1/EA7B4115-D8B3-4D25-8463-CD4D748729A3/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/113/1/62CF83FA-0B10-4D25-A188-368E2557F674/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10001/4/8F64C0B4-8E12-4C78-A0CB-8B7239AA5D4B/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10002/5/F229C2EC-A6BC-4671-91A0-65FBC9D71DDF/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10003/5/C8AAE478-A435-4DA4-8256-F32941E32204/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10005/4/55C7CDFC-2E36-4AC0-8E77-7327EBD9E0E9/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10006/3/30C45DFC-CD54-4D31-894F-9954C9A58C93/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10007/4/2E07F67B-3E73-4DC2-A300-93EB4AF56295/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10008/5/BB4E3444-7C8E-42CC-B427-08DA670C9D41/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10009/5/22B7E083-47EE-4292-AD3E-6622DA2DF064/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10015/4/EE2F3B42-E5BA-4A10-99CE-3BBF209E1035/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10016/4/4A58011C-EB98-4462-A8F7-8EDB8A69BBE7/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10017/3/6F4C9F52-725A-4AD2-83BD-A3D43D29A914/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10018/6/1C7686E9-BB02-4797-B131-7FA5BD1B6133/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10019/4/CA2236CA-EAD0-454F-8556-FA583BA70590/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10020/3/D44F2D64-E1DB-492F-9D23-1BFD26FF7EF2/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10022/5/62C081E5-0066-440B-BDE0-A89D5099C4B8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10023/3/7AAE9501-211F-4201-B017-2AAC7F0C2556/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10024/3/9BD19E63-1BFD-49E9-A941-5CA5A937682C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10025/4/5DB691AA-D403-4394-885D-0F1F18772715/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10026/3/346C760E-A60C-4D64-89A7-26C888CBBE0E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10027/3/F6386488-80EB-46AC-BFEE-23B81B5E621A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10028/3/F2DB18FC-F40C-4FB9-A080-5202CE2794CA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10032/6/69FD8F9D-5C29-4114-9C81-DF1ACC4B4BCA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10034/4/09670B48-23F9-4B12-87A7-05A34F146625/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10035/5/78F0B50C-C39D-4819-88A0-A1167D43FD7E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10037/6/63F99354-D35D-475C-8069-74E58C813299/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10038/4/42910CBB-36EB-4C09-9234-DAB67E71BAA6/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10039/5/38FCCB56-38F1-4E4D-B7D0-CF031642775A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10040/3/EA4705F8-227B-4C1A-81CF-328BB3CA9E68/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10041/4/A173FAFA-9D08-4E7F-9154-7B821167B78E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10043/3/F1797DDF-C678-4B65-9571-42C3B99B40B8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10044/4/A6EC2B38-5E5C-44B0-8CB4-F4B0118ADE6E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10045/3/F899BEB5-EBE4-422E-AE52-AEF752A194A0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10046/3/776B5FA8-B8C0-46DA-9EDE-7A0BE5F03772/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10048/4/8DF121DF-6825-4FBB-B570-A75F5A44CCB7/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10049/3/47260DC4-814E-466D-AD96-D29DFC5459BA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10050/5/358B551F-283C-4CD1-8172-DAC014727969/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10051/3/912F72F4-A83D-4923-A276-8B231CB7D837/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10052/5/241B4005-877E-40CD-91AA-4CE0714BB2E6/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10053/4/8898553F-A636-4C8D-AAAF-DE7463409879/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10054/4/E85249AE-F795-40DC-BD9E-A3E385906FE6/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10056/5/F52141E2-6868-4629-A64D-83E618CD6CD5/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10058/3/A2E41140-1058-4AFF-BF2C-5058A6588994/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10059/3/689200F0-E14A-4B93-A3B2-7D95D747540F/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10061/4/012AFD7A-B26E-4C25-9C6C-AB01D5336EA7/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10062/4/004375E4-2295-45FB-9D6D-20F1B8C3834C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10063/4/0942FD31-5504-40C2-A4E8-6AAAC31E459C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10064/9/B7DD74D7-2555-495C-9DA2-8A9B7D0C6D8B/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10065/5/C221E77C-502C-47CD-B0C4-9091B529DD77/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10066/4/3B036508-C4CA-4DA9-AE0F-83A6E607ADF8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10068/4/CD436E87-CE6B-4E99-A7EA-66C5A424B38B/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10069/4/1646A8BA-EEFA-4533-A631-3BCDF704A4EB/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10070/3/03E2BD27-04DD-4C07-A662-B94B7F784C65/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10071/4/A7198C26-97D7-49C3-8FE7-907808F342DE/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10072/4/03036EB8-1A2E-4ADD-A5A3-C50A9AFA841C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10074/3/1BF7E42D-BA6E-467E-9A03-1973DCC5E9A5/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10075/5/041C40B8-2F14-4B08-8406-CFCE8E85A1B0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10076/6/DC2BEEAC-6D3F-40B0-B9A7-0C46031B0C91/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10077/4/C35119F9-FA1E-478C-9638-E90C1153FACD/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10078/4/688F144C-0F4D-4F7B-B77A-F10A56978C49/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10079/5/31E85A57-3035-4B6A-9BA4-4A73D156F55E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10082/5/31EEEDCE-D908-48E8-AEDA-A40811515F69/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10083/4/B0CF7C82-605A-4F0E-9BF2-C1F540932B45/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10089/3/473B1E55-B2A6-42BB-AA96-EE2599D6E779/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10090/4/5A0AE4B4-BE39-434E-8B9E-0910F2FD152D/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10092/3/E39F623F-97FE-48C0-9987-898078EB9D8B/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10093/3/D9CE5DEB-FE73-4FEF-9993-9551EB58CBDC/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10094/4/6D2459BF-7717-4646-BE9A-E73C7E602DB9/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10095/5/DB09B90A-7453-4E3F-90E9-4AB7322DD253/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10096/3/A04D6D9D-A138-488C-A470-371FB62609AA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10097/4/1195D4FF-4AF5-48B0-BB92-948D01AF942B/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10098/4/0243E8FF-8341-4FD5-BACD-CEB81B4730DF/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10099/5/AE8329C9-B427-49CF-95BE-71C9B5F49627/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10100/8/7E160FF7-856D-4B6E-BE75-633EF8C15CA5/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10101/4/E7651C9D-CAC8-44A9-9BF8-8D0DC317F4A2/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10102/4/E03398C4-8CAE-4CA1-905A-22205249E038/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10103/7/0DA14AB6-97A2-4E95-A960-E27CBC5E5012/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10104/4/E095D698-00CE-4A00-812C-4BA755CE26DB/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10105/7/A0348CEB-F711-422B-9FA4-D1A0E1DB8BF8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10106/5/56A1A2BE-7EF5-4AA0-AE93-1F93BD885019/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10107/3/85B12DD5-27C3-420C-97F8-4C71326BB3D0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10108/4/1F8BF487-ABEF-47CD-AC84-C3AC2E35885A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10109/3/AC093573-81B2-4A1E-BA66-50E413DF5660/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10110/3/9DDED4EB-547B-46DD-AEE5-9D3F2C60CFF8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10115/3/76BE7C00-CB67-4FF0-A159-19D8B091CED3/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10116/5/1DD917FC-5154-4B41-93E7-4D8731FB6D2E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10117/3/BC2A00F7-7836-4346-B4DD-143192926205/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10119/4/8D4ACDA6-A3CE-4294-8DFE-B4CF5DE26D86/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10120/5/7685DE64-40AC-4C35-9865-8CDA798501E4/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10121/5/E497A884-24B9-4D6C-A35D-6F9BEEB985B6/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10122/5/F35FC4AA-E76F-444D-85D0-77A76E7D3E15/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10126/4/042EC236-E96E-4969-A68A-1D379C84D647/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10127/3/C6A70FDB-501E-42BB-A50E-9794D4050C07/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10128/3/A48642CC-4EA4-478D-BC86-9AD9FE213885/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10129/6/FB8C0A23-9B2F-4564-B2C3-D48F6B53BB9A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10131/4/AE5E1692-81D5-4EF7-A74E-667A4D426D71/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10132/3/E1001357-38F4-429C-A7E2-495996D84893/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10133/4/BBFD71DD-1E5F-4843-861E-0D333BAA1A3F/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10135/4/B6405ED7-98EE-473C-8174-144D5E72CA02/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10136/4/ED1436D1-9197-468B-8B26-5DAD9AEC3720/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10137/3/AB0A9BA9-E0B1-440B-98E6-E9C8A395FF34/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10139/5/67B2BB72-D5FC-4EAE-834D-E7D562F22A4D/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10140/3/7F5B5E00-19E6-4DBE-A169-044C9D0418F0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10141/4/31DD4CF1-C4A2-4A5C-A3C8-B231788AE125/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10142/4/D8D87522-CCCC-46BA-8C48-ECA2A5F9E36E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10143/3/585D8CA2-12BF-4F97-8DDD-11A5561BD143/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10144/3/B295073D-367F-4EAB-A65F-6FBB86ECDD6E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10145/4/1BA9D9C4-C8EC-4D33-A67A-2DFEBD032041/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10147/5/B7675782-6F3F-4D44-B56D-06CCE29D9E22/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10148/4/ABD3D85D-6492-4E1A-B39A-BEBABBC5C075/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10149/4/BAEE96D6-1250-4911-A2D6-A96AC4D48ED4/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10151/5/3F44347B-F0CF-4DFC-89A8-C801EE456545/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10152/3/0694C884-66D5-46B5-BAC6-A6671661A771/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10153/6/390C5399-8CDD-4D3E-8701-29B14E042A94/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10155/5/6A4BFEE6-F1BC-4E6A-9A03-13EBF7D38664/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10156/3/740DABB6-6584-492E-AA71-A628E023B346/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10157/3/A3A24118-7045-4049-9392-6B10E8CDD489/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10159/5/4B824E2A-E99A-492A-84EA-6F6ECC279EB3/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10160/5/F0ACC08B-EFC0-459E-AE6D-DEA492619F49/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10162/5/9408DC15-0B03-4DD9-8ADE-161821D0E7FC/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10166/3/144ECF3D-FAB1-44D1-A265-90946F21F612/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10167/5/043ACD7F-3BC9-4AA1-A5BC-7DCCF91098EA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10169/5/8F7E31FB-73E9-405E-8031-74902FC37BB8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/10170/3/30D42D9F-AF97-4B32-B470-C0A9B4D8C279/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110332/5/6B7CFC73-E018-439B-8755-EA807A72DEA3/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110335/4/0F40B105-473E-42E2-A5BD-C33EE3C6B743/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110336/4/DB808128-449E-420A-9FA1-E5CF7403B7FD/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110338/3/BCB38F17-7908-4BD8-8A7A-767EC8C59367/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110339/4/694E2DB9-0326-40B6-951C-7FF812E944EA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110340/3/B5BDA6B6-597F-460B-A88C-959265B6715D/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110341/3/B3803998-3525-4D12-A13D-CFE6C8435AAF/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110342/3/8AEC6435-2E81-4B94-BBED-35CCAD0AA0CA/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110343/4/61E55FAE-4837-4DAF-912C-8D101B7DF820/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110344/4/3BE29FF5-6545-4560-A014-79CD116180E9/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110345/4/D78059C0-3932-4CAA-8B45-098BEB4ACF45/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110347/3/9A5697EE-37FC-497A-AD9F-5033E026866E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110348/5/EF06F7AC-5379-4AFF-A0AB-FD1413B78098/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110349/4/767821EC-6C4C-4F9E-8C8D-1B231B8E1226/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110350/4/3B87EB1E-4E88-4D11-817A-16852AEA794C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110351/3/2B82DC62-6057-4460-93F4-B99CF7073221/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110352/3/961EB9A0-3340-443A-8C57-8665B9034F1D/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110353/4/518CFD38-CEE9-4052-8DB9-6692741930F2/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110354/5/BFF5625D-B11D-4C9D-B82B-E7A89A669475/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110355/4/459D7B80-E4A7-428F-ADA8-EF2543CE3350/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110356/6/AAC6A083-0D2D-4FFF-B60E-86EF01E37EE2/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110357/3/8FEA2DD3-43EE-44FB-A856-53169F90D683/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110358/6/CCCFE7E0-48F7-4D00-A8C1-6DB5E768F833/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110359/6/0515ED86-51DB-430A-9521-E5DB4FC59C61/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110360/3/95EF8495-F291-49FD-8958-276AC76C222D/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110361/3/7FB8FB7D-976B-432E-A47D-05ADDFE1BD45/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110362/3/629CBACC-AF8F-4856-98CA-075275ADEAA4/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110363/4/4D40D7E0-771C-43BE-A0B9-7948E0C69CE0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110364/4/BA2A87DE-2A6F-4BB2-A81F-2403413E96AB/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110367/3/0C225661-78FA-4245-9A79-C80C825B2DBE/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110368/6/40936E45-C4DD-4831-B7B9-146B53027E76/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110369/4/7FCC1556-7EF4-4CFA-B757-DBCDC51B836A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110370/7/31CCC67C-D5AC-4493-AFB4-7B833E2B8162/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110371/4/F41F7DFC-33C6-4BFA-9CC0-D212E30E6599/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110373/5/9E0B243C-9E0C-4E4E-91FF-AACD903146B2/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110374/5/69623440-5AFC-4AFB-9641-DBC4EEF46379/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110375/5/6C8AD7C3-EFFA-4253-82F2-3B8488E3519A/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110379/3/072CE81E-54AA-400F-82CC-3667BB3549E1/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110380/4/F37ED64E-304D-423D-B8FA-17687B8EC980/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110381/5/23FB4AFE-1352-4B9D-A2C6-A3F7C232C022/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110384/6/1F377839-E110-4222-BBC2-B0424F6E635C/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110401/6/E6B6BDA3-C922-4FC4-AF6B-EB6C290568A7/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110403/3/7B38146A-41F4-422B-A863-6E4277C76C6E/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110404/3/55253AC0-EEDC-49B5-884C-CE8F562CC023/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110427/5/60E9EBA5-592E-48D0-9429-A85E40C4C9F0/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110429/4/60E4BC61-632F-484F-AF90-EA3203042E76/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110441/4/C8C3F26A-9D07-41B4-A006-585B5F484C08/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110565/4/46CD2F39-5184-416E-A4F4-E57AEAF92AC8/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110929/1/E9996C71-5D71-46C7-BC47-4A26302DA7D6/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110930/1/73202A76-09C0-4221-9529-CA4DE5847169/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110931/1/B3A83EB2-B46E-432E-92E9-9F5D15D6E793/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110932/1/FAD2EDA0-088A-4ECC-A98A-4448F5568116/subtitles/zho/prog_index.m3u8 +https://devstreaming-cdn.apple.com/videos/wwdc/2022/110933/1/CF0900A1-24C8-4C33-B6D1-18B2343DC206/subtitles/zho/prog_index.m3u8 \ No newline at end of file diff --git a/eng/2022 Session 10001 Explore navigation design for iOS en.srt b/eng/2022 Session 10001 Explore navigation design for iOS en.srt new file mode 100644 index 0000000..677d2d1 --- /dev/null +++ b/eng/2022 Session 10001 Explore navigation design for iOS en.srt @@ -0,0 +1,2481 @@ +1 +00:00:00,067 --> 00:00:03,170 +♪ instrumental hip hop music ♪ + +2 +00:00:03,170 --> 00:00:09,676 +♪ + +3 +00:00:09,676 --> 00:00:11,378 +Hi, I'm Sarah McClanahan, + +4 +00:00:11,378 --> 00:00:14,882 +and I'm a designer +on the Evangelism team. + +5 +00:00:14,882 --> 00:00:18,085 +Today, I'm going to share +practical tips, guidance, + +6 +00:00:18,085 --> 00:00:21,021 +and best practices +for how to improve navigation + +7 +00:00:21,021 --> 00:00:22,890 +on your iOS apps. + +8 +00:00:22,890 --> 00:00:26,827 +When apps have great navigation, +it's often unnoticed + +9 +00:00:26,827 --> 00:00:31,064 +because people are able to focus +on the content and experience. + +10 +00:00:31,064 --> 00:00:33,734 +Navigation involves +teaching people about + +11 +00:00:33,734 --> 00:00:36,270 +how things behave, + +12 +00:00:36,270 --> 00:00:38,238 +where things belong, + +13 +00:00:38,238 --> 00:00:40,674 +and how things work in your app. + +14 +00:00:40,674 --> 00:00:43,543 +The goal of navigation +is to provide enough + +15 +00:00:43,543 --> 00:00:46,980 +of a foundation of familiarity +so that people can easily + +16 +00:00:46,980 --> 00:00:50,384 +discover your content +and interact with your app. + +17 +00:00:50,384 --> 00:00:53,587 +When navigation deviates +too far from our expectations + +18 +00:00:53,587 --> 00:00:57,291 +or doesn't match our natural +understanding of the world, + +19 +00:00:57,291 --> 00:00:59,760 +we often feel frustrated +and sense that an app + +20 +00:00:59,760 --> 00:01:01,228 +is hard to use. + +21 +00:01:01,228 --> 00:01:05,198 +But getting navigation right +requires focus and intention. + +22 +00:01:05,198 --> 00:01:08,502 +And although the concepts +I'm covering today are not new, + +23 +00:01:08,502 --> 00:01:10,370 +they are fundamental, + +24 +00:01:10,370 --> 00:01:14,074 +and they're essential to having +a successful app on iOS. + +25 +00:01:14,074 --> 00:01:16,710 +So whether you're new to +the platform or looking for ways + +26 +00:01:16,710 --> 00:01:20,347 +to improve your app experience, +then this session is for you. + +27 +00:01:20,347 --> 00:01:23,016 +Today, we're going +to discuss tab bars, + +28 +00:01:23,016 --> 00:01:27,220 +a common form +of navigation on iOS. + +29 +00:01:27,220 --> 00:01:30,691 +Then we'll discuss interactions +for moving between screens + +30 +00:01:30,691 --> 00:01:34,127 +by exploring +hierarchical navigation + +31 +00:01:34,127 --> 00:01:36,496 +and modal presentations. + +32 +00:01:36,496 --> 00:01:39,466 +As you can see, we'll only +be covering a subset + +33 +00:01:39,466 --> 00:01:42,369 +of the large topic area +of navigation. + +34 +00:01:42,369 --> 00:01:44,905 +But we'll start here +because these core patterns + +35 +00:01:44,905 --> 00:01:49,042 +represent a foundation +which we often see misused, + +36 +00:01:49,042 --> 00:01:51,979 +and understanding them +can set you up for success + +37 +00:01:51,979 --> 00:01:56,016 +as your app evolves +or supports other devices. + +38 +00:01:56,016 --> 00:01:58,485 +Let's get started with tab bars. + +39 +00:01:58,485 --> 00:02:01,121 +Tab bars are a global +navigation control + +40 +00:02:01,121 --> 00:02:02,956 +that sit at the bottom +of the screen, + +41 +00:02:02,956 --> 00:02:06,793 +categorizing an app's content +into different sections. + +42 +00:02:06,793 --> 00:02:08,895 +Think of tabs +as a control to reflect + +43 +00:02:08,895 --> 00:02:12,199 +your information hierarchy. + +44 +00:02:12,199 --> 00:02:14,267 +The UI control itself +should translate + +45 +00:02:14,267 --> 00:02:17,337 +an already-clear grouping +and establish relationship + +46 +00:02:17,337 --> 00:02:20,407 +between different areas +of your app. + +47 +00:02:20,407 --> 00:02:23,543 +So the Tab bar represents +your top-level content, + +48 +00:02:23,543 --> 00:02:26,446 +which should be the top +of your app's hierarchy. + +49 +00:02:26,446 --> 00:02:29,783 +Each tab communicates +your app's menu of options, + +50 +00:02:29,783 --> 00:02:33,720 +and these sections should be +meaningful and descriptive. + +51 +00:02:33,720 --> 00:02:36,156 +This likely sounds +really straightforward, + +52 +00:02:36,156 --> 00:02:39,159 +but in practice +and for various reasons, + +53 +00:02:39,159 --> 00:02:42,996 +it can be easy to lose sight of +in your app. + +54 +00:02:42,996 --> 00:02:45,098 +Let's look at +some good examples. + +55 +00:02:45,098 --> 00:02:47,968 +Without seeing any +of the content of these apps, + +56 +00:02:47,968 --> 00:02:51,138 +notice how the tabs +hint at functionality. + +57 +00:02:51,138 --> 00:02:53,874 +They tell a story about +what the app can do + +58 +00:02:53,874 --> 00:02:56,843 +just by displaying +concise labels. + +59 +00:02:56,843 --> 00:03:00,881 +Listen Now and Radio indicate +that this is a content-based app + +60 +00:03:00,881 --> 00:03:04,084 +with auditory media. + +61 +00:03:04,084 --> 00:03:08,789 +For this app, Library and Albums +hint at a content-rich app + +62 +00:03:08,789 --> 00:03:13,927 +with a For You tab that signals +strong personalization. + +63 +00:03:13,927 --> 00:03:17,597 +This app has tabs that are so +focused that their functionality + +64 +00:03:17,597 --> 00:03:20,467 +is self-evident and tell me +exactly what I can do + +65 +00:03:20,467 --> 00:03:23,403 +in those content areas. + +66 +00:03:23,403 --> 00:03:26,373 +What we see often +is that the first tab of apps + +67 +00:03:26,373 --> 00:03:29,409 +tend to be loaded +heaviest with features. + +68 +00:03:29,409 --> 00:03:31,311 +Try to create balance +in your interface + +69 +00:03:31,311 --> 00:03:34,381 +by distributing functionality +throughout your tabs. + +70 +00:03:34,381 --> 00:03:36,149 +Let's go through this +with an example + +71 +00:03:36,149 --> 00:03:39,486 +to explore how tabs can +oftentimes be misguiding + +72 +00:03:39,486 --> 00:03:41,822 +or confusing. + +73 +00:03:41,822 --> 00:03:45,058 +Imagine I have a fake app +that lets people discover + +74 +00:03:45,058 --> 00:03:48,695 +curated routes in cities +just like a local cyclist. + +75 +00:03:48,695 --> 00:03:51,531 +And if you're traveling, +moving to a new city, + +76 +00:03:51,531 --> 00:03:54,334 +or just getting into the sport, +then there's an easy way + +77 +00:03:54,334 --> 00:03:57,571 +to save routes +and create itineraries. + +78 +00:03:57,571 --> 00:03:58,939 +Here it is. + +79 +00:03:58,939 --> 00:04:01,708 +Since this app is about +finding routes to bike, + +80 +00:04:01,708 --> 00:04:07,247 +the first thing you'll see is a +map view with filters for rides. + +81 +00:04:07,247 --> 00:04:10,183 +Then, there's a section +with an upcoming itinerary + +82 +00:04:10,183 --> 00:04:12,853 +that I can interact with +by editing the content + +83 +00:04:12,853 --> 00:04:15,822 +or inviting friends. + +84 +00:04:15,822 --> 00:04:17,557 +And then there are routes +grouped together + +85 +00:04:17,557 --> 00:04:19,092 +in collection views. + +86 +00:04:19,092 --> 00:04:22,762 +It can be tempting to add all +your functionality into one tab + +87 +00:04:22,762 --> 00:04:26,366 +just like this, because +it's available at a glance. + +88 +00:04:26,366 --> 00:04:28,668 +Or maybe your app +has evolved over time + +89 +00:04:28,668 --> 00:04:31,204 +and you've lost sight of +grouping your functionality + +90 +00:04:31,204 --> 00:04:33,573 +into sections on the tab bar. + +91 +00:04:33,573 --> 00:04:38,145 +Well today, I invite you +to reconsider this in your app. + +92 +00:04:38,145 --> 00:04:41,148 +In this design, +people may have to scroll a lot + +93 +00:04:41,148 --> 00:04:43,817 +for what they're looking for, +and it takes effort + +94 +00:04:43,817 --> 00:04:47,687 +to mentally parse unrelated, +disparate features. + +95 +00:04:47,687 --> 00:04:50,590 +Filtering a map view +and editing an itinerary + +96 +00:04:50,590 --> 00:04:54,060 +are two very different +features -- and mindsets -- + +97 +00:04:54,060 --> 00:04:56,863 +when someone opens +this app to use it. + +98 +00:04:56,863 --> 00:04:59,966 +Be cautious of combining your +app's functionality in this way + +99 +00:04:59,966 --> 00:05:02,169 +or doing it out of fear +that people won't interact + +100 +00:05:02,169 --> 00:05:03,904 +with the rest of your app. + +101 +00:05:03,904 --> 00:05:06,973 +It's much easier to understand +an app's functionality + +102 +00:05:06,973 --> 00:05:11,011 +when it's well organized +and clearly communicated. + +103 +00:05:11,011 --> 00:05:15,015 +One way to do this is to take +a step back and ask yourself, + +104 +00:05:15,015 --> 00:05:17,417 +Why do people use your app? + +105 +00:05:17,417 --> 00:05:20,353 +Remember, great apps +have focused solutions. + +106 +00:05:20,353 --> 00:05:22,956 +They aim to do +a few things really well, + +107 +00:05:22,956 --> 00:05:25,892 +opposed to trying to solve +everything in one app. + +108 +00:05:25,892 --> 00:05:28,962 +Let's reconsider the tab bar +on this cycling app. + +109 +00:05:28,962 --> 00:05:30,997 +People use this app +to find routes + +110 +00:05:30,997 --> 00:05:34,034 +in the places they care about, +that are right for their level. + +111 +00:05:34,034 --> 00:05:36,403 +This is one of the most +important views in the app, + +112 +00:05:36,403 --> 00:05:39,973 +as it represents the content +that people care about the most. + +113 +00:05:39,973 --> 00:05:43,210 +So let's take a step back +and reassess the tab bar + +114 +00:05:43,210 --> 00:05:45,845 +to understand how a route +may be used + +115 +00:05:45,845 --> 00:05:48,415 +and how that content can be +represented in the app + +116 +00:05:48,415 --> 00:05:51,885 +in a way that feels +more balanced. + +117 +00:05:51,885 --> 00:05:54,421 +This is a route detail. + +118 +00:05:54,421 --> 00:05:56,590 +Someone would expect +to see an overview, + +119 +00:05:56,590 --> 00:06:00,026 +like distance +and elevation gain, + +120 +00:06:00,026 --> 00:06:02,729 +as well as access to the map +and road surfaces + +121 +00:06:02,729 --> 00:06:06,099 +throughout the route, +like sidewalks or roads. + +122 +00:06:06,099 --> 00:06:09,836 +Seeing specific callouts +for steep climbs or descents + +123 +00:06:09,836 --> 00:06:13,473 +could help me understand if this +route is right for my level. + +124 +00:06:13,473 --> 00:06:15,675 +And finally, food stops +along the route + +125 +00:06:15,675 --> 00:06:17,844 +are great for planning. + +126 +00:06:17,844 --> 00:06:22,549 +OK. So, how do people make sense +of this core functionality + +127 +00:06:22,549 --> 00:06:24,484 +of viewing routes? + +128 +00:06:24,484 --> 00:06:29,723 +Well, a route is only helpful +if you know where it is located. + +129 +00:06:29,723 --> 00:06:32,292 +Routes should likely +have a relationship + +130 +00:06:32,292 --> 00:06:35,929 +with the city +they're associated with. + +131 +00:06:35,929 --> 00:06:38,265 +That may lead me +to have a city overview + +132 +00:06:38,265 --> 00:06:41,768 +that tells me helpful +information about cycling there. + +133 +00:06:41,768 --> 00:06:43,203 +And if you scroll down the view, + +134 +00:06:43,203 --> 00:06:47,007 +you'd have that list of all +the routes you can ride. + +135 +00:06:47,007 --> 00:06:50,944 +But this app also supports +routes in different cities, + +136 +00:06:50,944 --> 00:06:56,316 +which means each city should go +back to a list of all places. + +137 +00:06:56,316 --> 00:06:59,185 +Places can become the top level +of the hierarchy + +138 +00:06:59,185 --> 00:07:01,454 +when navigating to routes. + +139 +00:07:01,454 --> 00:07:04,791 +As you can see, there's a lot of +content in this workflow alone, + +140 +00:07:04,791 --> 00:07:09,162 +and it's really key to the value +that this app provides. + +141 +00:07:09,162 --> 00:07:12,232 +This is great justification +for a tab-bar item. + +142 +00:07:12,232 --> 00:07:14,901 +Notice now how +it's self-contained. + +143 +00:07:14,901 --> 00:07:17,570 +It wouldn't make sense +to put anything in this tab + +144 +00:07:17,570 --> 00:07:20,674 +that isn't about a place. + +145 +00:07:20,674 --> 00:07:24,878 +Part of designing great tab +bars is organizing your content. + +146 +00:07:24,878 --> 00:07:28,081 +Look for these natural ways +to address relationships. + +147 +00:07:28,081 --> 00:07:30,850 +I can go through this exercise +with other key features + +148 +00:07:30,850 --> 00:07:34,321 +in the app, like itineraries, +and I can ask myself, + +149 +00:07:34,321 --> 00:07:35,889 +What is an itinerary? + +150 +00:07:35,889 --> 00:07:37,524 +How will people use it? + +151 +00:07:37,524 --> 00:07:41,127 +And where can I dedicate +a place for them in my app? + +152 +00:07:41,127 --> 00:07:44,564 +Even if people are unfamiliar +with the content of your app, + +153 +00:07:44,564 --> 00:07:46,999 +and perhaps +especially if they are, + +154 +00:07:46,999 --> 00:07:49,302 +it's best to communicate +your functionality + +155 +00:07:49,302 --> 00:07:51,004 +and content clearly, + +156 +00:07:51,004 --> 00:07:53,206 +assess where it belongs +in your hierarchy, + +157 +00:07:53,206 --> 00:07:55,408 +and how people engage with it. + +158 +00:07:55,408 --> 00:07:58,678 +Then, this app can improve +from having all of the features + +159 +00:07:58,678 --> 00:08:02,349 +crammed into the first tab, +to a much clearer + +160 +00:08:02,349 --> 00:08:05,385 +and straightforward form +of navigation. + +161 +00:08:05,385 --> 00:08:09,155 +Now, the core functionality +is more balanced on the tab bar + +162 +00:08:09,155 --> 00:08:11,891 +because these sections +hold up on their own. + +163 +00:08:11,891 --> 00:08:12,826 +They are related, + +164 +00:08:12,826 --> 00:08:16,863 +yet distinctly different +content areas and workflows. + +165 +00:08:16,863 --> 00:08:21,000 +This makes navigation +so much more intuitive. + +166 +00:08:21,000 --> 00:08:24,237 +Next, I want to discuss +a slightly related topic, + +167 +00:08:24,237 --> 00:08:26,573 +but we see it +expressed differently. + +168 +00:08:26,573 --> 00:08:28,675 +Avoid duplicating +your functionality + +169 +00:08:28,675 --> 00:08:32,746 +and consolidating it +into a single tab. + +170 +00:08:32,746 --> 00:08:34,814 +In content-rich apps +like this one, + +171 +00:08:34,814 --> 00:08:38,451 +a tab titled Home, may seem +like an attractive catchall + +172 +00:08:38,451 --> 00:08:40,954 +to showcase functionality +throughout an app + +173 +00:08:40,954 --> 00:08:43,757 +in a single place. + +174 +00:08:43,757 --> 00:08:46,860 +For example, maybe it seems like +people aren't engaging + +175 +00:08:46,860 --> 00:08:49,763 +with the Itineraries feature, +and you may be worried + +176 +00:08:49,763 --> 00:08:52,766 +it's because they don't know +the functionality exists. + +177 +00:08:54,434 --> 00:08:57,303 +So it may seem logical +to encourage engagement + +178 +00:08:57,303 --> 00:09:00,740 +by repeating actions in +the tab bar for more visibility, + +179 +00:09:00,740 --> 00:09:04,811 +such as New Itinerary +on the Places cards + +180 +00:09:04,811 --> 00:09:07,547 +and maybe creating a version +of an itinerary view + +181 +00:09:07,547 --> 00:09:09,749 +that has the features +front and center, + +182 +00:09:09,749 --> 00:09:12,485 +such as inviting friends; + +183 +00:09:12,485 --> 00:09:17,323 +or listing the stops +with an easy action to add. + +184 +00:09:17,323 --> 00:09:19,325 +It might be tempting to do +this out of fear + +185 +00:09:19,325 --> 00:09:23,062 +that some functionality won't be +discovered throughout your app. + +186 +00:09:23,062 --> 00:09:27,233 +And to clarify, this isn't +about duplicating content. + +187 +00:09:27,233 --> 00:09:29,302 +In many scenarios, +it can make sense + +188 +00:09:29,302 --> 00:09:33,406 +to have similar types of +content, like songs or photos, + +189 +00:09:33,406 --> 00:09:36,976 +exist on many tabs, but organized +in a different way. + +190 +00:09:36,976 --> 00:09:38,711 +But when it's your app's +functionality, + +191 +00:09:38,711 --> 00:09:41,848 +which are the actions people +can take to achieve things, + +192 +00:09:41,848 --> 00:09:45,785 +the redundancy +creates confusion. + +193 +00:09:45,785 --> 00:09:49,856 +And in practice, Home tabs +disrupt an app's hierarchy. + +194 +00:09:49,856 --> 00:09:54,027 +If functionality from different +tabs or areas throughout an app + +195 +00:09:54,027 --> 00:09:57,464 +are duplicated and added +to a single screen + +196 +00:09:57,464 --> 00:09:59,332 +without sufficient context, + +197 +00:09:59,332 --> 00:10:02,202 +it creates redundancy +and confusion. + +198 +00:10:02,202 --> 00:10:04,771 +Home becomes the tab +where every feature + +199 +00:10:04,771 --> 00:10:07,474 +is fighting for real estate, +because the tab + +200 +00:10:07,474 --> 00:10:10,610 +is trying to solve a problem +of discoverability. + +201 +00:10:10,610 --> 00:10:13,446 +But in reality, it creates +a dissociation + +202 +00:10:13,446 --> 00:10:16,883 +between understanding content +and how to act on it. + +203 +00:10:16,883 --> 00:10:18,017 +If this is your app, + +204 +00:10:18,017 --> 00:10:20,687 +consider removing +the Home tab altogether. + +205 +00:10:20,687 --> 00:10:23,122 +The redundancy of features +prohibits people + +206 +00:10:23,122 --> 00:10:26,960 +from understanding where +things belong and why. + +207 +00:10:26,960 --> 00:10:28,628 +Another concern about Home tabs + +208 +00:10:28,628 --> 00:10:30,597 +is that the repeated +functionality + +209 +00:10:30,597 --> 00:10:33,433 +may cause someone to tab-jump +because the action exists + +210 +00:10:33,433 --> 00:10:35,835 +in another tab too. + +211 +00:10:35,835 --> 00:10:39,005 +Transporting someone to another +tab by tapping on an element + +212 +00:10:39,005 --> 00:10:42,242 +within a view is jarring +and disorienting. + +213 +00:10:42,242 --> 00:10:46,746 +Never force someone +to change tabs automatically. + +214 +00:10:46,746 --> 00:10:50,283 +Next, one of the biggest selling +points of a tabbed navigation + +215 +00:10:50,283 --> 00:10:53,386 +is access to multiple +top-level hierarchies, + +216 +00:10:53,386 --> 00:10:55,655 +so avoid hiding +or removing the tab bar + +217 +00:10:55,655 --> 00:10:58,658 +throughout your navigation. + +218 +00:10:58,658 --> 00:11:01,961 +Persistent access to the tab +bar gives someone the ability + +219 +00:11:01,961 --> 00:11:03,963 +to toggle between +different levels + +220 +00:11:03,963 --> 00:11:05,732 +of your information hierarchy + +221 +00:11:05,732 --> 00:11:08,167 +while maintaining context +in each. + +222 +00:11:08,167 --> 00:11:11,437 +For example, I can look at a new +route I'm considering riding + +223 +00:11:11,437 --> 00:11:15,508 +in the Places tab + +224 +00:11:15,508 --> 00:11:17,510 +and compare it to an itinerary +I'm building + +225 +00:11:17,510 --> 00:11:20,613 +in the Itinerary tab + +226 +00:11:20,613 --> 00:11:23,383 +with routes I've already saved + +227 +00:11:23,383 --> 00:11:26,686 +that are two levels deep +into my hierarchy. + +228 +00:11:26,686 --> 00:11:30,189 +This only works well +if tabs have defined purpose + +229 +00:11:30,189 --> 00:11:34,727 +and represent specific +categories of content. + +230 +00:11:34,727 --> 00:11:36,996 +Finally, all of the work +you invest + +231 +00:11:36,996 --> 00:11:39,198 +in a solid information +architecture + +232 +00:11:39,198 --> 00:11:42,235 +should be honored +with clear and concise labels. + +233 +00:11:42,235 --> 00:11:43,670 +Let's look at +an Apple Design winner + +234 +00:11:43,670 --> 00:11:46,272 +from the Interaction category +this year. + +235 +00:11:46,272 --> 00:11:48,207 +This is the Slopes app. + +236 +00:11:48,207 --> 00:11:50,643 +I think it's so great +that when you launch the app, + +237 +00:11:50,643 --> 00:11:53,012 +you land on the middle tab, +which is your Logbook + +238 +00:11:53,012 --> 00:11:55,915 +with your season stats. + +239 +00:11:55,915 --> 00:11:57,817 +The other tabs are specific. + +240 +00:11:57,817 --> 00:12:01,254 +They're easy to understand, +and I have an immediate sense + +241 +00:12:01,254 --> 00:12:03,990 +of what the app does +and how to use it. + +242 +00:12:03,990 --> 00:12:07,260 +At a glance, this is because +the labels are representative + +243 +00:12:07,260 --> 00:12:08,795 +of the content. + +244 +00:12:08,795 --> 00:12:11,097 +Record a ski day, +browse resorts, + +245 +00:12:11,097 --> 00:12:13,967 +compare stats with friends; +they all represent + +246 +00:12:13,967 --> 00:12:18,438 +core functionality +with a succinct label. + +247 +00:12:18,438 --> 00:12:21,674 +Tab bars are a powerful control +for navigation. + +248 +00:12:21,674 --> 00:12:24,310 +Let's recap everything +we've learned. + +249 +00:12:24,310 --> 00:12:28,748 +Use tabs to reflect +your information hierarchy. + +250 +00:12:28,748 --> 00:12:31,184 +Organize your features +in a way that balances them + +251 +00:12:31,184 --> 00:12:33,319 +across your tabs. + +252 +00:12:33,319 --> 00:12:38,491 +Avoid duplicating features and +merging them into a single tab. + +253 +00:12:38,491 --> 00:12:42,395 +Always keep the tab bar +persistent throughout your app. + +254 +00:12:42,395 --> 00:12:46,933 +Finally, use clear and concise +labels for each tab. + +255 +00:12:46,933 --> 00:12:49,969 +All right, +let's dive into interactions. + +256 +00:12:49,969 --> 00:12:52,972 +When it comes to navigating +between screens of an app, + +257 +00:12:52,972 --> 00:12:55,475 +there are two very common +forms of transition: + +258 +00:12:55,475 --> 00:12:57,543 +you can navigate through +an app's hierarchy + +259 +00:12:57,543 --> 00:13:00,246 +with a term we sometimes +refer to as a "push," + +260 +00:13:00,246 --> 00:13:02,715 +such as pushing +into more detail. + +261 +00:13:02,715 --> 00:13:06,452 +Or, you can transition +with a modal presentation. + +262 +00:13:06,452 --> 00:13:09,055 +These are nonintrusive +and familiar ways + +263 +00:13:09,055 --> 00:13:12,125 +to traverse an app's hierarchy +and transition between views. + +264 +00:13:12,125 --> 00:13:14,160 +Let me show you both. + +265 +00:13:14,160 --> 00:13:17,096 +When you transition +through hierarchical navigation, + +266 +00:13:17,096 --> 00:13:19,332 +a view pushes, +which means a person + +267 +00:13:19,332 --> 00:13:21,834 +has tapped on an element +and the next screen + +268 +00:13:21,834 --> 00:13:24,804 +slides into view +from right to left. + +269 +00:13:24,804 --> 00:13:27,640 +A push transition +is an expected default + +270 +00:13:27,640 --> 00:13:30,810 +when drilling further +into an app's hierarchy. + +271 +00:13:30,810 --> 00:13:33,279 +Pushing is great +because it directly reflects + +272 +00:13:33,279 --> 00:13:35,081 +your information hierarchy. + +273 +00:13:35,081 --> 00:13:38,284 +It's a literal representation +of moving through content + +274 +00:13:38,284 --> 00:13:41,421 +from a high level +and drilling into more detail. + +275 +00:13:41,421 --> 00:13:43,790 +On the other hand, +a modal is reserved + +276 +00:13:43,790 --> 00:13:46,526 +for a self-contained task +in an interface. + +277 +00:13:46,526 --> 00:13:49,462 +Modals work great for workflows +that are independent, + +278 +00:13:49,462 --> 00:13:52,331 +meaning someone has enough +information in that view + +279 +00:13:52,331 --> 00:13:55,168 +to make decisions +and complete a task. + +280 +00:13:55,168 --> 00:13:57,937 +Modals are unique +because they create focus + +281 +00:13:57,937 --> 00:14:01,774 +by separating people +from the information hierarchy. + +282 +00:14:01,774 --> 00:14:04,377 +For example, +creating a new itinerary + +283 +00:14:04,377 --> 00:14:07,714 +is presented in a modal view. + +284 +00:14:07,714 --> 00:14:11,984 +Someone can make selections +or input data such as a title, + +285 +00:14:11,984 --> 00:14:16,255 +a city, a range of dates, +and even invite friends. + +286 +00:14:16,255 --> 00:14:18,858 +This is suitable for a modal +because the UI is intended + +287 +00:14:18,858 --> 00:14:22,428 +to be edited and completed +before dismissing the view + +288 +00:14:22,428 --> 00:14:24,764 +or navigating around +the rest of the app. + +289 +00:14:24,764 --> 00:14:28,301 +Since it's all user input, +the rest of the app isn't needed + +290 +00:14:28,301 --> 00:14:31,037 +as a reference +to complete the fields. + +291 +00:14:31,037 --> 00:14:33,372 +Now that you're familiar +with these interactions, + +292 +00:14:33,372 --> 00:14:35,274 +let's dive deep on both. + +293 +00:14:35,274 --> 00:14:37,810 +Starting +with hierarchical navigation. + +294 +00:14:37,810 --> 00:14:40,480 +Here are a couple +of guidelines to consider. + +295 +00:14:40,480 --> 00:14:42,648 +Use a push transition +to navigate between + +296 +00:14:42,648 --> 00:14:46,119 +different levels +of your app's hierarchy. + +297 +00:14:46,119 --> 00:14:49,689 +Hierarchical navigation +reinforces the relationship + +298 +00:14:49,689 --> 00:14:52,825 +between top-level +and secondary content. + +299 +00:14:52,825 --> 00:14:55,862 +The top-level content +is higher in the hierarchy. + +300 +00:14:55,862 --> 00:14:59,532 +As I want more detail, +I access the supplemental views + +301 +00:14:59,532 --> 00:15:03,002 +by drilling into the hierarchy. + +302 +00:15:03,002 --> 00:15:05,938 +As I make selections, +I narrow my options + +303 +00:15:05,938 --> 00:15:09,575 +and eliminate access +to the rest of the hierarchy. + +304 +00:15:09,575 --> 00:15:11,577 +This is how it should be. + +305 +00:15:11,577 --> 00:15:14,080 +Content should become +increasingly more specific + +306 +00:15:14,080 --> 00:15:16,983 +and there should be +fewer options as I push in + +307 +00:15:16,983 --> 00:15:20,186 +and drill into detail. + +308 +00:15:20,186 --> 00:15:21,788 +When using a push transition, + +309 +00:15:21,788 --> 00:15:23,589 +it's really important +that the tab bar + +310 +00:15:23,589 --> 00:15:27,860 +remains persistently anchored +to the bottom of the screen. + +311 +00:15:27,860 --> 00:15:29,595 +As we talked about before, + +312 +00:15:29,595 --> 00:15:31,230 +this is one of the biggest +selling points + +313 +00:15:31,230 --> 00:15:32,799 +of a tabbed navigation. + +314 +00:15:32,799 --> 00:15:34,133 +It's consistent. + +315 +00:15:34,133 --> 00:15:36,602 +It gives access +to core areas of your app + +316 +00:15:36,602 --> 00:15:38,704 +because it's always available. + +317 +00:15:38,704 --> 00:15:40,540 +This means people +can explore content + +318 +00:15:40,540 --> 00:15:42,608 +at different hierarchies. + +319 +00:15:42,608 --> 00:15:44,977 +As views push in, +it feels natural + +320 +00:15:44,977 --> 00:15:48,614 +to swipe left to right to go +back to where you came from + +321 +00:15:48,614 --> 00:15:50,583 +without losing access +to hierarchies + +322 +00:15:50,583 --> 00:15:54,887 +in other tabs where your state +should be preserved. + +323 +00:15:54,887 --> 00:15:57,456 +Next, it's important to use +the navigation bar + +324 +00:15:57,456 --> 00:16:00,193 +at the top of the screen +with appropriate labels + +325 +00:16:00,193 --> 00:16:02,428 +to orient people +in your hierarchy. + +326 +00:16:02,428 --> 00:16:04,931 +Let me show you. + +327 +00:16:04,931 --> 00:16:06,532 +As I drill into content + +328 +00:16:06,532 --> 00:16:08,601 +and move through +my information hierarchy, + +329 +00:16:08,601 --> 00:16:12,004 +notice how the back button +in the navigation bar changes + +330 +00:16:12,004 --> 00:16:16,375 +to reflect the title +of the screen I just came from. + +331 +00:16:16,375 --> 00:16:19,745 +This is helpful as I navigate +further into an app + +332 +00:16:19,745 --> 00:16:22,448 +by scrolling screens +and drilling in, + +333 +00:16:22,448 --> 00:16:24,851 +so I never have to remember +where I came from + +334 +00:16:24,851 --> 00:16:27,453 +or how to get back there, +because the back button + +335 +00:16:27,453 --> 00:16:31,958 +can indicate the level up +in the hierarchy. + +336 +00:16:31,958 --> 00:16:34,794 +The other place to use +hierarchical navigation + +337 +00:16:34,794 --> 00:16:40,032 +is in all instances when +a disclosure indicator is used. + +338 +00:16:40,032 --> 00:16:41,434 +A disclosure indicator, + +339 +00:16:41,434 --> 00:16:43,903 +which is also +referred to as a chevron, + +340 +00:16:43,903 --> 00:16:47,840 +points in the direction you're +expected to transition to. + +341 +00:16:47,840 --> 00:16:50,243 +When a chevron initiates +a different transition, + +342 +00:16:50,243 --> 00:16:52,812 +there's a disconnect +between what the UI represents + +343 +00:16:52,812 --> 00:16:55,414 +and the interaction +that follows. + +344 +00:16:55,414 --> 00:16:59,886 +The concept of pushing maps to +our mental model of progression. + +345 +00:16:59,886 --> 00:17:02,989 +In western cultures, +we read left to right, + +346 +00:17:02,989 --> 00:17:05,324 +and that direction +indicates progress. + +347 +00:17:05,324 --> 00:17:08,828 +But in right to left languages, +such as Arabic and Hebrew, + +348 +00:17:08,828 --> 00:17:13,266 +the mental model of progress is +flowing in the other direction. + +349 +00:17:13,266 --> 00:17:15,835 +If your app supports +right to left languages, + +350 +00:17:15,835 --> 00:17:17,970 +then the transition +of pushing is flipped + +351 +00:17:17,970 --> 00:17:22,975 +to create an association +with the flow of content. + +352 +00:17:22,975 --> 00:17:26,545 +The last consideration for when +to use hierarchical navigation + +353 +00:17:26,545 --> 00:17:28,748 +is about the context +of the workflow, + +354 +00:17:28,748 --> 00:17:31,017 +such as when someone +is navigating frequently + +355 +00:17:31,017 --> 00:17:33,753 +between content. + +356 +00:17:33,753 --> 00:17:36,155 +If you're presenting a workflow +that you expect people + +357 +00:17:36,155 --> 00:17:40,059 +to interact with frequently +by toggling between views often, + +358 +00:17:40,059 --> 00:17:41,928 +app switching +during the workflow, + +359 +00:17:41,928 --> 00:17:45,898 +or spending significant time +in the view, then use a push. + +360 +00:17:45,898 --> 00:17:48,267 +A familiar example +is the Messages app. + +361 +00:17:48,267 --> 00:17:50,636 +Even though the hierarchy +is relatively flat, + +362 +00:17:50,636 --> 00:17:53,205 +I can easily move +in and out of the messages + +363 +00:17:53,205 --> 00:17:55,308 +with a push transition. + +364 +00:17:55,308 --> 00:17:58,711 +If each message wasn't a push, +but instead a modal, + +365 +00:17:58,711 --> 00:18:00,546 +it would be difficult +to seamlessly move + +366 +00:18:00,546 --> 00:18:02,415 +between different chats. + +367 +00:18:02,415 --> 00:18:05,651 +Messaging should feel fluid, +but dismissing a modal + +368 +00:18:05,651 --> 00:18:08,487 +when it's not relevant +makes people have to think about + +369 +00:18:08,487 --> 00:18:11,657 +leaving the screen, +and that feels overcomplicated. + +370 +00:18:11,657 --> 00:18:13,993 +Pushing allows +frictionless transition + +371 +00:18:13,993 --> 00:18:17,263 +between core areas of an app. + +372 +00:18:17,263 --> 00:18:20,433 +That is an overview +of hierarchical navigation. + +373 +00:18:20,433 --> 00:18:21,701 +Let's review. + +374 +00:18:21,701 --> 00:18:24,437 +Primarily, push transitions +are used to traverse + +375 +00:18:24,437 --> 00:18:26,806 +an app's hierarchy. + +376 +00:18:26,806 --> 00:18:29,208 +Always keep the tab bar +persistently anchored + +377 +00:18:29,208 --> 00:18:31,911 +to the bottom of the screen. + +378 +00:18:31,911 --> 00:18:34,280 +Use the navigation bar +of each screen to reflect + +379 +00:18:34,280 --> 00:18:38,918 +a clear title and back label to +orient people in your hierarchy. + +380 +00:18:38,918 --> 00:18:42,455 +Use a push when a disclosure +indicator is present. + +381 +00:18:42,455 --> 00:18:44,890 +And when workflows require +navigating frequently + +382 +00:18:44,890 --> 00:18:47,326 +between content. + +383 +00:18:47,326 --> 00:18:49,695 +Hierarchical navigation +is a very common + +384 +00:18:49,695 --> 00:18:51,797 +and a relatively +simple interaction, + +385 +00:18:51,797 --> 00:18:56,635 +so this transition is likely to +be used frequently in your app. + +386 +00:18:56,635 --> 00:19:00,773 +However, modal presentations +are more about a context shift. + +387 +00:19:00,773 --> 00:19:03,809 +It's about isolating someone +into a focused workflow + +388 +00:19:03,809 --> 00:19:05,711 +or self-contained task. + +389 +00:19:05,711 --> 00:19:07,713 +When using modals on iOS, + +390 +00:19:07,713 --> 00:19:11,150 +always present them +from the bottom of the screen. + +391 +00:19:11,150 --> 00:19:13,552 +A modal interrupts +information hierarchy. + +392 +00:19:13,552 --> 00:19:17,823 +It comes from the bottom of the +screen to cover your tab bar. + +393 +00:19:17,823 --> 00:19:20,526 +This prevents people from +drilling further into your app. + +394 +00:19:20,526 --> 00:19:23,262 +And it's an intentional +disruption because the purpose + +395 +00:19:23,262 --> 00:19:27,400 +is to reinforce focus. + +396 +00:19:27,400 --> 00:19:30,736 +So now you may be wondering, +what is a self-contained task? + +397 +00:19:30,736 --> 00:19:34,073 +Let's talk about +three broad examples. + +398 +00:19:34,073 --> 00:19:37,543 +You can use modal presentations +for a simple task, + +399 +00:19:37,543 --> 00:19:41,447 +a multi-step task, +or for full-screen content. + +400 +00:19:41,447 --> 00:19:45,551 +I'll share an example of each. + +401 +00:19:45,551 --> 00:19:47,987 +First, use modality +for a workflow + +402 +00:19:47,987 --> 00:19:50,523 +that requires accomplishing +a simple task, + +403 +00:19:50,523 --> 00:19:53,926 +such as creating an event +or setting a reminder. + +404 +00:19:53,926 --> 00:19:55,628 +Creating a reminder requires + +405 +00:19:55,628 --> 00:19:58,564 +I edit and modify +the entry fields. + +406 +00:19:58,564 --> 00:20:00,833 +Locking in focus +while doing this helps someone + +407 +00:20:00,833 --> 00:20:03,669 +achieve this task +without distraction. + +408 +00:20:03,669 --> 00:20:06,372 +It also minimizes +the possibility of accidentally + +409 +00:20:06,372 --> 00:20:11,911 +abandoning the flow by tapping +on another element or tab. + +410 +00:20:11,911 --> 00:20:14,880 +Second, use modality +for a workflow that accommodates + +411 +00:20:14,880 --> 00:20:16,749 +a complicated task. + +412 +00:20:16,749 --> 00:20:18,784 +This is potentially +multiple steps + +413 +00:20:18,784 --> 00:20:21,987 +such as adding +a transit card to the wallet. + +414 +00:20:21,987 --> 00:20:24,256 +It may seem counterintuitive +to use a modal + +415 +00:20:24,256 --> 00:20:27,293 +for a complicated task, +but remember, + +416 +00:20:27,293 --> 00:20:30,763 +the purpose is to reinforce +focus by hiding the tab bar + +417 +00:20:30,763 --> 00:20:33,365 +and preventing people +from moving around the app + +418 +00:20:33,365 --> 00:20:37,336 +before the task is complete +or canceled. + +419 +00:20:37,336 --> 00:20:40,973 +And third, use a modal +when viewing an article, video, + +420 +00:20:40,973 --> 00:20:43,909 +or full-screen content +that requires minimal + +421 +00:20:43,909 --> 00:20:45,811 +additional navigation. + +422 +00:20:45,811 --> 00:20:48,214 +When in the Fitness app +starting a workout, + +423 +00:20:48,214 --> 00:20:50,116 +which is presented as a video, + +424 +00:20:50,116 --> 00:20:54,720 +is a great scenario +for a modal presentation. + +425 +00:20:54,720 --> 00:20:57,456 +In the hierarchical section, +we talked about the importance + +426 +00:20:57,456 --> 00:20:59,992 +of the navigation bar +to orient people. + +427 +00:20:59,992 --> 00:21:03,996 +It's equally important +with modal presentations. + +428 +00:21:03,996 --> 00:21:06,098 +When looking at +the anatomy of a modal, + +429 +00:21:06,098 --> 00:21:09,702 +think about the navigation bar +as a guide for wayfinding. + +430 +00:21:09,702 --> 00:21:13,105 +The use of labels and actions +can make people feel confident + +431 +00:21:13,105 --> 00:21:15,908 +about where they are +and what actions they can take + +432 +00:21:15,908 --> 00:21:19,145 +to go other places. + +433 +00:21:19,145 --> 00:21:22,581 +A title helps orient people +to the content of the screen, + +434 +00:21:22,581 --> 00:21:25,618 +such as "New Itinerary." + +435 +00:21:25,618 --> 00:21:28,487 +The right label is intended +to be the preferred action, + +436 +00:21:28,487 --> 00:21:33,225 +so it's often seen in a bolder +font to emphasize importance. + +437 +00:21:33,225 --> 00:21:36,095 +The label itself is a concise, +affirmative verb + +438 +00:21:36,095 --> 00:21:39,431 +that tells me exactly +what to expect when tapped. + +439 +00:21:39,431 --> 00:21:41,634 +The preferred action +dismisses the modal + +440 +00:21:41,634 --> 00:21:45,571 +and the previous state +of the app should be preserved. + +441 +00:21:45,571 --> 00:21:48,607 +If there is not yet input +or interaction on the modal, + +442 +00:21:48,607 --> 00:21:51,377 +then having the preferred +action be inactive + +443 +00:21:51,377 --> 00:21:53,779 +is a good way to clarify +that fields are required + +444 +00:21:53,779 --> 00:21:57,983 +in order to save or continue. + +445 +00:21:57,983 --> 00:22:00,786 +If you have a preferred action, +then using the left action + +446 +00:22:00,786 --> 00:22:03,322 +to dismiss the modal +with "Cancel" + +447 +00:22:03,322 --> 00:22:06,025 +clearly indicates that I'm +abandoning the workflow. + +448 +00:22:06,025 --> 00:22:08,994 +If I've entered any information +before tapping Cancel, + +449 +00:22:08,994 --> 00:22:12,097 +this is a good time to display +an alert, or an action sheet + +450 +00:22:12,097 --> 00:22:14,200 +as you see here. + +451 +00:22:14,200 --> 00:22:16,735 +Letting someone know that +they will lose the data + +452 +00:22:16,735 --> 00:22:18,804 +if continuing to cancel. + +453 +00:22:18,804 --> 00:22:21,240 +But if I haven't +interacted with the UI, + +454 +00:22:21,240 --> 00:22:25,711 +tapping Cancel should simply +dismiss the modal. + +455 +00:22:25,711 --> 00:22:29,248 +Use close symbols sparingly and +only when modal presentations + +456 +00:22:29,248 --> 00:22:33,452 +require minimal interaction +and no text input. + +457 +00:22:33,452 --> 00:22:36,121 +Sometimes, +you'll see an "X" in a modal + +458 +00:22:36,121 --> 00:22:38,857 +as the primary way to dismiss, +such as this article + +459 +00:22:38,857 --> 00:22:41,360 +from the Today tab +in the App Store. + +460 +00:22:41,360 --> 00:22:44,797 +The close symbol works here +because there is no user input, + +461 +00:22:44,797 --> 00:22:46,532 +so the subtle dismiss action + +462 +00:22:46,532 --> 00:22:49,134 +helps someone focus +on the content. + +463 +00:22:49,134 --> 00:22:52,104 +Here's an example of how the +close symbol can be problematic + +464 +00:22:52,104 --> 00:22:55,741 +in a modal that requires +input and interaction. + +465 +00:22:55,741 --> 00:22:58,811 +After I select a filter, +if I tap close, + +466 +00:22:58,811 --> 00:23:01,614 +will the selections be +applied or canceled? + +467 +00:23:01,614 --> 00:23:04,750 +Without a clearly labeled +action, people will wonder, + +468 +00:23:04,750 --> 00:23:07,219 +"What happens if I tap close?" + +469 +00:23:07,219 --> 00:23:10,389 +So keep in mind that using +labels in the navigation bar + +470 +00:23:10,389 --> 00:23:12,958 +is usually preferred, +as it's more affirming + +471 +00:23:12,958 --> 00:23:15,995 +and the actions +are more explicit. + +472 +00:23:15,995 --> 00:23:19,465 +Lastly, we try to limit +modals over modals + +473 +00:23:19,465 --> 00:23:23,602 +because it can feel +jarring and overcomplicated. + +474 +00:23:23,602 --> 00:23:25,904 +It's worth calling out, +that a modal view itself + +475 +00:23:25,904 --> 00:23:29,375 +can support subviews +and transitions when relevant. + +476 +00:23:29,375 --> 00:23:31,977 +I mentioned earlier +that this is an edit view, + +477 +00:23:31,977 --> 00:23:35,314 +meaning that the text fields +and table cells have selections + +478 +00:23:35,314 --> 00:23:37,416 +and input which are intended + +479 +00:23:37,416 --> 00:23:41,120 +to be interacted with +and not just viewed. + +480 +00:23:41,120 --> 00:23:43,722 +For example, +I can tap on a table cell + +481 +00:23:43,722 --> 00:23:46,859 +of one of my friends +I've added to the itinerary. + +482 +00:23:46,859 --> 00:23:50,262 +You expect this to push because +it has a chevron, remember? + +483 +00:23:50,262 --> 00:23:53,465 +When I push in, this view may +show information about Kate + +484 +00:23:53,465 --> 00:23:57,903 +and give me the option to remove +her from the itinerary. + +485 +00:23:57,903 --> 00:24:01,240 +But the labels "Add People" +and "Upload Photo" are in + +486 +00:24:01,240 --> 00:24:05,811 +tint color, which indicates +that these actions are tappable. + +487 +00:24:05,811 --> 00:24:07,146 +In each of these scenarios, + +488 +00:24:07,146 --> 00:24:09,448 +they're workflows +within workflows. + +489 +00:24:09,448 --> 00:24:11,483 +I started by adding +an itinerary, + +490 +00:24:11,483 --> 00:24:15,287 +now I'm adding +a photo to the itinerary. + +491 +00:24:15,287 --> 00:24:17,723 +Uploading a photo +is a workflow that includes + +492 +00:24:17,723 --> 00:24:19,725 +quite a bit of interaction, + +493 +00:24:19,725 --> 00:24:23,796 +like scrolling through my camera +roll and selecting a new photo. + +494 +00:24:23,796 --> 00:24:27,066 +I would also define +this as a self-contained task. + +495 +00:24:27,066 --> 00:24:30,102 +Once a photo is chosen, +that modal is dismissed + +496 +00:24:30,102 --> 00:24:34,073 +and I'm back to the first modal +of the new itinerary. + +497 +00:24:34,073 --> 00:24:36,575 +Try to limit +multiple modality stacks, + +498 +00:24:36,575 --> 00:24:38,210 +but sometimes they're required + +499 +00:24:38,210 --> 00:24:42,381 +to drive consistency +and focus in subviews. + +500 +00:24:42,381 --> 00:24:46,385 +That is an overview +of modal presentation on iOS. + +501 +00:24:46,385 --> 00:24:49,388 +Modals should present +from the bottom of the screen. + +502 +00:24:49,388 --> 00:24:51,824 +They can be used +for three types of tasks: + +503 +00:24:51,824 --> 00:24:55,260 +simple, multi-step, +or full-screen. + +504 +00:24:55,260 --> 00:25:00,132 +Use the preferred and cancel +actions in the navigation bar. + +505 +00:25:00,132 --> 00:25:04,036 +Use the close symbol for content +with minimal interaction. + +506 +00:25:04,036 --> 00:25:06,972 +And limit modals over modals. + +507 +00:25:06,972 --> 00:25:09,341 +I hope this deep dive +was helpful for you. + +508 +00:25:09,341 --> 00:25:12,044 +When designing navigation +for your iOS apps, + +509 +00:25:12,044 --> 00:25:14,580 +think about how +content is organized, + +510 +00:25:14,580 --> 00:25:16,582 +how people interact +with your functionality, + +511 +00:25:16,582 --> 00:25:20,185 +and how best to represent it +in relation to your hierarchy. + +512 +00:25:20,185 --> 00:25:23,255 +This way, people can easily +access and interact + +513 +00:25:23,255 --> 00:25:25,691 +with all of your app's +amazing features. + +514 +00:25:25,691 --> 00:25:26,792 +Thanks for listening. + +515 +00:25:26,792 --> 00:25:30,529 +♪ + diff --git a/eng/2022 Session 10002 Create macOS or Linux virtual machines en.srt b/eng/2022 Session 10002 Create macOS or Linux virtual machines en.srt new file mode 100644 index 0000000..355a6ae --- /dev/null +++ b/eng/2022 Session 10002 Create macOS or Linux virtual machines en.srt @@ -0,0 +1,1919 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,009 --> 00:00:14,014 +Benjamin Poulain: Hi everyone, and welcome +to our session about virtualization. + +3 +00:00:14,047 --> 00:00:17,451 +This is what we are going +to do together today. + +4 +00:00:17,484 --> 00:00:22,222 +We'll see how you can run macOS +and Linux inside virtual machines, + +5 +00:00:22,256 --> 00:00:24,258 +on Apple silicon. + +6 +00:00:24,291 --> 00:00:28,896 +By the end of this session, you will +be able to do the same on your own Mac. + +7 +00:00:28,929 --> 00:00:31,798 +This may seem a little ambitious, +but stick with us, + +8 +00:00:31,832 --> 00:00:33,567 +and we'll do it together. + +9 +00:00:33,600 --> 00:00:35,502 +Here is our agenda for today. + +10 +00:00:35,536 --> 00:00:38,739 +We will start with an overview +of virtualization technologies, + +11 +00:00:38,772 --> 00:00:43,744 +and we'll see how to use Virtualization +framework to build virtual machines. + +12 +00:00:43,777 --> 00:00:47,114 +Then we'll do a deep dive into macOS. + +13 +00:00:47,147 --> 00:00:51,919 +We'll see how we can set up a virtual Mac +and install macOS into it. + +14 +00:00:51,952 --> 00:00:56,423 +And finally, we'll do a second deep dive, +this time into Linux. + +15 +00:00:56,456 --> 00:00:58,825 +We'll see how +to run full Linux distributions + +16 +00:00:58,859 --> 00:01:01,261 +and some cool new features. + +17 +00:01:01,295 --> 00:01:03,830 +Let’s get started with the overview. + +18 +00:01:03,864 --> 00:01:08,068 +We'll first look into the stack +that enables virtualization. + +19 +00:01:08,101 --> 00:01:10,904 +It all starts with hardware. + +20 +00:01:10,938 --> 00:01:14,608 +Apple silicon has special hardware +that enables the virtualization + +21 +00:01:14,641 --> 00:01:16,143 +of CPUs and memory. + +22 +00:01:16,176 --> 00:01:20,848 +This means you can run multiple +operating systems on top of a single SoC. + +23 +00:01:20,881 --> 00:01:24,084 +Next, we need software +to take advantage of this hardware. + +24 +00:01:24,117 --> 00:01:27,421 +And this is built right +into the macOS kernel. + +25 +00:01:27,454 --> 00:01:30,424 +You no longer need to write +kernel extensions, or KEXTs. + +26 +00:01:30,457 --> 00:01:32,993 +It's all built in. + +27 +00:01:33,026 --> 00:01:35,162 +To use those capabilities +from your application, + +28 +00:01:35,195 --> 00:01:37,698 +you can use Hypervisor framework. + +29 +00:01:37,731 --> 00:01:43,537 +Hypervisor framework is a low-level API +that lets you virtualize CPUs and memory. + +30 +00:01:43,570 --> 00:01:46,206 +But, because it's +a low-level framework, + +31 +00:01:46,240 --> 00:01:50,377 +you need to write every single detail +of the virtual environment. + +32 +00:01:50,410 --> 00:01:53,547 +Oftentimes, +we want to run full operating systems. + +33 +00:01:53,580 --> 00:01:58,919 +For this, there is a higher-level API, +which is Virtualization framework. + +34 +00:01:58,952 --> 00:02:02,022 +Virtualization framework enables +the creation of virtual machines + +35 +00:02:02,055 --> 00:02:08,061 +running macOS on Apple silicon +or Linux on both Apple silicon and Intel. + +36 +00:02:08,095 --> 00:02:11,064 +Today, our session will focus +on Virtualization framework. + +37 +00:02:11,098 --> 00:02:14,668 +When using Virtualization framework, +we'll deal with two kinds of objects. + +38 +00:02:14,701 --> 00:02:17,070 +The first kind are configuration objects. + +39 +00:02:17,104 --> 00:02:20,107 +They define all the properties +of our virtual machines. + +40 +00:02:20,140 --> 00:02:22,743 +The second kind +are virtual machine objects. + +41 +00:02:22,776 --> 00:02:27,548 +Those objects abstract virtual machines +and how to interact with them. + +42 +00:02:27,581 --> 00:02:31,185 +We'll start +with looking at the configuration. + +43 +00:02:31,218 --> 00:02:34,221 +The configuration defines the hardware. + +44 +00:02:34,254 --> 00:02:38,425 +Creating a configuration is +like configuring a Mac on the Apple Store. + +45 +00:02:38,458 --> 00:02:42,863 +We define how many CPUs we want, +how much memory, what kind of devices. + +46 +00:02:42,896 --> 00:02:45,532 +We can start from a simple configuration. + +47 +00:02:45,566 --> 00:02:47,801 +We can add a display, +and we get to see the content. + +48 +00:02:47,835 --> 00:02:50,404 +We can add a keyboard, and we can type. + +49 +00:02:50,437 --> 00:02:53,707 +We can add a trackpad, +and we can interact with the UI. + +50 +00:02:53,740 --> 00:02:57,211 +Configuring a virtual machine +is just like that. + +51 +00:02:57,244 --> 00:02:59,313 +But since we are dealing +with virtual machines, + +52 +00:02:59,346 --> 00:03:01,515 +we'll do this in code. + +53 +00:03:01,548 --> 00:03:05,319 +Let’s see how we can write +the configuration in Swift. + +54 +00:03:05,352 --> 00:03:07,921 +Defining the hardware is very simple. + +55 +00:03:07,955 --> 00:03:12,025 +We start with an object of type +VZVirtualMachineConfiguration. + +56 +00:03:12,059 --> 00:03:15,762 +This is the root object +of all configurations. + +57 +00:03:15,796 --> 00:03:19,700 +Next, we define how many CPUs +our machine should have. + +58 +00:03:19,733 --> 00:03:22,102 +Here we give four CPUs. + +59 +00:03:22,135 --> 00:03:24,571 +Then, we set how much memory we want. + +60 +00:03:24,605 --> 00:03:27,975 +In this case, +we give four gigabytes of memory. + +61 +00:03:28,008 --> 00:03:31,578 +Finally, we define the devices +our machine will have. + +62 +00:03:31,612 --> 00:03:34,515 +In this example, +we set a single storage device, + +63 +00:03:34,548 --> 00:03:38,952 +the disk to boot from, +and a pointing device, like a mouse. + +64 +00:03:38,986 --> 00:03:40,721 +There are many devices available. + +65 +00:03:40,754 --> 00:03:44,558 +The ones you set up depend +on the problem you want to solve. + +66 +00:03:44,591 --> 00:03:46,059 +Now we've seen the configuration. + +67 +00:03:46,093 --> 00:03:48,629 +It starts +with VZVirtualMachineConfiguration, + +68 +00:03:48,662 --> 00:03:53,333 +on which we add the CPUs, +the memory, and the devices. + +69 +00:03:53,367 --> 00:03:56,970 +Next, we'll look +into the virtual machine objects. + +70 +00:03:58,205 --> 00:04:00,874 +After we have configured our Mac, +we get it by the mail. + +71 +00:04:00,908 --> 00:04:03,677 +It's time to unbox it and start it. + +72 +00:04:03,710 --> 00:04:06,079 +But since we are dealing +with virtual machine, + +73 +00:04:06,113 --> 00:04:08,515 +we need to do that in code. + +74 +00:04:08,549 --> 00:04:11,585 +Let’s see how we can do it in Swift. + +75 +00:04:11,618 --> 00:04:14,688 +First, we'll create an instance +of VZVirtualMachine + +76 +00:04:14,721 --> 00:04:16,223 +from our configuration. + +77 +00:04:16,256 --> 00:04:20,627 +A VZVirtualMachine abstracts +an instance of the virtual hardware. + +78 +00:04:20,661 --> 00:04:23,397 +Now that we have the virtual machine, +we can operate on it. + +79 +00:04:23,430 --> 00:04:27,901 +For example, in this case, +we call start() to start it. + +80 +00:04:27,935 --> 00:04:30,237 +We'll often want to interact +with our virtual machines. + +81 +00:04:30,270 --> 00:04:33,507 +For this, +we have other objects to help us. + +82 +00:04:33,540 --> 00:04:36,410 +For example, +if we want to show our virtual display, + +83 +00:04:36,443 --> 00:04:39,680 +we can use an object +of type VZVirtualMachineView. + +84 +00:04:39,713 --> 00:04:41,648 +We start by creating a view. + +85 +00:04:41,682 --> 00:04:45,619 +Then we set our virtual machine as +the virtualMachine property on the view, + +86 +00:04:45,652 --> 00:04:47,154 +and it's ready. + +87 +00:04:47,187 --> 00:04:51,525 +Now we can use this VZVirtualMachineView +object like any NSView. + +88 +00:04:51,558 --> 00:04:55,395 +We can integrate it in our app +to see the content of the virtual machine. + +89 +00:04:56,964 --> 00:04:59,967 +To wrap up, +we've seen the configuration. + +90 +00:05:00,000 --> 00:05:03,770 +The configuration starts +with VZVirtualMachineConfiguration, + +91 +00:05:03,804 --> 00:05:08,208 +from which we define the CPUs, +memory, and our devices. + +92 +00:05:08,242 --> 00:05:10,878 +From the configuration, +we will create a virtual machine, + +93 +00:05:10,911 --> 00:05:13,480 +and we will use virtual machine objects. + +94 +00:05:13,514 --> 00:05:17,651 +We've seen VZVirtualMachine +to abstract the VM itself, + +95 +00:05:17,684 --> 00:05:20,554 +VZVirtualMachineView to display content, + +96 +00:05:20,587 --> 00:05:23,624 +and there are other objects +that can help us use the VM. + +97 +00:05:23,657 --> 00:05:26,927 +We have seen that the configuration +gives a lot of flexibility + +98 +00:05:26,960 --> 00:05:29,162 +in how we define virtual machines. + +99 +00:05:29,196 --> 00:05:33,667 +Unfortunately, there are +too many features to cover in one session. + +100 +00:05:33,700 --> 00:05:37,437 +In this session, we will look +into some of the core capabilities. + +101 +00:05:37,471 --> 00:05:39,873 +For everything else, +we have documentation, + +102 +00:05:39,907 --> 00:05:42,943 +and I invite you to check it out. + +103 +00:05:42,976 --> 00:05:46,647 +In the overview, +we just saw how to build virtual machines. + +104 +00:05:46,680 --> 00:05:50,684 +Now it is time to look into how we can +run a full operating system in them. + +105 +00:05:50,717 --> 00:05:52,953 +And we will start with macOS. + +106 +00:05:52,986 --> 00:05:56,790 +Virtualization framework supports macOS +on Apple silicon. + +107 +00:05:56,823 --> 00:05:59,126 +When we built Virtualization framework +on Apple silicon, + +108 +00:05:59,159 --> 00:06:03,163 +we've developed macOS +and Virtualization framework together. + +109 +00:06:03,197 --> 00:06:05,966 +What this gives us +is incredible efficiency + +110 +00:06:05,999 --> 00:06:08,735 +when running macOS +inside virtual machines. + +111 +00:06:08,769 --> 00:06:11,038 +Here is what we are going to see: + +112 +00:06:11,071 --> 00:06:12,973 +First, we will look into what we need + +113 +00:06:13,006 --> 00:06:16,376 +to turn a virtual machine +into a virtual Mac. + +114 +00:06:16,410 --> 00:06:20,914 +Then we'll look into the steps +to install macOS on our virtual Mac. + +115 +00:06:20,948 --> 00:06:25,352 +Next, we'll see some +of the special devices we have for macOS. + +116 +00:06:25,385 --> 00:06:28,822 +And finally, we will look +into a very important use case, + +117 +00:06:28,856 --> 00:06:32,826 +which is sharing files between +the host system and the virtual Mac. + +118 +00:06:34,261 --> 00:06:36,830 +Let’s start with the configuration. + +119 +00:06:36,864 --> 00:06:40,167 +We have seen before how +to build a generic virtual machine. + +120 +00:06:40,200 --> 00:06:45,706 +Now we want to add the special properties +that will make a virtual machine a Mac. + +121 +00:06:45,739 --> 00:06:48,976 +So how do we make a virtual Mac? + +122 +00:06:49,009 --> 00:06:51,879 +First, we will define a special platform. + +123 +00:06:51,912 --> 00:06:55,315 +A platform is an object +that holds all the properties + +124 +00:06:55,349 --> 00:06:57,684 +of a particular type of virtual machine. + +125 +00:06:57,718 --> 00:07:02,122 +There are three properties that are unique +to the virtual Mac hardware. + +126 +00:07:02,155 --> 00:07:04,358 +First, we have the hardware model. + +127 +00:07:04,391 --> 00:07:09,196 +The hardware model specifies +which version of the virtual Mac we want. + +128 +00:07:09,229 --> 00:07:11,265 +Second, there is the auxiliary storage. + +129 +00:07:11,298 --> 00:07:15,969 +The auxiliary storage is a form +of non-volatile memory used by the system. + +130 +00:07:16,003 --> 00:07:19,239 +And third, +there is the machine identifier. + +131 +00:07:19,273 --> 00:07:22,676 +The machine identifier is +a unique number representing the machine, + +132 +00:07:22,709 --> 00:07:26,113 +just like a physical Mac +has a unique serial number. + +133 +00:07:26,146 --> 00:07:29,883 +Once we have the platform, we have +all the pieces to describe the hardware, + +134 +00:07:29,917 --> 00:07:34,621 +but we need one more piece, +which is a way to boot macOS. + +135 +00:07:34,655 --> 00:07:36,890 +For this, we will use +a special boot loader, + +136 +00:07:36,924 --> 00:07:39,126 +the macOS boot loader. + +137 +00:07:39,159 --> 00:07:43,063 +Let’s see how to do all of this in Swift. + +138 +00:07:43,096 --> 00:07:44,932 +We start from the same base as before. + +139 +00:07:44,965 --> 00:07:48,635 +This code is what +we have seen in the overview. + +140 +00:07:48,669 --> 00:07:52,506 +Then we create +a VZMacPlatformConfiguration. + +141 +00:07:52,539 --> 00:07:56,043 +This is our platform object +for virtual Macs. + +142 +00:07:56,076 --> 00:07:58,011 +We need a hardware model for this Mac. + +143 +00:07:58,045 --> 00:08:01,315 +Here we use one we previously saved. + +144 +00:08:01,348 --> 00:08:03,650 +In virtual machines, +the auxiliary storage is backed + +145 +00:08:03,684 --> 00:08:06,119 +by a file on the local filesystem. + +146 +00:08:06,153 --> 00:08:09,957 +Here, we initialize our auxiliary storage +from a file URL. + +147 +00:08:09,990 --> 00:08:13,760 +For the unique identifier, +we initialize a VZMacMachineIdentifier + +148 +00:08:13,794 --> 00:08:15,762 +from one we previously saved. + +149 +00:08:15,796 --> 00:08:19,633 +For a new install, +we can also create a new identifier. + +150 +00:08:19,666 --> 00:08:23,470 +We have set all three properties. +Our platform is ready. + +151 +00:08:23,504 --> 00:08:27,107 +All we have to do is set it +on the configuration object. + +152 +00:08:27,140 --> 00:08:31,011 +This gives us the hardware. +Next we need a way to boot it. + +153 +00:08:31,912 --> 00:08:36,383 +To do that, we set up the boot loader +with VZMacBootLoader. + +154 +00:08:36,416 --> 00:08:38,719 +Now our machine is ready to boot. + +155 +00:08:38,752 --> 00:08:43,123 +What we have done so far is define +the virtual Mac and how to start it. + +156 +00:08:43,156 --> 00:08:45,125 +But we still need to get software on it, + +157 +00:08:45,158 --> 00:08:48,395 +which brings us to the installation. + +158 +00:08:48,428 --> 00:08:51,265 +Installing macOS is done in three steps. + +159 +00:08:51,298 --> 00:08:54,501 +First, we need to download +a restore image with the version + +160 +00:08:54,535 --> 00:08:56,703 +of macOS we want to install. + +161 +00:08:56,737 --> 00:08:58,939 +Then we need to create a configuration + +162 +00:08:58,972 --> 00:09:01,942 +that is compatible +with that version of macOS. + +163 +00:09:01,975 --> 00:09:04,645 +And finally, +we’ll install our restore image + +164 +00:09:04,678 --> 00:09:07,114 +in the compatible virtual machine. + +165 +00:09:07,147 --> 00:09:10,117 +So first, +we need to download a restore image. + +166 +00:09:10,150 --> 00:09:12,753 +You can download restore images +from the developer website, + +167 +00:09:12,786 --> 00:09:15,989 +but Virtualization can also help us. + +168 +00:09:16,023 --> 00:09:19,927 +You can call +VZMacOSRestoreImage.latestSupported + +169 +00:09:19,960 --> 00:09:25,032 +to get a restore image object +for the latest stable version of macOS. + +170 +00:09:25,065 --> 00:09:29,570 +This object has a URL property +that we can use to download the file. + +171 +00:09:29,603 --> 00:09:32,739 +Then we want to create a virtual machine +that is compatible + +172 +00:09:32,773 --> 00:09:35,409 +with the version of macOS we downloaded. + +173 +00:09:35,442 --> 00:09:37,811 +Virtualization can also help us here. + +174 +00:09:37,845 --> 00:09:42,249 +We can ask the restore image object +for the configuration requirements. + +175 +00:09:42,282 --> 00:09:44,985 +If the restore image can be run +on the current system, + +176 +00:09:45,018 --> 00:09:47,888 +we get an object listing the requirements. + +177 +00:09:47,921 --> 00:09:50,858 +From the requirements, +we can obtain the hardware model needed + +178 +00:09:50,891 --> 00:09:52,993 +needed to run this version of macOS. + +179 +00:09:53,026 --> 00:09:55,929 +We have seen previously how +to restore a hardware model. + +180 +00:09:55,963 --> 00:09:58,665 +This is how we obtain a new one. + +181 +00:09:59,800 --> 00:10:03,470 +The requirements +also contain two useful properties. + +182 +00:10:03,504 --> 00:10:07,808 +The object can tell us how many CPUs +and how much memory is required + +183 +00:10:07,841 --> 00:10:10,410 +to run this version of macOS. + +184 +00:10:11,345 --> 00:10:14,348 +Finally, we are ready +to start installation. + +185 +00:10:14,381 --> 00:10:18,218 +We start by creating a new virtual machine +from our configuration. + +186 +00:10:18,252 --> 00:10:20,354 +Then we create an installer. + +187 +00:10:20,387 --> 00:10:22,789 +The installer takes two arguments, + +188 +00:10:22,823 --> 00:10:25,392 +the compatible virtual machine we created + +189 +00:10:25,425 --> 00:10:28,662 +and the path +to the restore image we downloaded. + +190 +00:10:28,695 --> 00:10:32,933 +Now we can just call install(), +and voilà, we are ready to run macOS. + +191 +00:10:33,901 --> 00:10:37,104 +Now that we can set up a virtual Mac +and install macOS, + +192 +00:10:37,137 --> 00:10:41,241 +let’s see some +of the special devices for the Mac. + +193 +00:10:41,275 --> 00:10:44,912 +A first cool capability +is GPU acceleration. + +194 +00:10:44,945 --> 00:10:48,582 +We have built a graphic device +that exposes the GPU capabilities + +195 +00:10:48,615 --> 00:10:50,184 +to the virtual Mac. + +196 +00:10:50,217 --> 00:10:53,020 +This means you can run Metal +in the virtual machine, + +197 +00:10:53,053 --> 00:10:56,156 +and get great graphics performance +in macOS. + +198 +00:10:56,190 --> 00:10:58,125 +Let’s see how to set it up. + +199 +00:10:59,626 --> 00:11:02,496 +We start by creating +the graphics device configuration. + +200 +00:11:02,529 --> 00:11:06,333 +Here, we will use +the VZMacGraphicsDeviceConfiguration. + +201 +00:11:06,366 --> 00:11:08,402 +Next, we want to give it a display. + +202 +00:11:08,435 --> 00:11:13,040 +We set up the display +by defining its size and pixel density. + +203 +00:11:13,073 --> 00:11:15,342 +Now our device configuration is ready. + +204 +00:11:15,375 --> 00:11:18,712 +As usual, we set it +on the main configuration object. + +205 +00:11:18,745 --> 00:11:22,249 +We set it as the graphics device +for our virtual machine. + +206 +00:11:23,383 --> 00:11:26,553 +Next, we have a new device +for interacting with the Mac. + +207 +00:11:26,587 --> 00:11:29,890 +In macOS Ventura, +we are adding the Mac trackpad support + +208 +00:11:29,923 --> 00:11:31,558 +to the virtual Mac. + +209 +00:11:31,592 --> 00:11:34,127 +With the new trackpad, +it is possible to use gestures + +210 +00:11:34,161 --> 00:11:37,431 +like rotation, pinch to zoom, and so on. + +211 +00:11:37,464 --> 00:11:40,300 +This new device uses new drivers in macOS. + +212 +00:11:40,334 --> 00:11:42,236 +So to use it, +you will need macOS 13 + +213 +00:11:42,269 --> 00:11:45,572 +both on the host system +and in the virtual machine. + +214 +00:11:45,606 --> 00:11:48,208 +Let’s see how to set it up. + +215 +00:11:48,242 --> 00:11:50,210 +It’s very easy. + +216 +00:11:50,244 --> 00:11:54,181 +We create a new object +of type VZMacTrackpadConfiguration. + +217 +00:11:54,214 --> 00:11:58,218 +Then we set it as the pointing device +on the virtual machine. + +218 +00:11:58,252 --> 00:12:03,557 +Now when we’ll use the view +with our virtual Mac, we can use gestures. + +219 +00:12:03,590 --> 00:12:06,627 +Finally, let’s look into a common use case +for many of us, + +220 +00:12:06,660 --> 00:12:10,998 +sharing files between the host system +and the virtual machine. + +221 +00:12:11,031 --> 00:12:14,635 +In macOS 12, +we introduced the Virtio file-system device + +222 +00:12:14,668 --> 00:12:16,637 +to share files on Linux. + +223 +00:12:16,670 --> 00:12:20,407 +In macOS Ventura, +we are adding support for macOS. + +224 +00:12:20,440 --> 00:12:23,744 +You can now pick folders that you want +to share with the virtual machine. + +225 +00:12:23,777 --> 00:12:26,980 +Any change you make from the host system +is instantly reflected + +226 +00:12:27,014 --> 00:12:29,850 +within the virtual machine +and vice versa. + +227 +00:12:29,883 --> 00:12:32,152 +Let’s see how to set it up. + +228 +00:12:32,186 --> 00:12:37,324 +First, we create a VZShareDirectory +with a directory we want to share. + +229 +00:12:37,357 --> 00:12:39,726 +Then we create a share object. + +230 +00:12:39,760 --> 00:12:43,931 +Here we'll use VZSingleDirectoryShare +to share a single directory. + +231 +00:12:43,964 --> 00:12:48,802 +You can also share multiple directories +by using VZMultipleDirectoryShare. + +232 +00:12:48,836 --> 00:12:51,438 +Now that we have the share, +we need to create a device. + +233 +00:12:51,471 --> 00:12:54,241 +But we will start we something special. + +234 +00:12:54,274 --> 00:12:56,810 +File system devices +are identified by a tag. + +235 +00:12:56,844 --> 00:12:59,913 +In macOS Ventura, +we have added a special tag + +236 +00:12:59,947 --> 00:13:03,217 +to tell the virtual machine +to automount this device. + +237 +00:13:03,250 --> 00:13:07,521 +Here, we take this special tag, +macOSGuestAutomountTag. + +238 +00:13:07,554 --> 00:13:11,325 +Then we create the device +and use our special tag. + +239 +00:13:11,358 --> 00:13:14,995 +We set the share +from the single directory we configured. + +240 +00:13:15,028 --> 00:13:19,266 +And finally, we add the device +to the configuration as usual. + +241 +00:13:19,299 --> 00:13:23,737 +Finally, let’s look +at everything together in a demo. + +242 +00:13:23,770 --> 00:13:26,273 +We start from a basic configuration. + +243 +00:13:26,306 --> 00:13:28,342 +We have a VZVirtualMachineConfiguration + +244 +00:13:28,375 --> 00:13:33,814 +with just CPU, +memory, a keyboard, and a disk. + +245 +00:13:33,847 --> 00:13:35,449 +We want a virtual Mac. + +246 +00:13:35,482 --> 00:13:39,086 +To do that, we need to start +by setting up the platform. + +247 +00:13:39,119 --> 00:13:43,524 +We'll use createMacPlatform +that is defined above to do that. + +248 +00:13:43,557 --> 00:13:46,426 +The second piece +of a virtual Mac is the boot loader. + +249 +00:13:46,460 --> 00:13:49,997 +We need a boot loader +that knows how to boot macOS. + +250 +00:13:50,030 --> 00:13:52,699 +To get that, +we set the platform's boot loader + +251 +00:13:52,733 --> 00:13:55,536 +to VZMacOSBootLoader(). + +252 +00:13:55,569 --> 00:13:58,238 +Next, we want to set up the devices. + +253 +00:13:58,272 --> 00:14:00,507 +We want accelerated graphics. + +254 +00:14:00,541 --> 00:14:04,711 +To get it, we will set up +a VZMacGraphicsConfiguration. + +255 +00:14:04,745 --> 00:14:06,413 +We create the object, + +256 +00:14:06,446 --> 00:14:09,383 +define the display size and pixel density, + +257 +00:14:09,416 --> 00:14:12,186 +and we add it to the configuration. + +258 +00:14:12,219 --> 00:14:14,755 +Next, we want to use the new trackpad. + +259 +00:14:14,788 --> 00:14:17,224 +All we need to do +is set the pointing device + +260 +00:14:17,257 --> 00:14:19,426 +to VZMacTrackpadConfiguration. + +261 +00:14:19,459 --> 00:14:20,694 +That's it. + +262 +00:14:20,727 --> 00:14:24,765 +Now, we could start the VM, +but let's add the cherry on top. + +263 +00:14:24,798 --> 00:14:27,134 +We have seen how we can share directories. + +264 +00:14:27,167 --> 00:14:29,169 +Let's do it here. + +265 +00:14:29,203 --> 00:14:32,406 +We start by creating +the filesystem device configuration. + +266 +00:14:32,439 --> 00:14:37,010 +Here, notice we use the special tag +to automount it into macOS. + +267 +00:14:37,044 --> 00:14:38,879 +Then we define our share. + +268 +00:14:38,912 --> 00:14:42,649 +Here we use a single directory share +from a path on the file system. + +269 +00:14:42,683 --> 00:14:46,019 +Here, we will share this project +we are editing right now. + +270 +00:14:47,254 --> 00:14:50,457 +We add the device to our configuration, +and we are done. + +271 +00:14:51,391 --> 00:14:55,128 +Everything is ready. +We launch our app. + +272 +00:14:55,162 --> 00:14:57,598 +Since we configured +the Mac graphics device, + +273 +00:14:57,631 --> 00:15:01,101 +the VZVirtualMachineView +can show the content. + +274 +00:15:01,134 --> 00:15:04,271 +This is what you see here in the window. + +275 +00:15:04,304 --> 00:15:08,008 +And here it is. +We have configured macOS from scratch. + +276 +00:15:08,041 --> 00:15:11,678 +We can see the shared directory +and the project we were editing right now. + +277 +00:15:11,712 --> 00:15:14,915 +Finally, we will turn our eyes onto Linux. + +278 +00:15:14,948 --> 00:15:17,150 +Virtualization framework has +supported Linux + +279 +00:15:17,184 --> 00:15:20,020 +since the very beginning in macOS Big Sur. + +280 +00:15:20,053 --> 00:15:23,357 +In macOS Ventura, we have +added some pretty cool new features, + +281 +00:15:23,390 --> 00:15:25,359 +and we want +to share some of them with you. + +282 +00:15:26,660 --> 00:15:29,830 +First, we will see how we can +install full Linux distributions, + +283 +00:15:29,863 --> 00:15:33,066 +completely unmodified, +in virtual machines. + +284 +00:15:33,100 --> 00:15:37,604 +Then we will look at a new device +we are adding to show UI from Linux. + +285 +00:15:37,638 --> 00:15:41,375 +And finally, we will look at how +we can take advantage of Rosetta 2 + +286 +00:15:41,408 --> 00:15:44,878 +to run Linux binaries in virtual machines. + +287 +00:15:44,912 --> 00:15:47,181 +Let’s start with installation. + +288 +00:15:47,214 --> 00:15:49,616 +If we wanted to install Linux +on a physical machine, + +289 +00:15:49,650 --> 00:15:52,819 +we'd start by downloading an ISO file +with the installer. + +290 +00:15:52,853 --> 00:15:55,923 +Then we'd erase a flash drive +with the ISO. + +291 +00:15:55,956 --> 00:15:59,893 +And finally, we'd plug the drive +in the computer and boot from it. + +292 +00:15:59,927 --> 00:16:03,363 +When dealing with virtual machines, +we will go through the same flow. + +293 +00:16:03,397 --> 00:16:07,868 +But instead of using a physical USB drive, +we will use a virtual one. + +294 +00:16:07,901 --> 00:16:09,970 +Let’s see how it works. + +295 +00:16:10,003 --> 00:16:14,775 +We start by creating an URL from the path +to the ISO file we downloaded. + +296 +00:16:14,808 --> 00:16:18,779 +Then we create a disk image attachment +from the file. + +297 +00:16:18,812 --> 00:16:24,651 +A disk image attachment represents a piece +of storage that we can attach to a device. + +298 +00:16:24,685 --> 00:16:28,155 +Next, we configure +a virtual storage device. + +299 +00:16:28,188 --> 00:16:30,624 +In this case, we want USB storage, + +300 +00:16:30,657 --> 00:16:35,028 +so we use +VZUSBMassStorageDeviceConfiguration. + +301 +00:16:35,062 --> 00:16:37,030 +Finally, as always, + +302 +00:16:37,064 --> 00:16:39,533 +we add our device +in the main configuration. + +303 +00:16:39,566 --> 00:16:42,736 +Here, the USB device appears +next to another storage device, + +304 +00:16:42,769 --> 00:16:45,706 +the main disk +on which we will install Linux. + +305 +00:16:45,739 --> 00:16:49,343 +Now we have a USB drive, +but we need a way to boot from it. + +306 +00:16:50,310 --> 00:16:54,081 +In macOS Ventura, +we have added support for EFI. + +307 +00:16:54,114 --> 00:16:58,952 +EFI is an industry standard +for booting both ARM and Intel hardware. + +308 +00:16:58,986 --> 00:17:02,556 +We are bringing the same support +to virtual machines. + +309 +00:17:02,589 --> 00:17:05,259 +EFI has a boot discovery mechanism. + +310 +00:17:05,292 --> 00:17:09,496 +What this will allow is discovering +the installer on our USB drive. + +311 +00:17:09,530 --> 00:17:13,100 +EFI looks at each drive +for one that can be booted. + +312 +00:17:13,133 --> 00:17:16,003 +It will find the installer +and start from there. + +313 +00:17:16,036 --> 00:17:19,373 +The installer itself will +tell EFI what drive to use next. + +314 +00:17:19,406 --> 00:17:23,577 +After the installation, +EFI can then start the Linux distribution. + +315 +00:17:23,610 --> 00:17:26,847 +Let’s see how to set up EFI in code. + +316 +00:17:26,880 --> 00:17:31,018 +First, we create a boot loader +of type VZEFIBootLoader. + +317 +00:17:31,051 --> 00:17:35,355 +EFI requires non-volatile memory +to store information between boots. + +318 +00:17:35,389 --> 00:17:38,258 +This is called the EFI variable store. + +319 +00:17:38,292 --> 00:17:40,761 +With virtual machines, +we can back such storage + +320 +00:17:40,794 --> 00:17:42,863 +by a file on the filesystem. + +321 +00:17:42,896 --> 00:17:46,033 +Here, we create +a new variable store from scratch. + +322 +00:17:46,066 --> 00:17:47,568 +Now EFI is ready. + +323 +00:17:47,601 --> 00:17:51,705 +We just need to set it +as the boot loader on the configuration. + +324 +00:17:51,738 --> 00:17:57,010 +Next, we will look into a new capability +for Linux VMs, graphics. + +325 +00:17:57,044 --> 00:18:01,515 +In macOS Ventura, +we have added support for Virtio GPU 2D. + +326 +00:18:01,548 --> 00:18:05,285 +Virtio GPU 2D is a paravirtualized device +that allows Linux + +327 +00:18:05,319 --> 00:18:08,455 +to provide surfaces to the host macOS. + +328 +00:18:08,488 --> 00:18:11,325 +Linux renders the content, +gives the rendered frame + +329 +00:18:11,358 --> 00:18:14,494 +to Virtualization framework, +which can then display it. + +330 +00:18:14,528 --> 00:18:18,765 +You can now show this content +in your app with VZVirtualMachineView + +331 +00:18:18,799 --> 00:18:21,201 +just like on macOS. + +332 +00:18:21,235 --> 00:18:23,237 +Let’s see how to set it up. + +333 +00:18:24,571 --> 00:18:28,542 +Setting up the device is similar +to what we did for macOS. + +334 +00:18:28,575 --> 00:18:32,946 +We start by creating +a VZVirtioGraphicsDeviceConfiguration. + +335 +00:18:32,980 --> 00:18:36,149 +We need to define the size +of our virtual display. + +336 +00:18:36,183 --> 00:18:39,953 +In Virtio terminology, +a virtual display is a "scanout." + +337 +00:18:39,987 --> 00:18:44,892 +So we create one scanout +with the size of the display. + +338 +00:18:44,925 --> 00:18:48,262 +Finally, we set the new device +as the graphics device + +339 +00:18:48,295 --> 00:18:49,963 +of our configuration. + +340 +00:18:49,997 --> 00:18:54,168 +Now our VM is ready to display content +with VZVirtualMachineView. + +341 +00:18:54,201 --> 00:18:58,405 +Next, let’s see everything +together in a demo. + +342 +00:18:58,438 --> 00:19:00,040 +We start from where we left off. + +343 +00:19:00,073 --> 00:19:03,143 +Let's delete the code +that is specific to the Mac. + +344 +00:19:03,177 --> 00:19:05,479 +Then let's change the disk +we are booting from. + +345 +00:19:05,512 --> 00:19:09,950 +We'll swap the path +from our Mac drive to our Linux drive. + +346 +00:19:09,983 --> 00:19:12,386 +Next, we need a boot loader. + +347 +00:19:12,419 --> 00:19:16,156 +We set up EFI with VZEFIBootLoader. + +348 +00:19:16,924 --> 00:19:19,326 +We first create +the EFI boot loader object. + +349 +00:19:19,359 --> 00:19:22,129 +Then we load +the variable store from its file. + +350 +00:19:22,162 --> 00:19:27,401 +And finally, we set EFI +as the boot loader on our configuration. + +351 +00:19:27,434 --> 00:19:31,238 +Now we can boot, +but it'd be nice to show the UI. + +352 +00:19:31,271 --> 00:19:35,008 +Let's add Virtio GPU to our configuration. + +353 +00:19:35,042 --> 00:19:36,643 +We simply create a graphics device + +354 +00:19:36,677 --> 00:19:40,013 +of type +VZVirtioGraphicsDeviceConfiguration. + +355 +00:19:40,047 --> 00:19:43,884 +Then we define a scanout +with the size of the virtual display. + +356 +00:19:43,917 --> 00:19:49,389 +And we set the Virtio GPU as +a graphics device on our configuration. + +357 +00:19:49,423 --> 00:19:52,993 +The last touch +is getting the mouse to work. + +358 +00:19:53,026 --> 00:19:56,330 +We just use a virtual +USB screen coordinate pointer device, + +359 +00:19:56,363 --> 00:19:58,665 +and we'll have a mouse in Linux. + +360 +00:19:58,699 --> 00:20:01,335 +That's it. +We can run the project. + +361 +00:20:01,368 --> 00:20:04,471 +EFI looks at the disk +and finds it bootable. + +362 +00:20:04,505 --> 00:20:09,977 +Then Linux shows the content of the UI +through the Virtio GPU device. + +363 +00:20:10,010 --> 00:20:12,946 +And we can use the mouse +to interact with Linux. + +364 +00:20:12,980 --> 00:20:15,983 +Last but not least, +we'll see how we can take advantage + +365 +00:20:16,016 --> 00:20:19,119 +of the Rosetta 2 technology inside Linux. + +366 +00:20:20,053 --> 00:20:23,490 +For many of us, +we love developing services on our Mac, + +367 +00:20:23,524 --> 00:20:25,025 +but once our work is ready, + +368 +00:20:25,058 --> 00:20:29,162 +the binaries we create may need +to run on x86 servers. + +369 +00:20:29,196 --> 00:20:32,332 +x86 instruction emulation +has been great for this, + +370 +00:20:32,366 --> 00:20:35,035 +but we can do better. + +371 +00:20:35,068 --> 00:20:37,204 +In macOS Ventura, +we are bringing the power + +372 +00:20:37,237 --> 00:20:39,673 +of Rosetta 2 to Linux binaries. + +373 +00:20:40,807 --> 00:20:44,378 +What Rosetta 2 does is translate +the Linux x86-64 binaries + +374 +00:20:44,411 --> 00:20:46,446 +inside your virtual machine. + +375 +00:20:46,480 --> 00:20:49,750 +This means you can run +your favorite ARM Linux distribution, + +376 +00:20:49,783 --> 00:20:53,253 +and its x86-64 apps can run with Rosetta. + +377 +00:20:53,287 --> 00:20:54,755 +And it's fast. + +378 +00:20:54,788 --> 00:20:57,658 +It's the same technology +we have been using on the Mac, + +379 +00:20:57,691 --> 00:21:00,460 +which means +we have incredible performance. + +380 +00:21:00,494 --> 00:21:03,530 +Let’s see how to use it. + +381 +00:21:03,564 --> 00:21:07,000 +First, we need +to give Linux access to Rosetta. + +382 +00:21:07,034 --> 00:21:11,705 +To do this, we use the same file sharing +technology we have seen on macOS. + +383 +00:21:11,738 --> 00:21:15,175 +Instead of sharing a folder, +we use a special kind of object, + +384 +00:21:15,209 --> 00:21:18,912 +a VZLinuxRosettaDirectoryShare. + +385 +00:21:18,946 --> 00:21:24,151 +Then we create a sharing device +and set up Rosetta directory share. + +386 +00:21:24,184 --> 00:21:28,455 +Finally, we set up our device +on the configuration as usual. + +387 +00:21:28,488 --> 00:21:32,059 +Now our virtual machine +is ready to use Rosetta. + +388 +00:21:32,092 --> 00:21:35,696 +Next, let’s see how Linux +can take advantage of it. + +389 +00:21:36,930 --> 00:21:41,335 +In Linux, we start by mounting +the shared directory in the file system. + +390 +00:21:41,368 --> 00:21:46,240 +What we see from Linux is a Rosetta binary +that can translate applications. + +391 +00:21:46,273 --> 00:21:50,511 +Then we can use update-binfmts +to tell the system to use Rosetta + +392 +00:21:50,544 --> 00:21:53,847 +to handle any x86-64 binary. + +393 +00:21:53,881 --> 00:21:55,849 +Don’t worry +about remembering this command. + +394 +00:21:55,883 --> 00:21:58,685 +It's all in the documentation. + +395 +00:21:58,719 --> 00:22:00,921 +Now Linux is ready. + +396 +00:22:00,954 --> 00:22:05,926 +Every x86-64 binary launched +will be translated by Rosetta. + +397 +00:22:07,361 --> 00:22:11,832 +Before we end our Linux section, +let’s see everything together. + +398 +00:22:11,865 --> 00:22:15,602 +Here, we have a full Linux +distribution installed from scratch. + +399 +00:22:15,636 --> 00:22:18,705 +We can show its UI with Virtio GPU 2D. + +400 +00:22:18,739 --> 00:22:22,776 +From within the VM, +we run a PHP server with Rosetta. + +401 +00:22:22,809 --> 00:22:26,079 +And we can just connect +to it from macOS host. + +402 +00:22:27,381 --> 00:22:31,518 +We've seen that creating +virtual machines has never been simpler. + +403 +00:22:31,552 --> 00:22:34,488 +With Virtualization framework, +you can get virtual machines running + +404 +00:22:34,521 --> 00:22:37,191 +with just a few lines of code. + +405 +00:22:37,224 --> 00:22:42,062 +We have also seen that virtual machines +are ridiculously fast on macOS. + +406 +00:22:42,095 --> 00:22:43,931 +To learn more about Virtualization, + +407 +00:22:43,964 --> 00:22:47,534 +I invite you to check out +the code samples and documentation. + +408 +00:22:47,568 --> 00:22:49,603 +And on behalf of the team, +we cannot wait + +409 +00:22:49,636 --> 00:22:51,772 +to see what you will do next +with this technology. + +410 +00:22:51,805 --> 00:22:57,110 +[upbeat music] + diff --git a/eng/2022 Session 10003 Meet WeatherKit en.srt b/eng/2022 Session 10003 Meet WeatherKit en.srt new file mode 100644 index 0000000..557f6f1 --- /dev/null +++ b/eng/2022 Session 10003 Meet WeatherKit en.srt @@ -0,0 +1,1184 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:10,077 +♪ + +3 +00:00:10,077 --> 00:00:13,914 +Welcome to "Meet WeatherKit" +at WWDC22. + +4 +00:00:13,914 --> 00:00:15,182 +My name is Novall, + +5 +00:00:15,182 --> 00:00:17,885 +and I'm an engineer +on the Weather team. + +6 +00:00:17,885 --> 00:00:20,721 +We rely on weather data +day in and day out, + +7 +00:00:20,721 --> 00:00:23,557 +and where we get +this information is important. + +8 +00:00:23,557 --> 00:00:25,459 +From checking the weather +on your Apple Watch + +9 +00:00:25,459 --> 00:00:27,094 +so you know to bring +an umbrella with you + +10 +00:00:27,094 --> 00:00:29,329 +before you head out the door, + +11 +00:00:29,329 --> 00:00:32,733 +to sustainable agriculture +where predicting rain and frost + +12 +00:00:32,733 --> 00:00:35,836 +can help farmers +plan crop rotation, + +13 +00:00:35,836 --> 00:00:39,573 +to staying safe and prepared +for winter storm travel -- + +14 +00:00:39,573 --> 00:00:42,175 +weather impacts everyone. + +15 +00:00:42,175 --> 00:00:44,778 +Accurate weather data +has become even more critical + +16 +00:00:44,778 --> 00:00:48,315 +in today's world affected +by our changing climate. + +17 +00:00:48,315 --> 00:00:50,517 +And having access +to accurate forecasts + +18 +00:00:50,517 --> 00:00:53,287 +is important now more than ever, + +19 +00:00:53,287 --> 00:00:55,923 +which is why +we created WeatherKit. + +20 +00:00:55,923 --> 00:00:59,693 +WeatherKit is powered by the +all-new Apple Weather Service, + +21 +00:00:59,693 --> 00:01:02,863 +a world-class +global weather forecast. + +22 +00:01:02,863 --> 00:01:05,832 +It uses high-resolution +weather models + +23 +00:01:05,832 --> 00:01:08,802 +and machine learning +and prediction algorithms + +24 +00:01:08,802 --> 00:01:11,038 +to give you hyperlocal +weather forecasts + +25 +00:01:11,038 --> 00:01:13,307 +around the globe. + +26 +00:01:13,307 --> 00:01:16,944 +With Apple Weather Service, +we have access to a lot of data, + +27 +00:01:16,944 --> 00:01:20,948 +and all of this is available +to you through WeatherKit. + +28 +00:01:20,948 --> 00:01:24,284 +Accurate weather data +requires location information. + +29 +00:01:24,284 --> 00:01:28,522 +And keeping that data private +is a shared responsibility. + +30 +00:01:28,522 --> 00:01:30,824 +In keeping with +our commitment to privacy, + +31 +00:01:30,824 --> 00:01:34,027 +WeatherKit is designed +to give hyperlocal forecasts + +32 +00:01:34,027 --> 00:01:37,130 +without compromising +user information. + +33 +00:01:37,130 --> 00:01:40,600 +Location is used only +to provide weather forecasts + +34 +00:01:40,600 --> 00:01:41,768 +and is not associated + +35 +00:01:41,768 --> 00:01:45,105 +with any personally +identifying information + +36 +00:01:45,105 --> 00:01:48,008 +and is never shared or sold. + +37 +00:01:48,008 --> 00:01:49,943 +With WeatherKit, +we've made it easy for you + +38 +00:01:49,943 --> 00:01:52,279 +to protect user privacy. + +39 +00:01:52,279 --> 00:01:55,515 +Today I will be diving into +more detail about WeatherKit + +40 +00:01:55,515 --> 00:01:58,919 +so you can get the most +out of our new API. + +41 +00:01:58,919 --> 00:02:00,887 +First, I'll cover +the available data sets + +42 +00:02:00,887 --> 00:02:02,356 +we offer through WeatherKit, + +43 +00:02:02,356 --> 00:02:05,525 +backed by our own +Apple Weather Service. + +44 +00:02:05,525 --> 00:02:08,362 +Next, I'll show you how +to request weather information + +45 +00:02:08,362 --> 00:02:10,330 +using the WeatherKit framework + +46 +00:02:10,330 --> 00:02:13,133 +and a REST API designed +so you can get weather data + +47 +00:02:13,133 --> 00:02:15,435 +on any platform. + +48 +00:02:15,435 --> 00:02:16,370 +And finally, + +49 +00:02:16,370 --> 00:02:18,839 +I'll cover some additional +implementation requirements + +50 +00:02:18,839 --> 00:02:21,341 +and best practices. + +51 +00:02:21,341 --> 00:02:25,746 +Let me start with an overview of +the available weather data sets. + +52 +00:02:25,746 --> 00:02:29,983 +As I mentioned, you have access +to a lot of data in WeatherKit. + +53 +00:02:29,983 --> 00:02:33,186 +So let's talk about +each data set. + +54 +00:02:33,186 --> 00:02:36,156 +The current weather data set +describes the "now" conditions + +55 +00:02:36,156 --> 00:02:38,658 +at the requested location. + +56 +00:02:38,658 --> 00:02:40,794 +It represents +a single point in time + +57 +00:02:40,794 --> 00:02:46,333 +and includes conditions like +UV index, temperature, and wind. + +58 +00:02:46,333 --> 00:02:48,668 +The minute forecast +contains minute-by-minute + +59 +00:02:48,668 --> 00:02:51,138 +precipitation conditions +for the next hour, + +60 +00:02:51,138 --> 00:02:53,073 +where available. + +61 +00:02:53,073 --> 00:02:55,475 +This data set is useful +for deciding whether or not + +62 +00:02:55,475 --> 00:02:58,845 +to bring an umbrella with you +as you walk out the door. + +63 +00:02:58,845 --> 00:03:01,281 +The hourly forecast +is a collection of forecasts + +64 +00:03:01,281 --> 00:03:03,183 +starting on the current hour + +65 +00:03:03,183 --> 00:03:07,154 +and provides data +for up to 240 hours. + +66 +00:03:07,154 --> 00:03:09,956 +Each hour in the hourly forecast +includes conditions + +67 +00:03:09,956 --> 00:03:15,362 +like humidity, visibility, +pressure, and dew point. + +68 +00:03:15,362 --> 00:03:19,866 +The daily forecast contains a +forecast collection of 10 days. + +69 +00:03:19,866 --> 00:03:21,468 +Each day in the daily forecast + +70 +00:03:21,468 --> 00:03:24,371 +provides information +about the entire day, + +71 +00:03:24,371 --> 00:03:26,540 +like the high +and low temperature, + +72 +00:03:26,540 --> 00:03:29,042 +sunrise, and sunset. + +73 +00:03:29,042 --> 00:03:31,445 +Weather alerts contains +severe weather warnings + +74 +00:03:31,445 --> 00:03:34,247 +issued for +the requested location. + +75 +00:03:34,247 --> 00:03:37,017 +This data set contains +important information + +76 +00:03:37,017 --> 00:03:41,455 +to keep your users safe, +informed, and prepared. + +77 +00:03:41,455 --> 00:03:45,258 +And finally, historical weather +provides saved weather forecasts + +78 +00:03:45,258 --> 00:03:46,626 +from the past, + +79 +00:03:46,626 --> 00:03:49,529 +so you can see trends +in weather data. + +80 +00:03:49,529 --> 00:03:51,164 +You can access historical data + +81 +00:03:51,164 --> 00:03:53,667 +by specifying +a start and end date + +82 +00:03:53,667 --> 00:03:56,770 +to the hourly +and daily requests. + +83 +00:03:56,770 --> 00:03:59,773 +This gives you access +to a lot of data. + +84 +00:03:59,773 --> 00:04:03,009 +We think there are a lot +of important and impactful ways + +85 +00:04:03,009 --> 00:04:05,779 +you can use historical weather. + +86 +00:04:05,779 --> 00:04:09,282 +Now that you've seen all of +the rich weather data available, + +87 +00:04:09,282 --> 00:04:11,585 +I'll walk you through +how to request this weather data + +88 +00:04:11,585 --> 00:04:14,821 +with the WeatherKit APIs. + +89 +00:04:14,821 --> 00:04:18,391 +Apple Weather data is available +through both a native framework + +90 +00:04:18,391 --> 00:04:21,561 +and a set of REST APIs. + +91 +00:04:21,561 --> 00:04:23,697 +First, let me show you +how easy it is + +92 +00:04:23,697 --> 00:04:27,200 +to access weather data +with our Swift framework. + +93 +00:04:27,200 --> 00:04:29,603 +All it takes +is a few lines of code. + +94 +00:04:29,603 --> 00:04:33,540 +And with Swift Concurrency, +requesting weather is simple. + +95 +00:04:33,540 --> 00:04:37,777 +First, you'll import WeatherKit +and CoreLocation. + +96 +00:04:37,777 --> 00:04:40,046 +Then you'll create +a weatherService object, + +97 +00:04:40,046 --> 00:04:43,383 +as your entry point +for the Weather Service. + +98 +00:04:43,383 --> 00:04:45,719 +You'll create a CLLocation +with coordinates + +99 +00:04:45,719 --> 00:04:48,288 +for your location of interest. + +100 +00:04:48,288 --> 00:04:52,225 +Here, I'm using my hometown +of Syracuse, New York. + +101 +00:04:52,225 --> 00:04:55,662 +Then you’ll call weather(for:) +on the weatherService instance + +102 +00:04:55,662 --> 00:04:59,332 +and pass in the location +created above. + +103 +00:04:59,332 --> 00:05:00,767 +When the call completes, + +104 +00:05:00,767 --> 00:05:04,171 +you can access the relevant data +you need for your app, + +105 +00:05:04,171 --> 00:05:08,575 +like the current temperature +and UV Index in this example. + +106 +00:05:08,575 --> 00:05:10,410 +Now that I've shown you +how easy it is + +107 +00:05:10,410 --> 00:05:12,779 +to request weather data +with Swift, + +108 +00:05:12,779 --> 00:05:14,681 +let me take you through +another example + +109 +00:05:14,681 --> 00:05:18,485 +I'm using a travel app +that I'm building in SwiftUI. + +110 +00:05:18,485 --> 00:05:20,520 +You can grab the completed +project from the link + +111 +00:05:20,520 --> 00:05:22,789 +associated with this session. + +112 +00:05:22,789 --> 00:05:25,058 +Since I'm really looking forward +to traveling again, + +113 +00:05:25,058 --> 00:05:27,093 +I've decided to create +a flight planner app + +114 +00:05:27,093 --> 00:05:29,663 +to plan my next trip. + +115 +00:05:29,663 --> 00:05:32,866 +I've already created the logic +for my flight itinerary, + +116 +00:05:32,866 --> 00:05:34,768 +but when I tap on each +of the flights in my trip, + +117 +00:05:34,768 --> 00:05:37,337 +I want to show columns +containing the condition, + +118 +00:05:37,337 --> 00:05:43,109 +precipitation, wind, and +temperature at each destination. + +119 +00:05:43,109 --> 00:05:45,378 +First step is to enable +WeatherKit. + +120 +00:05:45,378 --> 00:05:48,348 +Register the App ID +in the Developer Portal, + +121 +00:05:48,348 --> 00:05:51,084 +then select the Capability +and App Services t abs + +122 +00:05:51,084 --> 00:05:53,386 +to enable WeatherKit. + +123 +00:05:53,386 --> 00:05:55,755 +Then in Xcode, +add the WeatherKit capability + +124 +00:05:55,755 --> 00:05:57,958 +to the project. + +125 +00:05:57,958 --> 00:05:59,626 +With that prep out of the way, + +126 +00:05:59,626 --> 00:06:01,861 +let me walk through how +I'll get the weather data + +127 +00:06:01,861 --> 00:06:04,764 +for each of these locations. + +128 +00:06:04,764 --> 00:06:07,100 +Here I have an Airport struct +already set up + +129 +00:06:07,100 --> 00:06:08,868 +that contains +the latitude and longitude + +130 +00:06:08,868 --> 00:06:11,438 +of my destination airports. + +131 +00:06:11,438 --> 00:06:14,374 +I'll get the hourly forecast +by calling weather(for:) + +132 +00:06:14,374 --> 00:06:15,609 +on our shared weather service + +133 +00:06:15,609 --> 00:06:18,979 +and then pass in +our airport location. + +134 +00:06:22,983 --> 00:06:24,751 +Because I just want +a subset of data, + +135 +00:06:24,751 --> 00:06:27,787 +I've also specified to include +the hourly forecast + +136 +00:06:27,787 --> 00:06:29,823 +in the request. + +137 +00:06:29,823 --> 00:06:32,726 +Now, I'll build and run my app. + +138 +00:06:36,529 --> 00:06:38,565 +Now I can see +my custom view updated + +139 +00:06:38,565 --> 00:06:41,601 +to display the conditions +at each airport. + +140 +00:06:41,601 --> 00:06:43,603 +The next thing I need to do +while building this app + +141 +00:06:43,603 --> 00:06:48,341 +is to display attribution +for the data sources in my app. + +142 +00:06:51,678 --> 00:06:53,580 +First, I'll get +the attribution URL from + +143 +00:06:53,580 --> 00:06:57,083 +the attribution.legalPageURL +property. + +144 +00:06:57,083 --> 00:06:59,586 +This is a link to +the legal attribution page + +145 +00:06:59,586 --> 00:07:01,388 +that contains +copyright information + +146 +00:07:01,388 --> 00:07:03,723 +about the weather data sources. + +147 +00:07:03,723 --> 00:07:05,425 +I'll also need to get the URL + +148 +00:07:05,425 --> 00:07:07,494 +for the combined +Apple Weather mark. + +149 +00:07:11,598 --> 00:07:14,100 +It's available in both light +and dark variants, + +150 +00:07:14,100 --> 00:07:16,336 +so I'll check the +colorScheme environment value + +151 +00:07:16,336 --> 00:07:18,905 +to find out if the SwiftUI view +is currently displaying + +152 +00:07:18,905 --> 00:07:22,208 +in light or dark appearance. + +153 +00:07:22,208 --> 00:07:24,811 +Finally, +I'll build and run again. + +154 +00:07:29,816 --> 00:07:32,285 +Note that the Apple Weather mark +and attribution link + +155 +00:07:32,285 --> 00:07:36,256 +opens in an +SFSafariViewController. + +156 +00:07:36,256 --> 00:07:38,058 +That's all it takes +to get the weather + +157 +00:07:38,058 --> 00:07:40,760 +for our flight planner app, +and there are so many ways + +158 +00:07:40,760 --> 00:07:43,263 +you can use the WeatherKit API +to add weather data + +159 +00:07:43,263 --> 00:07:45,265 +to your apps. + +160 +00:07:45,265 --> 00:07:48,134 +But that was only +the native framework. + +161 +00:07:48,134 --> 00:07:51,037 +The REST API provides +the same rich weather data + +162 +00:07:51,037 --> 00:07:55,208 +as the Swift framework +and can be used on any platform. + +163 +00:07:55,208 --> 00:07:58,678 +In this example, I'm showing how +you can request weather alerts + +164 +00:07:58,678 --> 00:08:02,382 +from the weatherkit.apple.com +endpoint. + +165 +00:08:02,382 --> 00:08:04,651 +First, you request +an auth token. + +166 +00:08:04,651 --> 00:08:07,020 +I'll discuss that more in a bit. + +167 +00:08:07,020 --> 00:08:10,457 +Then, to get the weather object, +you first create a URL + +168 +00:08:10,457 --> 00:08:14,494 +indicating the desired weather +data set for a given location. + +169 +00:08:14,494 --> 00:08:16,629 +Be sure to set +the appropriate language + +170 +00:08:16,629 --> 00:08:19,032 +for a localized response. + +171 +00:08:19,032 --> 00:08:21,501 +Then, provide +the latitude and longitude + +172 +00:08:21,501 --> 00:08:24,037 +of the location of interest. + +173 +00:08:24,037 --> 00:08:26,473 +Indicate the desired data set. + +174 +00:08:26,473 --> 00:08:29,242 +You may notice this parameter +is plural so you can request + +175 +00:08:29,242 --> 00:08:33,313 +several at once by separating +each with a comma. + +176 +00:08:33,313 --> 00:08:36,916 +And finally, the country code +for the requested location. + +177 +00:08:36,916 --> 00:08:39,185 +But note, the country code +is only required + +178 +00:08:39,185 --> 00:08:43,156 +if you're requesting +the weather alerts data set. + +179 +00:08:43,156 --> 00:08:44,724 +Next, you'll fetch +the weather data + +180 +00:08:44,724 --> 00:08:48,328 +using the URL and your auth +token from above, + +181 +00:08:48,328 --> 00:08:51,231 +converting the results to JSON. + +182 +00:08:51,231 --> 00:08:53,366 +With that, you can access +the weather alerts + +183 +00:08:53,366 --> 00:08:55,668 +and their details. + +184 +00:08:55,668 --> 00:08:58,538 +So again, another example +of how easy it is + +185 +00:08:58,538 --> 00:09:02,942 +for you to access weather data, +only this time through REST. + +186 +00:09:02,942 --> 00:09:05,745 +To go into more depth +about the setup you need, + +187 +00:09:05,745 --> 00:09:08,815 +let's revisit auth. + +188 +00:09:08,815 --> 00:09:10,517 +For the WeatherKit REST API, + +189 +00:09:10,517 --> 00:09:14,821 +there are a few additional steps +to handle authentication. + +190 +00:09:14,821 --> 00:09:16,956 +In the Developer Portal, +you'll enable access + +191 +00:09:16,956 --> 00:09:20,226 +for WeatherKit requests by +creating an authentication key + +192 +00:09:20,226 --> 00:09:24,531 +enabled for WeatherKit +and an associated services ID. + +193 +00:09:24,531 --> 00:09:25,999 +The private key can be created + +194 +00:09:25,999 --> 00:09:29,269 +in the Keys section +of the Developer Portal. + +195 +00:09:29,269 --> 00:09:32,172 +WeatherKit requires tokens +to validate authorization + +196 +00:09:32,172 --> 00:09:33,706 +on each request. + +197 +00:09:33,706 --> 00:09:36,242 +So on your server, +you'll deploy a token service + +198 +00:09:36,242 --> 00:09:40,814 +for creating a signed JSON web +token using your private key. + +199 +00:09:40,814 --> 00:09:43,883 +For those familiar with JSON +web token authentication, + +200 +00:09:43,883 --> 00:09:46,853 +this is a fairly standard +authorization flow, + +201 +00:09:46,853 --> 00:09:48,021 +but let me share some details + +202 +00:09:48,021 --> 00:09:51,291 +in case this is your first time +working with it. + +203 +00:09:51,291 --> 00:09:52,592 +To generate a signed token + +204 +00:09:52,592 --> 00:09:55,762 +you'll create the header +containing the fields and values + +205 +00:09:55,762 --> 00:09:59,165 +described in the +developer documentation. + +206 +00:09:59,165 --> 00:10:02,068 +Then create the payload +containing the information + +207 +00:10:02,068 --> 00:10:06,105 +specific to the WeatherKit +REST API and your application, + +208 +00:10:06,105 --> 00:10:08,808 +including items such +as the issuer, subject, + +209 +00:10:08,808 --> 00:10:11,311 +and expiration time. + +210 +00:10:11,311 --> 00:10:13,246 +And finally, +you'll sign the token + +211 +00:10:13,246 --> 00:10:17,617 +for use with a subsequent call +to the WeatherKit REST API. + +212 +00:10:17,617 --> 00:10:20,086 +Going back to +my weather alerts example, + +213 +00:10:20,086 --> 00:10:24,190 +here's where you'll request the +token from your signing service, + +214 +00:10:24,190 --> 00:10:26,593 +and add the token +to the Authorization header + +215 +00:10:26,593 --> 00:10:31,030 +of your HTTP request +for weather data. + +216 +00:10:31,030 --> 00:10:33,633 +So that's +the WeatherKit REST API. + +217 +00:10:33,633 --> 00:10:36,269 +One of two great ways +for you to access weather data + +218 +00:10:36,269 --> 00:10:38,438 +from the Apple Weather Service. + +219 +00:10:38,438 --> 00:10:41,007 +Lastly, I'll cover a few +additional requirements + +220 +00:10:41,007 --> 00:10:44,110 +for publishing on the App Store +or before you go live + +221 +00:10:44,110 --> 00:10:47,914 +on any platform +using the REST API. + +222 +00:10:47,914 --> 00:10:50,049 +Each of these requirements +apply regardless + +223 +00:10:50,049 --> 00:10:54,387 +of whether you're using +the native Swift or REST APIs. + +224 +00:10:54,387 --> 00:10:57,090 +The first requirement +is attribution. + +225 +00:10:57,090 --> 00:10:58,858 +As you saw in my demo, +you'll get a link + +226 +00:10:58,858 --> 00:11:01,528 +from our Attribution API +which you'll need to display + +227 +00:11:01,528 --> 00:11:04,163 +in your native or web app. + +228 +00:11:04,163 --> 00:11:07,233 +The second requirement +is an attribution logo. + +229 +00:11:07,233 --> 00:11:10,303 +The WeatherKit API +makes this easy and convenient + +230 +00:11:10,303 --> 00:11:15,041 +by providing the image assets +you need to display in your app. + +231 +00:11:15,041 --> 00:11:17,644 +And finally, if you'll be +displaying weather alerts, + +232 +00:11:17,644 --> 00:11:19,512 +you'll also need to link +to an event page + +233 +00:11:19,512 --> 00:11:21,848 +provided in the response. + +234 +00:11:21,848 --> 00:11:24,284 +So that's how easy it is +to prepare your app + +235 +00:11:24,284 --> 00:11:27,554 +for publication +on the App Store or the web. + +236 +00:11:27,554 --> 00:11:28,888 +So that's WeatherKit -- + +237 +00:11:28,888 --> 00:11:32,559 +hyperlocal forecasts powered +the Apple Weather Service + +238 +00:11:32,559 --> 00:11:36,663 +accessible through our Swift +framework and our REST API. + +239 +00:11:36,663 --> 00:11:38,731 +Both open up a world +of possibilities + +240 +00:11:38,731 --> 00:11:40,934 +for you to use weather data +in your apps, + +241 +00:11:40,934 --> 00:11:43,670 +on any platform or device. + +242 +00:11:43,670 --> 00:11:45,805 +We hope you enjoyed +this session. + +243 +00:11:45,805 --> 00:11:48,741 +Besides checking out the links +associated with this session, + +244 +00:11:48,741 --> 00:11:51,311 +read the docs +and download the project. + +245 +00:11:51,311 --> 00:11:53,646 +And of course, +we'd love your feedback. + +246 +00:11:53,646 --> 00:11:57,116 +We can't wait to see all of +the creative and impactful ways + +247 +00:11:57,116 --> 00:11:58,818 +you use WeatherKit. + +248 +00:11:58,818 --> 00:12:01,621 +Thank you and have a great WWDC! + +249 +00:12:01,621 --> 00:12:05,725 +♪ + diff --git a/eng/2022 Session 10005 What's new in HealthKit en.srt b/eng/2022 Session 10005 What's new in HealthKit en.srt new file mode 100644 index 0000000..88beec1 --- /dev/null +++ b/eng/2022 Session 10005 What's new in HealthKit en.srt @@ -0,0 +1,2065 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hip music ♪ + +2 +00:00:03,003 --> 00:00:09,610 +♪ + +3 +00:00:09,610 --> 00:00:13,080 +Hello and welcome to WWDC. + +4 +00:00:13,080 --> 00:00:16,850 +My name's Karim, +and I'm a HealthKit engineer. + +5 +00:00:16,850 --> 00:00:19,753 +The HealthKit framework +provides the foundation + +6 +00:00:19,753 --> 00:00:22,322 +for building great +health experiences, + +7 +00:00:22,322 --> 00:00:24,124 +and Apple Watch comes packed + +8 +00:00:24,124 --> 00:00:26,860 +with many health +and safety features, + +9 +00:00:26,860 --> 00:00:29,429 +that keep an eye on things +for you. + +10 +00:00:29,429 --> 00:00:33,567 +One of the most popular +is sleep tracking, + +11 +00:00:33,567 --> 00:00:35,235 +and we are making updates + +12 +00:00:35,235 --> 00:00:38,839 +to capture more detailed +sleep data. + +13 +00:00:38,839 --> 00:00:41,875 +We also have other +great new additions + +14 +00:00:41,875 --> 00:00:44,478 +to HealthKit this year. + +15 +00:00:44,478 --> 00:00:47,281 +We enhanced our APIs +to make it easier + +16 +00:00:47,281 --> 00:00:51,885 +to query for data +with Swift async. + +17 +00:00:51,885 --> 00:00:57,557 +We're improving workouts with +a richer workout representation. + +18 +00:00:57,557 --> 00:01:02,029 +And we're adding support +for saving vision prescriptions, + +19 +00:01:02,029 --> 00:01:07,668 +including a digital copy +of the physical prescription. + +20 +00:01:07,668 --> 00:01:10,971 +I'm excited to tell you more +about all these updates + +21 +00:01:10,971 --> 00:01:14,041 +and how your apps +can leverage them. + +22 +00:01:14,041 --> 00:01:17,244 +Let's get started! + +23 +00:01:17,244 --> 00:01:20,080 +Sleep is such +an essential function + +24 +00:01:20,080 --> 00:01:23,884 +that allows our bodies +and minds to recharge, + +25 +00:01:23,884 --> 00:01:27,955 +and I love using my Apple Watch +to manage my sleep schedule + +26 +00:01:27,955 --> 00:01:30,757 +and get insights +that help me understand + +27 +00:01:30,757 --> 00:01:33,393 +and improve my sleep. + +28 +00:01:33,393 --> 00:01:37,397 +This year we are making +sleep tracking even better + +29 +00:01:37,397 --> 00:01:41,001 +by introducing sleep stages. + +30 +00:01:41,001 --> 00:01:43,704 +Apple Watch +will automatically track + +31 +00:01:43,704 --> 00:01:46,173 +all the different sleep stages +you go through + +32 +00:01:46,173 --> 00:01:47,574 +when you're asleep, + +33 +00:01:47,574 --> 00:01:51,478 +and this data will be accessible +from the Health app + +34 +00:01:51,478 --> 00:01:53,947 +and saved in HealthKit. + +35 +00:01:53,947 --> 00:01:57,417 +Of course, +your app will be able to read + +36 +00:01:57,417 --> 00:02:01,455 +and save sleep stages data. + +37 +00:02:01,455 --> 00:02:04,691 +Sleep data saved +by Apple Watch or your app + +38 +00:02:04,691 --> 00:02:08,261 +is represented in HealthKit +by category samples + +39 +00:02:08,261 --> 00:02:12,833 +with the identifier +sleepAnalysis. + +40 +00:02:12,833 --> 00:02:15,802 +We will support +three sleep stages: + +41 +00:02:15,802 --> 00:02:20,374 +REM, core, and deep. + +42 +00:02:20,374 --> 00:02:22,576 +When saving sleep data +to HealthKit, + +43 +00:02:22,576 --> 00:02:25,545 +you should make sure +to create one sample + +44 +00:02:25,545 --> 00:02:31,752 +for each continuous period +of time in a given sleep stage. + +45 +00:02:31,752 --> 00:02:35,522 +This is what the sleep sample +value enum looks like. + +46 +00:02:35,522 --> 00:02:41,128 +We're adding three cases +to represent sleep stages, + +47 +00:02:41,128 --> 00:02:43,730 +starting with asleepCore, + +48 +00:02:43,730 --> 00:02:48,268 +which corresponds to stages one +and two of the scoring model + +49 +00:02:48,268 --> 00:02:54,708 +from the American Academy +of Sleep Medicine or AASM; + +50 +00:02:54,708 --> 00:02:58,712 +asleepDeep, which corresponds +to stage three + +51 +00:02:58,712 --> 00:03:02,349 +of the AASM scoring model; + +52 +00:03:02,349 --> 00:03:04,785 +and finally, asleepREM, + +53 +00:03:04,785 --> 00:03:09,556 +which corresponds +to the rapid eye movement stage. + +54 +00:03:09,556 --> 00:03:11,825 +Now that we added sleep stages, + +55 +00:03:11,825 --> 00:03:14,761 +we are deprecating +the asleep case + +56 +00:03:14,761 --> 00:03:18,265 +in favor of asleepUnspecified, + +57 +00:03:18,265 --> 00:03:21,134 +which indicates +that the user is asleep + +58 +00:03:21,134 --> 00:03:24,071 +but no sleep stage +was specified. + +59 +00:03:24,071 --> 00:03:26,706 +With the updated sleep +samples enum, + +60 +00:03:26,706 --> 00:03:30,510 +it is now possible to save +and read sleep stages data + +61 +00:03:30,510 --> 00:03:32,045 +from HealthKit. + +62 +00:03:32,045 --> 00:03:36,416 +We added a new predicate to make +it easy to read sleep samples + +63 +00:03:36,416 --> 00:03:38,685 +for a given stage. + +64 +00:03:38,685 --> 00:03:42,022 +Let's say I want to read +sleep samples + +65 +00:03:42,022 --> 00:03:44,191 +in the REM stage. + +66 +00:03:44,191 --> 00:03:46,259 +First, I create a predicate + +67 +00:03:46,259 --> 00:03:49,529 +using the new +predicateForSamples method + +68 +00:03:49,529 --> 00:03:52,532 +with asleepREM as value. + +69 +00:03:52,532 --> 00:03:55,769 +Now, I can start +building my query. + +70 +00:03:55,769 --> 00:03:57,871 +I create a predicate +for my query + +71 +00:03:57,871 --> 00:04:00,340 +with the sleepAnalysis +sample type + +72 +00:04:00,340 --> 00:04:02,642 +and the sleep stage predicate. + +73 +00:04:02,642 --> 00:04:07,380 +And with this query predicate, +I can now create my query. + +74 +00:04:07,380 --> 00:04:09,616 +Running the query +will give me back + +75 +00:04:09,616 --> 00:04:13,553 +an array of sleep samples +in the REM stage. + +76 +00:04:13,553 --> 00:04:16,857 +However, if you're interested +in reading samples + +77 +00:04:16,857 --> 00:04:20,827 +for all sleep stages, +including unspecified, + +78 +00:04:20,827 --> 00:04:23,163 +it's important that +you update your app + +79 +00:04:23,163 --> 00:04:28,668 +to use the new .allAsleepValues +when building your predicate. + +80 +00:04:28,668 --> 00:04:31,805 +If you haven't looked +at HealthKit since last year, + +81 +00:04:31,805 --> 00:04:36,877 +this shorter syntax for querying +may look unfamiliar to you. + +82 +00:04:36,877 --> 00:04:39,179 +Since iOS 15.4, + +83 +00:04:39,179 --> 00:04:44,518 +we have updated our query API +to support Swift async. + +84 +00:04:44,518 --> 00:04:47,888 +Queries are an essential piece +of HealthKit, + +85 +00:04:47,888 --> 00:04:51,124 +and with Swift async support, +they're easier to use + +86 +00:04:51,124 --> 00:04:54,161 +with a more concise syntax. + +87 +00:04:54,161 --> 00:04:57,464 +Queries allow you to read +various data from HealthKit, + +88 +00:04:57,464 --> 00:04:59,933 +using predicates +to filter the results, + +89 +00:04:59,933 --> 00:05:03,870 +and also watch for new data +as it comes in. + +90 +00:05:03,870 --> 00:05:08,441 +All queries are subclasses +of HKQuery. + +91 +00:05:08,441 --> 00:05:12,045 +If I'm interested in knowing +the total calories burned + +92 +00:05:12,045 --> 00:05:14,014 +over a certain period of time, + +93 +00:05:14,014 --> 00:05:17,217 +a great query to get +these computed statistics + +94 +00:05:17,217 --> 00:05:20,554 +is HKStatisticsCollectionQuery. + +95 +00:05:20,554 --> 00:05:23,223 +To get the initial results, +you would set + +96 +00:05:23,223 --> 00:05:27,894 +the initialResultsHandler +closure of the query. + +97 +00:05:27,894 --> 00:05:31,398 +And if you're interested +in watching for any updates, + +98 +00:05:31,398 --> 00:05:33,099 +you would additionally set + +99 +00:05:33,099 --> 00:05:36,403 +the statisticsUpdateHandler +closure. + +100 +00:05:36,403 --> 00:05:38,104 +Once the query is started, + +101 +00:05:38,104 --> 00:05:41,841 +these closures will be called +with the results. + +102 +00:05:41,841 --> 00:05:46,313 +Thanks to Swift async, +we are making this even simpler. + +103 +00:05:46,313 --> 00:05:49,883 +Each query now has +a matching query descriptor, + +104 +00:05:49,883 --> 00:05:53,186 +so HKStatisticsCollectionQuery +becomes + +105 +00:05:53,186 --> 00:05:56,990 +HKStatisticsCollectionQuery +Descriptor. + +106 +00:05:56,990 --> 00:05:58,425 +You get the initial result + +107 +00:05:58,425 --> 00:06:02,696 +by simply calling +the async result(for:) method. + +108 +00:06:02,696 --> 00:06:06,333 +If, on the other hand, you want +to get the initial result + +109 +00:06:06,333 --> 00:06:10,270 +and also watch for updates, +you call results(for:), + +110 +00:06:10,270 --> 00:06:14,341 +which returns an AsyncSequence +that you can loop through + +111 +00:06:14,341 --> 00:06:16,676 +to read the results. + +112 +00:06:16,676 --> 00:06:20,213 +HealthKit is great +for tracking workouts + +113 +00:06:20,213 --> 00:06:24,317 +and relevant metrics +such as burned calories. + +114 +00:06:24,317 --> 00:06:28,021 +To find out how many calories +I burned this week, + +115 +00:06:28,021 --> 00:06:32,125 +I can use the statistics +collection query descriptor. + +116 +00:06:32,125 --> 00:06:35,028 +First, I create +the query descriptor + +117 +00:06:35,028 --> 00:06:38,732 +with a predicate +to match calorie samples. + +118 +00:06:38,732 --> 00:06:44,404 +I use the cumulativeSum option +because I want the total sum. + +119 +00:06:44,404 --> 00:06:46,506 +I'm interested +in this week's data, + +120 +00:06:46,506 --> 00:06:50,844 +so I use thisSunday +as my anchorDate. + +121 +00:06:50,844 --> 00:06:54,114 +And finally, +I want my calorie totals + +122 +00:06:54,114 --> 00:06:58,685 +to be computed for +a time interval of one week. + +123 +00:06:58,685 --> 00:07:00,987 +Once I created +my query descriptor, + +124 +00:07:00,987 --> 00:07:04,224 +all I need to do +is call result(for:) + +125 +00:07:04,224 --> 00:07:07,127 +with a healthStore object. + +126 +00:07:07,127 --> 00:07:09,562 +The returned +statisticsCollection object + +127 +00:07:09,562 --> 00:07:13,400 +gives me a snapshot +of my current data. + +128 +00:07:13,400 --> 00:07:16,770 +But if I want live updates +as my calories change, + +129 +00:07:16,770 --> 00:07:20,473 +it's as simple +as calling results(for:) + +130 +00:07:20,473 --> 00:07:23,043 +and looping through +the returned async sequence + +131 +00:07:23,043 --> 00:07:25,879 +to read the results. + +132 +00:07:25,879 --> 00:07:28,048 +When I'm done +watching for updates, + +133 +00:07:28,048 --> 00:07:32,986 +I just break out of the loop +and that will stop the query. + +134 +00:07:32,986 --> 00:07:36,589 +Now, let's talk about workouts. + +135 +00:07:36,589 --> 00:07:40,226 +HealthKit is a great place +for saving workouts + +136 +00:07:40,226 --> 00:07:42,495 +and all their related metrics + +137 +00:07:42,495 --> 00:07:45,332 +whether you're going +on a casual bike ride + +138 +00:07:45,332 --> 00:07:48,702 +or pushing yourself +to the limit at a race. + +139 +00:07:48,702 --> 00:07:49,969 +And oftentimes, + +140 +00:07:49,969 --> 00:07:53,873 +workouts may contain +more than one distinct activity. + +141 +00:07:53,873 --> 00:07:58,378 +You may be repeating the same +exercise with interval training + +142 +00:07:58,378 --> 00:08:01,181 +or participating +in a triathlon race + +143 +00:08:01,181 --> 00:08:05,685 +which comprises swimming, +cycling, and running. + +144 +00:08:05,685 --> 00:08:10,657 +We are updating our workouts API +in iOS 16 and watchOS 9 + +145 +00:08:10,657 --> 00:08:14,361 +to make it possible to capture +these types of workouts + +146 +00:08:14,361 --> 00:08:19,265 +and the relevant statistics +for each activity. + +147 +00:08:19,265 --> 00:08:22,836 +This is the timeline +of a swim-bike-run workout + +148 +00:08:22,836 --> 00:08:24,771 +I did recently. + +149 +00:08:24,771 --> 00:08:26,606 +I started with swimming, + +150 +00:08:26,606 --> 00:08:28,975 +after which +I took a little bit of time + +151 +00:08:28,975 --> 00:08:32,245 +to get ready for the cycling +portion of the workout, + +152 +00:08:32,245 --> 00:08:34,147 +and finally running. + +153 +00:08:34,147 --> 00:08:41,154 +Each activity is represented by +an HKWorkoutActivity object. + +154 +00:08:41,154 --> 00:08:42,756 +Each workout activity + +155 +00:08:42,756 --> 00:08:45,959 +is created with its own +workout configuration, + +156 +00:08:45,959 --> 00:08:49,729 +which includes +the activity type. + +157 +00:08:49,729 --> 00:08:52,866 +A workout activity holds +a list of events + +158 +00:08:52,866 --> 00:08:56,936 +that occurred +during the activity. + +159 +00:08:56,936 --> 00:09:01,274 +And you will be able to read +statistics for each activity, + +160 +00:09:01,274 --> 00:09:03,943 +which is great for when +you're only interested + +161 +00:09:03,943 --> 00:09:08,948 +in analyzing what happened +during a particular activity. + +162 +00:09:08,948 --> 00:09:10,750 +Going back to my timeline, + +163 +00:09:10,750 --> 00:09:12,852 +my three activities +are configured + +164 +00:09:12,852 --> 00:09:16,589 +with their appropriate +activity type. + +165 +00:09:16,589 --> 00:09:19,692 +Activities cannot +overlap in time. + +166 +00:09:19,692 --> 00:09:22,295 +And because there may be +a transition period + +167 +00:09:22,295 --> 00:09:24,264 +between each activity, + +168 +00:09:24,264 --> 00:09:27,834 +they are not required +to be contiguous. + +169 +00:09:27,834 --> 00:09:30,336 +If I'm interested +in analyzing what happened + +170 +00:09:30,336 --> 00:09:32,539 +during transition periods, + +171 +00:09:32,539 --> 00:09:35,675 +I can create an +HKWorkoutActivity + +172 +00:09:35,675 --> 00:09:39,612 +for each transition +with the type transition. + +173 +00:09:39,612 --> 00:09:44,451 +All of these activities will be +saved with the HKWorkout object + +174 +00:09:44,451 --> 00:09:47,720 +under the workoutActivities +property. + +175 +00:09:47,720 --> 00:09:52,091 +If you're using HKWorkoutBuilder +to add workouts to HealthKit, + +176 +00:09:52,091 --> 00:09:55,695 +adding an activity +is as easy as creating + +177 +00:09:55,695 --> 00:10:00,633 +an HKWorkoutActivity object +with a workoutConfiguration, + +178 +00:10:00,633 --> 00:10:04,170 +start and end date, +and an optional metadata. + +179 +00:10:04,170 --> 00:10:08,141 +And then, you simply call +addWorkoutActivity + +180 +00:10:08,141 --> 00:10:10,477 +on the workout builder. + +181 +00:10:10,477 --> 00:10:14,214 +On Apple Watch, you can use +a workout session + +182 +00:10:14,214 --> 00:10:16,616 +to track +a swim-bike-run workout, + +183 +00:10:16,616 --> 00:10:18,651 +and the associated +workout builder + +184 +00:10:18,651 --> 00:10:21,454 +to save the workout +in HealthKit. + +185 +00:10:21,454 --> 00:10:23,523 +Let's go back +to my workout timeline. + +186 +00:10:23,523 --> 00:10:26,593 +To track this workout +on Apple Watch, + +187 +00:10:26,593 --> 00:10:31,598 +I need to set up +a workout session and builder. + +188 +00:10:31,598 --> 00:10:35,101 +I start by creating +a workout configuration + +189 +00:10:35,101 --> 00:10:38,171 +with the type swimBikeRun. + +190 +00:10:38,171 --> 00:10:41,474 +Then I create +an HKWorkoutSession + +191 +00:10:41,474 --> 00:10:43,943 +using my configuration. + +192 +00:10:43,943 --> 00:10:45,812 +At the beginning of the workout + +193 +00:10:45,812 --> 00:10:48,982 +I simply call startActivity +on the session, + +194 +00:10:48,982 --> 00:10:53,286 +and beginCollection on the +associated workout builder. + +195 +00:10:53,286 --> 00:10:55,922 +Now that my session +and builder are ready, + +196 +00:10:55,922 --> 00:10:58,458 +I can add my first activity + +197 +00:10:58,458 --> 00:11:01,127 +using the +beginNewActivity method + +198 +00:11:01,127 --> 00:11:05,765 +with a swimming workout +configuration and a start date. + +199 +00:11:05,765 --> 00:11:08,368 +At the beginning +of each activity, + +200 +00:11:08,368 --> 00:11:11,437 +you should make sure to update +the workout builder data source + +201 +00:11:11,437 --> 00:11:15,675 +to only collect the data types +you're interested in. + +202 +00:11:15,675 --> 00:11:17,977 +Because this is +a swimming activity, + +203 +00:11:17,977 --> 00:11:21,180 +I want swimming distance +to be collected. + +204 +00:11:21,180 --> 00:11:22,982 +At the end of the activity, + +205 +00:11:22,982 --> 00:11:28,588 +I call endCurrentActivity +with the end date. + +206 +00:11:28,588 --> 00:11:32,091 +Because I'm interested in +analyzing the transition period + +207 +00:11:32,091 --> 00:11:34,394 +from swimming to cycling, + +208 +00:11:34,394 --> 00:11:37,063 +I will start +a new transition activity + +209 +00:11:37,063 --> 00:11:40,667 +immediately +after swimming has ended. + +210 +00:11:40,667 --> 00:11:44,704 +Again, because this is +the beginning of an activity, + +211 +00:11:44,704 --> 00:11:46,539 +I update the builder data source + +212 +00:11:46,539 --> 00:11:49,676 +to disable the collection +of swimming distance, + +213 +00:11:49,676 --> 00:11:53,680 +since it's no longer relevant. + +214 +00:11:53,680 --> 00:11:58,384 +I end the transition activity +right before cycling begins. + +215 +00:11:58,384 --> 00:12:01,154 +I can track the remaining +activities of my workout + +216 +00:12:01,154 --> 00:12:03,856 +the same way. + +217 +00:12:03,856 --> 00:12:05,458 +At the end of the workout, + +218 +00:12:05,458 --> 00:12:10,129 +ending the session will +also end any running activity. + +219 +00:12:10,129 --> 00:12:12,432 +Then I can finish +the workout builder, + +220 +00:12:12,432 --> 00:12:16,803 +which will save and return +an HKWorkout object. + +221 +00:12:16,803 --> 00:12:19,038 +I can use the returned workout + +222 +00:12:19,038 --> 00:12:21,674 +to read some +of the associated metrics + +223 +00:12:21,674 --> 00:12:25,311 +such as totalEnergyBurned +and totalDistance, + +224 +00:12:25,311 --> 00:12:29,949 +and display a summary +of the workout in my app. + +225 +00:12:29,949 --> 00:12:32,919 +However, +this small set of properties + +226 +00:12:32,919 --> 00:12:35,121 +is no longer sufficient. + +227 +00:12:35,121 --> 00:12:39,425 +totalSwimmingStrokeCount +is not relevant to all workouts, + +228 +00:12:39,425 --> 00:12:43,162 +and some workouts +may collect more metrics. + +229 +00:12:43,162 --> 00:12:45,732 +In order to make it easier +to read metrics + +230 +00:12:45,732 --> 00:12:47,500 +for all kinds of workouts, + +231 +00:12:47,500 --> 00:12:49,802 +we are deprecating +these properties + +232 +00:12:49,802 --> 00:12:53,873 +in favor of a new method +which returns statistics + +233 +00:12:53,873 --> 00:12:56,643 +for a given quantity type. + +234 +00:12:56,643 --> 00:12:59,846 +As a reminder, +this method is also available + +235 +00:12:59,846 --> 00:13:03,750 +on HKWorkoutActivity, +allowing you to focus + +236 +00:13:03,750 --> 00:13:08,187 +on just what happened +during an activity. + +237 +00:13:08,187 --> 00:13:11,758 +These statistics will be +automatically computed + +238 +00:13:11,758 --> 00:13:14,327 +from any samples collected +with the workout, + +239 +00:13:14,327 --> 00:13:20,033 +only when using HKWorkoutBuilder +or HKLiveWorkoutBuilder. + +240 +00:13:20,033 --> 00:13:23,102 +Along with this richer +workout representation, + +241 +00:13:23,102 --> 00:13:26,272 +we also have a new set +of predicates to help you query + +242 +00:13:26,272 --> 00:13:28,875 +for only the workouts +you're interested in + +243 +00:13:28,875 --> 00:13:32,979 +when driving your analysis +or visualization. + +244 +00:13:32,979 --> 00:13:34,847 +To give you an example, + +245 +00:13:34,847 --> 00:13:38,451 +here's a list +of my recent workouts + +246 +00:13:38,451 --> 00:13:42,622 +with the average heart rate +for each activity. + +247 +00:13:42,622 --> 00:13:47,060 +I would like to find my workouts +with high-intensity activities + +248 +00:13:47,060 --> 00:13:50,930 +where my average heart rate +was above 150. + +249 +00:13:50,930 --> 00:13:54,567 +First, I start by creating +a predicate using the new + +250 +00:13:54,567 --> 00:13:57,170 +predicateForWorkoutActivities +method, + +251 +00:13:57,170 --> 00:14:00,573 +which will act +on workout activities. + +252 +00:14:00,573 --> 00:14:02,108 +I want the average heart rate + +253 +00:14:02,108 --> 00:14:05,578 +to be greater +than 150 beats per minute. + +254 +00:14:05,578 --> 00:14:08,581 +Next, because +I want to query for workouts, + +255 +00:14:08,581 --> 00:14:13,286 +I wrap my heart rate predicate +inside a workout predicate. + +256 +00:14:13,286 --> 00:14:17,390 +Then, I create a query +using that predicate. + +257 +00:14:17,390 --> 00:14:20,059 +And I call the +result(for: healthStore) method + +258 +00:14:20,059 --> 00:14:23,396 +of my query descriptor +to get the list of workouts + +259 +00:14:23,396 --> 00:14:26,099 +that match my predicate. + +260 +00:14:26,099 --> 00:14:27,934 +And that's how I'm able to query + +261 +00:14:27,934 --> 00:14:31,270 +for just the workouts +I'm interested in. + +262 +00:14:31,270 --> 00:14:33,072 +One of my recent workouts + +263 +00:14:33,072 --> 00:14:36,476 +consisted of four +running intervals. + +264 +00:14:36,476 --> 00:14:40,246 +Using HKWorkoutActivity +is a great way + +265 +00:14:40,246 --> 00:14:42,615 +to capture these intervals. + +266 +00:14:42,615 --> 00:14:45,418 +You just need to make sure +that all activities + +267 +00:14:45,418 --> 00:14:49,021 +of an interval workout +have the same activity type + +268 +00:14:49,021 --> 00:14:50,857 +as the workout. + +269 +00:14:50,857 --> 00:14:54,460 +So for a running workout, +all activities are configured + +270 +00:14:54,460 --> 00:14:57,063 +with the .running type. + +271 +00:14:57,063 --> 00:15:01,033 +One benefit of using workout +activities to track intervals + +272 +00:15:01,033 --> 00:15:04,971 +is you can get statistics +for each interval. + +273 +00:15:04,971 --> 00:15:07,740 +With these updates, +workouts now include + +274 +00:15:07,740 --> 00:15:11,544 +a more comprehensive +picture of their activities + +275 +00:15:11,544 --> 00:15:14,614 +and the context +surrounding them. + +276 +00:15:14,614 --> 00:15:18,050 +In order to provide +an even richer picture, + +277 +00:15:18,050 --> 00:15:21,187 +we're introducing +new running metrics + +278 +00:15:21,187 --> 00:15:23,389 +that will be +automatically collected + +279 +00:15:23,389 --> 00:15:28,060 +on Apple Watch Series 6, +SE, and newer -- + +280 +00:15:28,060 --> 00:15:33,699 +metrics such as running stride +length, or power in watts. + +281 +00:15:33,699 --> 00:15:37,603 +For swimming workouts, +we're adding the SWOLF score. + +282 +00:15:37,603 --> 00:15:40,373 +It's defined as +the number of strokes + +283 +00:15:40,373 --> 00:15:42,508 +taken in a given length, + +284 +00:15:42,508 --> 00:15:45,812 +and the time it took +to swim that length. + +285 +00:15:45,812 --> 00:15:49,549 +This score will be calculated +for each lap event + +286 +00:15:49,549 --> 00:15:52,218 +and segment event +for swimming workouts + +287 +00:15:52,218 --> 00:15:55,588 +recorded on Apple Watch. + +288 +00:15:55,588 --> 00:15:58,758 +While these metrics enrich +your workouts and allow you + +289 +00:15:58,758 --> 00:16:02,695 +to get a better understanding +of how you performed, + +290 +00:16:02,695 --> 00:16:05,298 +another important metric +that is recorded + +291 +00:16:05,298 --> 00:16:09,402 +after a workout has ended +is heart rate recovery. + +292 +00:16:09,402 --> 00:16:13,472 +It's an estimate of how quickly +your heart rate lowers + +293 +00:16:13,472 --> 00:16:17,877 +after exercise, +and it can be used to understand + +294 +00:16:17,877 --> 00:16:20,213 +how the heart recovers +after stress, + +295 +00:16:20,213 --> 00:16:23,282 +and reveal potential +health problems. + +296 +00:16:23,282 --> 00:16:28,721 +With iOS 16, we are introducing +a new Cardio Recovery data type. + +297 +00:16:28,721 --> 00:16:31,324 +It will be accessible +from the Health app, + +298 +00:16:31,324 --> 00:16:34,760 +and your apps will be able +to read and save this data + +299 +00:16:34,760 --> 00:16:37,363 +in HealthKit. + +300 +00:16:37,363 --> 00:16:41,067 +Heart rate recovery +is a quantity type + +301 +00:16:41,067 --> 00:16:46,172 +with the identifier +.heartRateRecoveryOneMinute. + +302 +00:16:46,172 --> 00:16:48,274 +Additional context information + +303 +00:16:48,274 --> 00:16:50,710 +about each heart rate +recovery sample + +304 +00:16:50,710 --> 00:16:54,113 +can be added as metadata. + +305 +00:16:54,113 --> 00:16:56,649 +In my recent +swim-bike-run workout, + +306 +00:16:56,649 --> 00:16:59,151 +I pushed myself to my limits, + +307 +00:16:59,151 --> 00:17:03,055 +and then observed +my heart rate's recovery rate. + +308 +00:17:03,055 --> 00:17:05,491 +It took me about +three and a half hours + +309 +00:17:05,491 --> 00:17:08,294 +to complete the workout. + +310 +00:17:08,294 --> 00:17:12,732 +I reached a maximum heart rate +of 184 beats per minute + +311 +00:17:12,732 --> 00:17:14,533 +when I was running. + +312 +00:17:14,533 --> 00:17:16,802 +And the minute following +the workout, + +313 +00:17:16,802 --> 00:17:20,506 +my heart rate +dropped by 50 beats. + +314 +00:17:20,506 --> 00:17:24,176 +When using HKLiveWorkoutBuilder +on Apple Watch, + +315 +00:17:24,176 --> 00:17:26,279 +a heart rate recovery sample, + +316 +00:17:26,279 --> 00:17:28,748 +along with +its surrounding context, + +317 +00:17:28,748 --> 00:17:33,319 +is automatically saved +in HealthKit after a workout. + +318 +00:17:33,319 --> 00:17:37,023 +Otherwise, to save +a heart rate recovery sample, + +319 +00:17:37,023 --> 00:17:39,425 +I create a quantity sample +with the + +320 +00:17:39,425 --> 00:17:42,728 +.heartRateRecoveryOneMinute +type. + +321 +00:17:42,728 --> 00:17:44,530 +My heart rate dropped +by 50 beats + +322 +00:17:44,530 --> 00:17:46,332 +the minute +following the workout, + +323 +00:17:46,332 --> 00:17:50,503 +so I set that as the quantity +of my sample. + +324 +00:17:50,503 --> 00:17:54,073 +I also set a start and end date +for the sample. + +325 +00:17:54,073 --> 00:17:57,944 +Then, I put the additional +context information + +326 +00:17:57,944 --> 00:18:00,613 +in the metadata dictionary, + +327 +00:18:00,613 --> 00:18:03,449 +starting with +the recovery test type. + +328 +00:18:03,449 --> 00:18:06,185 +Because my workout +was an all-out effort, + +329 +00:18:06,185 --> 00:18:09,722 +the test type is .maxExercise. + +330 +00:18:09,722 --> 00:18:12,258 +My workout was a swimBikeRun, + +331 +00:18:12,258 --> 00:18:15,995 +so I set that +as the activity type. + +332 +00:18:15,995 --> 00:18:19,231 +I can also add the workout +duration using the + +333 +00:18:19,231 --> 00:18:23,169 +HeartRateRecoveryActivity +Duration key. + +334 +00:18:23,169 --> 00:18:27,306 +Finally, I add the maximum +heart rate observed + +335 +00:18:27,306 --> 00:18:32,845 +during the workout, +which was 184 beats per minute. + +336 +00:18:32,845 --> 00:18:35,915 +With these updates, +it is now easier than ever + +337 +00:18:35,915 --> 00:18:39,952 +to track swim-bike-run, +and interval workouts. + +338 +00:18:39,952 --> 00:18:43,456 +In addition, +the introduction of new metrics + +339 +00:18:43,456 --> 00:18:47,426 +provides a more comprehensive +picture for your workouts + +340 +00:18:47,426 --> 00:18:52,431 +and newer ways +to evaluate progress over time. + +341 +00:18:52,431 --> 00:18:57,069 +While activity and fitness is +something that impacts everyone + +342 +00:18:57,069 --> 00:18:59,472 +there are other aspects +of health + +343 +00:18:59,472 --> 00:19:03,275 +that touch on many of us, +such as vision. + +344 +00:19:03,275 --> 00:19:07,046 +In fact, according to +the Vision Council of America, + +345 +00:19:07,046 --> 00:19:11,951 +approximately 75 percent +of adults in the United States + +346 +00:19:11,951 --> 00:19:15,321 +rely on vision correction +with prescription glasses + +347 +00:19:15,321 --> 00:19:17,723 +or contact lenses. + +348 +00:19:17,723 --> 00:19:20,960 +These prescriptions, +however, are easy to lose, + +349 +00:19:20,960 --> 00:19:23,629 +and it's one more thing +you need to have with you + +350 +00:19:23,629 --> 00:19:27,967 +when ordering prescription +glasses or contact lenses. + +351 +00:19:27,967 --> 00:19:31,103 +So let's put them +in our phones. + +352 +00:19:31,103 --> 00:19:34,140 +Starting with iOS 16, +your apps can now save + +353 +00:19:34,140 --> 00:19:39,712 +glasses and contacts +prescriptions in HealthKit. + +354 +00:19:39,712 --> 00:19:42,348 +A vision prescription +is a sample + +355 +00:19:42,348 --> 00:19:45,684 +with the visionPrescriptionType. + +356 +00:19:45,684 --> 00:19:47,720 +The sample's start date +corresponds + +357 +00:19:47,720 --> 00:19:50,423 +to the prescription's +issue date, + +358 +00:19:50,423 --> 00:19:54,660 +while the end date corresponds +to the expiration date. + +359 +00:19:54,660 --> 00:19:59,065 +Optionally, a digital copy +of the physical prescription + +360 +00:19:59,065 --> 00:20:02,835 +can be attached +to the sample. + +361 +00:20:02,835 --> 00:20:06,005 +A glasses or contacts +prescription sample + +362 +00:20:06,005 --> 00:20:10,709 +is a subclass +of HKVisionPrescription. + +363 +00:20:10,709 --> 00:20:15,347 +For glasses, you use the +HKGlassesPrescription subclass, + +364 +00:20:15,347 --> 00:20:20,453 +and for contacts, the +HKContactsPrescription subclass. + +365 +00:20:20,453 --> 00:20:22,755 +Each glasses prescription +is created + +366 +00:20:22,755 --> 00:20:26,559 +using two glasses lens +specification objects; + +367 +00:20:26,559 --> 00:20:29,395 +one for each eye. + +368 +00:20:29,395 --> 00:20:32,765 +And similarly, contacts +prescriptions are created + +369 +00:20:32,765 --> 00:20:36,769 +with two contacts lens +specification objects. + +370 +00:20:36,769 --> 00:20:40,239 +Now, let's save +my reading glasses prescription + +371 +00:20:40,239 --> 00:20:42,274 +to HealthKit. + +372 +00:20:42,274 --> 00:20:46,579 +The first step is to create +an HKGlassesLensSpecification + +373 +00:20:46,579 --> 00:20:48,681 +for each eye. + +374 +00:20:48,681 --> 00:20:52,618 +Some parameters, such as +vertex distance and prism, + +375 +00:20:52,618 --> 00:20:54,820 +are optional. + +376 +00:20:54,820 --> 00:21:00,926 +I can create the right eye +lens specification the same way. + +377 +00:21:00,926 --> 00:21:04,563 +Next, I create a glasses +prescription sample + +378 +00:21:04,563 --> 00:21:09,168 +with my right eye and left eye +lens specifications. + +379 +00:21:09,168 --> 00:21:12,204 +This prescription +is for my reading glasses, + +380 +00:21:12,204 --> 00:21:16,308 +so I'm adding that +in the description. + +381 +00:21:16,308 --> 00:21:18,944 +Then, I just call save +on the healthStore + +382 +00:21:18,944 --> 00:21:20,880 +with my prescription. + +383 +00:21:20,880 --> 00:21:25,818 +And with that, my prescription +is now saved in HealthKit. + +384 +00:21:25,818 --> 00:21:27,686 +I also took a picture of it, + +385 +00:21:27,686 --> 00:21:31,891 +which I'd like to attach +to the sample I just saved. + +386 +00:21:31,891 --> 00:21:37,897 +A file attachment is represented +by an HKAttachment object. + +387 +00:21:37,897 --> 00:21:43,903 +You use HKAttachmentStore to +save and read file attachments. + +388 +00:21:43,903 --> 00:21:47,072 +Only static images or PDF files + +389 +00:21:47,072 --> 00:21:50,910 +can be attached +to prescriptions. + +390 +00:21:50,910 --> 00:21:55,414 +To attach the picture I took +to my prescription sample, + +391 +00:21:55,414 --> 00:21:59,451 +I start by creating +an HKAttachmentStore object + +392 +00:21:59,451 --> 00:22:01,720 +using a healthStore. + +393 +00:22:01,720 --> 00:22:07,126 +Then I call addAttachment(to:) +with my prescription sample. + +394 +00:22:07,126 --> 00:22:09,962 +I set a name for the attachment. + +395 +00:22:09,962 --> 00:22:13,332 +Here, I'm attaching a PNG file. + +396 +00:22:13,332 --> 00:22:17,870 +And finally, +pass the URL of the file. + +397 +00:22:17,870 --> 00:22:22,708 +The prescription I just attached +contains a lot more information + +398 +00:22:22,708 --> 00:22:25,511 +than just +the lens specification -- + +399 +00:22:25,511 --> 00:22:30,783 +sensitive information, like +my full name and date of birth. + +400 +00:22:30,783 --> 00:22:33,018 +One of HealthKit's +core principles + +401 +00:22:33,018 --> 00:22:36,355 +is to protect your privacy +and make sure + +402 +00:22:36,355 --> 00:22:40,492 +that you are always in control +of the data you share. + +403 +00:22:40,492 --> 00:22:44,496 +Because it can be easy to share +more data than intended + +404 +00:22:44,496 --> 00:22:47,099 +with prescriptions +that include an attachment, + +405 +00:22:47,099 --> 00:22:50,302 +we are introducing +a new authorization model + +406 +00:22:50,302 --> 00:22:52,404 +for prescriptions. + +407 +00:22:52,404 --> 00:22:54,640 +Read authorization is granted + +408 +00:22:54,640 --> 00:22:57,943 +for each prescription +object separately. + +409 +00:22:57,943 --> 00:23:00,713 +Users can select exactly +which prescriptions + +410 +00:23:00,713 --> 00:23:03,549 +they grant your app access to, + +411 +00:23:03,549 --> 00:23:07,253 +and update their selection +at any time. + +412 +00:23:07,253 --> 00:23:10,923 +We have a new API +for requesting authorization + +413 +00:23:10,923 --> 00:23:14,159 +for vision prescription objects. + +414 +00:23:14,159 --> 00:23:16,195 +Just like other data types, + +415 +00:23:16,195 --> 00:23:19,164 +you can use queries +to read prescriptions + +416 +00:23:19,164 --> 00:23:22,034 +that your app has access to. + +417 +00:23:22,034 --> 00:23:25,537 +If you're only interested +in certain prescriptions, + +418 +00:23:25,537 --> 00:23:27,873 +you can use a predicate. + +419 +00:23:27,873 --> 00:23:31,010 +To request authorization, +simply call the + +420 +00:23:31,010 --> 00:23:34,246 +requestPerObjectRead +Authorization method + +421 +00:23:34,246 --> 00:23:37,883 +of the healthStore +with the visionType. + +422 +00:23:37,883 --> 00:23:42,888 +Doing so will always display an +authorization prompt in your app + +423 +00:23:42,888 --> 00:23:45,557 +with a list of all +the prescriptions + +424 +00:23:45,557 --> 00:23:48,594 +that match your predicate. + +425 +00:23:48,594 --> 00:23:51,363 +Make sure you're asking +for authorization + +426 +00:23:51,363 --> 00:23:56,368 +in the appropriate context to +ensure the best user experience. + +427 +00:23:56,368 --> 00:24:00,572 +These are just some of the new +things we added to HealthKit + +428 +00:24:00,572 --> 00:24:03,509 +to empower your apps +to provide better health + +429 +00:24:03,509 --> 00:24:05,978 +and fitness experiences. + +430 +00:24:05,978 --> 00:24:09,948 +We can't wait to see +what you will build next. + +431 +00:24:09,948 --> 00:24:13,752 +Have a great WWDC, +and if you have any questions, + +432 +00:24:13,752 --> 00:24:17,356 +we'll be happy to help you +in the Developer Forums. + +433 +00:24:17,356 --> 00:24:21,460 +♪ + diff --git a/eng/2022 Session 10006 Meet Apple Maps Server APIs en.srt b/eng/2022 Session 10006 Meet Apple Maps Server APIs en.srt new file mode 100644 index 0000000..4121382 --- /dev/null +++ b/eng/2022 Session 10006 Meet Apple Maps Server APIs en.srt @@ -0,0 +1,1253 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,276 +♪ + +3 +00:00:09,276 --> 00:00:12,079 +Hi, everyone! +My name is Ankur Soni. + +4 +00:00:12,079 --> 00:00:14,882 +I'm an engineering manager +on the Maps Services team + +5 +00:00:14,882 --> 00:00:16,416 +here at Apple. + +6 +00:00:16,416 --> 00:00:19,353 +Today, we're going to look at +some exciting new capabilities + +7 +00:00:19,353 --> 00:00:21,255 +coming to the Maps +developer ecosystem. + +8 +00:00:21,255 --> 00:00:23,223 +So let's get started. + +9 +00:00:23,223 --> 00:00:26,326 +Our Maps app offers +various end-user experiences + +10 +00:00:26,326 --> 00:00:28,195 +to Apple customers +around the globe. + +11 +00:00:28,195 --> 00:00:30,264 +We empower developers to create + +12 +00:00:30,264 --> 00:00:33,533 +beautiful geolocation +experiences for their apps + +13 +00:00:33,533 --> 00:00:38,205 +and websites through our MapKit +and MapKit JS offerings. + +14 +00:00:38,205 --> 00:00:41,108 +However, our Apple Maps +developer offering + +15 +00:00:41,108 --> 00:00:43,911 +has always been +very client-centric. + +16 +00:00:43,911 --> 00:00:47,214 +We have listened carefully +to all your great feedback. + +17 +00:00:47,214 --> 00:00:50,817 +You wanted a way to augment +your own data on MapKit + +18 +00:00:50,817 --> 00:00:54,388 +without compromising +on performance or power. + +19 +00:00:54,388 --> 00:00:56,156 +So to round out our ecosystem, + +20 +00:00:56,156 --> 00:01:01,128 +we are excited to introduce +the Apple Maps Server APIs. + +21 +00:01:01,128 --> 00:01:04,398 +We are introducing +four new server APIs: + +22 +00:01:04,398 --> 00:01:08,335 +Geocoding, Reverse Geocoding, +Search, + +23 +00:01:08,335 --> 00:01:12,306 +Estimated Time of Arrival -- +or ETA. + +24 +00:01:12,306 --> 00:01:15,842 +These APIs will help you tackle +a variety of use cases + +25 +00:01:15,842 --> 00:01:20,047 +while integrating Maps +into your applications. + +26 +00:01:20,047 --> 00:01:23,150 +With Geocoding APIs, +you can convert an address + +27 +00:01:23,150 --> 00:01:26,486 +to geographic coordinates +latitude and longitude. + +28 +00:01:26,486 --> 00:01:28,956 +Similarly, +with Reverse Geocoding, + +29 +00:01:28,956 --> 00:01:30,257 +you can do the opposite -- + +30 +00:01:30,257 --> 00:01:33,860 +go from geographic coordinates +to an address. + +31 +00:01:33,860 --> 00:01:37,431 +With Search API, you can give +your users the capability + +32 +00:01:37,431 --> 00:01:40,367 +to enter a search string +to discover places + +33 +00:01:40,367 --> 00:01:43,637 +like businesses, +points of interest, and more. + +34 +00:01:43,637 --> 00:01:45,672 +Maybe you want to overlay +some of your own data + +35 +00:01:45,672 --> 00:01:47,774 +and present it to the user. + +36 +00:01:47,774 --> 00:01:51,078 +With ETA API, +you can help your customers + +37 +00:01:51,078 --> 00:01:53,680 +get a sense of how far +your business is from them + +38 +00:01:53,680 --> 00:01:56,717 +or do some computations +to find the closest store. + +39 +00:01:56,717 --> 00:02:00,287 +The possibilities are endless! + +40 +00:02:00,287 --> 00:02:05,525 +We think you'll love server APIs +for three important reasons. + +41 +00:02:05,525 --> 00:02:08,862 +You can now deliver a seamless +experience by leveraging + +42 +00:02:08,862 --> 00:02:13,367 +MapKit, MapKit JS, and the new +Apple Maps Server APIs. + +43 +00:02:13,367 --> 00:02:15,736 +This will simplify +your application architecture + +44 +00:02:15,736 --> 00:02:18,572 +giving you a full +Apple Maps stack. + +45 +00:02:18,572 --> 00:02:20,574 +This will make your life +much easier. + +46 +00:02:20,574 --> 00:02:21,908 +For sure, it helped me. + +47 +00:02:21,908 --> 00:02:24,611 +But hey, I'm biased! + +48 +00:02:24,611 --> 00:02:27,881 +The next benefit is +the reduction in network calls. + +49 +00:02:27,881 --> 00:02:30,650 +Many times, +we find ourselves in a situation + +50 +00:02:30,650 --> 00:02:33,086 +where we are making repetitive +and redundant requests + +51 +00:02:33,086 --> 00:02:36,790 +from our users' devices +like an iPhone, iPad, + +52 +00:02:36,790 --> 00:02:38,692 +websites, etcetera. + +53 +00:02:38,692 --> 00:02:41,862 +Maybe you are looking up the +same address over and over again + +54 +00:02:41,862 --> 00:02:44,798 +from your app running +on different user devices. + +55 +00:02:44,798 --> 00:02:48,268 +This causes a lot of network +calls and wasted bandwidth. + +56 +00:02:48,268 --> 00:02:51,171 +Delegating this common operation +to your server + +57 +00:02:51,171 --> 00:02:55,242 +and doing it only once in +the back end using server APIs + +58 +00:02:55,242 --> 00:02:58,812 +will help your application +consume less bandwidth. + +59 +00:02:58,812 --> 00:03:02,082 +A nice side effect of this +is that now your application + +60 +00:03:02,082 --> 00:03:05,786 +is power efficient too, since +some processing is now delegated + +61 +00:03:05,786 --> 00:03:09,222 +to your server using +Apple Maps Server APIs. + +62 +00:03:09,222 --> 00:03:12,292 +Now let's take some of +these APIs for a spin. + +63 +00:03:12,292 --> 00:03:14,828 +Let's say we are building +these contact cards + +64 +00:03:14,828 --> 00:03:17,397 +for your store locator +application. + +65 +00:03:17,397 --> 00:03:20,300 +Here we see three stores +with their addresses + +66 +00:03:20,300 --> 00:03:22,736 +and distance +from the customer location. + +67 +00:03:22,736 --> 00:03:25,772 +In this example, +we'll assume that the customer + +68 +00:03:25,772 --> 00:03:29,109 +has provided their location. + +69 +00:03:29,109 --> 00:03:32,846 +For now, let's focus on building +one of these contact cards. + +70 +00:03:32,846 --> 00:03:35,415 +We'll assume that these +addresses are on a server + +71 +00:03:35,415 --> 00:03:41,121 +which stores and serves the +locations of comic bookstores. + +72 +00:03:41,121 --> 00:03:44,191 +There are many ways +to build this, but for a second, + +73 +00:03:44,191 --> 00:03:47,260 +let's assume we don't have +these new server APIs. + +74 +00:03:47,260 --> 00:03:49,763 +What would a basic architecture +look like? + +75 +00:03:49,763 --> 00:03:52,666 +How would your client +application get this data? + +76 +00:03:52,666 --> 00:03:56,570 +In this diagram, our application +is making a call to the server + +77 +00:03:56,570 --> 00:03:58,972 +to get the list +of store addresses. + +78 +00:03:58,972 --> 00:04:01,741 +The back-end server returns +a list of store addresses + +79 +00:04:01,741 --> 00:04:04,511 +to your client device. + +80 +00:04:04,511 --> 00:04:07,514 +Since we don't have +the server APIs in this example, + +81 +00:04:07,514 --> 00:04:10,717 +now our client application +has to perform various actions + +82 +00:04:10,717 --> 00:04:13,520 +on the address +to build the contact card. + +83 +00:04:13,520 --> 00:04:15,355 +To perform a single task, + +84 +00:04:15,355 --> 00:04:17,757 +a client may have to make +multiple calls + +85 +00:04:17,757 --> 00:04:20,127 +to various back-end services. + +86 +00:04:20,127 --> 00:04:23,697 +Here you can see that +the client app is making a call + +87 +00:04:23,697 --> 00:04:25,866 +directly to +the Apple Maps Server, + +88 +00:04:25,866 --> 00:04:29,569 +either by using MapKit +or MapKit JS. + +89 +00:04:29,569 --> 00:04:32,772 +This chattiness between +a client and a back end + +90 +00:04:32,772 --> 00:04:35,075 +can adversely impact +the performance and scale + +91 +00:04:35,075 --> 00:04:36,643 +of the application. + +92 +00:04:36,643 --> 00:04:39,779 +Over a cellular network +with typically high latency, + +93 +00:04:39,779 --> 00:04:43,517 +using individual requests +in this manner is inefficient + +94 +00:04:43,517 --> 00:04:45,585 +and could result +in broken connectivity + +95 +00:04:45,585 --> 00:04:47,487 +or incomplete requests. + +96 +00:04:47,487 --> 00:04:50,190 +While each request +may be done in parallel, + +97 +00:04:50,190 --> 00:04:53,093 +the application must send, +wait, and process data + +98 +00:04:53,093 --> 00:04:55,896 +for each request +all on separate connections + +99 +00:04:55,896 --> 00:04:58,231 +increasing the chance +of failure. + +100 +00:04:58,231 --> 00:05:02,435 +Finally, you'll have to merge +all the responses on the client. + +101 +00:05:02,435 --> 00:05:04,738 +And while +all these calls happen, + +102 +00:05:04,738 --> 00:05:07,340 +you are showing a spinner +to the user. + +103 +00:05:07,340 --> 00:05:10,076 +Plus, the client device +is using more bandwidth + +104 +00:05:10,076 --> 00:05:12,078 +and power for these extra calls. + +105 +00:05:12,078 --> 00:05:14,848 +That is not +a good user experience. + +106 +00:05:14,848 --> 00:05:17,284 +Now, let's look at +a model architecture + +107 +00:05:17,284 --> 00:05:20,120 +with access +to Apple Maps Server APIs. + +108 +00:05:20,120 --> 00:05:23,156 +You can start using your +back-end server as a gateway + +109 +00:05:23,156 --> 00:05:27,894 +to reduce chattiness between +the client and the services. + +110 +00:05:27,894 --> 00:05:31,198 +Just like before, +here we request a list of stores + +111 +00:05:31,198 --> 00:05:34,367 +to be displayed +from your client. + +112 +00:05:34,367 --> 00:05:39,739 +Next, we make a request +from the server to do geocoding. + +113 +00:05:39,739 --> 00:05:42,842 +We then receive responses +for each API + +114 +00:05:42,842 --> 00:05:45,845 +from the Apple Maps Server. + +115 +00:05:45,845 --> 00:05:50,217 +The comic book server combines +the responses from each service + +116 +00:05:50,217 --> 00:05:52,919 +and sends the response +to the application. + +117 +00:05:52,919 --> 00:05:55,655 +This pattern can reduce +the number of requests + +118 +00:05:55,655 --> 00:05:58,458 +that the application makes +to back-end services, + +119 +00:05:58,458 --> 00:06:00,093 +and improve +application performance + +120 +00:06:00,093 --> 00:06:02,162 +over high-latency networks. + +121 +00:06:02,162 --> 00:06:05,899 +In summary, your client +makes one call to your server + +122 +00:06:05,899 --> 00:06:07,867 +to get the list of stores. + +123 +00:06:07,867 --> 00:06:10,337 +Your server then does +the heavy lifting + +124 +00:06:10,337 --> 00:06:13,940 +to make appropriate API calls +to compose a response + +125 +00:06:13,940 --> 00:06:16,810 +most suited for your user. + +126 +00:06:16,810 --> 00:06:19,779 +So let's go back to our +case study example here. + +127 +00:06:19,779 --> 00:06:22,249 +We'll use Geocoding and ETA API + +128 +00:06:22,249 --> 00:06:24,918 +to get the distance +to the store. + +129 +00:06:24,918 --> 00:06:29,256 +We can use the Geocode API to +find the latitude and longitude + +130 +00:06:29,256 --> 00:06:31,825 +for the store addresses, +which we'll later use + +131 +00:06:31,825 --> 00:06:33,627 +for ETA calculations. + +132 +00:06:33,627 --> 00:06:36,429 +In this example, first, +we are going to take + +133 +00:06:36,429 --> 00:06:42,102 +the address for the comic book +store and URL encode it. + +134 +00:06:42,102 --> 00:06:45,338 +Next, we'll use the Geocode API + +135 +00:06:45,338 --> 00:06:50,076 +and pass this URL-encoded +address as a query parameter. + +136 +00:06:50,076 --> 00:06:52,746 +We'll skip over the +authentication details for now + +137 +00:06:52,746 --> 00:06:55,749 +and come back to it +in a few slides. + +138 +00:06:55,749 --> 00:06:58,852 +In the response, you can see +the latitude and longitude + +139 +00:06:58,852 --> 00:07:01,554 +for the address returned. + +140 +00:07:01,554 --> 00:07:03,723 +We'll repeat +the same process to find + +141 +00:07:03,723 --> 00:07:06,993 +the latitude and longitude +for the customer's address. + +142 +00:07:06,993 --> 00:07:10,063 +This will be later used +for ETA calculations. + +143 +00:07:10,063 --> 00:07:13,600 +As you can see, there are +more fields in the response. + +144 +00:07:13,600 --> 00:07:15,268 +I'll link the detailed +documentation + +145 +00:07:15,268 --> 00:07:18,438 +in the Resources section below. + +146 +00:07:18,438 --> 00:07:23,109 +Now, we can set the origin +and destination on the ETA API + +147 +00:07:23,109 --> 00:07:26,946 +with the data we got +from the Geocode API. + +148 +00:07:26,946 --> 00:07:31,584 +As I mentioned before, we have +the origin latitude, longitude + +149 +00:07:31,584 --> 00:07:34,120 +and the destination +latitude, longitude. + +150 +00:07:34,120 --> 00:07:38,124 +We can specify up to 10 +destinations here if needed. + +151 +00:07:38,124 --> 00:07:41,628 +We'll feed that in the ETA API + +152 +00:07:41,628 --> 00:07:45,298 +as origin and as +destination query parameters + +153 +00:07:45,298 --> 00:07:47,701 +which are URL encoded. + +154 +00:07:47,701 --> 00:07:50,970 +The response to the API +is a list of ETAs, + +155 +00:07:50,970 --> 00:07:53,039 +one for each destination +provided. + +156 +00:07:53,039 --> 00:07:55,141 +In this case, +we only have one + +157 +00:07:55,141 --> 00:07:58,678 +since we provided +one destination. + +158 +00:07:58,678 --> 00:08:02,415 +Here for our example, we are +interested in distanceMeters + +159 +00:08:02,415 --> 00:08:04,918 +to calculate the distance +to the store. + +160 +00:08:04,918 --> 00:08:07,921 +With this, we have +all the pieces we need: + +161 +00:08:07,921 --> 00:08:10,757 +the store address +and the distance for the user + +162 +00:08:10,757 --> 00:08:12,625 +to reach your store. + +163 +00:08:12,625 --> 00:08:15,829 +You can also choose +to augment or overlay this data + +164 +00:08:15,829 --> 00:08:19,666 +with your own store information, +like store hours. + +165 +00:08:19,666 --> 00:08:23,136 +In this way, you can leverage +different server APIs + +166 +00:08:23,136 --> 00:08:25,238 +to build your applications. + +167 +00:08:25,238 --> 00:08:28,041 +For other APIs, +please refer to documentation + +168 +00:08:28,041 --> 00:08:29,909 +linked below this talk. + +169 +00:08:29,909 --> 00:08:33,813 +One critical piece we haven't +talked about is authentication. + +170 +00:08:33,813 --> 00:08:37,450 +All the Apple Maps Server APIs +are authenticated. + +171 +00:08:37,450 --> 00:08:41,154 +If you are using MapKit JS, +you are already halfway there. + +172 +00:08:41,154 --> 00:08:45,225 +Apple Maps Server APIs use +the same mechanism as MapKit JS + +173 +00:08:45,225 --> 00:08:47,327 +to authenticate. + +174 +00:08:47,327 --> 00:08:49,629 +First, you'll download +your private key + +175 +00:08:49,629 --> 00:08:51,564 +from your developer account. + +176 +00:08:51,564 --> 00:08:53,299 +You'll then use this private key + +177 +00:08:53,299 --> 00:08:56,703 +to generate a Maps auth token +in JWT format. + +178 +00:08:56,703 --> 00:08:59,038 +There is a detailed doc +about how to generate one + +179 +00:08:59,038 --> 00:09:00,874 +linked below. + +180 +00:09:00,874 --> 00:09:03,076 +You can then exchange +this Maps auth token + +181 +00:09:03,076 --> 00:09:06,713 +using the token API +to get Maps access token. + +182 +00:09:06,713 --> 00:09:10,417 +We'll authenticate the +Maps auth token on the back end + +183 +00:09:10,417 --> 00:09:13,052 +and send back Maps access token. + +184 +00:09:13,052 --> 00:09:15,422 +This is in JWT format +and will be used + +185 +00:09:15,422 --> 00:09:18,792 +for all API interactions. + +186 +00:09:18,792 --> 00:09:22,328 +This access token needs to be +refreshed every 30 minutes + +187 +00:09:22,328 --> 00:09:25,632 +by repeating the highlighted +process here. + +188 +00:09:25,632 --> 00:09:29,235 +Now that we saw how the +authentication flow looks like, + +189 +00:09:29,235 --> 00:09:33,106 +here is a simple example +of how to use the token API + +190 +00:09:33,106 --> 00:09:35,775 +to fetch the access token. + +191 +00:09:35,775 --> 00:09:39,078 +We are using the token API here. + +192 +00:09:39,078 --> 00:09:43,049 +We are passing +the Maps auth token as a header. + +193 +00:09:43,049 --> 00:09:46,019 +You'll get back +a Maps access token + +194 +00:09:46,019 --> 00:09:48,555 +that can be used +to access the API. + +195 +00:09:48,555 --> 00:09:52,158 +This will be in JWT format +and will have standard fields + +196 +00:09:52,158 --> 00:09:55,562 +like expiry, issuedAt, etcetera. + +197 +00:09:55,562 --> 00:09:58,298 +As a convenience, +the expiresInSeconds field + +198 +00:09:58,298 --> 00:10:01,000 +shows for how long +the token is valid for. + +199 +00:10:01,000 --> 00:10:04,737 +In this case, it's 30 minutes. + +200 +00:10:04,737 --> 00:10:07,207 +Keep in mind Maps auth token + +201 +00:10:07,207 --> 00:10:10,310 +is not the same +as Maps access token. + +202 +00:10:10,310 --> 00:10:12,712 +You exchange +the Maps auth token + +203 +00:10:12,712 --> 00:10:15,648 +to get a 30-minute long +Maps access token + +204 +00:10:15,648 --> 00:10:18,251 +to access the server APIs. + +205 +00:10:18,251 --> 00:10:21,054 +Let's take a quick look +at how the API interaction + +206 +00:10:21,054 --> 00:10:24,958 +with Maps access token +looks like. + +207 +00:10:24,958 --> 00:10:29,095 +We'll pass the Maps access token +along with server API call. + +208 +00:10:29,095 --> 00:10:31,931 +It is added as a header +to the API call, + +209 +00:10:31,931 --> 00:10:34,367 +just like we saw +a few slides ago. + +210 +00:10:34,367 --> 00:10:37,971 +The Apple Maps Server will +validate the Maps access token. + +211 +00:10:37,971 --> 00:10:40,073 +Once the validation +is successful, + +212 +00:10:40,073 --> 00:10:44,577 +the Apple Maps Server will +respond with an API response. + +213 +00:10:44,577 --> 00:10:47,847 +Now that I have covered +APIs and authentication, + +214 +00:10:47,847 --> 00:10:50,416 +let me talk about usage limits. + +215 +00:10:50,416 --> 00:10:53,152 +With great power +comes great responsibility, + +216 +00:10:53,152 --> 00:10:55,388 +so use your quota wisely. + +217 +00:10:55,388 --> 00:10:59,359 +There is a daily cap on +how many API calls you can make, + +218 +00:10:59,359 --> 00:11:01,094 +and it's big! + +219 +00:11:01,094 --> 00:11:05,632 +You'll get a quota of 25,000 +service calls per day in total. + +220 +00:11:05,632 --> 00:11:09,736 +Keep in mind, calling services +via MapKit JS and server APIs + +221 +00:11:09,736 --> 00:11:11,371 +use the same quota. + +222 +00:11:11,371 --> 00:11:14,507 +If you need more, +please reach out to us. + +223 +00:11:14,507 --> 00:11:18,011 +So, how do you keep track +of all this? + +224 +00:11:18,011 --> 00:11:23,082 +You can view your usage stats +at the Maps developer dashboard. + +225 +00:11:23,082 --> 00:11:24,884 +Anybody using MapKit JS? + +226 +00:11:24,884 --> 00:11:27,787 +This will look +very familiar to you. + +227 +00:11:27,787 --> 00:11:31,190 +The server API usage +is categorized as Services, + +228 +00:11:31,190 --> 00:11:34,961 +which you can see +highlighted here. + +229 +00:11:34,961 --> 00:11:37,430 +When the daily quota +is exceeded, + +230 +00:11:37,430 --> 00:11:41,868 +which means more than +25,000 server API calls, + +231 +00:11:41,868 --> 00:11:44,370 +we'll start rejecting +new service calls + +232 +00:11:44,370 --> 00:11:47,407 +and respond with +HTTP status 429, + +233 +00:11:47,407 --> 00:11:50,176 +which means too many requests. + +234 +00:11:50,176 --> 00:11:52,445 +You should make sure +that the app experience + +235 +00:11:52,445 --> 00:11:55,381 +degrades gracefully +in such scenarios. + +236 +00:11:55,381 --> 00:11:57,483 +In rare scenarios, +when your service + +237 +00:11:57,483 --> 00:12:00,520 +makes an unusual +amount of requests -- + +238 +00:12:00,520 --> 00:12:03,489 +maybe it's due to some bug in +your code or infrastructure -- + +239 +00:12:03,489 --> 00:12:07,226 +it's possible to get +HTTP status 429 as well. + +240 +00:12:07,226 --> 00:12:10,396 +When you receive HTTP 429, +it is important + +241 +00:12:10,396 --> 00:12:13,666 +not to simply loop repeatedly +in making requests. + +242 +00:12:13,666 --> 00:12:16,569 +A better approach is +to retry with increasing delays + +243 +00:12:16,569 --> 00:12:18,104 +in between attempts. + +244 +00:12:18,104 --> 00:12:21,774 +This approach is known +as exponential backoff. + +245 +00:12:21,774 --> 00:12:24,877 +So, what did we learn today? + +246 +00:12:24,877 --> 00:12:27,547 +We are releasing +four new server APIs. + +247 +00:12:27,547 --> 00:12:30,783 +These APIs are Geocoding, +Reverse Geocoding, + +248 +00:12:30,783 --> 00:12:33,686 +Search, and ETA. + +249 +00:12:33,686 --> 00:12:38,024 +Using these APIs in conjunction +with MapKit and MapKit JS + +250 +00:12:38,024 --> 00:12:40,026 +will help you +better architect your apps + +251 +00:12:40,026 --> 00:12:42,895 +using the Apple Maps stack. + +252 +00:12:42,895 --> 00:12:45,865 +You can optimize redundant +and repetitive calls + +253 +00:12:45,865 --> 00:12:48,334 +by delegating those tasks +to your back-end server + +254 +00:12:48,334 --> 00:12:51,871 +using Apple Maps Server APIs. + +255 +00:12:51,871 --> 00:12:55,541 +Daily quota for these APIs +is 25,000 + +256 +00:12:55,541 --> 00:12:58,745 +and is shared with your +MapKit JS service usage. + +257 +00:12:58,745 --> 00:13:01,848 +And that's the new +Apple Maps Server APIs for you. + +258 +00:13:01,848 --> 00:13:04,550 +Be sure to check out the other +sessions mentioned here + +259 +00:13:04,550 --> 00:13:07,020 +and detailed documentation +linked below. + +260 +00:13:07,020 --> 00:13:10,123 +We look forward to seeing +how you take advantage of them. + +261 +00:13:10,123 --> 00:13:11,457 +Thank you! + +262 +00:13:11,457 --> 00:13:15,461 +♪ + diff --git a/eng/2022 Session 10007 What's new with in-app purchase en.srt b/eng/2022 Session 10007 What's new with in-app purchase en.srt new file mode 100644 index 0000000..d17712f --- /dev/null +++ b/eng/2022 Session 10007 What's new with in-app purchase en.srt @@ -0,0 +1,4981 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,576 +♪ + +3 +00:00:09,576 --> 00:00:10,978 +Dani Chootong: Hello, +and welcome to + +4 +00:00:10,978 --> 00:00:13,213 +"What's new +with in-app purchase." + +5 +00:00:13,213 --> 00:00:16,550 +I'm Dani, and I'm an engineer +on the StoreKit team. + +6 +00:00:16,550 --> 00:00:19,186 +Today I'll be presenting +with my colleague Ian, + +7 +00:00:19,186 --> 00:00:20,854 +and we'll be going over +the new improvements + +8 +00:00:20,854 --> 00:00:23,657 +we're bringing +to in-app purchase this year. + +9 +00:00:23,657 --> 00:00:26,126 +Last year, +we introduced StoreKit 2, + +10 +00:00:26,126 --> 00:00:29,096 +a set of new APIs +designed from the ground up + +11 +00:00:29,096 --> 00:00:32,032 +to make it simple +to integrate in-app purchases. + +12 +00:00:32,032 --> 00:00:34,902 +StoreKit 2 uses +modern language features, + +13 +00:00:34,902 --> 00:00:38,538 +including Swift concurrency +using the async/await pattern. + +14 +00:00:38,538 --> 00:00:39,573 +On the server side, + +15 +00:00:39,573 --> 00:00:41,608 +we complemented these +new StoreKit features + +16 +00:00:41,608 --> 00:00:45,078 +with an entirely new set +of App Store Server endpoints. + +17 +00:00:45,078 --> 00:00:47,214 +These server endpoints +make it easy to retrieve + +18 +00:00:47,214 --> 00:00:49,917 +transaction information +and check subscription status + +19 +00:00:49,917 --> 00:00:52,219 +on your server. + +20 +00:00:52,219 --> 00:00:55,822 +We also released Version 2 of +App Store Server Notifications, + +21 +00:00:55,822 --> 00:00:57,691 +to make tracking +the subscription lifecycle + +22 +00:00:57,691 --> 00:01:00,227 +on your server easier than ever. + +23 +00:01:00,227 --> 00:01:02,996 +Today I'll be going over +these new APIs, + +24 +00:01:02,996 --> 00:01:04,598 +as well as +enhancements we're bringing + +25 +00:01:04,598 --> 00:01:07,467 +to the new StoreKit models. + +26 +00:01:07,467 --> 00:01:09,303 +Then, Ian will walk you through + +27 +00:01:09,303 --> 00:01:11,204 +some exciting new +server updates, + +28 +00:01:11,204 --> 00:01:13,707 +including App Store Server +API enhancements + +29 +00:01:13,707 --> 00:01:17,611 +and brand-new APIs for +App Store Server Notifications. + +30 +00:01:17,611 --> 00:01:20,514 +First, I'll go over +the new App Transaction API + +31 +00:01:20,514 --> 00:01:23,583 +for verifying +the purchase of your app. + +32 +00:01:23,583 --> 00:01:26,420 +Next, I'll dig into some +new properties we've added + +33 +00:01:26,420 --> 00:01:28,989 +to our StoreKit models. + +34 +00:01:28,989 --> 00:01:32,225 +I'll introduce you +to the new SwiftUI friendly APIs + +35 +00:01:32,225 --> 00:01:34,127 +for redeeming +subscription offer codes + +36 +00:01:34,127 --> 00:01:37,230 +and asking a customer +to review your app. + +37 +00:01:37,230 --> 00:01:39,967 +Then, I'll introduce you +to StoreKit Messages, + +38 +00:01:39,967 --> 00:01:41,735 +an API used to display + +39 +00:01:41,735 --> 00:01:44,171 +App Store messages +to your customers. + +40 +00:01:44,171 --> 00:01:46,740 +And finally, I'll go over +an enhancement we're adding + +41 +00:01:46,740 --> 00:01:49,343 +to preserve Application Username +when you're migrating + +42 +00:01:49,343 --> 00:01:52,879 +from the original +to the modern StoreKit APIs. + +43 +00:01:52,879 --> 00:01:55,949 +Throughout this presentation, +I'll be using my favorite app, + +44 +00:01:55,949 --> 00:01:57,017 +Food Truck. + +45 +00:01:57,017 --> 00:01:59,987 +In the Food Truck app, I manage +a pop-up donut food truck + +46 +00:01:59,987 --> 00:02:02,789 +that visits various cities +to make donut deliveries. + +47 +00:02:02,789 --> 00:02:05,392 +So, let's get started! + +48 +00:02:05,392 --> 00:02:06,960 +Meet App Transaction. + +49 +00:02:06,960 --> 00:02:09,196 +The App Transaction +is our new API + +50 +00:02:09,196 --> 00:02:12,432 +for verifying the purchase +of your app. + +51 +00:02:12,432 --> 00:02:14,968 +The App Transaction represents +the signed information + +52 +00:02:14,968 --> 00:02:18,939 +for the purchase of your app +for the device it's running on. + +53 +00:02:18,939 --> 00:02:21,508 +It's signed using JWS, + +54 +00:02:21,508 --> 00:02:24,444 +and it replaces the app detail +portion of the app receipt + +55 +00:02:24,444 --> 00:02:27,247 +from the original StoreKit API. + +56 +00:02:27,247 --> 00:02:29,416 +Just like +transaction verification, + +57 +00:02:29,416 --> 00:02:31,585 +StoreKit performs +automatic verification + +58 +00:02:31,585 --> 00:02:34,888 +of the app transaction for you. + +59 +00:02:34,888 --> 00:02:37,457 +However, if you wish, you can also perform + +60 +00:02:37,457 --> 00:02:39,326 +your own validation. + +61 +00:02:39,326 --> 00:02:43,230 +Validating a JWS signature +is a well-documented standard. + +62 +00:02:43,230 --> 00:02:45,065 +You can refer +to the public documentation + +63 +00:02:45,065 --> 00:02:48,335 +to implement +your own validation. + +64 +00:02:48,335 --> 00:02:50,370 +StoreKit takes care +of automatically updating + +65 +00:02:50,370 --> 00:02:52,672 +the App Transaction +when necessary. + +66 +00:02:52,672 --> 00:02:54,775 +However, in the rare case +that the user + +67 +00:02:54,775 --> 00:02:57,978 +thinks there's something wrong, +it can be refreshed. + +68 +00:02:57,978 --> 00:02:59,613 +You should provide +UI in your app + +69 +00:02:59,613 --> 00:03:03,417 +to allow your customers +to refresh the app transaction. + +70 +00:03:03,417 --> 00:03:06,186 +This should only be used +in response to user action, + +71 +00:03:06,186 --> 00:03:07,988 +as refreshing +the App Transaction + +72 +00:03:07,988 --> 00:03:11,091 +prompts the user +to authenticate. + +73 +00:03:11,091 --> 00:03:15,362 +Preventing fraud isn't the only +reason to love App Transaction. + +74 +00:03:15,362 --> 00:03:18,598 +If you're looking to switch +business models from a paid app + +75 +00:03:18,598 --> 00:03:21,368 +to a free app that offers +in-app purchases, + +76 +00:03:21,368 --> 00:03:23,170 +if you're curious about +which of your customers + +77 +00:03:23,170 --> 00:03:25,172 +preordered your app, + +78 +00:03:25,172 --> 00:03:27,874 +or even if you just want to know +when your app was purchased, + +79 +00:03:27,874 --> 00:03:31,878 +these are all situations you can +handle with App Transaction. + +80 +00:03:31,878 --> 00:03:33,747 +In the app receipt, +the receipt payload + +81 +00:03:33,747 --> 00:03:36,383 +combine the purchase data +about your application + +82 +00:03:36,383 --> 00:03:39,786 +along with all the in-app +purchases that have occurred. + +83 +00:03:39,786 --> 00:03:41,988 +These are now broken up +into two separate components + +84 +00:03:41,988 --> 00:03:43,290 +in StoreKit. + +85 +00:03:43,290 --> 00:03:46,393 +The first of these +is the Transaction History. + +86 +00:03:46,393 --> 00:03:48,495 +StoreKit's transaction APIs +give you insight + +87 +00:03:48,495 --> 00:03:51,064 +into the user's entire +in-app purchase history, + +88 +00:03:51,064 --> 00:03:52,933 +right on the device. + +89 +00:03:52,933 --> 00:03:56,269 +These APIs allow you to find +the exact information you need, + +90 +00:03:56,269 --> 00:03:58,672 +including the user's +latest transactions, + +91 +00:03:58,672 --> 00:04:02,476 +unfinished transactions, +and current entitlements. + +92 +00:04:02,476 --> 00:04:05,278 +If you prefer to perform these +calculations on your server, + +93 +00:04:05,278 --> 00:04:07,347 +you can also get +the user's purchase history + +94 +00:04:07,347 --> 00:04:09,516 +from the App Store Server API. + +95 +00:04:09,516 --> 00:04:11,651 +Ian will have some +exciting updates on this + +96 +00:04:11,651 --> 00:04:13,687 +later this session. + +97 +00:04:13,687 --> 00:04:16,089 +And the second component +is the App Transaction, + +98 +00:04:16,089 --> 00:04:18,058 +which contains the data +you need to make sure + +99 +00:04:18,058 --> 00:04:20,861 +that your app is valid +for the device it's running on. + +100 +00:04:20,861 --> 00:04:23,063 +It's easy to verify +the purchase of your app + +101 +00:04:23,063 --> 00:04:25,599 +using App Transaction, +and in just a moment, + +102 +00:04:25,599 --> 00:04:28,869 +I'll be going over an example +of how you can use it. + +103 +00:04:28,869 --> 00:04:30,537 +But first, +let me give you some background + +104 +00:04:30,537 --> 00:04:32,239 +about my favorite app. + +105 +00:04:32,239 --> 00:04:34,508 +With Food Truck, I'm able +to make donut deliveries, + +106 +00:04:34,508 --> 00:04:36,776 +check in on a basic social feed, + +107 +00:04:36,776 --> 00:04:39,045 +and visualize my sales history. + +108 +00:04:39,045 --> 00:04:41,047 +Keeping all this information +in a database + +109 +00:04:41,047 --> 00:04:45,218 +is an ongoing cost for my app, +so to help me cover the costs, + +110 +00:04:45,218 --> 00:04:47,487 +I'm going to turn +the yearly sales history chart + +111 +00:04:47,487 --> 00:04:49,523 +into a onetime purchase. + +112 +00:04:49,523 --> 00:04:52,792 +Additionally, I want to enhance +the social feed. + +113 +00:04:52,792 --> 00:04:54,628 +So instead of just seeing +what others are saying + +114 +00:04:54,628 --> 00:04:56,630 +about my food truck, +I want to provide the tools + +115 +00:04:56,630 --> 00:04:59,299 +so I can engage +with my customers as well. + +116 +00:04:59,299 --> 00:05:00,967 +This will be +a subscription service, + +117 +00:05:00,967 --> 00:05:04,504 +and I'll have a monthly +and a yearly plan. + +118 +00:05:04,504 --> 00:05:07,507 +Food Truck began as a paid app, +but I'm going to make the switch + +119 +00:05:07,507 --> 00:05:10,310 +to a free app that offers +in-app purchases. + +120 +00:05:10,310 --> 00:05:11,912 +But I don't want +my existing customers + +121 +00:05:11,912 --> 00:05:15,782 +who already purchased Food Truck +to feel left out. + +122 +00:05:15,782 --> 00:05:17,751 +So, I'll be using +the App Transaction + +123 +00:05:17,751 --> 00:05:20,220 +to make sure that the customers +who purchased Food Truck + +124 +00:05:20,220 --> 00:05:24,157 +continue to have access to the +premium content they paid for. + +125 +00:05:24,157 --> 00:05:26,993 +Here's the timeline +for Food Truck. + +126 +00:05:26,993 --> 00:05:29,262 +At the initial release, +Food Truck started out + +127 +00:05:29,262 --> 00:05:31,965 +as a paid app that cost $4.99. + +128 +00:05:31,965 --> 00:05:34,401 +Version 1.0 offered +donut deliveries, + +129 +00:05:34,401 --> 00:05:37,938 +a basic social feed, +and sales history charts. + +130 +00:05:37,938 --> 00:05:39,906 +Later, at the release +of version 8.0, + +131 +00:05:39,906 --> 00:05:42,008 +my business model changed. + +132 +00:05:42,008 --> 00:05:43,810 +Food Truck is now free, +but includes + +133 +00:05:43,810 --> 00:05:47,347 +a variety of in-app purchases +which unlock premium features. + +134 +00:05:47,347 --> 00:05:50,283 +The yearly sales history chart +is now a nonconsumable + +135 +00:05:50,283 --> 00:05:52,485 +onetime purchase, +and now there's + +136 +00:05:52,485 --> 00:05:55,255 +a new subscription service +for a premium social feed + +137 +00:05:55,255 --> 00:05:58,124 +that gives you +advanced engagement tools. + +138 +00:05:58,124 --> 00:06:00,760 +Now let's take a look at +two different types of customers + +139 +00:06:00,760 --> 00:06:03,263 +who might be affected by this. + +140 +00:06:03,263 --> 00:06:06,633 +Alice found out about my +Food Truck app in version 2.5, + +141 +00:06:06,633 --> 00:06:09,069 +and she decided she wants +to share her passion for donuts + +142 +00:06:09,069 --> 00:06:11,137 +in the digital world. + +143 +00:06:11,137 --> 00:06:13,506 +So, she purchased +my app for $4.99 + +144 +00:06:13,506 --> 00:06:16,476 +and began her +donut delivery journey. + +145 +00:06:16,476 --> 00:06:19,512 +A second customer, Bob, finds +out about my Food Truck app + +146 +00:06:19,512 --> 00:06:21,014 +though a friend +and downloads it for free + +147 +00:06:21,014 --> 00:06:23,683 +in the App Store in version 8.2. + +148 +00:06:23,683 --> 00:06:26,219 +In this scenario, Alice, +who purchased my app + +149 +00:06:26,219 --> 00:06:28,521 +before it became free, +should still have access + +150 +00:06:28,521 --> 00:06:33,093 +to all the premium content +she already paid for. + +151 +00:06:33,093 --> 00:06:35,095 +She still has the option +to purchase the premium + +152 +00:06:35,095 --> 00:06:37,764 +social feed subscription, +but I don't want to deny her + +153 +00:06:37,764 --> 00:06:42,569 +the yearly sales history chart +that was initially included. + +154 +00:06:42,569 --> 00:06:44,871 +Bob, however, +got my app for free. + +155 +00:06:44,871 --> 00:06:47,307 +I know then not to unlock +features and content + +156 +00:06:47,307 --> 00:06:49,509 +until they complete +the in-app purchase. + +157 +00:06:49,509 --> 00:06:51,344 +So, let's see how +we can achieve this + +158 +00:06:51,344 --> 00:06:53,880 +with App Transaction in code. + +159 +00:06:53,880 --> 00:06:55,815 +I'll start off +by fetching the app transaction + +160 +00:06:55,815 --> 00:06:59,352 +by calling +AppTransaction.shared. + +161 +00:06:59,352 --> 00:07:01,721 +This call gets me +a VerificationResult + +162 +00:07:01,721 --> 00:07:03,990 +containing my app transaction. + +163 +00:07:03,990 --> 00:07:05,992 +Within the result, +the AppTransaction type + +164 +00:07:05,992 --> 00:07:09,296 +contains the JWS payload. + +165 +00:07:09,296 --> 00:07:11,865 +Next, I'll switch on the result. + +166 +00:07:11,865 --> 00:07:14,134 +If the result is unverified, +this would be a good time + +167 +00:07:14,134 --> 00:07:16,136 +to alert the user +that their app purchase + +168 +00:07:16,136 --> 00:07:18,104 +could not be verified +by the App Store, + +169 +00:07:18,104 --> 00:07:22,142 +and then, I can prompt them +to refresh the app transaction. + +170 +00:07:22,142 --> 00:07:26,046 +At this time, I'll offer +a minimal experience for my app. + +171 +00:07:26,046 --> 00:07:28,281 +If the result is verified, +I'm going to use this as + +172 +00:07:28,281 --> 00:07:31,885 +an opportunity to check whether +the user has purchased my app. + +173 +00:07:31,885 --> 00:07:33,153 +Customers +that purchased my app + +174 +00:07:33,153 --> 00:07:36,289 +should be granted +the services they paid for. + +175 +00:07:36,289 --> 00:07:39,492 +For this, I'll use the original +app version property. + +176 +00:07:39,492 --> 00:07:41,761 +This property lets me know +the app version in which + +177 +00:07:41,761 --> 00:07:46,099 +the customer had downloaded +my app for the very first time. + +178 +00:07:46,099 --> 00:07:49,069 +Version 8.0 is the version +in which my app became free + +179 +00:07:49,069 --> 00:07:51,571 +with in-app purchases. + +180 +00:07:51,571 --> 00:07:53,940 +I'm going to pass the customer's +original app version + +181 +00:07:53,940 --> 00:07:57,010 +to my function which checks +if the user purchased my app + +182 +00:07:57,010 --> 00:07:58,812 +before version 8.0. + +183 +00:07:58,812 --> 00:08:01,081 +And with that, I can make +an informed decision about + +184 +00:08:01,081 --> 00:08:05,919 +how I should go about providing +premium content to my users. + +185 +00:08:05,919 --> 00:08:08,154 +For customers like Alice, +who purchased my app, + +186 +00:08:08,154 --> 00:08:10,490 +I'm going to provide the content +the user is entitled to + +187 +00:08:10,490 --> 00:08:13,093 +that they had +at the time of purchase. + +188 +00:08:13,093 --> 00:08:15,995 +In my case, I'm going to unlock +the yearly sales history chart + +189 +00:08:15,995 --> 00:08:18,331 +for her deliveries. + +190 +00:08:18,331 --> 00:08:20,900 +Also, I want to check +any additional in-app purchases + +191 +00:08:20,900 --> 00:08:25,171 +they may have made +so I can provide that as well. + +192 +00:08:25,171 --> 00:08:27,907 +Otherwise, I can be confident +that the user downloaded my app + +193 +00:08:27,907 --> 00:08:31,044 +after I switched +my business model, like Bob. + +194 +00:08:31,044 --> 00:08:32,078 +This can be a good time + +195 +00:08:32,078 --> 00:08:33,780 +to check the user's +current entitlements + +196 +00:08:33,780 --> 00:08:37,650 +so I can unlock the features +and content they paid for. + +197 +00:08:37,650 --> 00:08:39,152 +And with just +a few lines of code, + +198 +00:08:39,152 --> 00:08:41,755 +I was able to verify +the purchase of my app, + +199 +00:08:41,755 --> 00:08:43,323 +check whether +the user downloaded + +200 +00:08:43,323 --> 00:08:44,924 +the paid version of my app, + +201 +00:08:44,924 --> 00:08:47,494 +and I can immediately start +providing my premium content, + +202 +00:08:47,494 --> 00:08:52,031 +whether the customer +purchased my app or not. + +203 +00:08:52,031 --> 00:08:54,200 +With App Transaction, +you can easily support + +204 +00:08:54,200 --> 00:08:56,403 +your customers +whether they're early supporters + +205 +00:08:56,403 --> 00:08:59,472 +or if they've just recently +downloaded your app. + +206 +00:08:59,472 --> 00:09:01,441 +Now I'd like to move on +to the new properties + +207 +00:09:01,441 --> 00:09:04,344 +we're adding +to our StoreKit models. + +208 +00:09:04,344 --> 00:09:07,414 +The first of these properties +is the price locale. + +209 +00:09:07,414 --> 00:09:10,683 +The price locale is now +included in StoreKit products. + +210 +00:09:10,683 --> 00:09:12,585 +You may already be familiar +with price locale + +211 +00:09:12,585 --> 00:09:16,423 +from interfacing with +our original purchase APIs. + +212 +00:09:16,423 --> 00:09:19,592 +Next, I'll dig into +the server environment property. + +213 +00:09:19,592 --> 00:09:21,661 +Now, you can tell +the server environment + +214 +00:09:21,661 --> 00:09:25,131 +a transaction +or renewal info occurred in. + +215 +00:09:25,131 --> 00:09:27,333 +Then, I'll move onto +the recent subscription + +216 +00:09:27,333 --> 00:09:28,902 +start date property. + +217 +00:09:28,902 --> 00:09:31,471 +You can use this as a tool +to make informed decisions + +218 +00:09:31,471 --> 00:09:35,241 +for your customers based +on their subscription patterns. + +219 +00:09:35,241 --> 00:09:37,844 +And lastly, I'll go over +some special considerations + +220 +00:09:37,844 --> 00:09:39,546 +for these properties +when you're using them + +221 +00:09:39,546 --> 00:09:41,881 +with StoreKit Testing in Xcode. + +222 +00:09:41,881 --> 00:09:43,650 +These properties return +sentinel values + +223 +00:09:43,650 --> 00:09:46,519 +in older operating systems, +and I'll explain what this means + +224 +00:09:46,519 --> 00:09:48,521 +in just a bit. + +225 +00:09:48,521 --> 00:09:51,691 +The StoreKit APIs were designed +with flexibility in mind, + +226 +00:09:51,691 --> 00:09:53,026 +so I'm proud to announce + +227 +00:09:53,026 --> 00:09:55,295 +that you can take advantage +of these new properties + +228 +00:09:55,295 --> 00:09:58,431 +on devices as far back as +last year's operating systems, + +229 +00:09:58,431 --> 00:10:01,901 +even though they did not +originally ship with them. + +230 +00:10:01,901 --> 00:10:04,304 +All you'll need to make +this happen is to use + +231 +00:10:04,304 --> 00:10:07,073 +Xcode 14 to build your app, +and you'll have access + +232 +00:10:07,073 --> 00:10:10,176 +to these properties in +the previous operating systems. + +233 +00:10:10,176 --> 00:10:12,178 +This is possible +because the implementation + +234 +00:10:12,178 --> 00:10:14,514 +for these properties +are compiled into your app, + +235 +00:10:14,514 --> 00:10:16,983 +so when your customers +update to the new version, + +236 +00:10:16,983 --> 00:10:19,185 +they'll be able to get the +benefits of these enhancements + +237 +00:10:19,185 --> 00:10:21,955 +without needing to update +their operating system. + +238 +00:10:21,955 --> 00:10:23,790 +There is one thing +to keep in mind + +239 +00:10:23,790 --> 00:10:26,359 +when using these properties, +though. + +240 +00:10:26,359 --> 00:10:28,328 +These properties +will return sentinel values + +241 +00:10:28,328 --> 00:10:30,396 +when you're using +StoreKit testing in Xcode + +242 +00:10:30,396 --> 00:10:32,532 +in these older +operating systems. + +243 +00:10:32,532 --> 00:10:33,967 +When I say sentinel values, + +244 +00:10:33,967 --> 00:10:36,069 +I'm referring to +placeholder values that signal + +245 +00:10:36,069 --> 00:10:38,538 +that these are not real values +you should work with, + +246 +00:10:38,538 --> 00:10:41,307 +and I'll explain +why this occurs. + +247 +00:10:41,307 --> 00:10:43,543 +The sandbox and production +environments + +248 +00:10:43,543 --> 00:10:45,812 +make use of these properties +by extracting the values + +249 +00:10:45,812 --> 00:10:48,081 +from the App Store +server response. + +250 +00:10:48,081 --> 00:10:50,083 +StoreKit testing +in Xcode, however, + +251 +00:10:50,083 --> 00:10:52,785 +is a local testing environment +that operates independently + +252 +00:10:52,785 --> 00:10:54,821 +from the App Store server. + +253 +00:10:54,821 --> 00:10:57,156 +This means we're not able +to backport the value of these + +254 +00:10:57,156 --> 00:11:00,527 +properties to the previous +operating systems there. + +255 +00:11:00,527 --> 00:11:02,395 +You can easily +get around this limitation + +256 +00:11:02,395 --> 00:11:05,231 +by updating your test device +to a new operating system, + +257 +00:11:05,231 --> 00:11:07,100 +and you'll be all set +to test these values + +258 +00:11:07,100 --> 00:11:08,735 +in the local environment. + +259 +00:11:08,735 --> 00:11:10,970 +Let's discuss some situations +that demonstrate + +260 +00:11:10,970 --> 00:11:13,339 +how you can start using +these new properties, + +261 +00:11:13,339 --> 00:11:15,775 +the first of which +is price locale. + +262 +00:11:15,775 --> 00:11:18,478 +StoreKit products already have +a display price property + +263 +00:11:18,478 --> 00:11:21,281 +to label the purchase price, +but with price locale, + +264 +00:11:21,281 --> 00:11:22,715 +you can format numbers + +265 +00:11:22,715 --> 00:11:25,985 +deriving from +the product's decimal price. + +266 +00:11:25,985 --> 00:11:27,720 +If you have +a yearly subscription, + +267 +00:11:27,720 --> 00:11:29,756 +you might +use this as an opportunity + +268 +00:11:29,756 --> 00:11:32,725 +to show your customers how much +it would cost them per month. + +269 +00:11:32,725 --> 00:11:35,328 +In this example, you can see +that the yearly subscription + +270 +00:11:35,328 --> 00:11:38,598 +amounts to $4.17 per month. + +271 +00:11:38,598 --> 00:11:40,199 +Or perhaps +you'd want to show them + +272 +00:11:40,199 --> 00:11:42,468 +how much they would save if they +purchased your yearly service + +273 +00:11:42,468 --> 00:11:44,404 +over your monthly service. + +274 +00:11:44,404 --> 00:11:46,606 +With this information, +your customers can make + +275 +00:11:46,606 --> 00:11:48,474 +informed decisions +when they're considering + +276 +00:11:48,474 --> 00:11:51,144 +your purchase options. + +277 +00:11:51,144 --> 00:11:54,013 +Now, let's move on +to the environment property. + +278 +00:11:54,013 --> 00:11:55,515 +The environment property +is available + +279 +00:11:55,515 --> 00:11:57,917 +in the Transaction +and renewal Info. + +280 +00:11:57,917 --> 00:11:59,919 +This property tells you +the server environment + +281 +00:11:59,919 --> 00:12:02,989 +in which the transaction +or renewal info originated in, + +282 +00:12:02,989 --> 00:12:06,092 +which could be Xcode, +sandbox, or production. + +283 +00:12:06,092 --> 00:12:07,327 +Your app may communicate + +284 +00:12:07,327 --> 00:12:09,729 +transaction information +to your server after a customer + +285 +00:12:09,729 --> 00:12:13,132 +makes a purchase +for bookkeeping and analytics. + +286 +00:12:13,132 --> 00:12:15,301 +When your app generates +these transactions, + +287 +00:12:15,301 --> 00:12:18,171 +it could be from any one +of these server environments. + +288 +00:12:18,171 --> 00:12:19,672 +Like most of you, +I don't want to add noise + +289 +00:12:19,672 --> 00:12:22,842 +to my analytics +with irrelevant test data. + +290 +00:12:22,842 --> 00:12:25,044 +So, knowing the environment +can help you + +291 +00:12:25,044 --> 00:12:27,614 +filter out unnecessary +information from being sent up + +292 +00:12:27,614 --> 00:12:29,148 +to your server. + +293 +00:12:29,148 --> 00:12:30,350 +Finally, let's take a look at + +294 +00:12:30,350 --> 00:12:32,785 +the recent subscription +start date. + +295 +00:12:32,785 --> 00:12:34,954 +The recent subscription +start date is available + +296 +00:12:34,954 --> 00:12:37,624 +within a product's +subscription information, + +297 +00:12:37,624 --> 00:12:39,325 +and it represents +the most recent period + +298 +00:12:39,325 --> 00:12:41,594 +of continuous subscription. + +299 +00:12:41,594 --> 00:12:44,230 +A subscription is considered +continuous if there is no more + +300 +00:12:44,230 --> 00:12:48,234 +than a 60-day gap between +any two subscribed periods. + +301 +00:12:48,234 --> 00:12:50,269 +Keep in mind that this period +can contain gaps + +302 +00:12:50,269 --> 00:12:53,239 +where the customer was not +subscribed to your product, + +303 +00:12:53,239 --> 00:12:54,874 +so don't use this +as an indicator + +304 +00:12:54,874 --> 00:12:59,212 +for the number of days +a customer has been subscribed. + +305 +00:12:59,212 --> 00:13:01,514 +The recent subscription +start date can help you + +306 +00:13:01,514 --> 00:13:05,485 +determine a pattern of loyalty +between you and your customers. + +307 +00:13:05,485 --> 00:13:07,954 +For your loyal customers, +you might offer them a reward + +308 +00:13:07,954 --> 00:13:10,890 +as a way to keep them +engaged with your product. + +309 +00:13:10,890 --> 00:13:12,291 +Or if you notice that a customer + +310 +00:13:12,291 --> 00:13:14,193 +has unsubscribed +from your service, + +311 +00:13:14,193 --> 00:13:16,996 +you can use it as a chance +to win back a lapsed customer + +312 +00:13:16,996 --> 00:13:21,634 +by offering them an incentive to +start using your product again. + +313 +00:13:21,634 --> 00:13:23,703 +I mentioned earlier +that we'd take a closer look + +314 +00:13:23,703 --> 00:13:26,572 +at the sentinel values +for these properties. + +315 +00:13:26,572 --> 00:13:28,841 +As a reminder, +when I say sentinel values, + +316 +00:13:28,841 --> 00:13:30,510 +I'm referring +to placeholder values + +317 +00:13:30,510 --> 00:13:34,781 +that serve as an indicator +of the absence of a real value. + +318 +00:13:34,781 --> 00:13:38,251 +The sentinel values for these +properties are easy to identify. + +319 +00:13:38,251 --> 00:13:39,986 +When you're dealing +with price locale, + +320 +00:13:39,986 --> 00:13:45,525 +the sentinel value is a locale +with the identifier xx_XX. + +321 +00:13:45,525 --> 00:13:49,062 +For the environment property, +it'll be an empty string. + +322 +00:13:49,062 --> 00:13:51,698 +And finally, for the recent +subscription start date, + +323 +00:13:51,698 --> 00:13:54,300 +this value is Date.distantPast. + +324 +00:13:54,300 --> 00:13:56,502 +Luckily, the occurrence +of these sentinel values + +325 +00:13:56,502 --> 00:13:58,738 +are predictable -- +you'll only encounter them + +326 +00:13:58,738 --> 00:14:00,707 +if you're using +StoreKit testing in Xcode + +327 +00:14:00,707 --> 00:14:02,442 +in older operating systems, + +328 +00:14:02,442 --> 00:14:05,878 +and you can get around this +by updating your test device. + +329 +00:14:05,878 --> 00:14:07,513 +So now you've seen +the enhancements + +330 +00:14:07,513 --> 00:14:09,649 +we've made +to our StoreKit models. + +331 +00:14:09,649 --> 00:14:11,684 +And my favorite part is, +they're backward compatible + +332 +00:14:11,684 --> 00:14:13,720 +all the way back +to the operating system + +333 +00:14:13,720 --> 00:14:15,822 +in which the models +were introduced, + +334 +00:14:15,822 --> 00:14:17,824 +so your customers can see +the benefits right away + +335 +00:14:17,824 --> 00:14:19,559 +just by updating your app. + +336 +00:14:19,559 --> 00:14:22,195 +When you perform arithmetic +with price values, + +337 +00:14:22,195 --> 00:14:24,430 +the price locale +helps you correctly format it + +338 +00:14:24,430 --> 00:14:26,799 +so that it matches +the App Store's locale. + +339 +00:14:26,799 --> 00:14:29,268 +For transactions +and subscription information, + +340 +00:14:29,268 --> 00:14:30,503 +the environment +tells you exactly + +341 +00:14:30,503 --> 00:14:32,271 +where they originated from, + +342 +00:14:32,271 --> 00:14:33,973 +so if you store this data +on your server, + +343 +00:14:33,973 --> 00:14:37,376 +you can act on it accordingly +depending on the environment. + +344 +00:14:37,376 --> 00:14:40,012 +The recent subscription +start date helps you understand + +345 +00:14:40,012 --> 00:14:42,482 +customer loyalty, so you can +tailor specific offers + +346 +00:14:42,482 --> 00:14:45,084 +to long-time customers, +or maybe you can provide + +347 +00:14:45,084 --> 00:14:49,422 +an incentive for customers +who have unsubscribed. + +348 +00:14:49,422 --> 00:14:52,692 +And in case you were wondering, +yes, the environment + +349 +00:14:52,692 --> 00:14:55,528 +and recent subscription +start date are also available + +350 +00:14:55,528 --> 00:14:59,031 +via App Store Server API and +App Store Server Notifications, + +351 +00:14:59,031 --> 00:15:01,100 +which Ian will discuss. + +352 +00:15:01,100 --> 00:15:04,470 +Now I'd like talk about the +new SwiftUI APIs we're providing + +353 +00:15:04,470 --> 00:15:08,107 +for redeeming offer codes +and requesting a review. + +354 +00:15:08,107 --> 00:15:11,244 +Offer codes can help +you acquire, retain, + +355 +00:15:11,244 --> 00:15:14,080 +and win back subscribers +by providing subscriptions + +356 +00:15:14,080 --> 00:15:16,983 +at a discount +or free for a limited time. + +357 +00:15:16,983 --> 00:15:19,452 +Now in App Store Connect, +you can create uniquely named + +358 +00:15:19,452 --> 00:15:20,820 +custom codes. + +359 +00:15:20,820 --> 00:15:23,489 +There, you can set +a maximum redemption limit + +360 +00:15:23,489 --> 00:15:26,526 +and you can choose whether +or not to set an expiration. + +361 +00:15:26,526 --> 00:15:28,995 +Let's look at the SwiftUI +implementation to present + +362 +00:15:28,995 --> 00:15:32,365 +an offer code redemption sheet +straight from your app. + +363 +00:15:32,365 --> 00:15:34,033 +Here, I've got a SwiftUI view + +364 +00:15:34,033 --> 00:15:36,836 +with a button to trigger +the offer code redeem sheet. + +365 +00:15:36,836 --> 00:15:38,671 +The offer code redemption sheet + +366 +00:15:38,671 --> 00:15:42,842 +now has its own +view modifier in SwiftUI. + +367 +00:15:42,842 --> 00:15:44,644 +The view modifier +is easy to use, + +368 +00:15:44,644 --> 00:15:47,914 +it just needs a binding Boolean +to start the process. + +369 +00:15:47,914 --> 00:15:49,882 +And once the offer code sheet +is dismissed, + +370 +00:15:49,882 --> 00:15:51,050 +you'll get a result +representing + +371 +00:15:51,050 --> 00:15:54,954 +whether or not the sheet +presented successfully. + +372 +00:15:54,954 --> 00:15:57,557 +When a customer redeems +an offer code for your app, + +373 +00:15:57,557 --> 00:15:58,591 +the resulting transaction + +374 +00:15:58,591 --> 00:16:02,061 +is sent +to the transaction listener. + +375 +00:16:02,061 --> 00:16:04,230 +So, be sure to set up +a transaction listener + +376 +00:16:04,230 --> 00:16:06,232 +as soon as your app launches +to receive new + +377 +00:16:06,232 --> 00:16:09,969 +and updated transactions +while your app is running. + +378 +00:16:09,969 --> 00:16:14,841 +The offer code view modifier +is available starting on iOS 16. + +379 +00:16:14,841 --> 00:16:18,477 +Next, I'd like to talk about +updates to request review. + +380 +00:16:18,477 --> 00:16:21,280 +Getting customer feedback +is important. + +381 +00:16:21,280 --> 00:16:23,549 +Potential new customers +might use reviews + +382 +00:16:23,549 --> 00:16:27,153 +as a deciding factor in their +decision to download your app. + +383 +00:16:27,153 --> 00:16:29,355 +Others might want to leave +a review as a way to provide + +384 +00:16:29,355 --> 00:16:32,158 +feedback or suggestions. + +385 +00:16:32,158 --> 00:16:34,760 +Either way, we want to give you +the tools to make it easy + +386 +00:16:34,760 --> 00:16:37,296 +for you to request +a rating from your customers, + +387 +00:16:37,296 --> 00:16:39,065 +so you can let them know +you're listening + +388 +00:16:39,065 --> 00:16:41,467 +and you can continue +engaging with them. + +389 +00:16:41,467 --> 00:16:44,070 +Let's review the code. + +390 +00:16:44,070 --> 00:16:45,271 +Here I have a very simple view + +391 +00:16:45,271 --> 00:16:48,274 +to demonstrate +the Request Review APIs. + +392 +00:16:48,274 --> 00:16:50,743 +In SwiftUI, +there's now an environment value + +393 +00:16:50,743 --> 00:16:52,511 +called requestReview. + +394 +00:16:52,511 --> 00:16:54,313 +You can use this value +to get an instance + +395 +00:16:54,313 --> 00:16:56,549 +of the RequestReviewAction, + +396 +00:16:56,549 --> 00:16:58,417 +and when you're ready +to request a rating, + +397 +00:16:58,417 --> 00:17:00,419 +simply call the instance +as a function + +398 +00:17:00,419 --> 00:17:03,522 +to request to display +the review prompt. + +399 +00:17:03,522 --> 00:17:05,191 +You can decide +the right time to request + +400 +00:17:05,191 --> 00:17:06,826 +a review for your app. + +401 +00:17:06,826 --> 00:17:09,428 +However, you should be aware +that the prompt will only be + +402 +00:17:09,428 --> 00:17:12,031 +displayed to customers +a maximum of three times + +403 +00:17:12,031 --> 00:17:14,901 +within a 365-day period. + +404 +00:17:14,901 --> 00:17:16,502 +And you shouldn't ask customers + +405 +00:17:16,502 --> 00:17:19,739 +to review the same version +of your app multiple times. + +406 +00:17:19,739 --> 00:17:22,541 +Avoid interrupting customers +with a review prompt. + +407 +00:17:22,541 --> 00:17:24,810 +A good time to ask for a review +could be after they've + +408 +00:17:24,810 --> 00:17:27,813 +had a positive interaction, +such as completing a purchase + +409 +00:17:27,813 --> 00:17:31,117 +on an e-commerce app, +or completing a level in a game. + +410 +00:17:31,117 --> 00:17:33,853 +Finally, customers can disable +requests from ever appearing + +411 +00:17:33,853 --> 00:17:35,922 +on their device, +so you shouldn't request + +412 +00:17:35,922 --> 00:17:39,926 +a review as a result +of a user action. + +413 +00:17:39,926 --> 00:17:42,194 +These APIs are really +going to come in handy + +414 +00:17:42,194 --> 00:17:43,663 +for your SwiftUI apps. + +415 +00:17:43,663 --> 00:17:45,564 +Next, +I'd like to introduce you + +416 +00:17:45,564 --> 00:17:48,467 +to our new API +for StoreKit messages. + +417 +00:17:48,467 --> 00:17:51,070 +A StoreKit message represents +a sheet that appears over + +418 +00:17:51,070 --> 00:17:54,807 +your app to display important +information to the user. + +419 +00:17:54,807 --> 00:17:57,243 +Messages are vended +by the App Store. + +420 +00:17:57,243 --> 00:17:58,744 +Each message has a reason, + +421 +00:17:58,744 --> 00:18:01,881 +which is included +in the message metadata. + +422 +00:18:01,881 --> 00:18:03,716 +StoreKit messages are retrieved + +423 +00:18:03,716 --> 00:18:05,584 +when your app +comes to the foreground. + +424 +00:18:05,584 --> 00:18:07,320 +As an example, +let's take a look at + +425 +00:18:07,320 --> 00:18:10,856 +one of the message reasons -- +price increase consent. + +426 +00:18:10,856 --> 00:18:13,225 +When you increase +the price of a subscription + +427 +00:18:13,225 --> 00:18:15,962 +and it requires user consent, +the App Store will inform + +428 +00:18:15,962 --> 00:18:19,832 +affected subscribers through +email, push notification, + +429 +00:18:19,832 --> 00:18:21,867 +and an in-app +price consent sheet. + +430 +00:18:21,867 --> 00:18:23,970 +In this case, the App Store +requires that the user + +431 +00:18:23,970 --> 00:18:25,905 +agrees to the new price +of your subscription + +432 +00:18:25,905 --> 00:18:28,441 +before it renews +at the higher price. + +433 +00:18:28,441 --> 00:18:30,977 +So, if you decide to charge +more for your subscription, + +434 +00:18:30,977 --> 00:18:32,979 +a price increase consent sheet +may appear + +435 +00:18:32,979 --> 00:18:36,315 +when a user opens your app if +they haven't already responded + +436 +00:18:36,315 --> 00:18:38,284 +to your price increase. + +437 +00:18:38,284 --> 00:18:40,653 +By default, StoreKit messages +appear over your app + +438 +00:18:40,653 --> 00:18:42,989 +when the user brings +your app to the foreground, + +439 +00:18:42,989 --> 00:18:44,890 +and it may ask the user +to take some action + +440 +00:18:44,890 --> 00:18:46,692 +relating to your app. + +441 +00:18:46,692 --> 00:18:48,494 +Let's review this. + +442 +00:18:48,494 --> 00:18:51,297 +The entire process +starts with your app. + +443 +00:18:51,297 --> 00:18:53,466 +When your app +enters the foreground, + +444 +00:18:53,466 --> 00:18:54,633 +StoreKit knows to check + +445 +00:18:54,633 --> 00:18:56,902 +if there's pending messages +to display. + +446 +00:18:56,902 --> 00:19:00,006 +And if there are, StoreKit +checks in with the App Store. + +447 +00:19:00,006 --> 00:19:01,907 +The App Store returns +information about the message + +448 +00:19:01,907 --> 00:19:03,676 +to StoreKit. + +449 +00:19:03,676 --> 00:19:05,578 +At this time, +StoreKit checks whether your app + +450 +00:19:05,578 --> 00:19:07,613 +is set up to receive messages. + +451 +00:19:07,613 --> 00:19:10,649 +You can do this by setting up +a message listener in your app, + +452 +00:19:10,649 --> 00:19:12,485 +which I'll get into shortly. + +453 +00:19:12,485 --> 00:19:14,720 +If your app has set up +a message listener, + +454 +00:19:14,720 --> 00:19:17,923 +StoreKit sends information +about the message to your app. + +455 +00:19:17,923 --> 00:19:20,760 +Now's your chance to decide +whether or not it's a good time + +456 +00:19:20,760 --> 00:19:22,795 +for your app +to display the message, + +457 +00:19:22,795 --> 00:19:25,498 +or if you want to defer +the presentation for later. + +458 +00:19:25,498 --> 00:19:28,134 +If you don't set up +a message listener, + +459 +00:19:28,134 --> 00:19:29,835 +StoreKit displays +the message right away + +460 +00:19:29,835 --> 00:19:32,338 +by presenting the message sheet +over your app. + +461 +00:19:32,338 --> 00:19:34,407 +I'll go over +how to do this in code. + +462 +00:19:34,407 --> 00:19:36,409 +But before I do that, +I'll explain a situation + +463 +00:19:36,409 --> 00:19:38,778 +in which it would be useful +to control the presentation + +464 +00:19:38,778 --> 00:19:41,147 +of an App Store message. + +465 +00:19:41,147 --> 00:19:43,449 +In the Food Truck app, +I'm able to customize the donuts + +466 +00:19:43,449 --> 00:19:45,484 +I'm delivering +to different cities. + +467 +00:19:45,484 --> 00:19:48,087 +If a message gets delivered +to my app during this time, + +468 +00:19:48,087 --> 00:19:50,289 +it would be confusing +to the user if they're suddenly + +469 +00:19:50,289 --> 00:19:53,459 +interrupted by a message sheet, +so I'm going to be implementing + +470 +00:19:53,459 --> 00:19:56,128 +the messages API +to make sure this doesn't happen + +471 +00:19:56,128 --> 00:19:59,331 +by controlling when incoming +messages are presented. + +472 +00:19:59,331 --> 00:20:01,534 +Now let's get into the code. + +473 +00:20:01,534 --> 00:20:04,303 +Here, I have a simple view +for the donut editor. + +474 +00:20:04,303 --> 00:20:06,605 +As I mentioned earlier, +pending messages are sent + +475 +00:20:06,605 --> 00:20:09,141 +each time your app +comes to the foreground. + +476 +00:20:09,141 --> 00:20:12,011 +So, I want to set up +a message listener in each view + +477 +00:20:12,011 --> 00:20:15,781 +in which I want to defer +the presentation of a message. + +478 +00:20:15,781 --> 00:20:17,983 +I'll add a binding array +to collect all the messages + +479 +00:20:17,983 --> 00:20:20,920 +that are delivered to my app +while I'm in the editing view. + +480 +00:20:20,920 --> 00:20:24,090 +This is important, because if I +don't set up a message listener, + +481 +00:20:24,090 --> 00:20:26,759 +StoreKit is going to display +the message sheet right away + +482 +00:20:26,759 --> 00:20:28,961 +when my app +comes to the foreground. + +483 +00:20:28,961 --> 00:20:32,531 +As soon as the view appears, +I set up my message listener. + +484 +00:20:32,531 --> 00:20:34,667 +I'll do this by setting up +a task that iterates + +485 +00:20:34,667 --> 00:20:37,503 +over a static property +on the message type. + +486 +00:20:37,503 --> 00:20:39,538 +This property +is an async sequence, + +487 +00:20:39,538 --> 00:20:42,675 +and I'm able to receive messages +as they come in. + +488 +00:20:42,675 --> 00:20:44,443 +For my use case, +I'm going to save the message + +489 +00:20:44,443 --> 00:20:46,745 +in the pendingMessages array. + +490 +00:20:46,745 --> 00:20:48,647 +Since pending messages +get delivered each time + +491 +00:20:48,647 --> 00:20:50,749 +your app enters the foreground, +your app could receive + +492 +00:20:50,749 --> 00:20:53,486 +the same message more than once, +so I have this condition + +493 +00:20:53,486 --> 00:20:56,755 +to avoid adding +duplicate messages to my array. + +494 +00:20:56,755 --> 00:20:58,757 +Then, once the view dismisses, + +495 +00:20:58,757 --> 00:21:01,760 +I'll display the messages +in the parent view. + +496 +00:21:01,760 --> 00:21:03,729 +This is the parent view +which holds a navigation link + +497 +00:21:03,729 --> 00:21:05,498 +to the donut editor. + +498 +00:21:05,498 --> 00:21:07,433 +Here, I've collected +all the pending messages + +499 +00:21:07,433 --> 00:21:10,669 +I need to display +in this pendingMessages array. + +500 +00:21:10,669 --> 00:21:13,506 +So how do I display +these pending messages? + +501 +00:21:13,506 --> 00:21:15,174 +Well, now there's +an environment value + +502 +00:21:15,174 --> 00:21:17,243 +displayStoreKitMessage. + +503 +00:21:17,243 --> 00:21:19,879 +This gets you an instance +of a DisplayMessageAction, + +504 +00:21:19,879 --> 00:21:23,382 +which you can then use +to display a given message. + +505 +00:21:23,382 --> 00:21:24,416 +When the view appears, + +506 +00:21:24,416 --> 00:21:25,985 +I'll iterate through +the pending messages + +507 +00:21:25,985 --> 00:21:27,853 +and call displayStoreKitMessage + +508 +00:21:27,853 --> 00:21:30,389 +passing in the message +I want to display. + +509 +00:21:30,389 --> 00:21:34,693 +StoreKit takes care +of presenting the message sheet. + +510 +00:21:34,693 --> 00:21:36,195 +Earlier, I mentioned +that the same message + +511 +00:21:36,195 --> 00:21:38,797 +may get delivered to your app +more than once. + +512 +00:21:38,797 --> 00:21:40,866 +That's because a message +doesn't get marked as read + +513 +00:21:40,866 --> 00:21:42,935 +until it's presented +to the user. + +514 +00:21:42,935 --> 00:21:45,638 +So, StoreKit makes sure +that each unique message + +515 +00:21:45,638 --> 00:21:48,440 +is only presented once. + +516 +00:21:48,440 --> 00:21:50,476 +And that was +a quick implementation + +517 +00:21:50,476 --> 00:21:52,611 +of the Messages API. + +518 +00:21:52,611 --> 00:21:55,848 +Remember, StoreKit messages +are sent to your app each time + +519 +00:21:55,848 --> 00:21:58,350 +it comes to the foreground, +so you'll want to set up + +520 +00:21:58,350 --> 00:22:01,187 +a message listener in each view +in which you want to control + +521 +00:22:01,187 --> 00:22:04,256 +the timing of when +the messages are presented. + +522 +00:22:04,256 --> 00:22:07,293 +You can ensure customers have a +great experience by making sure + +523 +00:22:07,293 --> 00:22:10,796 +message sheets don't appear +at unexpected moments. + +524 +00:22:10,796 --> 00:22:12,565 +Or perhaps you want +to tailor your logic + +525 +00:22:12,565 --> 00:22:14,366 +for certain messages types. + +526 +00:22:14,366 --> 00:22:16,368 +With a price increase +consent message, + +527 +00:22:16,368 --> 00:22:17,803 +you may want to educate +your customer + +528 +00:22:17,803 --> 00:22:19,672 +about the additional value +you're providing + +529 +00:22:19,672 --> 00:22:23,075 +before the price increase +consent sheet appears. + +530 +00:22:23,075 --> 00:22:25,711 +Finally, let's review +how StoreKit preserves + +531 +00:22:25,711 --> 00:22:28,280 +the applicationUsername +as an appAccountToken + +532 +00:22:28,280 --> 00:22:31,183 +after a user makes a purchase. + +533 +00:22:31,183 --> 00:22:34,086 +If you have a user account +system on your server, + +534 +00:22:34,086 --> 00:22:35,888 +chances are +you're already making use + +535 +00:22:35,888 --> 00:22:38,424 +of the applicationUsername +property. + +536 +00:22:38,424 --> 00:22:41,360 +The applicationUsername +is a string that you create + +537 +00:22:41,360 --> 00:22:45,831 +to associate a transaction with +a user account on your service. + +538 +00:22:45,831 --> 00:22:48,234 +In the original API +for in-app purchase, + +539 +00:22:48,234 --> 00:22:50,936 +you set the applicationUsername +value when you add a payment + +540 +00:22:50,936 --> 00:22:53,339 +to the payment queue. + +541 +00:22:53,339 --> 00:22:56,809 +Although the applicationUsername +accepts any string, + +542 +00:22:56,809 --> 00:22:59,211 +we recommend that you provide +the string representation + +543 +00:22:59,211 --> 00:23:01,814 +of a UUID. + +544 +00:23:01,814 --> 00:23:04,183 +When you provide it +a UUID string, + +545 +00:23:04,183 --> 00:23:06,085 +StoreKit persists +the value and you'll see it + +546 +00:23:06,085 --> 00:23:08,520 +in the transaction +that the queue updates. + +547 +00:23:08,520 --> 00:23:10,456 +If you don't provide +a UUID string + +548 +00:23:10,456 --> 00:23:13,792 +for the applicationUsername, +StoreKit may not persist it. + +549 +00:23:13,792 --> 00:23:15,894 +There's no guarantee +the value will persist + +550 +00:23:15,894 --> 00:23:18,697 +between the time you add the +payment transaction to the queue + +551 +00:23:18,697 --> 00:23:21,934 +and when the queue updates +the transaction. + +552 +00:23:21,934 --> 00:23:25,070 +When you provide the string +representation of a UUID, + +553 +00:23:25,070 --> 00:23:27,306 +you can identify which of your +app's user accounts + +554 +00:23:27,306 --> 00:23:30,409 +began and completed +a transaction. + +555 +00:23:30,409 --> 00:23:33,312 +In the modern StoreKit APIs, +we implement this concept + +556 +00:23:33,312 --> 00:23:35,881 +as a purchase option +called appAccountToken + +557 +00:23:35,881 --> 00:23:39,385 +and it requires a UUID format. + +558 +00:23:39,385 --> 00:23:41,620 +Now, when you set +the applicationUsername + +559 +00:23:41,620 --> 00:23:44,523 +to a UUID string during payment, + +560 +00:23:44,523 --> 00:23:47,760 +the App Store server stores it +as an appAccountToken. + +561 +00:23:47,760 --> 00:23:51,030 +So you'll see its UUID appear +in the signed transaction info + +562 +00:23:51,030 --> 00:23:53,265 +returned by +the App Store Server API + +563 +00:23:53,265 --> 00:23:57,336 +and in V2 App Store +Server Notifications. + +564 +00:23:57,336 --> 00:24:00,539 +And as a UUID, it's compatible +with the appAccountToken + +565 +00:24:00,539 --> 00:24:03,909 +in the modern +StoreKit transaction APIs. + +566 +00:24:03,909 --> 00:24:06,445 +So, now you can be sure that +when you update your codebase + +567 +00:24:06,445 --> 00:24:08,247 +to the modern StoreKit APIs, + +568 +00:24:08,247 --> 00:24:11,083 +the UUID you used +for the applicationUsername + +569 +00:24:11,083 --> 00:24:12,985 +is preserved +as an appAccountToken + +570 +00:24:12,985 --> 00:24:15,788 +in the StoreKit transactions. + +571 +00:24:15,788 --> 00:24:18,123 +We touched on +a lot of things today. + +572 +00:24:18,123 --> 00:24:20,426 +Before moving on +to the server updates, + +573 +00:24:20,426 --> 00:24:24,263 +let's review this year's +StoreKit updates. + +574 +00:24:24,263 --> 00:24:26,398 +We discussed validating +your app's purchase + +575 +00:24:26,398 --> 00:24:28,033 +with App Transaction, + +576 +00:24:28,033 --> 00:24:31,603 +redeeming an offer code and +requesting a review in SwiftUI, + +577 +00:24:31,603 --> 00:24:35,407 +and controlling the presentation +of StoreKit messages. + +578 +00:24:35,407 --> 00:24:38,577 +We talked about +new price locale, environment, + +579 +00:24:38,577 --> 00:24:41,714 +and recent subscription +start date properties. + +580 +00:24:41,714 --> 00:24:43,782 +And, we went over +the importance of using + +581 +00:24:43,782 --> 00:24:48,087 +a string representation of a +UUID for the applicationUsername + +582 +00:24:48,087 --> 00:24:50,923 +to persist it +as an app account token. + +583 +00:24:50,923 --> 00:24:53,659 +I highly recommend +you check out our other session + +584 +00:24:53,659 --> 00:24:56,061 +"What's new +in StoreKit testing." + +585 +00:24:56,061 --> 00:24:58,897 +And if you need a refresher +on the StoreKit 2 APIs, + +586 +00:24:58,897 --> 00:25:01,934 +check out last year's session +"Meet StoreKit 2." + +587 +00:25:01,934 --> 00:25:03,635 +Now I'd like +to hand it over to Ian + +588 +00:25:03,635 --> 00:25:06,672 +to walk you through the updates +to the App Store server. + +589 +00:25:06,672 --> 00:25:07,973 +Ian Zanger: Thanks, Dani. + +590 +00:25:07,973 --> 00:25:09,775 +Hi, everyone. +My name is Ian, + +591 +00:25:09,775 --> 00:25:12,911 +and I'm an engineer +on the App Store Server team. + +592 +00:25:12,911 --> 00:25:14,179 +Now that you've heard the latest + +593 +00:25:14,179 --> 00:25:16,415 +about in-app purchase +with StoreKit, + +594 +00:25:16,415 --> 00:25:19,251 +I'm going to switch gears +and talk about the server. + +595 +00:25:19,251 --> 00:25:23,222 +First, I'll review some recent +developments from the past year + +596 +00:25:23,222 --> 00:25:25,557 +before moving on +to some exciting new updates + +597 +00:25:25,557 --> 00:25:27,860 +coming to +the App Store Server API + +598 +00:25:27,860 --> 00:25:32,297 +and App Store Server +Notifications Version 2. + +599 +00:25:32,297 --> 00:25:34,533 +Let's get started. + +600 +00:25:34,533 --> 00:25:36,068 +Last year was big. + +601 +00:25:36,068 --> 00:25:39,037 +We brought you an entirely new +suite of endpoints + +602 +00:25:39,037 --> 00:25:41,006 +with the App Store Server API + +603 +00:25:41,006 --> 00:25:44,343 +and App Store Server +Notifications V2, + +604 +00:25:44,343 --> 00:25:46,578 +including full +sandbox testing support + +605 +00:25:46,578 --> 00:25:48,814 +for all these new features. + +606 +00:25:48,814 --> 00:25:52,651 +We shared how you can use the +Get Transaction History endpoint + +607 +00:25:52,651 --> 00:25:55,888 +to get the full history +of a user's in-app purchases, + +608 +00:25:55,888 --> 00:25:59,091 +or the Get All Subscription +Statuses endpoint + +609 +00:25:59,091 --> 00:26:01,059 +to stay up to date +with the current state + +610 +00:26:01,059 --> 00:26:03,495 +of a user's subscriptions. + +611 +00:26:03,495 --> 00:26:05,898 +Both of these endpoints +conveniently key + +612 +00:26:05,898 --> 00:26:09,168 +off of a user's +originalTransactionId, + +613 +00:26:09,168 --> 00:26:11,603 +so you can access +this trove of data + +614 +00:26:11,603 --> 00:26:14,807 +by storing just +this one simple value. + +615 +00:26:14,807 --> 00:26:16,775 +We also covered how version 2 + +616 +00:26:16,775 --> 00:26:19,011 +of App Store Server +Notifications + +617 +00:26:19,011 --> 00:26:22,080 +can simplify event processing +on your server + +618 +00:26:22,080 --> 00:26:25,384 +and complement +the App Store Server API. + +619 +00:26:25,384 --> 00:26:27,386 +With V2 notifications, + +620 +00:26:27,386 --> 00:26:30,522 +the App Store server +calls your server directly, + +621 +00:26:30,522 --> 00:26:33,892 +providing in-app purchase +updates as they happen. + +622 +00:26:33,892 --> 00:26:37,262 +The streamlined notification +type and subtype + +623 +00:26:37,262 --> 00:26:40,265 +make it easy to understand +what's happening. + +624 +00:26:40,265 --> 00:26:42,134 +You can use these +to track changes + +625 +00:26:42,134 --> 00:26:45,938 +related to in-app subscriptions +and other events. + +626 +00:26:45,938 --> 00:26:47,573 +With all of these data sources, + +627 +00:26:47,573 --> 00:26:51,410 +we wanted to make that data +as easy as possible to parse. + +628 +00:26:51,410 --> 00:26:53,679 +Receipts are now a thing +of the past, + +629 +00:26:53,679 --> 00:26:56,582 +as these new services +provide in-app data + +630 +00:26:56,582 --> 00:27:00,486 +in signed JSON format, +so you can easily parse it + +631 +00:27:00,486 --> 00:27:04,089 +and trust that it came +from the App Store server. + +632 +00:27:04,089 --> 00:27:07,626 +Last year was a big year +for the App Store server. + +633 +00:27:07,626 --> 00:27:10,062 +It may have been big +for you as well + +634 +00:27:10,062 --> 00:27:12,431 +if you worked to update +your server code + +635 +00:27:12,431 --> 00:27:15,067 +to leverage all +these new features. + +636 +00:27:15,067 --> 00:27:18,237 +Rest assured that effort +will continue to pay off + +637 +00:27:18,237 --> 00:27:21,340 +as we bring powerful new +enhancements and features + +638 +00:27:21,340 --> 00:27:27,179 +to App Store Server API and App +Store Server Notifications V2. + +639 +00:27:27,179 --> 00:27:28,747 +That's our year in review, + +640 +00:27:28,747 --> 00:27:30,482 +but if you'd like +more of a refresher + +641 +00:27:30,482 --> 00:27:32,518 +after hearing +this year's updates, + +642 +00:27:32,518 --> 00:27:36,989 +be sure to check out +the WWDC21 sessions titled + +643 +00:27:36,989 --> 00:27:40,125 +"Manage in-app purchases +on your server," + +644 +00:27:40,125 --> 00:27:42,261 +"Meet StoreKit 2," + +645 +00:27:42,261 --> 00:27:45,831 +and "Support customers +and handle refunds." + +646 +00:27:45,831 --> 00:27:48,467 +Now let's move on +to brand-new updates + +647 +00:27:48,467 --> 00:27:53,205 +coming to the App Store server +for WWDC22. + +648 +00:27:53,205 --> 00:27:56,141 +First I'll share +some updates to transaction + +649 +00:27:56,141 --> 00:27:58,377 +and renewal info fields. + +650 +00:27:58,377 --> 00:28:01,146 +Next I'll tell you about +new enhancements + +651 +00:28:01,146 --> 00:28:03,916 +to the App Store Server API. + +652 +00:28:03,916 --> 00:28:06,585 +And finally, I'll share +exciting new features + +653 +00:28:06,585 --> 00:28:11,189 +coming to App Store Server +Notifications V2. + +654 +00:28:11,189 --> 00:28:14,459 +Now let's dive in with +the first of our new topics: + +655 +00:28:14,459 --> 00:28:18,497 +new fields found in +transaction and renewal info. + +656 +00:28:18,497 --> 00:28:21,733 +Earlier, you heard from Dani +about a couple new fields + +657 +00:28:21,733 --> 00:28:24,136 +coming to the transaction +and renewal info + +658 +00:28:24,136 --> 00:28:26,038 +of in-app purchases. + +659 +00:28:26,038 --> 00:28:30,075 +These fields, environment +and recentSubscriptionStartDate, + +660 +00:28:30,075 --> 00:28:32,077 +are also coming +to the transaction + +661 +00:28:32,077 --> 00:28:33,512 +and renewal info payloads + +662 +00:28:33,512 --> 00:28:36,315 +you receive from +the App Store Server API + +663 +00:28:36,315 --> 00:28:40,686 +and in V2 App Store Server +Notifications. + +664 +00:28:40,686 --> 00:28:44,389 +Let's take a fresh look at the +data you can expect to receive + +665 +00:28:44,389 --> 00:28:48,393 +from the App Store server +with these new fields included. + +666 +00:28:48,393 --> 00:28:50,929 +First is the transaction +info payload, + +667 +00:28:50,929 --> 00:28:53,732 +which we can see here +after decoding. + +668 +00:28:53,732 --> 00:28:57,803 +Down at the bottom, you can see +our new field: environment. + +669 +00:28:57,803 --> 00:28:59,771 +You can use it to tell, +at a glance, + +670 +00:28:59,771 --> 00:29:01,573 +whether the transaction +took place + +671 +00:29:01,573 --> 00:29:04,876 +in the production +or sandbox environment. + +672 +00:29:04,876 --> 00:29:07,245 +Next is the renewal info +payload, + +673 +00:29:07,245 --> 00:29:10,015 +also seen here after decoding. + +674 +00:29:10,015 --> 00:29:13,619 +As you can see, the environment +field is also available here + +675 +00:29:13,619 --> 00:29:15,654 +for your reference. + +676 +00:29:15,654 --> 00:29:18,190 +Additionally, +recentSubscriptionStartDate + +677 +00:29:18,190 --> 00:29:21,426 +will now appear +in every renewal info payload. + +678 +00:29:21,426 --> 00:29:23,795 +This is the start date +of the user's first + +679 +00:29:23,795 --> 00:29:27,799 +subscription purchase in their +most recent string of renewals, + +680 +00:29:27,799 --> 00:29:32,104 +ignoring any gaps +of 60 days or fewer. + +681 +00:29:32,104 --> 00:29:35,540 +recentSubscriptionStartDate +is an easy way to get an idea + +682 +00:29:35,540 --> 00:29:38,510 +of a customer's loyalty +at a glance. + +683 +00:29:38,510 --> 00:29:40,045 +But if you'd like more detail, + +684 +00:29:40,045 --> 00:29:43,749 +including the timing and length +of any gaps in service, + +685 +00:29:43,749 --> 00:29:47,019 +you can call the +Get Transaction History endpoint + +686 +00:29:47,019 --> 00:29:48,620 +and examine the full history + +687 +00:29:48,620 --> 00:29:51,923 +of a user's subscription +renewal purchases. + +688 +00:29:51,923 --> 00:29:54,092 +Or for even more detail, + +689 +00:29:54,092 --> 00:29:56,962 +with App Store Server +Notifications V2, + +690 +00:29:56,962 --> 00:29:59,865 +the App Store server +automatically sends updates + +691 +00:29:59,865 --> 00:30:03,068 +about user subscriptions +to your server. + +692 +00:30:03,068 --> 00:30:05,937 +These notifications give you +maximum insight + +693 +00:30:05,937 --> 00:30:09,708 +into the timing of events like +renewal preference changes, + +694 +00:30:09,708 --> 00:30:14,546 +offer redemptions, +billing failures, and more. + +695 +00:30:14,546 --> 00:30:17,349 +As you can see, +recentSubscriptionStartDate + +696 +00:30:17,349 --> 00:30:19,151 +rounds out a suite of options + +697 +00:30:19,151 --> 00:30:21,586 +for determining +customer loyalty. + +698 +00:30:21,586 --> 00:30:23,755 +Use these tools to target offers + +699 +00:30:23,755 --> 00:30:27,059 +and reward your most +loyal customers. + +700 +00:30:27,059 --> 00:30:30,495 +Now let's move on to some +convenient new enhancements + +701 +00:30:30,495 --> 00:30:33,732 +to the Get Transaction History +endpoint. + +702 +00:30:33,732 --> 00:30:36,001 +With the Get Transaction History +endpoint, + +703 +00:30:36,001 --> 00:30:39,204 +you can fetch the full history +of a user's purchases + +704 +00:30:39,204 --> 00:30:41,006 +in your app. + +705 +00:30:41,006 --> 00:30:43,375 +The endpoint response +is paginated + +706 +00:30:43,375 --> 00:30:46,945 +so you can process this data +in reasonable chunks. + +707 +00:30:46,945 --> 00:30:49,981 +Each response contains a +revision token that you provide + +708 +00:30:49,981 --> 00:30:53,919 +in the next request in order +to get the next page. + +709 +00:30:53,919 --> 00:30:56,621 +And the pages are sorted +by modified date, + +710 +00:30:56,621 --> 00:30:59,691 +meaning each subsequent page +contains transactions + +711 +00:30:59,691 --> 00:31:02,928 +that are more recently modified. + +712 +00:31:02,928 --> 00:31:05,297 +Let's take a look +at how this works. + +713 +00:31:05,297 --> 00:31:08,266 +You call the Get Transaction +History endpoint, + +714 +00:31:08,266 --> 00:31:12,104 +and provide +an originalTransactionId. + +715 +00:31:12,104 --> 00:31:16,408 +The App Store server will return +up to 20 signed transactions + +716 +00:31:16,408 --> 00:31:18,043 +for that user. + +717 +00:31:18,043 --> 00:31:21,179 +It will also return +an updated revision value + +718 +00:31:21,179 --> 00:31:25,517 +that you will provide in your +next page request for this user. + +719 +00:31:25,517 --> 00:31:27,686 +You'll know there's +more data available + +720 +00:31:27,686 --> 00:31:31,723 +when the hasMore field +in the response is true. + +721 +00:31:31,723 --> 00:31:32,924 +Let's say in this case + +722 +00:31:32,924 --> 00:31:35,861 +that there's another page +of data available. + +723 +00:31:35,861 --> 00:31:37,996 +You make another request +to the endpoint, + +724 +00:31:37,996 --> 00:31:41,633 +and you include that revision +value from the first response. + +725 +00:31:41,633 --> 00:31:43,535 +You receive +the next page of data, + +726 +00:31:43,535 --> 00:31:46,404 +including an updated +revision value. + +727 +00:31:46,404 --> 00:31:49,741 +hasMore is now false, +so you know you're up to date + +728 +00:31:49,741 --> 00:31:52,511 +with the latest +transaction data. + +729 +00:31:52,511 --> 00:31:54,179 +Except this time, +you notice something + +730 +00:31:54,179 --> 00:31:56,681 +about the final transaction +in the response; + +731 +00:31:56,681 --> 00:31:58,483 +you've seen it before! + +732 +00:31:58,483 --> 00:32:01,286 +It was one of +the original 20 you received + +733 +00:32:01,286 --> 00:32:03,889 +in response to your first +request. + +734 +00:32:03,889 --> 00:32:06,591 +This means the transaction +must have been modified, + +735 +00:32:06,591 --> 00:32:10,495 +so it was put back at the top +of the sort order. + +736 +00:32:10,495 --> 00:32:13,465 +Now, you can examine +the data of that transaction + +737 +00:32:13,465 --> 00:32:15,700 +and take note of what's changed. + +738 +00:32:15,700 --> 00:32:18,637 +In this instance, +you notice the revocationDate + +739 +00:32:18,637 --> 00:32:21,807 +and revocationReason fields +are now populated, + +740 +00:32:21,807 --> 00:32:24,209 +meaning the transaction +was revoked. + +741 +00:32:24,209 --> 00:32:26,845 +You can take action +by revoking any content + +742 +00:32:26,845 --> 00:32:29,614 +associated with the purchase. + +743 +00:32:29,614 --> 00:32:32,150 +It's a good idea to store +the revision value + +744 +00:32:32,150 --> 00:32:34,519 +from this final response +alongside + +745 +00:32:34,519 --> 00:32:39,324 +the originalTransactionId +you used to identify the user. + +746 +00:32:39,324 --> 00:32:41,860 +The next time you call +the endpoint for this user, + +747 +00:32:41,860 --> 00:32:44,062 +you can provide +that revision and know + +748 +00:32:44,062 --> 00:32:47,199 +that you're getting back +only fresh transaction data + +749 +00:32:47,199 --> 00:32:50,969 +that has been modified +since your last request. + +750 +00:32:50,969 --> 00:32:54,072 +As you've seen, the +Get Transaction History endpoint + +751 +00:32:54,072 --> 00:32:57,809 +provides you a simple way +to retrieve a comprehensive set + +752 +00:32:57,809 --> 00:33:00,011 +of in-app purchase data. + +753 +00:33:00,011 --> 00:33:03,849 +But maybe sometimes it can be +a bit too comprehensive. + +754 +00:33:03,849 --> 00:33:06,284 +Some users have +lengthy purchase histories + +755 +00:33:06,284 --> 00:33:09,054 +dating back several years. + +756 +00:33:09,054 --> 00:33:12,090 +For these users, +this endpoint could return + +757 +00:33:12,090 --> 00:33:16,895 +hundreds of purchases +of a variety of types. + +758 +00:33:16,895 --> 00:33:20,232 +Even with pages, +this can be a lot to handle. + +759 +00:33:20,232 --> 00:33:22,801 +That's why this year, +we're enhancing this endpoint + +760 +00:33:22,801 --> 00:33:27,305 +with a variety of new sort +and filter options. + +761 +00:33:27,305 --> 00:33:31,109 +Now, you can tell us exactly the +data you want from the start, + +762 +00:33:31,109 --> 00:33:33,511 +saving processing time +on your server + +763 +00:33:33,511 --> 00:33:35,447 +and reducing the number +of network calls + +764 +00:33:35,447 --> 00:33:39,317 +needed to retrieve +all available pages. + +765 +00:33:39,317 --> 00:33:41,887 +You can sort +by descending modified date + +766 +00:33:41,887 --> 00:33:43,889 +if you're interested +in seeing the most recently + +767 +00:33:43,889 --> 00:33:47,792 +modified purchases +on the first page of results. + +768 +00:33:47,792 --> 00:33:50,595 +You can also filter +by several useful fields + +769 +00:33:50,595 --> 00:33:54,199 +such as product type, +product ID, + +770 +00:33:54,199 --> 00:33:56,968 +Family sharing status, +and more. + +771 +00:33:56,968 --> 00:33:59,704 +To apply these new +sort and filter options, + +772 +00:33:59,704 --> 00:34:03,141 +just append them as query +parameters to your request + +773 +00:34:03,141 --> 00:34:06,344 +to the Get Transaction History +endpoint. + +774 +00:34:06,344 --> 00:34:09,180 +Let's take a closer look +at how that works. + +775 +00:34:09,180 --> 00:34:12,684 +Here you can see all +the new parameter options. + +776 +00:34:12,684 --> 00:34:15,453 +These may look familiar, +since most are taken directly + +777 +00:34:15,453 --> 00:34:18,623 +from the transaction info +payload. + +778 +00:34:18,623 --> 00:34:20,392 +You can mix and match +these parameters + +779 +00:34:20,392 --> 00:34:22,594 +to get very specific results. + +780 +00:34:22,594 --> 00:34:24,863 +For example, +maybe we want to fetch + +781 +00:34:24,863 --> 00:34:28,333 +only the nonconsumable purchases +a user has made + +782 +00:34:28,333 --> 00:34:31,002 +since the beginning +of this year. + +783 +00:34:31,002 --> 00:34:34,739 +We also want to exclude +any revoked purchases. + +784 +00:34:34,739 --> 00:34:36,508 +We will build our custom request + +785 +00:34:36,508 --> 00:34:39,444 +by setting the productType +to NON_CONSUMABLE + +786 +00:34:39,444 --> 00:34:43,114 +and specifying the startDate +as the beginning of this year + +787 +00:34:43,114 --> 00:34:45,183 +represented in milliseconds. + +788 +00:34:45,183 --> 00:34:49,187 +Finally, we'll set +excludeRevoked to true. + +789 +00:34:49,187 --> 00:34:50,889 +And that's our request! + +790 +00:34:50,889 --> 00:34:53,091 +Since we did not specify +a sort order, + +791 +00:34:53,091 --> 00:34:55,093 +the response will default +to sorting + +792 +00:34:55,093 --> 00:34:58,129 +by ascending modified date. + +793 +00:34:58,129 --> 00:35:00,932 +Now even with a request +as specific as this, + +794 +00:35:00,932 --> 00:35:04,336 +there could be multiple pages +of purchases to retrieve. + +795 +00:35:04,336 --> 00:35:06,671 +For follow-up requests, +we should make sure + +796 +00:35:06,671 --> 00:35:09,341 +to include the exact same +query parameters, + +797 +00:35:09,341 --> 00:35:12,978 +in addition to the revision +from the previous response. + +798 +00:35:12,978 --> 00:35:14,779 +For even more flexibility, + +799 +00:35:14,779 --> 00:35:18,183 +three of the filter fields +support multiple values, + +800 +00:35:18,183 --> 00:35:20,585 +so you can filter +to only those purchases + +801 +00:35:20,585 --> 00:35:24,689 +that match at least +one of the provided values. + +802 +00:35:24,689 --> 00:35:28,960 +These fields are productType, +productId, + +803 +00:35:28,960 --> 00:35:32,197 +and subscriptionGroupIdentifier. + +804 +00:35:32,197 --> 00:35:35,367 +To provide multiple values +for these parameters, + +805 +00:35:35,367 --> 00:35:39,170 +simply define them +multiple times. + +806 +00:35:39,170 --> 00:35:44,376 +Next let's move on to App Store +Server Notification updates. + +807 +00:35:44,376 --> 00:35:47,312 +With App Store Server +Notifications V2, + +808 +00:35:47,312 --> 00:35:50,415 +you can take your server +to the next level. + +809 +00:35:50,415 --> 00:35:53,485 +V2 notifications +give detailed insights + +810 +00:35:53,485 --> 00:35:55,086 +about in-app purchase events + +811 +00:35:55,086 --> 00:35:57,822 +that you can't get +anywhere else. + +812 +00:35:57,822 --> 00:36:01,159 +These are especially useful +for tracking the lifecycle + +813 +00:36:01,159 --> 00:36:05,030 +of autorenewable subscriptions +offered in your app. + +814 +00:36:05,030 --> 00:36:08,566 +You can use these insights +to retain customers, + +815 +00:36:08,566 --> 00:36:10,802 +win back those +that have churned, + +816 +00:36:10,802 --> 00:36:15,106 +resolve customer support +requests, and more. + +817 +00:36:15,106 --> 00:36:19,444 +With all of these benefits, you +might wonder how to get started. + +818 +00:36:19,444 --> 00:36:21,179 +As with any new feature, + +819 +00:36:21,179 --> 00:36:24,849 +the sandbox testing environment +is the best place to start. + +820 +00:36:24,849 --> 00:36:26,084 +That's why last year, + +821 +00:36:26,084 --> 00:36:29,421 +we added the ability to set +a separate server URL + +822 +00:36:29,421 --> 00:36:31,790 +in App Store Connect +for receiving + +823 +00:36:31,790 --> 00:36:35,260 +App Store Server Notifications +in sandbox. + +824 +00:36:35,260 --> 00:36:37,862 +After registering +your server URL, + +825 +00:36:37,862 --> 00:36:39,597 +you'll want to confirm +your server + +826 +00:36:39,597 --> 00:36:43,401 +is receiving notifications +from the App Store server. + +827 +00:36:43,401 --> 00:36:45,270 +You might set up +a sandbox account + +828 +00:36:45,270 --> 00:36:49,274 +just to trigger a notification +through a user action. + +829 +00:36:49,274 --> 00:36:52,777 +For example, let's say you +perform a first time buy + +830 +00:36:52,777 --> 00:36:56,214 +of a subscription +using that sandbox account. + +831 +00:36:56,214 --> 00:37:00,685 +You should receive a V2 +notification of type SUBSCRIBED + +832 +00:37:00,685 --> 00:37:03,254 +and subtype INITIAL_BUY. + +833 +00:37:03,254 --> 00:37:05,623 +But what if that notification +doesn't come? + +834 +00:37:05,623 --> 00:37:08,259 +You might wonder if there +was an issue with your server + +835 +00:37:08,259 --> 00:37:12,097 +or the steps you took +to trigger a notification. + +836 +00:37:12,097 --> 00:37:15,166 +This situation can generate +a lot of uncertainty + +837 +00:37:15,166 --> 00:37:16,801 +right as you're getting started. + +838 +00:37:16,801 --> 00:37:19,571 +We want to simplify this +experience and give you a way + +839 +00:37:19,571 --> 00:37:23,041 +to easily verify that App Store +Server Notifications + +840 +00:37:23,041 --> 00:37:25,343 +can reach your server. + +841 +00:37:25,343 --> 00:37:27,979 +That's why this year, +we're introducing the new + +842 +00:37:27,979 --> 00:37:31,149 +Request a Test Notification +endpoint. + +843 +00:37:31,149 --> 00:37:32,884 +By calling this simple +endpoint, + +844 +00:37:32,884 --> 00:37:37,689 +you can ask us to send +a V2 Notification of type TEST + +845 +00:37:37,689 --> 00:37:42,427 +to the server URL registered for +your app in App Store Connect. + +846 +00:37:42,427 --> 00:37:45,897 +The new TEST notification type +is used exclusively + +847 +00:37:45,897 --> 00:37:47,932 +for this endpoint. + +848 +00:37:47,932 --> 00:37:51,035 +You can call the endpoint +in sandbox or production + +849 +00:37:51,035 --> 00:37:55,340 +to test your saved URL +for either environment. + +850 +00:37:55,340 --> 00:37:59,344 +Use this new endpoint +to quickly test new server URLs + +851 +00:37:59,344 --> 00:38:01,746 +and configurations. + +852 +00:38:01,746 --> 00:38:04,916 +Let's see how this simplifies +first-time setup. + +853 +00:38:04,916 --> 00:38:08,553 +Now, if you're just looking to +trigger your first notification, + +854 +00:38:08,553 --> 00:38:10,922 +there's no need to set up +a sandbox account + +855 +00:38:10,922 --> 00:38:12,957 +or perform a purchase. + +856 +00:38:12,957 --> 00:38:14,325 +Just call the new endpoint + +857 +00:38:14,325 --> 00:38:16,961 +in whichever environment +you want to test + +858 +00:38:16,961 --> 00:38:20,632 +and you'll receive +an HTTP 200 response + +859 +00:38:20,632 --> 00:38:22,734 +confirming your request. + +860 +00:38:22,734 --> 00:38:25,136 +The response +will contain a new field, + +861 +00:38:25,136 --> 00:38:29,607 +testNotificationToken, which +identifies the test notification + +862 +00:38:29,607 --> 00:38:31,709 +your server will receive. + +863 +00:38:31,709 --> 00:38:34,379 +We will come back +to this field later. + +864 +00:38:34,379 --> 00:38:38,816 +Shortly afterward, your server +should receive a V2 notification + +865 +00:38:38,816 --> 00:38:43,621 +of type TEST at the URL +saved in App Store Connect. + +866 +00:38:43,621 --> 00:38:46,491 +Now let's see +how to call this endpoint. + +867 +00:38:46,491 --> 00:38:49,527 +Just send a simple POST request +to this new path + +868 +00:38:49,527 --> 00:38:52,030 +on the App Store server. + +869 +00:38:52,030 --> 00:38:55,233 +You'll receive +an HTTP 200 response + +870 +00:38:55,233 --> 00:38:58,570 +and know that your request +has been submitted. + +871 +00:38:58,570 --> 00:39:01,439 +The response will contain +that new field I mentioned, + +872 +00:39:01,439 --> 00:39:03,808 +testNotificationToken. + +873 +00:39:03,808 --> 00:39:06,144 +Take note of this for later. + +874 +00:39:06,144 --> 00:39:10,315 +Soon you'll receive +a signed TEST Notification. + +875 +00:39:10,315 --> 00:39:12,517 +Here's what that notification +will look like + +876 +00:39:12,517 --> 00:39:14,485 +once it's decoded. + +877 +00:39:14,485 --> 00:39:17,488 +You'll notice it contains +all the usual top-level fields + +878 +00:39:17,488 --> 00:39:19,524 +of a V2 notification, + +879 +00:39:19,524 --> 00:39:23,228 +including the new +notificationType, TEST. + +880 +00:39:23,228 --> 00:39:26,097 +The contents of the data object +are a bit shorter + +881 +00:39:26,097 --> 00:39:27,899 +than a normal notification. + +882 +00:39:27,899 --> 00:39:29,634 +Since this is just a test, + +883 +00:39:29,634 --> 00:39:32,837 +there are no transaction-related +data to include, + +884 +00:39:32,837 --> 00:39:35,640 +so we omit +transaction-specific fields, + +885 +00:39:35,640 --> 00:39:40,078 +most notably +the signedTransactionInfo. + +886 +00:39:40,078 --> 00:39:43,681 +When calling the new Request +a Test Notification endpoint, + +887 +00:39:43,681 --> 00:39:46,184 +keep in mind that App Store +Server Notifications + +888 +00:39:46,184 --> 00:39:48,653 +are sent asynchronously. + +889 +00:39:48,653 --> 00:39:50,822 +Your successful call +to the endpoint + +890 +00:39:50,822 --> 00:39:55,460 +will return an HTTP 200 +but the actual test notification + +891 +00:39:55,460 --> 00:39:59,464 +will arrive separately, +a short while later. + +892 +00:39:59,464 --> 00:40:01,366 +Given that this endpoint +is all about + +893 +00:40:01,366 --> 00:40:03,701 +testing your server +configuration, + +894 +00:40:03,701 --> 00:40:07,105 +you might be wondering +what to do when that test fails. + +895 +00:40:07,105 --> 00:40:12,110 +In other words, what if the test +notification doesn't arrive? + +896 +00:40:12,110 --> 00:40:14,979 +To further enhance +your testing capabilities, + +897 +00:40:14,979 --> 00:40:18,750 +we're releasing the Get Test +Notification Status endpoint, + +898 +00:40:18,750 --> 00:40:20,518 +which you will use +in conjunction + +899 +00:40:20,518 --> 00:40:24,088 +with the Request a Test +Notification endpoint. + +900 +00:40:24,088 --> 00:40:26,791 +With this new endpoint, +you can check on the status + +901 +00:40:26,791 --> 00:40:30,628 +of a previously requested +TEST notification. + +902 +00:40:30,628 --> 00:40:33,531 +The endpoint response will tell +you if the App Store server + +903 +00:40:33,531 --> 00:40:35,366 +was able to reach your server + +904 +00:40:35,366 --> 00:40:39,170 +and successfully send +the TEST notification. + +905 +00:40:39,170 --> 00:40:42,340 +If the send failed, +it will give you an idea of why, + +906 +00:40:42,340 --> 00:40:46,477 +so you can better troubleshoot +your server configuration. + +907 +00:40:46,477 --> 00:40:50,281 +Let's check out how you'll use +this endpoint. + +908 +00:40:50,281 --> 00:40:54,886 +Send a GET request to this path +on the App Store server. + +909 +00:40:54,886 --> 00:40:58,089 +In the path, include +the testNotificationToken + +910 +00:40:58,089 --> 00:41:02,460 +you received from the Request +a Test Notification endpoint. + +911 +00:41:02,460 --> 00:41:04,862 +This will tell us +which test notification + +912 +00:41:04,862 --> 00:41:07,231 +you want to check the status of. + +913 +00:41:07,231 --> 00:41:09,167 +Now for the response. + +914 +00:41:09,167 --> 00:41:13,071 +The signedPayload field contains +the TEST notification payload + +915 +00:41:13,071 --> 00:41:15,540 +that the App Store server +attempted to send + +916 +00:41:15,540 --> 00:41:17,375 +to your server. + +917 +00:41:17,375 --> 00:41:21,279 +And the firstSendAttemptResult +field indicates the result + +918 +00:41:21,279 --> 00:41:23,848 +of that send attempt. + +919 +00:41:23,848 --> 00:41:27,652 +Here, SUCCESS indicates +that the send was successful, + +920 +00:41:27,652 --> 00:41:31,723 +meaning the App Store server +received an HTTP 200 response + +921 +00:41:31,723 --> 00:41:34,125 +from your server. + +922 +00:41:34,125 --> 00:41:36,527 +If the send was unsuccessful, + +923 +00:41:36,527 --> 00:41:40,665 +you'll instead see one of +several different error values. + +924 +00:41:40,665 --> 00:41:44,502 +These values indicate the error +the App Store server experienced + +925 +00:41:44,502 --> 00:41:48,005 +trying to reach your server +with the test notification. + +926 +00:41:48,005 --> 00:41:51,809 +With this information, you can +troubleshoot your server issue, + +927 +00:41:51,809 --> 00:41:54,746 +request new test notifications +as needed, + +928 +00:41:54,746 --> 00:41:58,116 +and get your server +running reliably. + +929 +00:41:58,116 --> 00:42:00,918 +Collectively, these test +notification endpoints + +930 +00:42:00,918 --> 00:42:03,721 +are simple to use and can +save you a lot of trouble + +931 +00:42:03,721 --> 00:42:06,591 +when setting up +or reconfiguring your server + +932 +00:42:06,591 --> 00:42:11,696 +to receive V2 App Store Server +Notifications. + +933 +00:42:11,696 --> 00:42:13,131 +Now with the help +of these endpoints, + +934 +00:42:13,131 --> 00:42:17,001 +you can set up your server and +confirm it's running smoothly. + +935 +00:42:17,001 --> 00:42:20,204 +But servers aren't perfect +and outages happen. + +936 +00:42:20,204 --> 00:42:23,040 +How do you recover +when your server goes down, + +937 +00:42:23,040 --> 00:42:26,911 +leading you to miss +App Store Server Notifications? + +938 +00:42:26,911 --> 00:42:30,481 +The current solution to this +is a retry system. + +939 +00:42:30,481 --> 00:42:33,451 +When the App Store server +fails to reach your server, + +940 +00:42:33,451 --> 00:42:36,087 +it initiates a retry process. + +941 +00:42:36,087 --> 00:42:40,358 +It will retry sending the same +notification up to five times, + +942 +00:42:40,358 --> 00:42:43,361 +with a longer wait +between each attempt. + +943 +00:42:43,361 --> 00:42:47,698 +These retries take place only +in the production environment. + +944 +00:42:47,698 --> 00:42:50,635 +Retries help you eventually +recover from an outage, + +945 +00:42:50,635 --> 00:42:53,738 +but they're not perfect +for every situation. + +946 +00:42:53,738 --> 00:42:57,108 +For example, some outages +can be extensive. + +947 +00:42:57,108 --> 00:42:58,976 +If your server +is down long enough + +948 +00:42:58,976 --> 00:43:02,480 +to miss the final retry attempt +from the App Store server, + +949 +00:43:02,480 --> 00:43:05,316 +that notification is lost. + +950 +00:43:05,316 --> 00:43:06,584 +Or more commonly, + +951 +00:43:06,584 --> 00:43:09,587 +your server could experience +a very brief issue, + +952 +00:43:09,587 --> 00:43:13,558 +during which it misses +only a handful of notifications. + +953 +00:43:13,558 --> 00:43:16,194 +But missing even +a single notification + +954 +00:43:16,194 --> 00:43:18,830 +means some of your customer +records are out of date + +955 +00:43:18,830 --> 00:43:20,565 +for at least an hour. + +956 +00:43:20,565 --> 00:43:23,434 +Yet you don't know which ones! + +957 +00:43:23,434 --> 00:43:26,237 +Clearly, server outages +are stressful, + +958 +00:43:26,237 --> 00:43:29,307 +and recovering from them +can be a complex task. + +959 +00:43:29,307 --> 00:43:31,843 +That's why we want to make it +as easy as possible + +960 +00:43:31,843 --> 00:43:35,179 +to recover missed +App Store Server Notifications, + +961 +00:43:35,179 --> 00:43:39,884 +so you can get your server back +on track as soon as possible. + +962 +00:43:39,884 --> 00:43:42,687 +That's why this year, +we are introducing the new + +963 +00:43:42,687 --> 00:43:45,323 +Get Notification History +endpoint. + +964 +00:43:45,323 --> 00:43:46,991 +With this endpoint, + +965 +00:43:46,991 --> 00:43:51,329 +you can fetch the history of V2 +App Store Server Notifications + +966 +00:43:51,329 --> 00:43:53,764 +generated for your app. + +967 +00:43:53,764 --> 00:43:58,236 +Whether your server successfully +received a notification or not, + +968 +00:43:58,236 --> 00:44:02,740 +that notification will appear in +the response of this endpoint. + +969 +00:44:02,740 --> 00:44:04,108 +When calling this endpoint, + +970 +00:44:04,108 --> 00:44:08,513 +you'll specify a date range +of notifications to fetch. + +971 +00:44:08,513 --> 00:44:11,916 +With WWDC, we have started +recording this data, + +972 +00:44:11,916 --> 00:44:15,419 +and we will build up to the cap +of the latest six months + +973 +00:44:15,419 --> 00:44:18,189 +of rolling history +being available. + +974 +00:44:18,189 --> 00:44:22,293 +You can optionally filter your +request by type and subtype, + +975 +00:44:22,293 --> 00:44:25,696 +or fetch just +a single user's notifications + +976 +00:44:25,696 --> 00:44:29,033 +by providing +an originalTransactionId. + +977 +00:44:29,033 --> 00:44:31,903 +And the existing retry system +is still available, + +978 +00:44:31,903 --> 00:44:35,840 +so you can use it in tandem +with this new endpoint. + +979 +00:44:35,840 --> 00:44:39,043 +Let's take a look at how +you'll call this endpoint. + +980 +00:44:39,043 --> 00:44:41,846 +You'll send a POST request +to this new path + +981 +00:44:41,846 --> 00:44:43,681 +on the App Store server. + +982 +00:44:43,681 --> 00:44:48,819 +In the request body, you'll +include a startDate and endDate. + +983 +00:44:48,819 --> 00:44:51,422 +The response will contain +only notifications + +984 +00:44:51,422 --> 00:44:54,792 +we first attempted to send +in this window. + +985 +00:44:54,792 --> 00:44:58,062 +Keep in mind that the earliest +notifications available + +986 +00:44:58,062 --> 00:45:02,967 +will be those sent six months +before the date of your request. + +987 +00:45:02,967 --> 00:45:06,470 +Optionally, you can specify +a notificationType + +988 +00:45:06,470 --> 00:45:08,606 +and notificationSubtype. + +989 +00:45:08,606 --> 00:45:12,510 +If you do, the history will be +filtered to only notifications + +990 +00:45:12,510 --> 00:45:15,146 +that match both of these values. + +991 +00:45:15,146 --> 00:45:19,483 +Keep in mind that some +notifications have no subtype. + +992 +00:45:19,483 --> 00:45:21,319 +Alternatively, +you can provide + +993 +00:45:21,319 --> 00:45:23,988 +an originalTransactionId +of a user, + +994 +00:45:23,988 --> 00:45:28,659 +to fetch the notification +history of only that user. + +995 +00:45:28,659 --> 00:45:31,629 +Finally, you should provide +a paginationToken + +996 +00:45:31,629 --> 00:45:34,765 +as a query parameter +for every follow-up request + +997 +00:45:34,765 --> 00:45:37,168 +in order to get the next page. + +998 +00:45:37,168 --> 00:45:39,737 +Make sure you use +the same request body + +999 +00:45:39,737 --> 00:45:41,339 +for follow-up requests, + +1000 +00:45:41,339 --> 00:45:44,508 +changing only +this paginationToken. + +1001 +00:45:44,508 --> 00:45:47,445 +Now let's take a look +at the response. + +1002 +00:45:47,445 --> 00:45:51,749 +The notificationHistory array +contains up to 20 notifications, + +1003 +00:45:51,749 --> 00:45:55,620 +with the oldest +notifications first. + +1004 +00:45:55,620 --> 00:45:59,857 +Each entry in this array +represents a notification + +1005 +00:45:59,857 --> 00:46:02,927 +and inside you'll find +the signedPayload, + +1006 +00:46:02,927 --> 00:46:07,431 +which you can decode as usual +to view the transaction data. + +1007 +00:46:07,431 --> 00:46:10,334 +The data within +is identical to the payload + +1008 +00:46:10,334 --> 00:46:14,572 +the App Store server sent +in the original notification. + +1009 +00:46:14,572 --> 00:46:16,607 +You'll see that we've also +brought the new + +1010 +00:46:16,607 --> 00:46:20,845 +firstSendAttemptResult field +to this endpoint response. + +1011 +00:46:20,845 --> 00:46:24,148 +You can use this field to look +for sequences of timeouts + +1012 +00:46:24,148 --> 00:46:27,485 +and other errors to better +understand why your server + +1013 +00:46:27,485 --> 00:46:30,855 +missed notifications +in the past. + +1014 +00:46:30,855 --> 00:46:33,624 +The response also contains +a paginationToken + +1015 +00:46:33,624 --> 00:46:36,193 +if there are more pages +to retrieve. + +1016 +00:46:36,193 --> 00:46:38,262 +You should provide this +in your next request + +1017 +00:46:38,262 --> 00:46:41,799 +in order to get the next page +of notifications. + +1018 +00:46:41,799 --> 00:46:43,934 +You'll know there are +more pages to retrieve + +1019 +00:46:43,934 --> 00:46:47,705 +as long as +the hasMore field is true. + +1020 +00:46:47,705 --> 00:46:49,240 +And that's everything +you need to know + +1021 +00:46:49,240 --> 00:46:52,543 +about this useful new endpoint. + +1022 +00:46:52,543 --> 00:46:56,113 +That concludes our App Store +server updates for today. + +1023 +00:46:56,113 --> 00:46:59,150 +Every server feature +announced today is available now + +1024 +00:46:59,150 --> 00:47:02,386 +in both sandbox and production. + +1025 +00:47:02,386 --> 00:47:04,755 +We hope you'll take advantage +of these new features + +1026 +00:47:04,755 --> 00:47:07,758 +to make your server +the best it can be. + +1027 +00:47:07,758 --> 00:47:11,462 +For more great content on using +a server with in-app purchase, + +1028 +00:47:11,462 --> 00:47:13,698 +including how to use +the latest features + +1029 +00:47:13,698 --> 00:47:15,800 +while supporting legacy clients, + +1030 +00:47:15,800 --> 00:47:20,371 +I encourage you to check out +another session at WWDC22, + +1031 +00:47:20,371 --> 00:47:24,208 +"Explore in-app purchase +integration and migration." + +1032 +00:47:24,208 --> 00:47:27,545 +Both: Thanks for joining us +at WWDC22! + +1033 +00:47:27,545 --> 00:47:31,582 +♪ + diff --git a/eng/2022 Session 10008 What's new in Nearby Interaction en.srt b/eng/2022 Session 10008 What's new in Nearby Interaction en.srt new file mode 100644 index 0000000..7c83c96 --- /dev/null +++ b/eng/2022 Session 10008 What's new in Nearby Interaction en.srt @@ -0,0 +1,2647 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,910 +♪ + +3 +00:00:09,910 --> 00:00:12,713 +Hi, I'm Jon Schoenberg, +and I'm an engineer + +4 +00:00:12,713 --> 00:00:15,082 +on the Location Technologies +team at Apple. + +5 +00:00:15,082 --> 00:00:18,018 +In this session, I'll cover +the new features we've brought + +6 +00:00:18,018 --> 00:00:20,721 +to Nearby Interaction, +that are going to enable you + +7 +00:00:20,721 --> 00:00:23,123 +to build richer +and more diverse experiences + +8 +00:00:23,123 --> 00:00:25,259 +with spatial awareness. + +9 +00:00:25,259 --> 00:00:27,928 +The Nearby Interaction +framework makes it simple + +10 +00:00:27,928 --> 00:00:31,398 +to take advantage +of the capabilities of U1 -- + +11 +00:00:31,398 --> 00:00:34,935 +Apple's chip for Ultra Wideband +technology -- + +12 +00:00:34,935 --> 00:00:39,139 +and enables creating precise +and spatially aware interactions + +13 +00:00:39,139 --> 00:00:42,276 +between nearby Apple devices +or accessories + +14 +00:00:42,276 --> 00:00:47,014 +compatible with Apple's U1 chip +for Ultra Wideband. + +15 +00:00:47,014 --> 00:00:48,749 +Let's get started +with a quick review + +16 +00:00:48,749 --> 00:00:52,519 +of what's been available to you +over the last two years. + +17 +00:00:52,519 --> 00:00:56,189 +When Nearby Interaction +was introduced at WWDC 2020, + +18 +00:00:56,189 --> 00:00:58,258 +the functionality +focused on creating + +19 +00:00:58,258 --> 00:01:02,462 +and running a session +between two iPhones with U1. + +20 +00:01:02,462 --> 00:01:06,333 +At WWDC 2021, +the functionality was extended + +21 +00:01:06,333 --> 00:01:09,569 +to support running sessions +with Apple Watch and third-party + +22 +00:01:09,569 --> 00:01:13,240 +Ultra Wideband-compatible +accessories. + +23 +00:01:13,240 --> 00:01:14,875 +If you're interested +in a deep dive + +24 +00:01:14,875 --> 00:01:17,511 +into the Nearby Interaction +framework's APIs, + +25 +00:01:17,511 --> 00:01:20,847 +please review the WWDC talk +"Meet Nearby Interaction" + +26 +00:01:20,847 --> 00:01:22,082 +from 2020 + +27 +00:01:22,082 --> 00:01:25,285 +and "Explore Nearby Interaction +with third-party accessories" + +28 +00:01:25,285 --> 00:01:27,354 +from 2021. + +29 +00:01:27,354 --> 00:01:29,489 +We've been blown away +by the community's response + +30 +00:01:29,489 --> 00:01:31,992 +to Nearby Interaction, +and in this session, + +31 +00:01:31,992 --> 00:01:34,227 +I'm excited to unveil +the new capabilities + +32 +00:01:34,227 --> 00:01:36,530 +and improvements for you. + +33 +00:01:36,530 --> 00:01:39,099 +I will focus on two main topics: + +34 +00:01:39,099 --> 00:01:42,135 +enhancing Nearby Interaction +with ARKit + +35 +00:01:42,135 --> 00:01:44,404 +and background sessions. + +36 +00:01:44,404 --> 00:01:46,573 +Along the way, +I'll share some improvements + +37 +00:01:46,573 --> 00:01:50,610 +that make it easier to use +the Nearby Interaction framework + +38 +00:01:50,610 --> 00:01:54,314 +and I'll conclude with an update +on third-party hardware support + +39 +00:01:54,314 --> 00:01:56,450 +that was announced +last year. + +40 +00:01:56,450 --> 00:01:58,018 +We're excited about +what you will do + +41 +00:01:58,018 --> 00:01:59,519 +with the new capabilities, + +42 +00:01:59,519 --> 00:02:02,556 +so let's dive right +into the details. + +43 +00:02:02,556 --> 00:02:04,992 +I'll start with an exciting +new capability + +44 +00:02:04,992 --> 00:02:09,062 +that tightly integrates ARKit +with Nearby Interaction. + +45 +00:02:09,062 --> 00:02:12,532 +This new capability +enhances Nearby Interaction + +46 +00:02:12,532 --> 00:02:17,371 +by leveraging the device +trajectory computed from ARKit. + +47 +00:02:17,371 --> 00:02:19,406 +ARKit-enhanced +Nearby Interaction + +48 +00:02:19,406 --> 00:02:22,109 +leverages the same +underlying technology + +49 +00:02:22,109 --> 00:02:25,345 +that powers Precision Finding +with AirTag, + +50 +00:02:25,345 --> 00:02:29,783 +and we're making it available +to you via Nearby Interaction. + +51 +00:02:29,783 --> 00:02:32,619 +The best use cases are +experiences that guide a user + +52 +00:02:32,619 --> 00:02:36,189 +to a specific nearby object +such as a misplaced item, + +53 +00:02:36,189 --> 00:02:37,524 +object of interest, + +54 +00:02:37,524 --> 00:02:41,461 +or object that the user +wants to interact with. + +55 +00:02:41,461 --> 00:02:44,231 +By integrating ARKit +and Nearby Interaction, + +56 +00:02:44,231 --> 00:02:46,366 +the distance and direction +information + +57 +00:02:46,366 --> 00:02:48,235 +is more consistently available + +58 +00:02:48,235 --> 00:02:50,537 +than using +Nearby Interaction alone, + +59 +00:02:50,537 --> 00:02:55,342 +effectively broadening the +Ultra Wideband field of view. + +60 +00:02:55,342 --> 00:02:58,245 +Finally, this new capability +is best used + +61 +00:02:58,245 --> 00:03:02,049 +for interacting with +stationary devices. + +62 +00:03:02,049 --> 00:03:04,384 +Let's jump right +into a demonstration + +63 +00:03:04,384 --> 00:03:07,687 +of the possibilities +this new integration of ARKit + +64 +00:03:07,687 --> 00:03:11,358 +and Nearby Interaction +enable with your application. + +65 +00:03:11,358 --> 00:03:14,428 +I've got an application here +for my Jetpack Museum + +66 +00:03:14,428 --> 00:03:16,430 +that has +Ultra Wideband accessories + +67 +00:03:16,430 --> 00:03:18,532 +to help guide users +to the exhibits. + +68 +00:03:18,532 --> 00:03:21,968 +Let's go find the next jetpack. + +69 +00:03:21,968 --> 00:03:24,271 +As the user selects +to go to the next exhibit, + +70 +00:03:24,271 --> 00:03:27,307 +the application discovers +the Ultra Wideband accessory + +71 +00:03:27,307 --> 00:03:29,142 +and performs +the necessary exchanges + +72 +00:03:29,142 --> 00:03:31,645 +to start using +Nearby Interaction. + +73 +00:03:31,645 --> 00:03:33,914 +The application +then instructs the user + +74 +00:03:33,914 --> 00:03:37,584 +to move the phone side to side +while it begins to determine + +75 +00:03:37,584 --> 00:03:40,387 +the physical location +of the next exhibit, + +76 +00:03:40,387 --> 00:03:45,058 +using the enhanced Nearby +Interaction mode with ARKit. + +77 +00:03:45,058 --> 00:03:47,727 +Now that the application +understands the direction + +78 +00:03:47,727 --> 00:03:49,996 +that corresponds +to the next exhibit, + +79 +00:03:49,996 --> 00:03:52,499 +a simple arrow icon +telling the user + +80 +00:03:52,499 --> 00:03:55,869 +the direction to head +to check it out appears. + +81 +00:03:55,869 --> 00:03:58,505 +This rich, spatially-aware +information + +82 +00:03:58,505 --> 00:04:02,576 +utilizing the combination +of ARKit and Nearby Interaction + +83 +00:04:02,576 --> 00:04:06,313 +can even indicate when +the exhibit is behind the user + +84 +00:04:06,313 --> 00:04:10,250 +and the user is heading in a +direction away from the exhibit. + +85 +00:04:10,250 --> 00:04:12,719 +Finally, the application +can display, + +86 +00:04:12,719 --> 00:04:16,690 +in the AR world, an overlay +of the next exhibit's location, + +87 +00:04:16,690 --> 00:04:19,159 +and the application prompts +the user to move the iPhone + +88 +00:04:19,159 --> 00:04:22,262 +up and down slightly +to resolve where the exhibit is + +89 +00:04:22,262 --> 00:04:23,964 +in the AR world. + +90 +00:04:23,964 --> 00:04:26,566 +Once the AR content +is placed in the scene, + +91 +00:04:26,566 --> 00:04:29,769 +the powerful combination +of Nearby Interaction -- + +92 +00:04:29,769 --> 00:04:31,705 +with its Ultra Wideband +measurements -- + +93 +00:04:31,705 --> 00:04:35,642 +and ARKit, allows the user +to easily head over + +94 +00:04:35,642 --> 00:04:38,111 +to check out the next jetpack. + +95 +00:04:38,111 --> 00:04:42,349 +I may not have found a jetpack, +but I found a queen. + +96 +00:04:42,349 --> 00:04:44,818 +Let's turn now +to how you can enable + +97 +00:04:44,818 --> 00:04:47,287 +this enhanced +Nearby Interaction mode. + +98 +00:04:47,287 --> 00:04:50,757 +With iOS 15, you probably have +a method in your application + +99 +00:04:50,757 --> 00:04:52,559 +that accepts +the NIDiscoveryToken + +100 +00:04:52,559 --> 00:04:55,829 +from a nearby peer, +creates a session configuration, + +101 +00:04:55,829 --> 00:04:57,964 +and runs the NISession. + +102 +00:04:57,964 --> 00:05:01,067 +Enabling the enhanced mode +with ARKit is easy + +103 +00:05:01,067 --> 00:05:04,938 +on new and existing uses of +Nearby Interaction with a new + +104 +00:05:04,938 --> 00:05:08,375 +isCameraAssistanceEnabled +property on the subclasses + +105 +00:05:08,375 --> 00:05:11,244 +of NIConfiguration. + +106 +00:05:11,244 --> 00:05:13,079 +Setting the +isCameraAssistanceEnabled + +107 +00:05:13,079 --> 00:05:16,650 +property is all that's required +to leverage the enhanced mode + +108 +00:05:16,650 --> 00:05:18,451 +with ARKit. + +109 +00:05:18,451 --> 00:05:20,353 +Camera assistance is available +when interacting + +110 +00:05:20,353 --> 00:05:23,723 +between two Apple devices, +and an Apple device + +111 +00:05:23,723 --> 00:05:27,694 +to third-party +Ultra Wideband accessories. + +112 +00:05:27,694 --> 00:05:30,063 +Let's look at the details +of what happens + +113 +00:05:30,063 --> 00:05:34,401 +when an NISession is run +with camera assistance enabled. + +114 +00:05:34,401 --> 00:05:35,902 +When camera assistance +is enabled, + +115 +00:05:35,902 --> 00:05:38,872 +an ARSession +is automatically created + +116 +00:05:38,872 --> 00:05:41,808 +within the Nearby Interaction +framework. + +117 +00:05:41,808 --> 00:05:46,413 +You are not responsible +for creating this ARSession. + +118 +00:05:46,413 --> 00:05:49,082 +Running an NISession +with camera assistance enabled + +119 +00:05:49,082 --> 00:05:52,819 +will also run the ARSession +that was automatically created + +120 +00:05:52,819 --> 00:05:56,423 +within the Nearby Interaction +framework. + +121 +00:05:56,423 --> 00:06:00,393 +The ARSession is running +within the application process. + +122 +00:06:00,393 --> 00:06:01,795 +As a result, + +123 +00:06:01,795 --> 00:06:06,199 +the application must provide +a camera usage description key + +124 +00:06:06,199 --> 00:06:09,336 +within the application's +Info.plist. + +125 +00:06:09,336 --> 00:06:13,673 +Be sure to make this a useful +string to inform your users + +126 +00:06:13,673 --> 00:06:19,379 +why the camera is necessary +to provide a good experience. + +127 +00:06:19,379 --> 00:06:23,717 +Only a single ARSession can be +running for a given application. + +128 +00:06:23,717 --> 00:06:26,620 +This means that if you already +have an ARKit experience + +129 +00:06:26,620 --> 00:06:31,424 +in your app, it is necessary to +share the ARSession you create + +130 +00:06:31,424 --> 00:06:34,160 +with the NISession. + +131 +00:06:34,160 --> 00:06:36,429 +To share the ARSession +with the NISession, + +132 +00:06:36,429 --> 00:06:39,466 +a new setARSession method +is available + +133 +00:06:39,466 --> 00:06:41,601 +on the NISession class. + +134 +00:06:41,601 --> 00:06:46,306 +When setARSession is called, +prior to run, on the NISession, + +135 +00:06:46,306 --> 00:06:49,943 +an ARSession will not be +automatically created + +136 +00:06:49,943 --> 00:06:51,978 +within the Nearby Interaction +framework + +137 +00:06:51,978 --> 00:06:54,247 +when the session is run. + +138 +00:06:54,247 --> 00:06:57,284 +This ensures the application +ARKit experience + +139 +00:06:57,284 --> 00:07:00,553 +happens concurrently +to the camera assistance + +140 +00:07:00,553 --> 00:07:02,722 +in Nearby Interaction. + +141 +00:07:02,722 --> 00:07:06,893 +In this SwiftUI example, as part +of the makeUIView function, + +142 +00:07:06,893 --> 00:07:09,996 +the underlying ARSession +within the ARView + +143 +00:07:09,996 --> 00:07:16,303 +is shared with the NISession +via the new setARSession method. + +144 +00:07:16,303 --> 00:07:19,072 +If you are using +an ARSession directly, + +145 +00:07:19,072 --> 00:07:22,475 +it is necessary to call run +on the ARSession + +146 +00:07:22,475 --> 00:07:26,313 +with +an ARWorldTrackingConfiguration. + +147 +00:07:26,313 --> 00:07:29,449 +In addition, several properties +must be configured + +148 +00:07:29,449 --> 00:07:32,952 +in a specific manner +within this ARConfiguration + +149 +00:07:32,952 --> 00:07:35,188 +to ensure high-quality +performance + +150 +00:07:35,188 --> 00:07:37,924 +from camera assistance. + +151 +00:07:37,924 --> 00:07:41,127 +The worldAlignment +should be set to gravity, + +152 +00:07:41,127 --> 00:07:44,698 +collaboration and +userFaceTracking disabled, + +153 +00:07:44,698 --> 00:07:47,767 +a nil initialWorldMap, +and a delegate + +154 +00:07:47,767 --> 00:07:50,670 +whose sessionShouldAttempt +Relocalization method + +155 +00:07:50,670 --> 00:07:52,672 +returns false. + +156 +00:07:52,672 --> 00:07:54,240 +Let's turn to some +best practices + +157 +00:07:54,240 --> 00:07:57,277 +when sharing an ARSession +you created. + +158 +00:07:57,277 --> 00:08:01,014 +In your NISessionDelegate +didInvalidateWith error method, + +159 +00:08:01,014 --> 00:08:04,784 +always inspect the error code. + +160 +00:08:04,784 --> 00:08:08,855 +If the ARConfiguration used +to run the shared ARSession + +161 +00:08:08,855 --> 00:08:11,725 +does not conform +to the outlined properties, + +162 +00:08:11,725 --> 00:08:14,694 +the NISession +will be invalidated. + +163 +00:08:14,694 --> 00:08:18,665 +A new NIError code +invalidARConfiguration + +164 +00:08:18,665 --> 00:08:21,534 +will be returned. + +165 +00:08:21,534 --> 00:08:24,471 +To receive nearby object updates +in your app, + +166 +00:08:24,471 --> 00:08:28,141 +continue to use the +didUpdateNearbyObjects method + +167 +00:08:28,141 --> 00:08:30,577 +in your NISessionDelegate. + +168 +00:08:30,577 --> 00:08:32,946 +In your didUpdateNearbyObjects +method, + +169 +00:08:32,946 --> 00:08:36,616 +you probably check the nearby +objects for your desired peer + +170 +00:08:36,616 --> 00:08:38,852 +and update the UI +based on distance + +171 +00:08:38,852 --> 00:08:43,323 +and direction properties of the +NINearbyObject when available, + +172 +00:08:43,323 --> 00:08:47,460 +always being careful +to recall these can be nil. + +173 +00:08:47,460 --> 00:08:49,496 +When camera assistance +is enabled, + +174 +00:08:49,496 --> 00:08:54,401 +two new properties are available +within the NINearbyObject. + +175 +00:08:54,401 --> 00:08:57,704 +The first is horizontalAngle. + +176 +00:08:57,704 --> 00:08:59,639 +This is the 1D angle in radians + +177 +00:08:59,639 --> 00:09:03,376 +indicating the azimuthal +direction to the nearby object. + +178 +00:09:03,376 --> 00:09:07,447 +When unavailable, +this value will be nil. + +179 +00:09:07,447 --> 00:09:10,016 +Second, +verticalDirectionEstimate + +180 +00:09:10,016 --> 00:09:13,219 +is the positional relationship +to the nearby object + +181 +00:09:13,219 --> 00:09:14,954 +in the vertical dimension. + +182 +00:09:14,954 --> 00:09:19,526 +This is a new +VerticalDirectionEstimate type. + +183 +00:09:19,526 --> 00:09:23,496 +Distance and direction represent +the key spatial relationship + +184 +00:09:23,496 --> 00:09:27,066 +between the user's device +and a nearby object. + +185 +00:09:27,066 --> 00:09:31,304 +Distance is measured in meters +and direction is a 3D vector + +186 +00:09:31,304 --> 00:09:34,974 +from your device +to the nearby object. + +187 +00:09:34,974 --> 00:09:38,478 +Horizontal angle is defined +as the angle between the device + +188 +00:09:38,478 --> 00:09:41,214 +running the NISession +and the nearby object + +189 +00:09:41,214 --> 00:09:44,250 +within a local +horizontal plane. + +190 +00:09:44,250 --> 00:09:46,419 +This accounts for any +vertical displacement + +191 +00:09:46,419 --> 00:09:50,590 +offset between the two devices +and any horizontal rotation + +192 +00:09:50,590 --> 00:09:52,926 +of the device itself. + +193 +00:09:52,926 --> 00:09:55,962 +While direction is 3D, +horizontal angle + +194 +00:09:55,962 --> 00:10:02,469 +is a 1D representation of the +heading between the two devices. + +195 +00:10:02,469 --> 00:10:04,471 +This horizontal angle property + +196 +00:10:04,471 --> 00:10:07,574 +is complimentary +to the direction property, + +197 +00:10:07,574 --> 00:10:09,709 +and if the direction +cannot be resolved, + +198 +00:10:09,709 --> 00:10:12,212 +the horizontal angle +can be available + +199 +00:10:12,212 --> 00:10:16,716 +to help you guide your user +to a nearby object. + +200 +00:10:16,716 --> 00:10:20,253 +Vertical direction estimate +is a qualitative assessment + +201 +00:10:20,253 --> 00:10:22,822 +of the vertical position +information. + +202 +00:10:22,822 --> 00:10:27,160 +You should use it to guide +the user between floor levels. + +203 +00:10:27,160 --> 00:10:32,599 +Let's look now at the new +VerticalDirectionEstimate type. + +204 +00:10:32,599 --> 00:10:35,702 +VerticalDirectionEstimate +is a nested enum + +205 +00:10:35,702 --> 00:10:38,705 +within the NINearbyObject +and represents + +206 +00:10:38,705 --> 00:10:41,808 +a qualitative assessment +of the vertical relationship + +207 +00:10:41,808 --> 00:10:44,611 +to the nearby object. + +208 +00:10:44,611 --> 00:10:47,146 +Be sure to check if +the VerticalDirectionEstimate + +209 +00:10:47,146 --> 00:10:50,517 +is unknown +before using the property. + +210 +00:10:50,517 --> 00:10:55,755 +The vertical relationship +can be same, above, below, + +211 +00:10:55,755 --> 00:10:58,925 +or a special +aboveOrBelow value + +212 +00:10:58,925 --> 00:11:02,996 +that represents the nearby +object is not on the same level, + +213 +00:11:02,996 --> 00:11:07,967 +but not clearly above +or below the device. + +214 +00:11:07,967 --> 00:11:11,070 +The Ultra Wideband measurements +are subject to field of view + +215 +00:11:11,070 --> 00:11:12,505 +and obstructions. + +216 +00:11:12,505 --> 00:11:14,574 +The field of view +for direction information + +217 +00:11:14,574 --> 00:11:19,746 +corresponds to a cone projecting +from the rear of the device. + +218 +00:11:19,746 --> 00:11:22,315 +The device trajectory +computed from ARKit + +219 +00:11:22,315 --> 00:11:26,085 +when camera assistance +is enabled allows the distance, + +220 +00:11:26,085 --> 00:11:30,390 +direction, horizontal angle, +and vertical direction estimate + +221 +00:11:30,390 --> 00:11:35,495 +to be available in more +scenarios, effectively expanding + +222 +00:11:35,495 --> 00:11:39,098 +the Ultra Wideband sensor +field of view. + +223 +00:11:39,098 --> 00:11:42,936 +Let's turn now to leveraging +this integration of ARKit + +224 +00:11:42,936 --> 00:11:48,474 +and Nearby Interaction to place +AR objects in your scene. + +225 +00:11:48,474 --> 00:11:52,579 +To make it easier for you +to overlay 3D virtual content + +226 +00:11:52,579 --> 00:11:54,547 +that represents +the nearby object + +227 +00:11:54,547 --> 00:11:56,783 +onto a camera feed +visualization, + +228 +00:11:56,783 --> 00:12:02,388 +we've added a helper method: +worldTransform on NISession. + +229 +00:12:02,388 --> 00:12:05,058 +This method returns +a worldTransform + +230 +00:12:05,058 --> 00:12:07,860 +in ARKit's coordinate space +that represents + +231 +00:12:07,860 --> 00:12:10,330 +the given nearby +object's position + +232 +00:12:10,330 --> 00:12:13,466 +in the physical environment +when it's available. + +233 +00:12:13,466 --> 00:12:18,271 +When not available, +this method returns nil. + +234 +00:12:18,271 --> 00:12:20,173 +We used this method +in the demonstration + +235 +00:12:20,173 --> 00:12:23,843 +to place the floating spheres +above the next exhibit. + +236 +00:12:23,843 --> 00:12:26,512 +We want to make it as easy +as possible for you + +237 +00:12:26,512 --> 00:12:29,916 +to leverage Nearby Interaction +positional output + +238 +00:12:29,916 --> 00:12:34,220 +to manipulate content +in the AR world in your app. + +239 +00:12:34,220 --> 00:12:38,858 +Two powerful systems in iOS, +combined. + +240 +00:12:38,858 --> 00:12:42,428 +Your users must sweep the +device sufficiently in vertical + +241 +00:12:42,428 --> 00:12:46,032 +and horizontal directions +to allow the camera assistance + +242 +00:12:46,032 --> 00:12:49,402 +to adequately compute +the world transform. + +243 +00:12:49,402 --> 00:12:52,472 +This method can return nil +when the user motion + +244 +00:12:52,472 --> 00:12:55,541 +is insufficient to allow +the camera assistance + +245 +00:12:55,541 --> 00:12:59,946 +to fully converge +to an ARKit world transform. + +246 +00:12:59,946 --> 00:13:04,250 +When this transform is important +to your app experience, + +247 +00:13:04,250 --> 00:13:07,887 +it is important to coach +the user to take action + +248 +00:13:07,887 --> 00:13:11,290 +to generate this transform. + +249 +00:13:11,290 --> 00:13:14,027 +Let's look now at some +additions we've made + +250 +00:13:14,027 --> 00:13:17,063 +to the NISessionDelegate +to make it possible + +251 +00:13:17,063 --> 00:13:20,400 +for you to guide the user +similar to what you saw + +252 +00:13:20,400 --> 00:13:22,035 +in the demonstration. + +253 +00:13:22,035 --> 00:13:24,504 +To aid in guiding the user +towards your object, + +254 +00:13:24,504 --> 00:13:27,440 +an NISessionDelegate callback +provides information + +255 +00:13:27,440 --> 00:13:30,977 +about the Nearby Interaction +algorithm convergence + +256 +00:13:30,977 --> 00:13:33,880 +via the new +didUpdateAlgorithmConvergence + +257 +00:13:33,880 --> 00:13:36,149 +delegate method. + +258 +00:13:36,149 --> 00:13:39,285 +Algorithm convergence +can help you understand why + +259 +00:13:39,285 --> 00:13:42,388 +horizontal angle, +vertical direction estimate, + +260 +00:13:42,388 --> 00:13:45,024 +and worldTransform +are unavailable + +261 +00:13:45,024 --> 00:13:47,727 +and what actions +the user can take + +262 +00:13:47,727 --> 00:13:50,129 +to resolve those properties. + +263 +00:13:50,129 --> 00:13:51,230 +The delegate provides + +264 +00:13:51,230 --> 00:13:54,734 +a new NIAlgorithmConvergence +object + +265 +00:13:54,734 --> 00:13:58,604 +and an optional NINearbyObject. + +266 +00:13:58,604 --> 00:14:02,975 +This delegate method is only +called when you have enabled + +267 +00:14:02,975 --> 00:14:07,046 +camera assistance +in your NIConfiguration. + +268 +00:14:07,046 --> 00:14:11,517 +Let's look at the new +NIAlgorithmConvergence type. + +269 +00:14:11,517 --> 00:14:15,054 +NIAlgorithmConvergence +has a single-status property + +270 +00:14:15,054 --> 00:14:18,791 +that is an NIAlgorithm +ConvergenceStatus type. + +271 +00:14:18,791 --> 00:14:22,795 +The NIAlgorithmConvergenceStatus +type is an enum that represents + +272 +00:14:22,795 --> 00:14:26,232 +whether the algorithm +is converged or not. + +273 +00:14:26,232 --> 00:14:28,434 +If the algorithm +is not converged, + +274 +00:14:28,434 --> 00:14:30,269 +an array of associated values + +275 +00:14:30,269 --> 00:14:36,109 +NIAlgorithmConvergenceStatus +.Reasons is provided. + +276 +00:14:36,109 --> 00:14:38,177 +Let's return +to the new delegate method + +277 +00:14:38,177 --> 00:14:39,412 +and say you want to update + +278 +00:14:39,412 --> 00:14:43,015 +the status of the camera +assistance to the user, + +279 +00:14:43,015 --> 00:14:44,984 +you can switch on +the convergence status + +280 +00:14:44,984 --> 00:14:46,519 +and if unknown or converged, + +281 +00:14:46,519 --> 00:14:49,856 +display that information +to the user. + +282 +00:14:49,856 --> 00:14:53,459 +Be sure to inspect +the NINearbyObject. + +283 +00:14:53,459 --> 00:14:54,894 +When the object is nil, + +284 +00:14:54,894 --> 00:14:59,732 +the NIAlgorithmConvergence state +applies to the session itself, + +285 +00:14:59,732 --> 00:15:04,270 +rather than a specific +NINearbyObject. + +286 +00:15:04,270 --> 00:15:06,472 +When the status is +notConverged, + +287 +00:15:06,472 --> 00:15:10,610 +it also includes an associated +value that describes the reasons + +288 +00:15:10,610 --> 00:15:13,112 +the algorithm is not converged. + +289 +00:15:13,112 --> 00:15:16,015 +A localized description +is available for this reason + +290 +00:15:16,015 --> 00:15:19,619 +to help you communicate better +with your users. + +291 +00:15:19,619 --> 00:15:23,389 +Let's look next +at how to use these values. + +292 +00:15:23,389 --> 00:15:26,125 +Inspecting the notConverged +case more closely + +293 +00:15:26,125 --> 00:15:28,027 +and the associated +reasons value, + +294 +00:15:28,027 --> 00:15:31,264 +it is possible to guide +the user to take actions + +295 +00:15:31,264 --> 00:15:33,766 +that helps produce +the desired information + +296 +00:15:33,766 --> 00:15:36,536 +about a nearby object. + +297 +00:15:36,536 --> 00:15:38,905 +The associated value +is an array of + +298 +00:15:38,905 --> 00:15:41,941 +NIAlgorithmConvergence +StatusReasons. + +299 +00:15:41,941 --> 00:15:45,344 +The reason can indicate there's +insufficient total motion, + +300 +00:15:45,344 --> 00:15:48,648 +insufficient motion in the +horizontal or vertical sweep, + +301 +00:15:48,648 --> 00:15:51,050 +and insufficient lighting. + +302 +00:15:51,050 --> 00:15:56,088 +Be mindful that multiple reasons +may exist at the same time + +303 +00:15:56,088 --> 00:15:59,592 +and guide the user sequentially +through each action + +304 +00:15:59,592 --> 00:16:03,763 +based on which is most important +for your application. + +305 +00:16:03,763 --> 00:16:06,766 +Recall how I moved the phone +in the demonstration + +306 +00:16:06,766 --> 00:16:09,468 +and needed to sweep +in both the horizontal + +307 +00:16:09,468 --> 00:16:13,406 +and vertical direction +to resolve the world transform. + +308 +00:16:13,406 --> 00:16:15,174 +That's the most +important bits about + +309 +00:16:15,174 --> 00:16:18,711 +the enhanced Nearby Interaction +mode with camera assistance. + +310 +00:16:18,711 --> 00:16:20,379 +We've made some +additional changes + +311 +00:16:20,379 --> 00:16:23,316 +to help you better +leverage this mode. + +312 +00:16:23,316 --> 00:16:27,954 +Previously, a single isSupported +class variable on the NISession + +313 +00:16:27,954 --> 00:16:31,390 +was all that was necessary +to check if Nearby Interaction + +314 +00:16:31,390 --> 00:16:33,826 +was supported on a given device. + +315 +00:16:33,826 --> 00:16:36,195 +This is now deprecated. + +316 +00:16:36,195 --> 00:16:38,164 +With the addition +of camera assistance, + +317 +00:16:38,164 --> 00:16:40,099 +we've made +the device capabilities + +318 +00:16:40,099 --> 00:16:43,669 +supported by Nearby Interaction +more descriptive + +319 +00:16:43,669 --> 00:16:48,040 +with a new deviceCapabilities +class member on the NISession + +320 +00:16:48,040 --> 00:16:52,879 +that returns a new +NIDeviceCapability object. + +321 +00:16:52,879 --> 00:16:54,580 +At a minimum, checking the + +322 +00:16:54,580 --> 00:16:57,450 +supportsPreciseDistance +Measurement property + +323 +00:16:57,450 --> 00:16:59,986 +is the equivalent +of the now deprecated + +324 +00:16:59,986 --> 00:17:03,222 +isSupported class variable. + +325 +00:17:03,222 --> 00:17:05,892 +Once you've established +that the device supports + +326 +00:17:05,892 --> 00:17:07,960 +the precise distance +measurement, + +327 +00:17:07,960 --> 00:17:10,396 +you should use +NIDeviceCapability + +328 +00:17:10,396 --> 00:17:13,566 +to fully understand +the capabilities available + +329 +00:17:13,566 --> 00:17:18,504 +from Nearby Interaction on the +device running your application. + +330 +00:17:18,504 --> 00:17:22,074 +It is recommended +you tailor your app experience + +331 +00:17:22,074 --> 00:17:23,976 +to the capabilities +of the device + +332 +00:17:23,976 --> 00:17:27,880 +by checking the additional +supportsDirectionMeasurement + +333 +00:17:27,880 --> 00:17:30,349 +and supportsCameraAssistance +properties + +334 +00:17:30,349 --> 00:17:33,986 +of the NIDeviceCapability +object. + +335 +00:17:33,986 --> 00:17:36,923 +Not all devices will support +direction measurements + +336 +00:17:36,923 --> 00:17:39,091 +nor camera assistance, + +337 +00:17:39,091 --> 00:17:41,961 +so be sure to include +experiences that are tailored + +338 +00:17:41,961 --> 00:17:45,131 +to the capabilities +of this device. + +339 +00:17:45,131 --> 00:17:48,367 +In particular, +be mindful to include + +340 +00:17:48,367 --> 00:17:50,636 +distance-only experiences + +341 +00:17:50,636 --> 00:17:53,806 +in order to best support +Apple Watch. + +342 +00:17:53,806 --> 00:17:56,475 +That's all for camera assistance +as a way to enhance + +343 +00:17:56,475 --> 00:18:00,579 +Nearby Interaction with ARKit. +So let's turn our attention now + +344 +00:18:00,579 --> 00:18:03,649 +to accessory background +sessions. + +345 +00:18:03,649 --> 00:18:07,186 +Today, you can use +Nearby Interaction in your app + +346 +00:18:07,186 --> 00:18:11,324 +to allow users to point +to other devices, find friends, + +347 +00:18:11,324 --> 00:18:13,359 +and show controls or other UI + +348 +00:18:13,359 --> 00:18:16,829 +based on distance and direction +to an accessory. + +349 +00:18:16,829 --> 00:18:20,299 +However, when your app +transitions to the background + +350 +00:18:20,299 --> 00:18:24,236 +or when the user locks +the screen on iOS and watchOS, + +351 +00:18:24,236 --> 00:18:27,073 +any running NISessions +are suspended + +352 +00:18:27,073 --> 00:18:30,443 +until the application +returns to the foreground. + +353 +00:18:30,443 --> 00:18:34,714 +This means you needed to focus +on hands-on user experiences + +354 +00:18:34,714 --> 00:18:38,017 +when interacting +with your accessory. + +355 +00:18:38,017 --> 00:18:43,422 +Starting in iOS 16, Nearby +Interaction has gone hands-free. + +356 +00:18:43,422 --> 00:18:45,658 +You're now able to use +Nearby Interaction + +357 +00:18:45,658 --> 00:18:47,860 +to start playing music +when you walk into a room + +358 +00:18:47,860 --> 00:18:51,731 +with a smart speaker, turn on +your eBike when you get on it, + +359 +00:18:51,731 --> 00:18:56,168 +or trigger other hands-free +actions on an accessory. + +360 +00:18:56,168 --> 00:18:58,371 +You can do this +even when the user + +361 +00:18:58,371 --> 00:19:00,606 +isn't actively using your app + +362 +00:19:00,606 --> 00:19:03,909 +via accessory +background sessions. + +363 +00:19:03,909 --> 00:19:05,945 +Let's look at +how you can accomplish + +364 +00:19:05,945 --> 00:19:09,615 +this exciting new capability. + +365 +00:19:09,615 --> 00:19:11,884 +Let's spend just a minute +reviewing the sequence + +366 +00:19:11,884 --> 00:19:15,921 +for how to configure and run +an NISession with an accessory. + +367 +00:19:15,921 --> 00:19:17,590 +You might recognize +this sequence + +368 +00:19:17,590 --> 00:19:20,893 +from last year's +WWDC presentation. + +369 +00:19:20,893 --> 00:19:23,062 +The accessory sends +its Ultra Wideband + +370 +00:19:23,062 --> 00:19:26,399 +accessory configuration data +over to your application + +371 +00:19:26,399 --> 00:19:29,168 +via a data channel, +and you create an + +372 +00:19:29,168 --> 00:19:33,105 +NINearbyAccessoryConfiguration +from this data. + +373 +00:19:33,105 --> 00:19:36,876 +You create an NISession, +set an NISessionDelegate + +374 +00:19:36,876 --> 00:19:40,279 +to get Ultra Wideband +measurements from the accessory. + +375 +00:19:40,279 --> 00:19:43,049 +You run the NISession +with your configuration + +376 +00:19:43,049 --> 00:19:46,685 +and the session will return +sharable configuration data + +377 +00:19:46,685 --> 00:19:48,921 +to setup the accessory +to interoperate + +378 +00:19:48,921 --> 00:19:51,023 +with your application. + +379 +00:19:51,023 --> 00:19:53,559 +After sending this sharable +configuration data + +380 +00:19:53,559 --> 00:19:55,194 +back to the accessory, + +381 +00:19:55,194 --> 00:19:58,264 +you are now able to receive +Ultra Wideband measurements + +382 +00:19:58,264 --> 00:20:01,901 +in your application +and at the accessory. + +383 +00:20:01,901 --> 00:20:04,036 +For all the details +on configuring and running + +384 +00:20:04,036 --> 00:20:06,872 +Nearby Interaction +with third-party accessories, + +385 +00:20:06,872 --> 00:20:10,509 +please review +last year's WWDC session. + +386 +00:20:10,509 --> 00:20:14,513 +Let's look now at how you set up +the new background sessions. + +387 +00:20:14,513 --> 00:20:17,583 +The previous sequence diagram +showed data flowing + +388 +00:20:17,583 --> 00:20:21,153 +between your application +and the accessory. + +389 +00:20:21,153 --> 00:20:24,090 +It is very common to have +the communication channel + +390 +00:20:24,090 --> 00:20:29,261 +between an accessory and your +application use Bluetooth LE. + +391 +00:20:29,261 --> 00:20:32,298 +When paired to the accessory +using Bluetooth LE, + +392 +00:20:32,298 --> 00:20:34,066 +you can enable +Nearby Interaction + +393 +00:20:34,066 --> 00:20:37,903 +to start and continue sessions +in the background. + +394 +00:20:37,903 --> 00:20:40,573 +Let's look closely +at how this is possible. + +395 +00:20:40,573 --> 00:20:43,976 +Today, you can configure +your app to use Core Bluetooth + +396 +00:20:43,976 --> 00:20:46,779 +to discover, connect to, +and exchange data + +397 +00:20:46,779 --> 00:20:48,681 +with Bluetooth LE accessories + +398 +00:20:48,681 --> 00:20:51,417 +while your app +is in the background. + +399 +00:20:51,417 --> 00:20:54,386 +Check out the existing +Core Bluetooth Programming Guide + +400 +00:20:54,386 --> 00:20:59,425 +or the WWDC session from 2017 +for more details. + +401 +00:20:59,425 --> 00:21:02,294 +Taking advantage of the powerful +background operations + +402 +00:21:02,294 --> 00:21:05,431 +from Core Bluetooth +to efficiently discover + +403 +00:21:05,431 --> 00:21:08,868 +the accessory and run your +application in the background, + +404 +00:21:08,868 --> 00:21:12,304 +your application can start +an NISession + +405 +00:21:12,304 --> 00:21:14,073 +with a Bluetooth LE accessory + +406 +00:21:14,073 --> 00:21:18,344 +that also supports Ultra +Wideband in the background. + +407 +00:21:18,344 --> 00:21:21,380 +Let's look now at how +the sequence diagram updates + +408 +00:21:21,380 --> 00:21:24,283 +to reflect this new mode. + +409 +00:21:24,283 --> 00:21:25,951 +To interact with this accessory, + +410 +00:21:25,951 --> 00:21:29,889 +first, ensure that it is +Bluetooth LE-paired. + +411 +00:21:29,889 --> 00:21:32,691 +Then, connect to the accessory. + +412 +00:21:32,691 --> 00:21:34,260 +When the accessory generates + +413 +00:21:34,260 --> 00:21:37,830 +its accessory Ultra Wideband +configuration data, + +414 +00:21:37,830 --> 00:21:40,799 +it should both +send it to your application + +415 +00:21:40,799 --> 00:21:44,270 +and populate the Nearby +Interaction GATT service; + +416 +00:21:44,270 --> 00:21:46,071 +more on this next. + +417 +00:21:46,071 --> 00:21:48,174 +Finally, when your +application receives + +418 +00:21:48,174 --> 00:21:51,076 +the accessory's +configuration data, construct an + +419 +00:21:51,076 --> 00:21:55,681 +NINearbyAccessoryConfiguration +object using a new initializer + +420 +00:21:55,681 --> 00:21:59,451 +providing both your accessory's +UWB configuration data + +421 +00:21:59,451 --> 00:22:02,154 +and its Bluetooth +peer identifier. + +422 +00:22:02,154 --> 00:22:04,657 +Run your NISession +with this configuration + +423 +00:22:04,657 --> 00:22:07,293 +and ensure you complete +the setup by receiving + +424 +00:22:07,293 --> 00:22:11,163 +the sharable configuration +in your NISessionDelegate + +425 +00:22:11,163 --> 00:22:15,501 +and send the sharable +configuration to the accessory. + +426 +00:22:15,501 --> 00:22:18,637 +In order for your accessory +to create a relationship + +427 +00:22:18,637 --> 00:22:20,773 +between its Bluetooth identifier + +428 +00:22:20,773 --> 00:22:23,275 +and the Ultra Wideband +configuration, + +429 +00:22:23,275 --> 00:22:27,780 +it must implement the new +Nearby Interaction GATT service. + +430 +00:22:27,780 --> 00:22:31,283 +The Nearby Interaction service +contains a single encrypted + +431 +00:22:31,283 --> 00:22:35,287 +characteristic called +Accessory Configuration Data. + +432 +00:22:35,287 --> 00:22:38,490 +It contains the same +UWB configuration data + +433 +00:22:38,490 --> 00:22:42,394 +used to initialize the +NINearbyAccessoryConfiguration + +434 +00:22:42,394 --> 00:22:43,696 +object. + +435 +00:22:43,696 --> 00:22:48,867 +iOS uses this characteristic +to verify the association + +436 +00:22:48,867 --> 00:22:53,472 +between your Bluetooth peer +identifier and your NISession. + +437 +00:22:53,472 --> 00:22:57,843 +Your app cannot read from +this characteristic directly. + +438 +00:22:57,843 --> 00:23:00,112 +You can find out more +about the details + +439 +00:23:00,112 --> 00:23:02,881 +of this new Nearby Interaction +GATT service + +440 +00:23:02,881 --> 00:23:07,786 +on developer.apple.com/ +nearby-interaction. + +441 +00:23:07,786 --> 00:23:11,590 +If your accessory supports +multiple NISessions in parallel, + +442 +00:23:11,590 --> 00:23:15,661 +create multiple instances of +Accessory Configuration Data, + +443 +00:23:15,661 --> 00:23:20,165 +each with a different +NISession's UWB configuration. + +444 +00:23:20,165 --> 00:23:23,202 +That's what's necessary +on the accessory. + +445 +00:23:23,202 --> 00:23:27,206 +Let's turn to what you need +to implement in your application + +446 +00:23:27,206 --> 00:23:30,276 +by diving into some code! + +447 +00:23:30,276 --> 00:23:33,712 +Accessory background sessions +require that the accessory + +448 +00:23:33,712 --> 00:23:37,016 +is LE-paired +to the user's iPhone. + +449 +00:23:37,016 --> 00:23:40,953 +Your app is responsible +for triggering this process. + +450 +00:23:40,953 --> 00:23:44,957 +To do this, implement methods +to scan for your accessory, + +451 +00:23:44,957 --> 00:23:50,362 +connect to it, and discover its +services and characteristics. + +452 +00:23:50,362 --> 00:23:52,998 +Then, implement +a method to read + +453 +00:23:52,998 --> 00:23:56,302 +one of your accessory's +encrypted characteristics. + +454 +00:23:56,302 --> 00:23:59,271 +You only need to do this once. + +455 +00:23:59,271 --> 00:24:03,842 +It will show the user +a prompt to accept pairing. + +456 +00:24:03,842 --> 00:24:06,478 +Accessory background sessions +also require + +457 +00:24:06,478 --> 00:24:09,548 +a Bluetooth connection +to your accessory. + +458 +00:24:09,548 --> 00:24:12,384 +Your app must be able +to form this connection + +459 +00:24:12,384 --> 00:24:15,054 +even when it's backgrounded. + +460 +00:24:15,054 --> 00:24:18,824 +To do this, implement a method +to initiate a connection attempt + +461 +00:24:18,824 --> 00:24:20,426 +to your accessory. + +462 +00:24:20,426 --> 00:24:22,828 +You should do this +even if the accessory + +463 +00:24:22,828 --> 00:24:26,498 +is not within Bluetooth range. + +464 +00:24:26,498 --> 00:24:29,635 +Then, implement +CBManagerDelegate methods + +465 +00:24:29,635 --> 00:24:35,174 +to restore state after your app +is relaunched by Core Bluetooth + +466 +00:24:35,174 --> 00:24:38,510 +and handle when your +connection is established. + +467 +00:24:38,510 --> 00:24:42,848 +Now you're ready to run +an accessory background session. + +468 +00:24:42,848 --> 00:24:45,351 +Create an +NINearbyAccessoryConfiguration + +469 +00:24:45,351 --> 00:24:48,354 +object by providing +both the accessory's + +470 +00:24:48,354 --> 00:24:53,058 +UWB configuration data and its +Bluetooth peer identifier + +471 +00:24:53,058 --> 00:24:55,894 +from the CBPeripheral +identifier. + +472 +00:24:55,894 --> 00:24:58,697 +Run an NISession +with that configuration + +473 +00:24:58,697 --> 00:25:02,101 +and it will run while your app +is backgrounded. + +474 +00:25:02,101 --> 00:25:03,402 +That's it! + +475 +00:25:03,402 --> 00:25:05,871 +Well, there is one more setting + +476 +00:25:05,871 --> 00:25:09,441 +you need to update +for your app in Xcode. + +477 +00:25:09,441 --> 00:25:13,078 +This background mode requires +the Nearby Interaction string + +478 +00:25:13,078 --> 00:25:17,616 +in the UIBackgroundModes array +in your app's Info.plist. + +479 +00:25:17,616 --> 00:25:20,719 +You can also use +Xcode capabilities editor + +480 +00:25:20,719 --> 00:25:23,155 +to add this background mode. + +481 +00:25:23,155 --> 00:25:25,190 +You will also want to ensure +you have + +482 +00:25:25,190 --> 00:25:29,261 +"Uses Bluetooth LE accessories" +enabled to ensure your app + +483 +00:25:29,261 --> 00:25:33,732 +can connect with the accessory +in the background. + +484 +00:25:33,732 --> 00:25:35,501 +One important note + +485 +00:25:35,501 --> 00:25:38,537 +about this new accessory +background session. + +486 +00:25:38,537 --> 00:25:41,206 +When your application +is in the background, + +487 +00:25:41,206 --> 00:25:45,377 +the NISession will continue to +run and will not be suspended, + +488 +00:25:45,377 --> 00:25:49,615 +so Ultra Wideband measurements +are available on the accessory. + +489 +00:25:49,615 --> 00:25:53,252 +You must consume and act on +the Ultra Wideband measurements + +490 +00:25:53,252 --> 00:25:54,987 +on the accessory. + +491 +00:25:54,987 --> 00:25:58,190 +Your application +will not receive runtime, + +492 +00:25:58,190 --> 00:26:00,926 +and you will not receive +didUpdateNearbyObject + +493 +00:26:00,926 --> 00:26:02,261 +delegate callbacks + +494 +00:26:02,261 --> 00:26:06,064 +until your application +returns to the foreground. + +495 +00:26:06,064 --> 00:26:08,200 +When using this new +background mode, + +496 +00:26:08,200 --> 00:26:11,370 +let's review the following +best practices. + +497 +00:26:11,370 --> 00:26:13,972 +Triggering LE pairing +with your accessory + +498 +00:26:13,972 --> 00:26:17,342 +will show the user a prompt +to accept the pairing. + +499 +00:26:17,342 --> 00:26:20,312 +Do this at a time +that is intuitive to the user + +500 +00:26:20,312 --> 00:26:23,282 +why they want to pair +the accessory. + +501 +00:26:23,282 --> 00:26:26,151 +This could be in the setup flow +that it creates the relationship + +502 +00:26:26,151 --> 00:26:29,154 +with the accessory or when +the user clearly indicates + +503 +00:26:29,154 --> 00:26:32,758 +their desire to interact +with the accessory. + +504 +00:26:32,758 --> 00:26:34,927 +While your app is backgrounded, + +505 +00:26:34,927 --> 00:26:37,863 +your NISession +will not be suspended, + +506 +00:26:37,863 --> 00:26:40,466 +but it will not receive +didUpdateNearbyObject + +507 +00:26:40,466 --> 00:26:42,367 +delegate callbacks. + +508 +00:26:42,367 --> 00:26:45,537 +However, your accessory +will receive + +509 +00:26:45,537 --> 00:26:47,539 +Ultra Wideband measurements. + +510 +00:26:47,539 --> 00:26:50,676 +Process these measurements +directly on your accessory + +511 +00:26:50,676 --> 00:26:54,513 +to determine what action +should happen for the user. + +512 +00:26:54,513 --> 00:26:56,882 +Finally, manage battery usage + +513 +00:26:56,882 --> 00:27:00,419 +by only sending data +from your accessory to your app + +514 +00:27:00,419 --> 00:27:02,821 +during a significant +user interaction; + +515 +00:27:02,821 --> 00:27:06,458 +for example, to show +a notification to the user. + +516 +00:27:06,458 --> 00:27:09,394 +That's all you need to know +on background sessions + +517 +00:27:09,394 --> 00:27:14,333 +and leads me to the last topic +on third-party hardware support. + +518 +00:27:14,333 --> 00:27:17,836 +Today, I'm happy to announce +that the previously available + +519 +00:27:17,836 --> 00:27:20,906 +beta U1-compatible +development kits + +520 +00:27:20,906 --> 00:27:26,044 +are now out of beta +and available for wider use. + +521 +00:27:26,044 --> 00:27:29,615 +Please visit developer.apple.com +/nearby-interaction + +522 +00:27:29,615 --> 00:27:31,083 +to find out more about + +523 +00:27:31,083 --> 00:27:35,053 +compatible Ultra Wideband +development kits. + +524 +00:27:35,053 --> 00:27:36,855 +We've also updated +the specification + +525 +00:27:36,855 --> 00:27:39,258 +for accessory manufacturers +to support + +526 +00:27:39,258 --> 00:27:41,793 +the new accessory +background sessions, + +527 +00:27:41,793 --> 00:27:44,696 +including the Nearby Interaction +GATT service, + +528 +00:27:44,696 --> 00:27:47,699 +and it is available +on the same website. + +529 +00:27:47,699 --> 00:27:52,638 +So, let's summarize what we've +discussed in this session. + +530 +00:27:52,638 --> 00:27:56,875 +Nearby Interaction now includes +a new camera-assisted mode + +531 +00:27:56,875 --> 00:28:00,379 +that tightly integrates ARKit +and Nearby Interaction + +532 +00:28:00,379 --> 00:28:02,548 +to provide a seamless experience + +533 +00:28:02,548 --> 00:28:05,918 +for you to create +spatially aware experiences + +534 +00:28:05,918 --> 00:28:09,821 +that guide users +to a nearby object. + +535 +00:28:09,821 --> 00:28:13,292 +The accessory background +sessions enable you to initiate + +536 +00:28:13,292 --> 00:28:16,061 +and extend sessions +into the background + +537 +00:28:16,061 --> 00:28:19,197 +for you to build +a more hands-off experience + +538 +00:28:19,197 --> 00:28:21,767 +for your users. + +539 +00:28:21,767 --> 00:28:23,735 +We've announced +exciting updates + +540 +00:28:23,735 --> 00:28:28,507 +to the third-party compatible +Ultra Wideband hardware support. + +541 +00:28:28,507 --> 00:28:31,843 +That's it for the Nearby +Interaction updates this year. + +542 +00:28:31,843 --> 00:28:33,445 +Download the demos, + +543 +00:28:33,445 --> 00:28:36,415 +reach out with feedback +on the updated capabilities, + +544 +00:28:36,415 --> 00:28:39,217 +review the updated +third-party specification, + +545 +00:28:39,217 --> 00:28:43,188 +and go build amazing apps +with spatial experiences. + +546 +00:28:43,188 --> 00:28:44,890 +Thank you. + +547 +00:28:44,890 --> 00:28:48,927 +♪ + diff --git "a/eng/2022 Session 10009 What\342\200\231s new in iPad app design en.srt" "b/eng/2022 Session 10009 What\342\200\231s new in iPad app design en.srt" new file mode 100644 index 0000000..0a26d46 --- /dev/null +++ "b/eng/2022 Session 10009 What\342\200\231s new in iPad app design en.srt" @@ -0,0 +1,1800 @@ +1 +00:00:00,000 --> 00:00:04,638 +♪ instrumental hip hop music ♪ + +2 +00:00:04,638 --> 00:00:08,775 +♪ + +3 +00:00:08,775 --> 00:00:11,845 +Hi, welcome to +our session on iPad App design. + +4 +00:00:11,845 --> 00:00:12,946 +I'm Bryant Jow. + +5 +00:00:12,946 --> 00:00:14,147 +And I'm Grant Paul, + +6 +00:00:14,147 --> 00:00:16,250 +and we're from +the Apple Design Team. + +7 +00:00:16,250 --> 00:00:18,318 +Bryant: Today, we'll show you +some of the new UIKit + +8 +00:00:18,318 --> 00:00:20,520 +and SwiftUI updates +in iPadOS 16, + +9 +00:00:20,520 --> 00:00:21,655 +as well as some general tips + +10 +00:00:21,655 --> 00:00:24,958 +for incorporating these +into your app designs. + +11 +00:00:24,958 --> 00:00:26,426 +With iPadOS 16, + +12 +00:00:26,426 --> 00:00:28,862 +people can take advantage +of larger resolutions + +13 +00:00:28,862 --> 00:00:31,832 +with extended display support +and display zoom + +14 +00:00:31,832 --> 00:00:33,367 +and work with their apps +in a single view + +15 +00:00:33,367 --> 00:00:35,569 +by using Stage Manager. + +16 +00:00:35,569 --> 00:00:38,372 +So what does this mean +for iPad apps? + +17 +00:00:38,372 --> 00:00:40,674 +Your app design should always +embrace larger screens + +18 +00:00:40,674 --> 00:00:42,075 +and different window sizes, + +19 +00:00:42,075 --> 00:00:45,545 +as well as support all +the different inputs of iPadOS. + +20 +00:00:45,545 --> 00:00:47,614 +And now, your iPad apps +should consider + +21 +00:00:47,614 --> 00:00:49,283 +how to lean into +more advanced use cases + +22 +00:00:49,283 --> 00:00:52,319 +that come with using iPad +like a desktop. + +23 +00:00:52,319 --> 00:00:54,788 +So that's why we're introducing +a number of improvements + +24 +00:00:54,788 --> 00:00:58,358 +to help apps become +more powerful and easier to use. + +25 +00:00:58,358 --> 00:01:01,528 +Here's a preview +of the features we'll cover: + +26 +00:01:01,528 --> 00:01:04,064 +toolbars and customization, + +27 +00:01:04,064 --> 00:01:06,700 +document menu, + +28 +00:01:06,700 --> 00:01:08,635 +editing menus, + +29 +00:01:08,635 --> 00:01:10,604 +find and replace, + +30 +00:01:10,604 --> 00:01:12,239 +navigation, + +31 +00:01:12,239 --> 00:01:13,807 +search, + +32 +00:01:13,807 --> 00:01:15,542 +tables, + +33 +00:01:15,542 --> 00:01:17,878 +and selection. + +34 +00:01:17,878 --> 00:01:19,446 +Today, we're going +to show examples + +35 +00:01:19,446 --> 00:01:21,281 +of how you can +incorporate these updates + +36 +00:01:21,281 --> 00:01:24,251 +in designing two kinds +of experiences: + +37 +00:01:24,251 --> 00:01:28,055 +document editing +and content browsing. + +38 +00:01:28,055 --> 00:01:29,957 +We'll start with +document editing + +39 +00:01:29,957 --> 00:01:31,725 +and look at how these +improvements can help people + +40 +00:01:31,725 --> 00:01:34,361 +be more productive +when using your app. + +41 +00:01:34,361 --> 00:01:36,330 +Let's jump into toolbars. + +42 +00:01:36,330 --> 00:01:38,999 +Toolbars are a really important +component in app design + +43 +00:01:38,999 --> 00:01:41,168 +because they organize +your app's functionality, + +44 +00:01:41,168 --> 00:01:42,469 +and this impacts how easy it is + +45 +00:01:42,469 --> 00:01:45,038 +for people to go about +their workflows. + +46 +00:01:45,038 --> 00:01:49,309 +Here's the previous toolbar +in the Pages app in iPadOS 15. + +47 +00:01:49,309 --> 00:01:52,646 +In iPadOS 16, we're introducing +a new toolbar layout + +48 +00:01:52,646 --> 00:01:54,681 +that aligns titles +to the left side + +49 +00:01:54,681 --> 00:01:58,018 +and allows apps to offer more +toolbar items in the center. + +50 +00:01:58,018 --> 00:02:00,554 +Let's take a closer look. + +51 +00:02:00,554 --> 00:02:02,756 +Here's the leading section +of the toolbar. + +52 +00:02:02,756 --> 00:02:05,292 +It should contain items +that help people navigate within + +53 +00:02:05,292 --> 00:02:06,693 +and outside of the document, + +54 +00:02:06,693 --> 00:02:08,962 +like the back and sidebar +buttons. + +55 +00:02:08,962 --> 00:02:11,231 +And it displays the new +document title and menu, + +56 +00:02:11,231 --> 00:02:13,600 +which I'll come back to later. + +57 +00:02:13,600 --> 00:02:15,302 +With more space in the toolbar, + +58 +00:02:15,302 --> 00:02:18,305 +apps can elevate commonly used +actions in the center. + +59 +00:02:18,305 --> 00:02:19,673 +In iPadOS 16, + +60 +00:02:19,673 --> 00:02:22,709 +the Pages app shows actions +for inserting content. + +61 +00:02:22,709 --> 00:02:23,377 +You should consider + +62 +00:02:23,377 --> 00:02:25,412 +what are the most common +workflows in your app + +63 +00:02:25,412 --> 00:02:27,848 +where people could benefit +from one-tap access. + +64 +00:02:27,848 --> 00:02:30,117 +But be careful not +to pack your app's toolbar + +65 +00:02:30,117 --> 00:02:31,918 +with too many items. + +66 +00:02:31,918 --> 00:02:33,720 +Let's look at a couple more +tools to consider + +67 +00:02:33,720 --> 00:02:36,223 +if your app +has a lot of functionality. + +68 +00:02:36,223 --> 00:02:39,826 +In iPadOS 16, you can +enable customizable toolbars + +69 +00:02:39,826 --> 00:02:41,028 +so people can curate the items + +70 +00:02:41,028 --> 00:02:43,230 +that are most important +for their workflow. + +71 +00:02:43,230 --> 00:02:45,032 +If your app has +a lot of toolbar items + +72 +00:02:45,032 --> 00:02:47,534 +or advanced features +that not all people need, + +73 +00:02:47,534 --> 00:02:50,103 +you should consider +enabling customization. + +74 +00:02:50,103 --> 00:02:51,838 +Note how items +in the center section + +75 +00:02:51,838 --> 00:02:55,342 +can be added, removed, +or rearranged. + +76 +00:02:55,342 --> 00:02:57,944 +Items outside of this section +are not customizable + +77 +00:02:57,944 --> 00:03:00,147 +because these may include +navigation buttons + +78 +00:03:00,147 --> 00:03:04,985 +or important features that need +to be always accessible. + +79 +00:03:04,985 --> 00:03:08,188 +Toolbar items can also be +grouped or collapsed. + +80 +00:03:08,188 --> 00:03:10,223 +Use groups +to keep related actions together + +81 +00:03:10,223 --> 00:03:13,026 +in the toolbar +or the overflow menu. + +82 +00:03:13,026 --> 00:03:14,728 +In the Pages app, +similar actions + +83 +00:03:14,728 --> 00:03:18,598 +like inserting tables, charts, +shapes, and photos + +84 +00:03:18,598 --> 00:03:20,734 +are grouped together. + +85 +00:03:20,734 --> 00:03:22,869 +When there's not enough room +in the toolbar, + +86 +00:03:22,869 --> 00:03:25,238 +these actions collapse +into a plus button. + +87 +00:03:25,238 --> 00:03:26,706 +You can also +make wide toolbar items + +88 +00:03:26,706 --> 00:03:30,877 +collapse into a compact glyph +at smaller window sizes. + +89 +00:03:30,877 --> 00:03:35,816 +Now, let's take a look at the +trailing section of the toolbar. + +90 +00:03:35,816 --> 00:03:38,385 +This section may contain +a variety of items: + +91 +00:03:38,385 --> 00:03:40,987 +any button that invokes +nearby inspectors, + +92 +00:03:40,987 --> 00:03:43,990 +important items that should stay +visible at any window size, + +93 +00:03:43,990 --> 00:03:45,792 +and an optional overflow button + +94 +00:03:45,792 --> 00:03:47,527 +for people to access +hidden items + +95 +00:03:47,527 --> 00:03:50,497 +as well as toolbar +customization. + +96 +00:03:50,497 --> 00:03:52,532 +When people resize your app, + +97 +00:03:52,532 --> 00:03:55,068 +the toolbar items in the center +will automatically hide + +98 +00:03:55,068 --> 00:03:57,737 +into an overflow menu +when there's not enough room. + +99 +00:03:57,737 --> 00:03:59,840 +And when the window +is resized even smaller, + +100 +00:03:59,840 --> 00:04:03,076 +only the leading and trailing +sections remain. + +101 +00:04:03,076 --> 00:04:04,945 +So make sure to arrange +important items + +102 +00:04:04,945 --> 00:04:07,314 +in the trailing section +if you want them to be visible + +103 +00:04:07,314 --> 00:04:09,916 +at most window sizes. + +104 +00:04:09,916 --> 00:04:12,119 +So here's a quick recap +of toolbars. + +105 +00:04:12,119 --> 00:04:13,587 +Consider if there are +common workflows + +106 +00:04:13,587 --> 00:04:16,289 +that you can elevate +in the toolbar. + +107 +00:04:16,289 --> 00:04:19,025 +Arrange important toolbar items +in the trailing section + +108 +00:04:19,025 --> 00:04:22,295 +to avoid overflow +in smaller window sizes. + +109 +00:04:22,295 --> 00:04:25,265 +Enable customization if your app +has many toolbar items + +110 +00:04:25,265 --> 00:04:27,234 +and advanced use cases. + +111 +00:04:27,234 --> 00:04:29,002 +And wherever possible, + +112 +00:04:29,002 --> 00:04:32,772 +try to organize similar items +into groups. + +113 +00:04:32,772 --> 00:04:34,641 +Now let's take a look +at the new document menu + +114 +00:04:34,641 --> 00:04:36,376 +that we mentioned earlier. + +115 +00:04:36,376 --> 00:04:39,279 +To make it easier for people +to find document-related actions + +116 +00:04:39,279 --> 00:04:41,214 +in one place across apps, + +117 +00:04:41,214 --> 00:04:44,451 +we're introducing a new title +and menu in the toolbar. + +118 +00:04:44,451 --> 00:04:47,554 +This layout is intended for +document viewers like Quick Look + +119 +00:04:47,554 --> 00:04:50,323 +or document-editing apps +like Keynote. + +120 +00:04:50,323 --> 00:04:52,659 +Note, these apps may also +have a browser view, + +121 +00:04:52,659 --> 00:04:54,060 +which can appear +as a back button + +122 +00:04:54,060 --> 00:04:55,962 +near the document title. + +123 +00:04:55,962 --> 00:04:57,330 +If your app is primarily used + +124 +00:04:57,330 --> 00:04:59,633 +for editing or viewing +documents, + +125 +00:04:59,633 --> 00:05:01,801 +you should consider +using this new menu. + +126 +00:05:01,801 --> 00:05:03,637 +Let's open Keynote's +document menu + +127 +00:05:03,637 --> 00:05:05,372 +by clicking the title. + +128 +00:05:05,372 --> 00:05:07,474 +Here, you can see +there is a combined set + +129 +00:05:07,474 --> 00:05:11,845 +of standard document actions +as well as app-specific ones. + +130 +00:05:11,845 --> 00:05:13,947 +So, what should go inside here? + +131 +00:05:13,947 --> 00:05:15,749 +The menu should contain actions + +132 +00:05:15,749 --> 00:05:19,085 +that affect the document +as a whole. + +133 +00:05:19,085 --> 00:05:21,254 +Here are some examples +of standard actions + +134 +00:05:21,254 --> 00:05:23,490 +that should go inside +the document menu: + +135 +00:05:23,490 --> 00:05:28,528 +Duplicate, Rename, Move, +Export, and Print. + +136 +00:05:28,528 --> 00:05:31,565 +You can also add custom actions +for your app. + +137 +00:05:31,565 --> 00:05:34,701 +But not everything should go +inside in the document menu. + +138 +00:05:34,701 --> 00:05:37,170 +Actions that take content +outside of your app + +139 +00:05:37,170 --> 00:05:38,972 +should be placed under Share, + +140 +00:05:38,972 --> 00:05:42,509 +and actions that directly affect +content inside a document + +141 +00:05:42,509 --> 00:05:44,444 +should support +toolbar customization + +142 +00:05:44,444 --> 00:05:48,682 +and appear in edit menus +where appropriate. + +143 +00:05:48,682 --> 00:05:51,084 +Next, I want to talk about +a couple of features + +144 +00:05:51,084 --> 00:05:54,187 +that can help users work more +efficiently in your iPad app. + +145 +00:05:54,187 --> 00:05:56,723 +Let's look at edit menus. + +146 +00:05:56,723 --> 00:05:58,858 +These menus may appear +over selected text + +147 +00:05:58,858 --> 00:06:01,861 +and contain edit options +such as Copy and Paste. + +148 +00:06:01,861 --> 00:06:05,131 +In iPadOS 16, +edit menus have a new look + +149 +00:06:05,131 --> 00:06:08,301 +and are optimized +for touch and pointer. + +150 +00:06:08,301 --> 00:06:11,037 +When using touch, +the menu appears horizontally + +151 +00:06:11,037 --> 00:06:13,006 +and people can now scroll +back and forth + +152 +00:06:13,006 --> 00:06:16,276 +between the list of options +like this. + +153 +00:06:16,276 --> 00:06:18,178 +When using pointer, +the edit menu + +154 +00:06:18,178 --> 00:06:20,146 +shows a more comprehensive +list of options + +155 +00:06:20,146 --> 00:06:22,182 +in a vertical layout like this. + +156 +00:06:22,182 --> 00:06:23,683 +Your app should support +both styles + +157 +00:06:23,683 --> 00:06:27,053 +for touch and pointer +interactions. + +158 +00:06:27,053 --> 00:06:30,156 +When you incorporate +these edit menus into your app, + +159 +00:06:30,156 --> 00:06:33,326 +here are a couple of things +to remember. + +160 +00:06:33,326 --> 00:06:35,128 +Don't remove +the standard actions + +161 +00:06:35,128 --> 00:06:37,097 +like Cut, Copy, and Paste. + +162 +00:06:37,097 --> 00:06:39,799 +These editing tools are +important to many workflows + +163 +00:06:39,799 --> 00:06:42,068 +and should be always available. + +164 +00:06:42,068 --> 00:06:43,937 +And you should organize +custom actions + +165 +00:06:43,937 --> 00:06:46,406 +close to related system actions. + +166 +00:06:46,406 --> 00:06:49,509 +In iPadOS 16, the Notes app +groups formatting options + +167 +00:06:49,509 --> 00:06:53,546 +for checklists and text +in the same section. + +168 +00:06:53,546 --> 00:06:56,383 +Edit menus are not just limited +to text fields. + +169 +00:06:56,383 --> 00:06:59,419 +They can also apply to objects +on a document canvas. + +170 +00:06:59,419 --> 00:07:04,491 +Here's an example of selecting +layers in Keynote with touch. + +171 +00:07:04,491 --> 00:07:07,627 +And that same menu +when using pointer. + +172 +00:07:07,627 --> 00:07:10,630 +Try to integrate these menus +into any editable content + +173 +00:07:10,630 --> 00:07:15,068 +so that people can be more +productive when using your app. + +174 +00:07:15,068 --> 00:07:16,603 +So that's edit menus. + +175 +00:07:16,603 --> 00:07:20,040 +Now, let's dive into +find and replace. + +176 +00:07:20,040 --> 00:07:22,742 +In iPadOS 16, we're introducing +find and replace + +177 +00:07:22,742 --> 00:07:25,211 +into the system keyboard +so people can quickly search + +178 +00:07:25,211 --> 00:07:30,216 +for specific words, phrases, +and more within a document. + +179 +00:07:30,216 --> 00:07:32,085 +This also supports +advanced use cases + +180 +00:07:32,085 --> 00:07:34,587 +like matching specific parts +of a phrase + +181 +00:07:34,587 --> 00:07:38,091 +or replacing all matches +with a different word. + +182 +00:07:38,091 --> 00:07:40,827 +And when iPad is attached +to a hardware keyboard, + +183 +00:07:40,827 --> 00:07:43,096 +the find and replace interface +sits above the app + +184 +00:07:43,096 --> 00:07:46,833 +in this compact appearance. + +185 +00:07:46,833 --> 00:07:49,069 +To make find and replace +easy to access, + +186 +00:07:49,069 --> 00:07:51,771 +consider adding an item +inside the overflow menu + +187 +00:07:51,771 --> 00:07:55,141 +as well as the system keyboard +shortcuts. + +188 +00:07:55,141 --> 00:07:56,543 +So those are some +of the new features + +189 +00:07:56,543 --> 00:07:58,878 +we're introducing +for document editing. + +190 +00:07:58,878 --> 00:08:00,513 +Now I'm going to hand it off +to Grant Paul, + +191 +00:08:00,513 --> 00:08:02,549 +who will talk about +some more improvements. + +192 +00:08:02,549 --> 00:08:05,185 +Grant: Thanks, Bryant. +Next up, I'm going to talk about + +193 +00:08:05,185 --> 00:08:07,554 +improvements +for content browsing, + +194 +00:08:07,554 --> 00:08:11,024 +something that you do +in a lot of different iPad apps. + +195 +00:08:11,024 --> 00:08:14,427 +Content browsing experiences +are everywhere on iPad, + +196 +00:08:14,427 --> 00:08:17,230 +whether you're managing +in your photo library in Photos, + +197 +00:08:17,230 --> 00:08:20,900 +browsing Apple Music, +or choosing a document in Pages. + +198 +00:08:20,900 --> 00:08:23,636 +iPadOS 16 has +some great new features + +199 +00:08:23,636 --> 00:08:26,606 +and patterns +for content browsing. + +200 +00:08:26,606 --> 00:08:28,975 +I'm going to focus +on three areas + +201 +00:08:28,975 --> 00:08:31,778 +that are important +to this part of an app. + +202 +00:08:31,778 --> 00:08:36,182 +The first one is some updates +to app navigation. + +203 +00:08:36,182 --> 00:08:39,819 +iPadOS 16 has +a new style of navigation + +204 +00:08:39,819 --> 00:08:43,256 +that we call "browser-style" +navigation. + +205 +00:08:43,256 --> 00:08:47,527 +For example, the Files app +uses back and forward buttons + +206 +00:08:47,527 --> 00:08:49,729 +to let you easily +browse between folders + +207 +00:08:49,729 --> 00:08:53,967 +that might have come from +different places in the sidebar. + +208 +00:08:53,967 --> 00:08:56,169 +With browser-style navigation, + +209 +00:08:56,169 --> 00:08:58,838 +it's up to your app +to choose the buttons + +210 +00:08:58,838 --> 00:09:01,941 +that go to the left +of the title. + +211 +00:09:01,941 --> 00:09:05,545 +You should make sure to keep to +just those navigational buttons + +212 +00:09:05,545 --> 00:09:09,883 +like back and forward, +or maybe a sidebar button. + +213 +00:09:09,883 --> 00:09:12,419 +Browser-style navigation +works well + +214 +00:09:12,419 --> 00:09:15,622 +when your app +has a complex hierarchy + +215 +00:09:15,622 --> 00:09:19,025 +where people are often jumping +between different levels; + +216 +00:09:19,025 --> 00:09:23,096 +things like a file browser +or a web browser. + +217 +00:09:23,096 --> 00:09:27,534 +If your app has a shallow +or flat hierarchy, like Photos, + +218 +00:09:27,534 --> 00:09:30,370 +you might not need +browser-style navigation + +219 +00:09:30,370 --> 00:09:34,340 +because all of the destinations +in the app are already available + +220 +00:09:34,340 --> 00:09:38,011 +with a single tap +in the sidebar. + +221 +00:09:38,011 --> 00:09:41,181 +If you do use browser-style +navigation, + +222 +00:09:41,181 --> 00:09:45,285 +it goes great with another +new feature in iPadOS 16: + +223 +00:09:45,285 --> 00:09:50,390 +putting search in the top right +of the navigation bar. + +224 +00:09:50,390 --> 00:09:53,893 +Search in the top right +is great when your search bar + +225 +00:09:53,893 --> 00:09:57,363 +is used to filter the content +that you're looking at below + +226 +00:09:57,363 --> 00:09:59,599 +on the same screen. + +227 +00:09:59,599 --> 00:10:02,235 +And even though +it's in the navigation bar, + +228 +00:10:02,235 --> 00:10:04,571 +it supports suggestions. + +229 +00:10:04,571 --> 00:10:09,642 +Suggestions show up right off +the field as you're typing. + +230 +00:10:09,642 --> 00:10:12,512 +Your app can suggest +recent searches. + +231 +00:10:12,512 --> 00:10:14,647 +It can also suggest +recommendations + +232 +00:10:14,647 --> 00:10:16,516 +for what to search for. + +233 +00:10:16,516 --> 00:10:21,354 +And it can suggest filters +to narrow down your search. + +234 +00:10:21,354 --> 00:10:24,324 +Again, search in the top right +is meant for searching + +235 +00:10:24,324 --> 00:10:27,393 +the content +that's showing below. + +236 +00:10:27,393 --> 00:10:30,096 +If you want to search +your entire app at once, + +237 +00:10:30,096 --> 00:10:32,432 +it's better to keep that +in a search tab + +238 +00:10:32,432 --> 00:10:34,400 +so that people can get to search + +239 +00:10:34,400 --> 00:10:38,671 +from wherever they are +in your app. + +240 +00:10:38,671 --> 00:10:41,007 +That's what's new +for navigation. + +241 +00:10:41,007 --> 00:10:45,078 +Next up, I want to talk about +selection and menus. + +242 +00:10:45,078 --> 00:10:48,681 +iPadOS 15 introduced +band selection, + +243 +00:10:48,681 --> 00:10:54,254 +using the pointer to quickly +select multiple notes at once. + +244 +00:10:54,254 --> 00:10:57,857 +But you still have to use the +toolbar to act on the selection + +245 +00:10:57,857 --> 00:11:01,728 +and to leave edit mode +when you're done. + +246 +00:11:01,728 --> 00:11:05,532 +iPadOS 16 makes that +much easier. + +247 +00:11:05,532 --> 00:11:07,233 +If you use band selection, + +248 +00:11:07,233 --> 00:11:12,005 +iPadOS no longer enters +editing mode automatically. + +249 +00:11:12,005 --> 00:11:14,173 +You can now use +keyboard modifiers + +250 +00:11:14,173 --> 00:11:18,011 +like Command and Shift +to select and deselect, + +251 +00:11:18,011 --> 00:11:21,180 +also without going into +editing mode. + +252 +00:11:21,180 --> 00:11:23,683 +And once you have +the notes selected, + +253 +00:11:23,683 --> 00:11:29,956 +it's just a secondary click +to act on all of them together. + +254 +00:11:29,956 --> 00:11:33,893 +With touch, you can long press +to get a context menu + +255 +00:11:33,893 --> 00:11:36,663 +for those same actions. + +256 +00:11:36,663 --> 00:11:42,135 +And these interactions +work just as well with a list. + +257 +00:11:42,135 --> 00:11:45,371 +You can hold Command +to select multiple notes, + +258 +00:11:45,371 --> 00:11:48,174 +use drag-and-drop +to move them into a folder, + +259 +00:11:48,174 --> 00:11:49,842 +and bring up a context menu + +260 +00:11:49,842 --> 00:11:53,246 +to act on multiple notes +together. + +261 +00:11:53,246 --> 00:11:56,149 +Along with these +multi-item context menus, + +262 +00:11:56,149 --> 00:12:01,287 +iPadOS 16 also supports +context menus in empty areas, + +263 +00:12:01,287 --> 00:12:04,257 +which you can use +to create something new. + +264 +00:12:04,257 --> 00:12:08,428 +For example, you can make +a new folder in Files. + +265 +00:12:08,428 --> 00:12:15,001 +Or, you can paste +a copied event in Calendar. + +266 +00:12:15,001 --> 00:12:18,805 +OK. That's a lot of things +to keep in mind for selection. + +267 +00:12:18,805 --> 00:12:21,741 +Let's go through the checklist. + +268 +00:12:21,741 --> 00:12:24,243 +Your app should support +keyboard focus + +269 +00:12:24,243 --> 00:12:28,948 +to navigate around using +the arrow keys and with tab. + +270 +00:12:28,948 --> 00:12:32,018 +Support band selection +to quickly select + +271 +00:12:32,018 --> 00:12:36,022 +multiple items in a grid +by using the pointer. + +272 +00:12:36,022 --> 00:12:40,026 +Allow multiple selection +without entering editing mode + +273 +00:12:40,026 --> 00:12:43,396 +when using the pointer +or the keyboard. + +274 +00:12:43,396 --> 00:12:45,765 +Add context menus to act + +275 +00:12:45,765 --> 00:12:49,435 +on multiple selected items +together. + +276 +00:12:49,435 --> 00:12:51,070 +And in the empty area, + +277 +00:12:51,070 --> 00:12:54,841 +use a context menu +to create new items. + +278 +00:12:54,841 --> 00:12:58,678 +The next thing I want +to talk about is submenus. + +279 +00:12:58,678 --> 00:13:02,215 +On iPhone, +submenus open vertically. + +280 +00:13:02,215 --> 00:13:05,485 +Since space is limited +and they're an extra tap away, + +281 +00:13:05,485 --> 00:13:07,720 +it's best to use submenus +on iPhone + +282 +00:13:07,720 --> 00:13:11,024 +only when you really need one. + +283 +00:13:11,024 --> 00:13:13,860 +But on iPadOS 16, + +284 +00:13:13,860 --> 00:13:17,764 +submenus open horizontally +when they have space available. + +285 +00:13:17,764 --> 00:13:22,402 +This makes them much faster, +especially with a pointer. + +286 +00:13:22,402 --> 00:13:25,571 +For example, +Calendar uses a submenu + +287 +00:13:25,571 --> 00:13:30,209 +to really quickly move an event +into a different calendar. + +288 +00:13:30,209 --> 00:13:34,213 +And Reminders uses submenus +to quickly change the due date + +289 +00:13:34,213 --> 00:13:36,783 +and the priority. + +290 +00:13:36,783 --> 00:13:38,985 +So on iPad, +you should consider + +291 +00:13:38,985 --> 00:13:42,555 +including submenus +in your app's context menus + +292 +00:13:42,555 --> 00:13:46,693 +to make quick changes +just like these. + +293 +00:13:46,693 --> 00:13:51,531 +Along with submenus, iPadOS 16 +also adds a new control + +294 +00:13:51,531 --> 00:13:54,434 +for pop-up buttons in lists. + +295 +00:13:54,434 --> 00:13:57,070 +Just like any other +pop-up button, + +296 +00:13:57,070 --> 00:14:00,039 +these show a menu +to let you choose an option. + +297 +00:14:00,039 --> 00:14:02,775 +Let's look at these in practice. + +298 +00:14:02,775 --> 00:14:05,545 +In the past, +editing a reminder's priority + +299 +00:14:05,545 --> 00:14:07,980 +meant pushing +into the detail view, + +300 +00:14:07,980 --> 00:14:10,016 +then popping back out. + +301 +00:14:10,016 --> 00:14:15,088 +It's a bunch of taps, and it +also took you out of context. + +302 +00:14:15,088 --> 00:14:19,892 +In iPadOS 16, you change +the priority right in place. + +303 +00:14:19,892 --> 00:14:23,863 +It's much faster, and you stay +right where you are. + +304 +00:14:23,863 --> 00:14:25,231 +So as you've seen here, + +305 +00:14:25,231 --> 00:14:28,101 +the main use for these +pop-up buttons in lists + +306 +00:14:28,101 --> 00:14:34,207 +is to replace navigation pushes +in popovers and in modals. + +307 +00:14:34,207 --> 00:14:36,342 +When you're using +a pop-up button, + +308 +00:14:36,342 --> 00:14:37,477 +choose places where you have + +309 +00:14:37,477 --> 00:14:40,880 +a well-defined set of options +to pick from. + +310 +00:14:40,880 --> 00:14:43,683 +Make sure to only +use a pop-up button + +311 +00:14:43,683 --> 00:14:47,887 +if your options really do +fit nicely into a menu. + +312 +00:14:47,887 --> 00:14:51,390 +If your option is better +as a switch, use a switch. + +313 +00:14:51,390 --> 00:14:53,793 +And if the options +really need more controls, + +314 +00:14:53,793 --> 00:14:56,162 +don't try and force it. + +315 +00:14:56,162 --> 00:14:58,898 +But if you only sometimes +need more controls, + +316 +00:14:58,898 --> 00:15:03,402 +a good choice can be to add +a Custom option into the menu. + +317 +00:15:03,402 --> 00:15:05,471 +When someone chooses Custom, + +318 +00:15:05,471 --> 00:15:07,940 +your app can reveal +the extra controls + +319 +00:15:07,940 --> 00:15:12,345 +without getting in the way +of the other choices. + +320 +00:15:12,345 --> 00:15:14,947 +And if your options +need an explanation, + +321 +00:15:14,947 --> 00:15:19,452 +you can put that underneath +the pop-up button in the list. + +322 +00:15:19,452 --> 00:15:22,688 +All right. That's what's new +for selection and menus. + +323 +00:15:22,688 --> 00:15:25,625 +Lastly, I want to talk about +tables. + +324 +00:15:25,625 --> 00:15:29,295 +Tables are a great new component +in iPadOS 16 + +325 +00:15:29,295 --> 00:15:32,598 +to make your lists of content +more powerful. + +326 +00:15:32,598 --> 00:15:36,435 +Now, in the past, you may have +used a control called a table. + +327 +00:15:36,435 --> 00:15:38,070 +But that wasn't quite right; + +328 +00:15:38,070 --> 00:15:42,074 +it's not much of a table +if it only has a single column. + +329 +00:15:42,074 --> 00:15:45,378 +iPadOS 16 has a real table. + +330 +00:15:45,378 --> 00:15:49,682 +Tables in SwiftUI show +multiple columns of information. + +331 +00:15:49,682 --> 00:15:53,886 +And they let you sort +just by tapping a header. + +332 +00:15:53,886 --> 00:15:57,156 +Now, since iPad screens +are relatively small, + +333 +00:15:57,156 --> 00:16:01,294 +you'll only be able to show +the most important columns. + +334 +00:16:01,294 --> 00:16:03,162 +But you can swap +those columns out, + +335 +00:16:03,162 --> 00:16:05,331 +like replacing Size with Kind, + +336 +00:16:05,331 --> 00:16:09,135 +just by choosing +a different sort option. + +337 +00:16:09,135 --> 00:16:11,204 +And as makes sense +for something that's all about + +338 +00:16:11,204 --> 00:16:14,273 +having powerful access +to your content, + +339 +00:16:14,273 --> 00:16:17,577 +sortable tables also support +all of the selection features + +340 +00:16:17,577 --> 00:16:21,347 +I talked about earlier. + +341 +00:16:21,347 --> 00:16:24,550 +So tables are +a really powerful control, + +342 +00:16:24,550 --> 00:16:27,386 +but it's important +to use them correctly. + +343 +00:16:27,386 --> 00:16:30,923 +These tables built into iOS 16 +are built to show + +344 +00:16:30,923 --> 00:16:34,594 +more information +about a list of content, + +345 +00:16:34,594 --> 00:16:37,230 +not to show +a spreadsheet of data. + +346 +00:16:37,230 --> 00:16:39,498 +You can think of them +as an extended version + +347 +00:16:39,498 --> 00:16:43,502 +of the list views that you +already have in your app. + +348 +00:16:43,502 --> 00:16:47,640 +In fact, when you resize an app +to a compact width, + +349 +00:16:47,640 --> 00:16:52,845 +tables will switch back +into being single-column lists. + +350 +00:16:52,845 --> 00:16:55,114 +And when they do, +we recommend + +351 +00:16:55,114 --> 00:16:57,817 +taking those details +from the secondary columns + +352 +00:16:57,817 --> 00:16:59,752 +and moving that information + +353 +00:16:59,752 --> 00:17:03,522 +into a secondary line of text +within each row + +354 +00:17:03,522 --> 00:17:07,727 +so all of that information +is still available. + +355 +00:17:07,727 --> 00:17:10,663 +And for sorting, +you can use a toolbar button + +356 +00:17:10,663 --> 00:17:13,699 +to quickly reveal +the sort options. + +357 +00:17:13,699 --> 00:17:17,870 +So those are the new components +and features in iPadOS 16. + +358 +00:17:17,870 --> 00:17:19,171 +Let's go back to Bryant + +359 +00:17:19,171 --> 00:17:22,375 +to recap everything +we talked about today. + +360 +00:17:22,375 --> 00:17:23,676 +Bryant: Thanks, Grant. + +361 +00:17:23,676 --> 00:17:25,111 +A lot of the features +we covered today + +362 +00:17:25,111 --> 00:17:29,215 +are aimed around helping users +be more productive with iPad. + +363 +00:17:29,215 --> 00:17:31,684 +As you think about how +these relate to your iPad app, + +364 +00:17:31,684 --> 00:17:34,186 +consider how you can +make your app more powerful + +365 +00:17:34,186 --> 00:17:37,023 +by offering more functionality, +better organization, + +366 +00:17:37,023 --> 00:17:41,060 +and make common editing and +browsing tasks more efficient. + +367 +00:17:41,060 --> 00:17:43,529 +Evaluate your app design +in resizable windows + +368 +00:17:43,529 --> 00:17:45,431 +and larger screens. + +369 +00:17:45,431 --> 00:17:47,233 +And ensure your app +works seamlessly + +370 +00:17:47,233 --> 00:17:49,468 +with touch and pointer. + +371 +00:17:49,468 --> 00:17:51,270 +Grant: And if you want +an overview + +372 +00:17:51,270 --> 00:17:54,407 +of what goes into designing +a great iPad app, + +373 +00:17:54,407 --> 00:17:57,109 +you should make sure +to check out our previous talk, + +374 +00:17:57,109 --> 00:17:59,145 +"Designed for iPad." + +375 +00:17:59,145 --> 00:18:01,847 +Bryant: Thanks for watching. + +376 +00:18:01,847 --> 00:18:07,353 +♪ + diff --git a/eng/2022 Session 10015 Design for Collaboration with Messages en.srt b/eng/2022 Session 10015 Design for Collaboration with Messages en.srt new file mode 100644 index 0000000..795fc5b --- /dev/null +++ b/eng/2022 Session 10015 Design for Collaboration with Messages en.srt @@ -0,0 +1,936 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,044 +Hello. My name is Jaewoo. + +3 +00:00:11,078 --> 00:00:13,213 +I'm a designer on the Apple Design team. + +4 +00:00:13,247 --> 00:00:16,116 +Whether preparing Keynote presentations +with colleagues + +5 +00:00:16,149 --> 00:00:18,485 +or collecting ideas with friends, + +6 +00:00:18,519 --> 00:00:22,289 +people collaborate +on all types of things everyday. + +7 +00:00:22,322 --> 00:00:25,092 +To create a first-class +collaboration experience, + +8 +00:00:25,125 --> 00:00:29,530 +we made a handful of changes +to iOS 16 and macOS Ventura. + +9 +00:00:29,563 --> 00:00:32,266 +Today, I'm going to talk about +why we put Messages + +10 +00:00:32,299 --> 00:00:35,235 +in the center of our +collaboration experience + +11 +00:00:35,269 --> 00:00:37,771 +and walk you through an end-to-end +collaboration flow, + +12 +00:00:37,804 --> 00:00:40,974 +showcasing all the new changes we've made. + +13 +00:00:41,008 --> 00:00:43,443 +I'll also share a few considerations + +14 +00:00:43,477 --> 00:00:46,246 +to help you design a great +collaboration experience in your app + +15 +00:00:46,280 --> 00:00:48,048 +using Messages. + +16 +00:00:48,081 --> 00:00:51,318 +Without further ado, let's get started. + +17 +00:00:51,351 --> 00:00:55,556 +Email is one of the great communication +tools that people use every day. + +18 +00:00:55,589 --> 00:01:00,294 +Many collaborative apps use email +as a primary way of connecting people. + +19 +00:01:00,327 --> 00:01:05,566 +However, chatting over email during +collaboration doesn't feel responsive. + +20 +00:01:05,599 --> 00:01:10,037 +That's because email was designed +as an asynchronous communication tool. + +21 +00:01:10,070 --> 00:01:14,274 +So, we brought Messages +into collaboration to supplement email. + +22 +00:01:14,308 --> 00:01:17,377 +Messages allows people +to send collaboration invitations + +23 +00:01:17,411 --> 00:01:19,780 +using existing conversations. + +24 +00:01:19,813 --> 00:01:23,016 +No need to ask for email addresses. + +25 +00:01:23,050 --> 00:01:26,887 +Effective communication is a key +to successful collaboration. + +26 +00:01:26,920 --> 00:01:31,491 +Messages already has many +great features to facilitate that. + +27 +00:01:31,525 --> 00:01:36,363 +Messages conversations can +also easily turn into FaceTime calls. + +28 +00:01:36,396 --> 00:01:40,267 +Talking over FaceTime and using +screen sharing can make collaboration + +29 +00:01:40,300 --> 00:01:42,002 +far more productive. + +30 +00:01:42,035 --> 00:01:46,073 +Let's take a look at how Messages +and collaborative apps work together. + +31 +00:01:46,106 --> 00:01:47,941 +My friends and I love baking, + +32 +00:01:47,975 --> 00:01:51,278 +and we often share recipes +with each other. + +33 +00:01:51,311 --> 00:01:54,214 +I'm working on a Pages document +with some recipes, + +34 +00:01:54,248 --> 00:01:57,451 +and I'd like to share it with my friends. + +35 +00:01:57,484 --> 00:02:01,154 +I'll start by tapping +on the share button in the toolbar. + +36 +00:02:01,188 --> 00:02:04,324 +The new system share popover allows me +to share this document + +37 +00:02:04,358 --> 00:02:06,927 +in two different ways. + +38 +00:02:06,960 --> 00:02:11,598 +I can either start a new collaboration +or send a copy. + +39 +00:02:11,632 --> 00:02:15,636 +With the collaboration option, +I can send invitations using Messages, + +40 +00:02:15,669 --> 00:02:18,939 +Mail, or other communication apps. + +41 +00:02:18,972 --> 00:02:23,010 +My baking friends and I have +a chat group called Cupcake Designers. + +42 +00:02:23,043 --> 00:02:26,914 +The system share popover knows +I talk to them quite often + +43 +00:02:26,947 --> 00:02:30,317 +and show them as a conversation suggestion +in here. + +44 +00:02:30,350 --> 00:02:33,487 +I'll select the group +to start a collaboration. + +45 +00:02:33,520 --> 00:02:36,790 +The collaboration invitation is +now in the Messages input field + +46 +00:02:36,823 --> 00:02:39,259 +and is ready to be sent. + +47 +00:02:39,293 --> 00:02:42,329 +Send button kicks off +collaboration immediately. + +48 +00:02:42,362 --> 00:02:46,800 +If you look at the toolbar, you can see +the Cupcake Designers photo appeared. + +49 +00:02:46,834 --> 00:02:50,771 +The conversation is now linked +with this document. + +50 +00:02:50,804 --> 00:02:54,107 +Looks like my friends +Lauren and Chris just joined. + +51 +00:02:55,342 --> 00:02:59,880 +Tapping on the group conversation photo +brings up the collaboration popover. + +52 +00:02:59,913 --> 00:03:04,885 +This collaboration popover was added +for easy access to communication tools + +53 +00:03:04,918 --> 00:03:08,255 +and is also +a quick collaboration overview. + +54 +00:03:08,288 --> 00:03:11,592 +Pages displays +who's currently in the document. + +55 +00:03:11,625 --> 00:03:14,261 +Tapping on the Messages button +in the popover + +56 +00:03:14,294 --> 00:03:18,365 +brings the Messages conversation +over the shared document. + +57 +00:03:18,398 --> 00:03:21,034 +I can quickly go back +to the group conversation + +58 +00:03:21,068 --> 00:03:23,504 +and talk about this document. + +59 +00:03:23,537 --> 00:03:26,306 +If I need to talk to people over FaceTime, + +60 +00:03:26,340 --> 00:03:30,043 +I can simply tap on the audio +or video button in this popover. + +61 +00:03:31,211 --> 00:03:35,115 +Now, my friends and I can +make edits in real-time. + +62 +00:03:35,148 --> 00:03:38,852 +Tight integration with Messages +unlocks more superpowers. + +63 +00:03:38,886 --> 00:03:42,589 +Now Messages can display notifications +from collaborative apps. + +64 +00:03:43,790 --> 00:03:47,461 +Looks like Chris just made edits +to the Baking Recipes document. + +65 +00:03:48,495 --> 00:03:53,300 +This Messages banner allows me to directly +open the document and see the new updates. + +66 +00:03:54,434 --> 00:03:57,337 +Collaborative apps and Messages +now have a strong connection + +67 +00:03:57,371 --> 00:04:00,874 +to make collaboration workflows +more streamlined. + +68 +00:04:00,908 --> 00:04:05,078 +Okay, we looked at an example +of the new collaboration experience. + +69 +00:04:05,112 --> 00:04:07,915 +Starting a collaboration is simple +using the share button + +70 +00:04:07,948 --> 00:04:10,517 +and the system share popover. + +71 +00:04:10,551 --> 00:04:14,655 +People can easily drop invitations +in existing conversations. + +72 +00:04:14,688 --> 00:04:17,691 +And the collaboration popover +allows people to easily go back + +73 +00:04:17,724 --> 00:04:21,562 +to Messages conversations +or start FaceTime calls. + +74 +00:04:22,930 --> 00:04:26,967 +Edit notifications from collaborative apps +let people stay up-to-date. + +75 +00:04:28,435 --> 00:04:32,005 +Now that we've seen how Messages +and collaborative apps work together, + +76 +00:04:32,039 --> 00:04:34,775 +let's switch gears +and look at some practical tips + +77 +00:04:34,808 --> 00:04:38,912 +for how to design collaboration +into your app. + +78 +00:04:38,946 --> 00:04:41,949 +Starting with the system share sheet. + +79 +00:04:41,982 --> 00:04:46,486 +The people who use your app are +very familiar with the system share sheet. + +80 +00:04:46,520 --> 00:04:51,458 +They use it for all kinds of things, +including sharing websites, + +81 +00:04:51,491 --> 00:04:56,296 +songs and albums, photos, +and other types of content. + +82 +00:04:56,330 --> 00:04:58,966 +And conversation suggestions help people + +83 +00:04:58,999 --> 00:05:02,769 +quickly find destinations +of the content they are sharing. + +84 +00:05:02,803 --> 00:05:07,407 +The system share sheet gives people +an easy way to begin a collaboration. + +85 +00:05:07,441 --> 00:05:12,346 +It will help make collaboration +in your app more consistent and familiar. + +86 +00:05:12,379 --> 00:05:15,716 +If you do decide to take advantage +of the system share sheet, + +87 +00:05:15,749 --> 00:05:19,887 +the next step is to place +a share button in your app. + +88 +00:05:19,920 --> 00:05:23,190 +Put the button in a place +where people can easily access. + +89 +00:05:23,223 --> 00:05:26,326 +Toolbars are a good candidate. + +90 +00:05:26,360 --> 00:05:29,296 +Now let's look more closely +at how you can customize + +91 +00:05:29,329 --> 00:05:32,533 +the share sheet for your needs. + +92 +00:05:32,566 --> 00:05:37,237 +The popup button in the header allows +people to choose how they share files. + +93 +00:05:37,271 --> 00:05:40,007 +If your app +doesn't support sending a copy, + +94 +00:05:40,040 --> 00:05:42,743 +then the popup button will be hidden. + +95 +00:05:42,776 --> 00:05:45,646 +"Everyone can make changes" +text below the popup button + +96 +00:05:45,679 --> 00:05:49,383 +is an entry point +to the collaboration permission settings. + +97 +00:05:49,416 --> 00:05:54,288 +You need to provide a descriptive copy +of default settings your app supports. + +98 +00:05:54,321 --> 00:05:58,926 +To avoid truncation, +be as concise as possible. + +99 +00:05:58,959 --> 00:06:01,261 +The permission settings screen +will be presented + +100 +00:06:01,295 --> 00:06:04,464 +when tapping on +"Everyone can make changes" text. + +101 +00:06:04,498 --> 00:06:08,368 +When customizing the screen, +keep the structure of choices simple + +102 +00:06:08,402 --> 00:06:13,106 +so people can easily skim the options +and make quick decisions. + +103 +00:06:13,140 --> 00:06:16,443 +The share popover +in macOS has been redesigned + +104 +00:06:16,476 --> 00:06:22,282 +to reflect the changes in iOS and iPadOS, +including the conversation suggestions. + +105 +00:06:22,316 --> 00:06:27,054 +The dropdown menu lets people choose +how they wish to share their files. + +106 +00:06:27,087 --> 00:06:30,357 +Permission settings are also +accessible within this popover. + +107 +00:06:30,390 --> 00:06:32,993 +Again, use a concise +permission summary string + +108 +00:06:33,026 --> 00:06:35,429 +and keep the settings simple. + +109 +00:06:35,462 --> 00:06:38,932 +Next, let's talk about +how to optimize the experience + +110 +00:06:38,966 --> 00:06:41,935 +of initiating collaboration in Messages. + +111 +00:06:41,969 --> 00:06:44,404 +People can choose not only how they share, + +112 +00:06:44,438 --> 00:06:48,675 +but also customize collaboration settings +from the Messages input field. + +113 +00:06:48,709 --> 00:06:53,247 +People don't need to go back to +the share sheet to change those options. + +114 +00:06:53,280 --> 00:06:57,084 +Using drag and drop to add a file +into a Messages conversation + +115 +00:06:57,117 --> 00:07:00,654 +is another convenient way +of starting a collaboration. + +116 +00:07:00,687 --> 00:07:04,658 +So help people easily set the permissions +from the Messages input field + +117 +00:07:04,691 --> 00:07:07,895 +and let them start a collaboration +with the send button. + +118 +00:07:07,928 --> 00:07:11,164 +And it's really important +to put the collaboration button + +119 +00:07:11,198 --> 00:07:13,467 +where people can easily find it. + +120 +00:07:13,500 --> 00:07:15,569 +Once people have begun collaborating, + +121 +00:07:15,602 --> 00:07:19,273 +the collaboration button +will appear in your app. + +122 +00:07:19,306 --> 00:07:22,643 +This collaboration button is one +of the most important UI elements + +123 +00:07:22,676 --> 00:07:25,179 +of the entire collaboration experience. + +124 +00:07:25,212 --> 00:07:29,850 +Find a place where it can stand out +and draw people's attention. + +125 +00:07:29,883 --> 00:07:33,987 +It's best to place the collaboration +button right next to the share button. + +126 +00:07:34,021 --> 00:07:36,857 +The appearance of the collaboration +button will be slightly different + +127 +00:07:36,890 --> 00:07:40,460 +depending on +how people started collaboration. + +128 +00:07:40,494 --> 00:07:43,931 +If people sent an invitation +to a one-on-one Messages conversation, + +129 +00:07:43,964 --> 00:07:46,667 +the recipient's picture +will appear as the button. + +130 +00:07:46,700 --> 00:07:49,436 +If collaboration started +with a group conversation, + +131 +00:07:49,469 --> 00:07:51,338 +and the conversation has a photo, + +132 +00:07:51,371 --> 00:07:53,874 +the system will show +the photo in your app. + +133 +00:07:53,907 --> 00:07:56,577 +If a group conversation photo +is not available, + +134 +00:07:56,610 --> 00:08:00,314 +a system provided symbol +will be used instead. + +135 +00:08:00,347 --> 00:08:03,016 +Once you decide where to put +the collaboration button, + +136 +00:08:03,050 --> 00:08:07,087 +it's time to customize +the collaboration popover for your app. + +137 +00:08:07,120 --> 00:08:11,291 +The collaboration popover +consists of three separate sections. + +138 +00:08:11,325 --> 00:08:14,595 +The top section shows +who you are collaborating with. + +139 +00:08:14,628 --> 00:08:18,799 +The system will find corresponding +profile pictures from the Contacts app + +140 +00:08:18,832 --> 00:08:21,201 +and display them here. + +141 +00:08:21,235 --> 00:08:23,904 +There are also buttons for communication. + +142 +00:08:23,937 --> 00:08:27,474 +If there is no Messages conversation +linked with the document, + +143 +00:08:27,508 --> 00:08:30,344 +the message button +will let you connect one. + +144 +00:08:30,377 --> 00:08:34,181 +The middle section is for you, +and it's fully customizable. + +145 +00:08:34,214 --> 00:08:37,417 +Think about what information +or functions will be essential + +146 +00:08:37,451 --> 00:08:39,620 +for people using your app. + +147 +00:08:39,653 --> 00:08:43,590 +Pages shows the active participant list +with a display setting. + +148 +00:08:43,624 --> 00:08:47,828 +In contrast, +the Notes app shows the latest activities + +149 +00:08:47,861 --> 00:08:49,730 +along with two action buttons. + +150 +00:08:49,763 --> 00:08:52,199 +Try not to overload +this collaboration popover + +151 +00:08:52,232 --> 00:08:54,902 +with too much information +or too many buttons. + +152 +00:08:54,935 --> 00:08:57,004 +Keep it glanceable. + +153 +00:08:57,037 --> 00:09:00,574 +Also make sure everything +you are adding here is visually consistent + +154 +00:09:00,607 --> 00:09:03,143 +with other elements in the popover. + +155 +00:09:03,177 --> 00:09:06,380 +If your app doesn't have +anything to show in this popover, + +156 +00:09:06,413 --> 00:09:08,715 +it's okay to leave +the middle section empty, + +157 +00:09:08,749 --> 00:09:11,852 +like Reminders and Files. + +158 +00:09:11,885 --> 00:09:16,223 +At the bottom of the popover, +there is the "Manage Shared File" button. + +159 +00:09:16,256 --> 00:09:19,493 +You can customize +the button label if needed. + +160 +00:09:19,526 --> 00:09:23,230 +This button opens +the collaboration management screen. + +161 +00:09:23,263 --> 00:09:25,332 +People can add and remove participants + +162 +00:09:25,365 --> 00:09:28,268 +and also change other settings +from this screen. + +163 +00:09:28,302 --> 00:09:30,737 +If your app is using CloudKit sharing, + +164 +00:09:30,771 --> 00:09:34,074 +the management screen will be +provided by the system. + +165 +00:09:34,107 --> 00:09:36,076 +If you're not using CloudKit sharing, + +166 +00:09:36,109 --> 00:09:39,213 +you can have your own +management screen for your app. + +167 +00:09:40,781 --> 00:09:45,018 +The collaboration popover on macOS +has the same structure as iOS. + +168 +00:09:45,052 --> 00:09:48,255 +Use macOS UI components +and design patterns + +169 +00:09:48,288 --> 00:09:51,158 +when customizing the middle section. + +170 +00:09:51,191 --> 00:09:54,027 +Lastly, you will want to give +special consideration + +171 +00:09:54,061 --> 00:09:57,397 +to the appearance of the banners +in Messages. + +172 +00:09:57,431 --> 00:10:01,134 +The banners in Messages are +how your app can notify collaborators + +173 +00:10:01,168 --> 00:10:03,437 +of important changes. + +174 +00:10:03,470 --> 00:10:07,508 +These banners allow people +to stay informed about new updates + +175 +00:10:07,541 --> 00:10:09,843 +without having to open the document. + +176 +00:10:09,877 --> 00:10:14,081 +Messages offers banner templates +for you to choose from. + +177 +00:10:14,114 --> 00:10:18,218 +Edits made, comments added, +you were mentioned, + +178 +00:10:18,252 --> 00:10:21,121 +and also files modified. + +179 +00:10:21,154 --> 00:10:24,157 +Tell Messages what type of updates +you want to display. + +180 +00:10:24,191 --> 00:10:28,095 +Those updates will end up +in the right conversations. + +181 +00:10:28,128 --> 00:10:30,697 +You might ask, +what if multiple people made + +182 +00:10:30,731 --> 00:10:33,767 +multiple edits +in many different documents? + +183 +00:10:33,800 --> 00:10:36,870 +How will Messages handle multiple banners? + +184 +00:10:36,904 --> 00:10:39,973 +To not clutter conversation +with too many banners, + +185 +00:10:40,007 --> 00:10:43,544 +Messages will consolidate +those banners into one. + +186 +00:10:43,577 --> 00:10:46,980 +When people tap +on the view button in this banner, + +187 +00:10:47,014 --> 00:10:50,083 +Messages will show all the updates +in one place + +188 +00:10:50,117 --> 00:10:53,153 +and let people choose +which one they want to open. + +189 +00:10:53,187 --> 00:10:55,822 +Okay, we've taken a look +at all the details + +190 +00:10:55,856 --> 00:10:59,293 +to design the best collaboration +experience using Messages. + +191 +00:10:59,326 --> 00:11:03,463 +Adopt the new system share sheet +for better collaboration initiation, + +192 +00:11:03,497 --> 00:11:05,732 +and customize the collaboration popover + +193 +00:11:05,766 --> 00:11:10,204 +to provide communication tools +and a good collaboration overview. + +194 +00:11:10,237 --> 00:11:13,340 +Let people enjoy a productive +and successful collaboration + +195 +00:11:13,373 --> 00:11:15,609 +with the Messages integration. + +196 +00:11:15,642 --> 00:11:19,780 +I hope all these updates help you +design a better experience for your app. + +197 +00:11:19,813 --> 00:11:21,515 +Thank you for watching. ♪ ♪ + diff --git a/eng/2022 Session 10016 Get more mileage out of your app with CarPlay en.srt b/eng/2022 Session 10016 Get more mileage out of your app with CarPlay en.srt new file mode 100644 index 0000000..fd800a6 --- /dev/null +++ b/eng/2022 Session 10016 Get more mileage out of your app with CarPlay en.srt @@ -0,0 +1,1527 @@ +1 +00:00:00,434 --> 00:00:06,440 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:13,747 +Well, greetings and welcome to "Get more +mileage out of your app with CarPlay." + +3 +00:00:13,780 --> 00:00:18,018 +My name is André, and I'll be +driving you through the session today. + +4 +00:00:19,553 --> 00:00:24,525 +As you know, CarPlay is the smarter, +safer way to use your iPhone in the car. + +5 +00:00:24,558 --> 00:00:30,163 +Today's session is all about your +apps and how to enable them in CarPlay. + +6 +00:00:30,197 --> 00:00:34,334 +We'll start with a quick refresher +on the types of apps that are supported, + +7 +00:00:34,368 --> 00:00:38,672 +then talk about some new app types +we are enabling this year, + +8 +00:00:38,705 --> 00:00:42,242 +and finally go over +a new tool we have made available + +9 +00:00:42,276 --> 00:00:45,245 +to support the development of your apps. + +10 +00:00:45,279 --> 00:00:50,184 +We'll finish today with an important +new feature specific to navigation apps. + +11 +00:00:50,217 --> 00:00:53,220 +Without further ado, +let's get right into it! + +12 +00:00:55,155 --> 00:01:00,327 +One of the most fundamental things about +CarPlay is that it is built for drivers. + +13 +00:01:00,360 --> 00:01:04,932 +People who are actively driving +are the primary users you should consider + +14 +00:01:04,965 --> 00:01:06,867 +when building your app. + +15 +00:01:06,900 --> 00:01:12,272 +As a result, you should only enable use +cases that are relevant while driving, + +16 +00:01:12,306 --> 00:01:17,377 +and you should omit any use cases +that people shouldn't do while driving. + +17 +00:01:17,411 --> 00:01:20,881 +Things like one time configuration, +signing in to your app, + +18 +00:01:20,914 --> 00:01:24,952 +or reading terms and conditions, +are things that are better to do before + +19 +00:01:24,985 --> 00:01:29,289 +or after driving, so they shouldn't +appear in your app's CarPlay UI. + +20 +00:01:30,490 --> 00:01:34,828 +Note that an entitlement is needed +for your app to appear in CarPlay. + +21 +00:01:34,862 --> 00:01:38,699 +You can request the entitlement +on the Apple CarPlay developer website + +22 +00:01:38,732 --> 00:01:42,402 +based on the type of app +you'd like to make available. + +23 +00:01:42,436 --> 00:01:47,541 +Here are the types of apps +we support in CarPlay today. + +24 +00:01:47,574 --> 00:01:52,112 +These cover a wide number of tasks +drivers may want to do while driving, + +25 +00:01:52,145 --> 00:01:55,482 +but we've heard from many of +you that there are even more + +26 +00:01:55,516 --> 00:01:59,219 +driving-relevant apps +you'd like to enable. + +27 +00:01:59,253 --> 00:02:03,156 +I'm happy to announce we are adding +two new types to our list this year, + +28 +00:02:03,190 --> 00:02:06,326 +Fueling and Driving Task apps. + +29 +00:02:06,360 --> 00:02:09,630 +We'll go over +these in more detail shortly, + +30 +00:02:09,663 --> 00:02:14,301 +but first, let's take a quick pit stop +and talk a bit about Templates. + +31 +00:02:14,334 --> 00:02:18,005 +Templates are how apps in +CarPlay present their UI. + +32 +00:02:18,038 --> 00:02:22,242 +Your app supplies the data, +and the system draws the UI + +33 +00:02:22,276 --> 00:02:25,279 +onto the vehicle's display on your behalf. + +34 +00:02:25,312 --> 00:02:28,949 +This system of templates is +simple for your app to adopt + +35 +00:02:28,982 --> 00:02:32,519 +and has several benefits, +including helping steer your app + +36 +00:02:32,553 --> 00:02:35,355 +towards making it +appropriate for the road. + +37 +00:02:35,389 --> 00:02:38,392 +You don't have to worry +about things like font sizes, + +38 +00:02:38,425 --> 00:02:42,462 +the templates help you ensure +your UI is of low complexity, + +39 +00:02:42,496 --> 00:02:47,601 +and finally, the UI of your app is +consistent with other apps in CarPlay, + +40 +00:02:47,634 --> 00:02:52,105 +making it easier for your users +to quickly take actions in it as needed. + +41 +00:02:52,139 --> 00:02:56,643 +Just as important, the templates take +care of making sure your app's UI + +42 +00:02:56,677 --> 00:03:00,714 +works great in any car that supports +CarPlay, regardless of the size of + +43 +00:03:00,747 --> 00:03:04,651 +the screen or the type of +input device used in the car. + +44 +00:03:04,685 --> 00:03:08,488 +You may still want to test +your app in different configurations, + +45 +00:03:08,522 --> 00:03:11,592 +and we'll talk more about +testing later on in the session, + +46 +00:03:11,625 --> 00:03:16,663 +but suffice it to say, the templates +take care of most of the work for you. + +47 +00:03:16,697 --> 00:03:19,499 +You have several templates to choose +from when building your app. + +48 +00:03:19,533 --> 00:03:23,036 +From the grid template showing +an array of buttons, + +49 +00:03:23,070 --> 00:03:25,472 +to the list template showing a table, + +50 +00:03:25,506 --> 00:03:31,178 +these templates should be familiar to +you, both as a developer, and an iOS user. + +51 +00:03:31,211 --> 00:03:34,882 +Most importantly, +they will be familiar to your users + +52 +00:03:34,915 --> 00:03:38,986 +driving with CarPlay as +they appear all throughout CarPlay. + +53 +00:03:39,019 --> 00:03:43,223 +We spoke just before about the different +types of apps we support in CarPlay. + +54 +00:03:43,257 --> 00:03:49,196 +Some of these templates are more relevant +to some of those types than others. + +55 +00:03:49,229 --> 00:03:53,667 +This chart helps you understand +which templates your app can use, + +56 +00:03:53,700 --> 00:03:55,769 +depending on which type it is. + +57 +00:03:55,802 --> 00:04:01,708 +I realize trying to read this is a bit +like taking a DMV eye exam, but fear not, + +58 +00:04:01,742 --> 00:04:07,014 +you'll find this exact chart in +our developer documentation online. + +59 +00:04:07,047 --> 00:04:11,118 +The thing to take away here now +is that the templates your app can use + +60 +00:04:11,151 --> 00:04:13,253 +depend on its type. + +61 +00:04:13,287 --> 00:04:16,490 +Only templates that are +relevant and appropriate + +62 +00:04:16,523 --> 00:04:19,793 +for a particular app type are permitted. + +63 +00:04:19,826 --> 00:04:23,330 +Now that we've talked about templates, +let's take a closer look at the + +64 +00:04:23,363 --> 00:04:26,667 +new app types +we are launching with iOS 16. + +65 +00:04:26,700 --> 00:04:30,604 +First let's talk about +the new Fueling app type. + +66 +00:04:31,972 --> 00:04:37,211 +As you may remember, in iOS 14 we +launched support for EV Charging apps. + +67 +00:04:37,244 --> 00:04:40,948 +These apps aren't just for +finding locations of EV chargers, + +68 +00:04:40,981 --> 00:04:45,953 +they do more than that - for example, +they may help the user connect to + +69 +00:04:45,986 --> 00:04:48,589 +the right +charging station and start it up. + +70 +00:04:48,622 --> 00:04:52,025 +We've heard from many developers +that this type of functionality + +71 +00:04:52,059 --> 00:04:56,196 +would be great for more +than just electric vehicles. + +72 +00:04:56,230 --> 00:05:00,634 +Whether it is traditional gasoline-enabled +cars or alternative fuel vehicles, + +73 +00:05:00,667 --> 00:05:06,273 +this new category enables you to +support CarPlay in your fueling app. + +74 +00:05:06,306 --> 00:05:09,443 +Note that many users use +navigation apps to find and drive + +75 +00:05:09,476 --> 00:05:12,546 +to particular locations, +so your fueling app should enable + +76 +00:05:12,579 --> 00:05:17,351 +more functionality in its CarPlay UI +than simply finding a location. + +77 +00:05:17,384 --> 00:05:19,887 +A great example +of what your app could enable is, + +78 +00:05:19,920 --> 00:05:22,656 +for instance, starting up a gas pump. + +79 +00:05:22,689 --> 00:05:27,528 +Now, let's talk about Driving Task +applications. + +80 +00:05:27,561 --> 00:05:30,964 +Driving Task is a new type +of CarPlay app + +81 +00:05:30,998 --> 00:05:36,003 +designed to enable +a wider variety of very simple apps. + +82 +00:05:36,036 --> 00:05:40,073 +Keep in mind the primary purpose +of these apps must be to enable tasks + +83 +00:05:40,107 --> 00:05:44,511 +people need to do while driving-- +it's for tasks that actually help with + +84 +00:05:44,545 --> 00:05:47,814 +the drive, not just a task +to be done while you drive. + +85 +00:05:49,349 --> 00:05:53,187 +Some examples of apps that +would fall under this type include apps + +86 +00:05:53,220 --> 00:05:58,825 +to help control car accessories, +apps that provide driving or road status + +87 +00:05:58,859 --> 00:06:05,165 +and information, and apps to help with +tasks at the start and end of a drive. + +88 +00:06:05,199 --> 00:06:08,202 +Let's take a look at some more +concrete examples of these. + +89 +00:06:09,670 --> 00:06:13,707 +First, here we have a road +status app that can inform users + +90 +00:06:13,740 --> 00:06:16,677 +about important road information. + +91 +00:06:16,710 --> 00:06:21,181 +This app was built using the +CPPointOfInterestTemplate. + +92 +00:06:21,215 --> 00:06:24,184 +Keep in mind, +a user using this app is driving, + +93 +00:06:24,218 --> 00:06:28,956 +so an app like this should provide +a very short list of important items + +94 +00:06:28,989 --> 00:06:31,225 +near where the user is located. + +95 +00:06:31,258 --> 00:06:34,828 +This is not intended for apps helping users +do full route planning + +96 +00:06:34,862 --> 00:06:36,697 +ahead of a drive. + +97 +00:06:38,465 --> 00:06:43,136 +In this app, here's what a user +sees when they select a location. + +98 +00:06:43,170 --> 00:06:47,341 +Note that the amount of space for text +is intentionally limited to keep this + +99 +00:06:47,374 --> 00:06:51,879 +information glanceable, so you should be +concise in the language you choose here. + +100 +00:06:54,181 --> 00:06:58,819 +Next, let's look at an application +designed to control a car accessory-- + +101 +00:06:58,852 --> 00:07:01,855 +in this case a trailer controller. + +102 +00:07:01,889 --> 00:07:06,693 +This app uses the CPInformationTemplate +to provide basic information on a + +103 +00:07:06,727 --> 00:07:12,900 +connected accessory, as well as a couple +of buttons for the user to take actions. + +104 +00:07:12,933 --> 00:07:17,871 +Note that in this example, +this is the app's entire UI in CarPlay. + +105 +00:07:17,905 --> 00:07:20,073 +There are no other screens! + +106 +00:07:20,107 --> 00:07:23,110 +Of course, the app has plenty +of other functionality, + +107 +00:07:23,143 --> 00:07:26,980 +for instance, the ability to +manage paired accessories, + +108 +00:07:27,014 --> 00:07:30,884 +but any functionality that +isn't needed for driving is simply + +109 +00:07:30,918 --> 00:07:34,755 +not included +in the CarPlay UI for that app. + +110 +00:07:34,788 --> 00:07:38,225 +Users are best served doing +non-driving tasks using the + +111 +00:07:38,258 --> 00:07:42,362 +app's primary UI on iPhone +when they're out of the vehicle. + +112 +00:07:44,031 --> 00:07:49,870 +Finally, let's look at a couple +examples using the CPGridTemplate. + +113 +00:07:49,903 --> 00:07:53,974 +This is an extremely simple +app that has two buttons - that's it! + +114 +00:07:54,007 --> 00:07:56,543 +And lets the users keep track +of their miles + +115 +00:07:56,577 --> 00:07:59,546 +as either personal or business miles. + +116 +00:07:59,580 --> 00:08:03,283 +This app fits the new +Driving Task app type perfectly, + +117 +00:08:03,317 --> 00:08:07,921 +as it enables a very simple task +that users need to do while driving, + +118 +00:08:07,955 --> 00:08:11,658 +without enabling +any other non-critical tasks. + +119 +00:08:11,692 --> 00:08:14,695 +Simple, and to the point. + +120 +00:08:14,728 --> 00:08:18,765 +Just to show you that this style +of UI can serve multiple types of apps, + +121 +00:08:18,799 --> 00:08:23,170 +here's another example +with a near identical UI. + +122 +00:08:23,203 --> 00:08:28,108 +This is an express lane toll transponder +app that uses the CPGridTemplate + +123 +00:08:28,141 --> 00:08:32,479 +to let users choose +how many occupants are in the car. + +124 +00:08:32,513 --> 00:08:36,383 +It meets the exact same goals +as the previous example and is + +125 +00:08:36,416 --> 00:08:39,019 +another perfect Driving Task app. + +126 +00:08:40,287 --> 00:08:42,990 +To recap, when designing +your Driving Task app, + +127 +00:08:43,023 --> 00:08:46,627 +definitely consider making a +single screen app that provides + +128 +00:08:46,660 --> 00:08:50,130 +the minimum functionality +your users need while driving + +129 +00:08:50,163 --> 00:08:54,801 +and only enable tasks that can +be accomplished in a few seconds. + +130 +00:08:54,835 --> 00:08:59,072 +You should avoid enabling complex +or infrequent use cases. + +131 +00:08:59,106 --> 00:09:04,044 +A great example of this is first time +set up or detailed configuration. + +132 +00:09:04,077 --> 00:09:07,080 +And finally, you should not +add functionality to your app + +133 +00:09:07,114 --> 00:09:11,151 +that isn't needed while driving, +even if it's car-related. + +134 +00:09:11,185 --> 00:09:13,921 +The kitchen sink, this is not. + +135 +00:09:13,954 --> 00:09:16,657 +And that's it on Driving Task apps. + +136 +00:09:16,690 --> 00:09:21,795 +Now let's shift gears a bit and +talk about how to test your CarPlay app. + +137 +00:09:21,828 --> 00:09:25,098 +We'll review the different ways +in which you can go about this, + +138 +00:09:25,132 --> 00:09:29,102 +and I'll introduce a new tool +called CarPlay Simulator. + +139 +00:09:29,136 --> 00:09:32,840 +As a developer, you have a few +different tools at your disposal + +140 +00:09:32,873 --> 00:09:36,276 +to test your CarPlay-enabled app. + +141 +00:09:36,310 --> 00:09:42,282 +First, you can use the Xcode simulator, +which has a built-in CarPlay window. + +142 +00:09:42,316 --> 00:09:46,320 +If you're already using the Xcode +simulator to test the rest of your app, + +143 +00:09:46,353 --> 00:09:50,991 +this is a great way to quickly +test your CarPlay UI, too. + +144 +00:09:51,024 --> 00:09:54,161 +Second, +for testing your app on a real iPhone, + +145 +00:09:54,194 --> 00:09:58,332 +you can, of course, connect your phone +to a real CarPlay-enabled vehicle, + +146 +00:09:58,365 --> 00:10:00,601 +or an aftermarket head unit. + +147 +00:10:00,634 --> 00:10:04,204 +Until recently, +this was the only way to test your app's + +148 +00:10:04,238 --> 00:10:08,675 +CarPlay UI running on a real iPhone, +but I'm happy to report + +149 +00:10:08,709 --> 00:10:13,013 +we have a third option for you now +that may just become your favorite-- + +150 +00:10:13,046 --> 00:10:14,948 +the CarPlay Simulator! + +151 +00:10:14,982 --> 00:10:18,752 +Let's look at this in more detail. + +152 +00:10:18,785 --> 00:10:20,821 +So what is it? + +153 +00:10:20,854 --> 00:10:24,491 +CarPlay Simulator is a +stand-alone Mac application that + +154 +00:10:24,525 --> 00:10:27,027 +replicates a CarPlay environment. + +155 +00:10:27,060 --> 00:10:30,597 +You simply download the +"Additional Tools for Xcode" package + +156 +00:10:30,631 --> 00:10:34,168 +on the Apple developer website, +run the app, + +157 +00:10:34,201 --> 00:10:38,472 +and connect your iPhone +to your Mac using a cable. + +158 +00:10:38,505 --> 00:10:41,375 +CarPlay will start on the phone +and run just the same + +159 +00:10:41,408 --> 00:10:44,811 +as if you had it connected to a real car. + +160 +00:10:44,845 --> 00:10:48,048 +So what's the big deal, +and why would you want to use this? + +161 +00:10:48,081 --> 00:10:50,050 +Well, there are several benefits. + +162 +00:10:51,852 --> 00:10:54,888 +First of all, when you are using +CarPlay Simulator, + +163 +00:10:54,922 --> 00:10:57,090 +CarPlay is actually +running on your iPhone + +164 +00:10:57,124 --> 00:11:00,460 +the same way it would be in a real car. + +165 +00:11:00,494 --> 00:11:03,363 +This means you can test your +app running on your phone without + +166 +00:11:03,397 --> 00:11:06,400 +constantly having to run back +and forth to your parking spot + +167 +00:11:06,433 --> 00:11:10,404 +or having to buy an aftermarket head unit. + +168 +00:11:10,437 --> 00:11:14,641 +Another huge benefit is that because +your phone is connected to your Mac + +169 +00:11:14,675 --> 00:11:18,178 +when you're using CarPlay Simulator, +you can use all of the other + +170 +00:11:18,212 --> 00:11:21,615 +fantastic developer tools +on your Mac simultaneously, + +171 +00:11:21,648 --> 00:11:26,854 +whether it is debugging in Xcode +or tuning performance in Instruments. + +172 +00:11:26,887 --> 00:11:30,224 +Similarly on the iOS side of things, +because your app is running + +173 +00:11:30,257 --> 00:11:34,928 +on an actual iPhone, you have access to +complete iPhone functionality. + +174 +00:11:34,962 --> 00:11:39,299 +Some scenarios cannot be tested +without either a real CarPlay system, + +175 +00:11:39,333 --> 00:11:41,568 +or now, CarPlay Simulator. + +176 +00:11:43,470 --> 00:11:46,740 +A great example is testing that +your navigation app's voice instructions + +177 +00:11:46,773 --> 00:11:51,979 +correctly mix with a car's +native audio source, like FM radio. + +178 +00:11:52,012 --> 00:11:55,349 +You can now test this +at the convenience of your desk. + +179 +00:11:56,950 --> 00:11:59,686 +Last but not least, +you can use CarPlay Simulator + +180 +00:11:59,720 --> 00:12:02,756 +to test multiple different +configurations of cars, too, + +181 +00:12:02,789 --> 00:12:06,593 +for example, +cars with different display sizes. + +182 +00:12:06,627 --> 00:12:10,197 +Let's see what CarPlay Simulator +looks like in action. + +183 +00:12:10,230 --> 00:12:11,832 +So here it is. + +184 +00:12:11,865 --> 00:12:14,835 +As you can see, the CarPlay +display from the connected iPhone + +185 +00:12:14,868 --> 00:12:18,639 +appears right in the app, +alongside several controls. + +186 +00:12:18,672 --> 00:12:22,643 +Let's go through what some of these do. + +187 +00:12:22,676 --> 00:12:25,312 +At the bottom of the screen +are buttons that simulate + +188 +00:12:25,345 --> 00:12:28,916 +various different hard keys +and knob controls in a car. + +189 +00:12:31,018 --> 00:12:34,321 +You can also click directly in +the CarPlay view to simulate a touch + +190 +00:12:34,354 --> 00:12:36,123 +in touchscreen vehicles. + +191 +00:12:38,192 --> 00:12:41,295 +At the top of the window +we have some quick controls. + +192 +00:12:41,328 --> 00:12:45,032 +The limit UI button allows you +to simulate when a car in motion + +193 +00:12:45,065 --> 00:12:48,535 +requests for CarPlay +to limit certain content on screen, + +194 +00:12:48,569 --> 00:12:53,140 +which, for example, could shorten +the contents of lists in an audio app. + +195 +00:12:55,576 --> 00:12:59,079 +The next two buttons are to +simulate when a car requests dark + +196 +00:12:59,112 --> 00:13:02,916 +or light appearance for UI and map +content, respectively. + +197 +00:13:04,952 --> 00:13:08,322 +The last button lets you quickly +simulate disconnecting + +198 +00:13:08,355 --> 00:13:11,491 +and reconnecting your phone to CarPlay. + +199 +00:13:11,525 --> 00:13:13,927 +Because your phone will still +remain connected to your Mac + +200 +00:13:13,961 --> 00:13:17,431 +when you use this button, +you can use it to debug CarPlay + +201 +00:13:17,464 --> 00:13:20,567 +reconnection scenarios +in your app using Xcode. + +202 +00:13:22,870 --> 00:13:25,572 +But what about +that first button that I skipped? + +203 +00:13:25,606 --> 00:13:29,409 +As you might have guessed, +this will pop up a secondary window + +204 +00:13:29,443 --> 00:13:32,346 +with more advanced functionality. + +205 +00:13:32,379 --> 00:13:34,815 +Let's take a look +at those advanced features now. + +206 +00:13:36,817 --> 00:13:40,354 +In the General tab, +you're able to choose the display size + +207 +00:13:40,387 --> 00:13:43,657 +for the main CarPlay display. + +208 +00:13:43,690 --> 00:13:47,027 +If your application UI is composed +only of templates, + +209 +00:13:47,060 --> 00:13:51,064 +you can try different sizes +to see what your UI will look like + +210 +00:13:51,098 --> 00:13:54,835 +in different cars, but as we talked +about before, + +211 +00:13:54,868 --> 00:13:58,305 +the system will make sure +everything works great regardless. + +212 +00:13:58,338 --> 00:14:00,674 +If your app is a navigation app, however, + +213 +00:14:00,707 --> 00:14:04,178 +it's critically important that you +try different sizes and aspect ratios + +214 +00:14:04,211 --> 00:14:07,714 +to ensure your map drawing code +works correctly. + +215 +00:14:07,748 --> 00:14:10,918 +Here are some recommended display +sizes to test with your app. + +216 +00:14:10,951 --> 00:14:14,855 +Let's take +a look at the Cluster Display tab. + +217 +00:14:14,888 --> 00:14:17,090 +As you will see here, +you are able to simulate cars + +218 +00:14:17,124 --> 00:14:20,160 +with a second display +in the instrument cluster! + +219 +00:14:20,194 --> 00:14:24,965 +Simply check the box to enable it, +restart the session, + +220 +00:14:24,998 --> 00:14:27,434 +and a second window will appear +for the instrument cluster + +221 +00:14:27,467 --> 00:14:30,470 +alongside the primary display. + +222 +00:14:30,504 --> 00:14:34,775 +Again, +this is most relevant to navigation apps. + +223 +00:14:34,808 --> 00:14:39,112 +The instrument cluster display is used +to display either a map or turn card + +224 +00:14:39,146 --> 00:14:43,750 +for the driver right in their field of +view in the car's instrument cluster. + +225 +00:14:43,784 --> 00:14:46,119 +We'll chat more about the +instrument cluster in a second, + +226 +00:14:46,153 --> 00:14:50,057 +but that's a quick tour of +the new CarPlay Simulator app. + +227 +00:14:50,090 --> 00:14:53,460 +We hope you find it as useful as we do! + +228 +00:14:53,493 --> 00:14:58,498 +We just saw how CarPlay can now draw +a live map in the instrument cluster. + +229 +00:14:58,532 --> 00:15:00,634 +But what about your navigation app? + +230 +00:15:00,667 --> 00:15:05,038 +How can you add - and test - +instrument cluster support in your app? + +231 +00:15:05,072 --> 00:15:07,174 +Let's take a look. + +232 +00:15:07,207 --> 00:15:10,844 +You may remember that back in iOS 13, +we added APIs + +233 +00:15:10,878 --> 00:15:14,982 +to enable navigation apps to +appear in the CarPlay Dashboard. + +234 +00:15:15,015 --> 00:15:17,885 +To do this, you edited your app's +Info.plist + +235 +00:15:17,918 --> 00:15:22,489 +to declare support for Dashboard +and implemented the required delegates. + +236 +00:15:22,523 --> 00:15:27,261 +The delegates notify your app when it is +appearing and disappearing in Dashboard, + +237 +00:15:27,294 --> 00:15:33,200 +and also pass over a UIWindow to your +app in which to draw your map content. + +238 +00:15:33,233 --> 00:15:34,968 +It's easy. + +239 +00:15:35,002 --> 00:15:37,037 +If you've already done this, +then the great news is + +240 +00:15:37,070 --> 00:15:39,706 +adding instrument cluster support +will be a piece of cake, + +241 +00:15:39,740 --> 00:15:42,709 +as it follows the exact same pattern. + +242 +00:15:42,743 --> 00:15:49,049 +Let's look at how I did this in my +own navigation test app, Space Roads. + +243 +00:15:49,082 --> 00:15:51,552 +I edited my Info.plist to declare support + +244 +00:15:51,585 --> 00:15:55,756 +for the instrument cluster +navigation scene + +245 +00:15:55,789 --> 00:15:59,493 +and added the required +Scene Session Role. + +246 +00:15:59,526 --> 00:16:01,528 +Then, I implemented my + +247 +00:16:01,562 --> 00:16:05,365 +CPTemplateApplicationInstrumentCluster +Scene delegate + +248 +00:16:05,399 --> 00:16:10,170 +as well as my +CPInstrumentClusterControllerDelegate. + +249 +00:16:10,204 --> 00:16:13,307 +These will both give you a window +in which to draw your content + +250 +00:16:13,340 --> 00:16:17,811 +as well as notify you when the +instrument cluster starts and finishes, + +251 +00:16:17,845 --> 00:16:20,681 +making your instrument cluster view +visible. + +252 +00:16:20,714 --> 00:16:23,283 +That's all it takes to have +your map appear live + +253 +00:16:23,317 --> 00:16:26,253 +in the car's instrument cluster! + +254 +00:16:26,286 --> 00:16:29,690 +While this is all very similar to +implementing Dashboard support, + +255 +00:16:29,723 --> 00:16:34,361 +there are a few more considerations +specific to the instrument cluster. + +256 +00:16:34,394 --> 00:16:40,734 +First of all, the instrument cluster may +allow users to zoom the map in and out. + +257 +00:16:40,767 --> 00:16:44,137 +It's your responsibility to +implement this in your app using the + +258 +00:16:44,171 --> 00:16:48,075 +CPInstrumentClusterControllerDelegate. + +259 +00:16:48,108 --> 00:16:51,111 +Similarly, if your app includes +a compass or speed limit, + +260 +00:16:51,144 --> 00:16:53,680 +the corresponding delegates +will tell your app + +261 +00:16:53,714 --> 00:16:57,017 +when it is appropriate +to draw them or not. + +262 +00:16:57,050 --> 00:17:01,722 +Finally, note that your instrument +cluster view may be partially obscured + +263 +00:17:01,755 --> 00:17:05,092 +by other elements +in the car's instrument cluster. + +264 +00:17:05,125 --> 00:17:10,063 +Of course, iOS already has a first class +mechanism for dealing with such a thing, + +265 +00:17:10,097 --> 00:17:11,832 +safe areas! + +266 +00:17:11,865 --> 00:17:15,636 +You can override +viewSafeAreaInsetsDidChange + +267 +00:17:15,669 --> 00:17:19,006 +on your view controller to +know when the safe area changes, + +268 +00:17:19,039 --> 00:17:22,843 +and use the safeAreaLayoutGuide +on your cluster view + +269 +00:17:22,876 --> 00:17:28,448 +to keep critical content in the area of +the view guaranteed to be visible. + +270 +00:17:28,482 --> 00:17:31,385 +If you have a blue route line +showing the user's location, + +271 +00:17:31,418 --> 00:17:34,121 +for instance, +you'd want to ensure the critical parts + +272 +00:17:34,154 --> 00:17:36,957 +are contained within the safe area. + +273 +00:17:36,990 --> 00:17:41,795 +We just saw some new features for you +as a developer building CarPlay apps, + +274 +00:17:41,828 --> 00:17:44,865 +and we saw a +new tool for you to test your apps. + +275 +00:17:44,898 --> 00:17:47,367 +Let's take a look at it in action. + +276 +00:17:47,401 --> 00:17:51,972 +First, I'm going to start here on my Mac, +showing you CarPlay Simulator. + +277 +00:17:52,005 --> 00:17:55,642 +I have the app running, +and I'll simply connect my phone. + +278 +00:18:02,716 --> 00:18:05,152 +And voilà, here it is running CarPlay. + +279 +00:18:05,185 --> 00:18:09,356 +Let's see how CarPlay Simulator +can help you when testing your apps. + +280 +00:18:09,389 --> 00:18:13,827 +Even if your app is primarily template +based, you can use it to make sure + +281 +00:18:13,861 --> 00:18:18,899 +the artwork in your app works great +in both light and dark appearances. + +282 +00:18:18,932 --> 00:18:21,568 +Let me run the Express Lane app... + +283 +00:18:24,137 --> 00:18:26,273 +And I can use the button in the toolbar + +284 +00:18:26,306 --> 00:18:30,277 +to toggle between light and dark +appearances. + +285 +00:18:30,310 --> 00:18:35,048 +Notice how my app has provided +different artwork for both styles. + +286 +00:18:35,082 --> 00:18:38,552 +Looks great. + +287 +00:18:38,585 --> 00:18:42,523 +Now let's switch over to Space Roads, +a navigation test app I've written. + +288 +00:18:44,658 --> 00:18:47,895 +I'll use the main configuration panel + +289 +00:18:47,928 --> 00:18:51,798 +to try my map drawing code +at a different screen size. + +290 +00:18:57,804 --> 00:19:01,074 +And finally, I will enable +the instrument cluster display + +291 +00:19:01,108 --> 00:19:03,844 +to test the instrument cluster support. + +292 +00:19:08,081 --> 00:19:09,716 +Here it is. It works great! + +293 +00:19:10,617 --> 00:19:14,755 +Earlier, I fully tested my apps +in CarPlay Simulator, so now, + +294 +00:19:14,788 --> 00:19:18,458 +I have full confidence +they will work great in a real car. + +295 +00:19:18,492 --> 00:19:20,360 +Let's give it a try! + +296 +00:19:20,394 --> 00:19:23,297 +Alright, here we are in my car, +and as you can see, + +297 +00:19:23,330 --> 00:19:26,466 +I have my phone connected to the +vehicle and running CarPlay. + +298 +00:19:27,868 --> 00:19:31,772 +First, let's try running my +connected trailer controller app + +299 +00:19:31,805 --> 00:19:36,310 +so I can show you how templates have +taken care of making the app work great + +300 +00:19:36,343 --> 00:19:38,345 +in a knob enabled vehicle. + +301 +00:19:40,247 --> 00:19:44,184 +This particular vehicle has both a touch +screen and a knob controller, + +302 +00:19:44,218 --> 00:19:47,621 +but many users like to use +the knob controller while driving, + +303 +00:19:47,654 --> 00:19:51,758 +so it's important for apps +to work great using the knob. + +304 +00:19:51,792 --> 00:19:54,661 +As you can see, +I have full access to the buttons + +305 +00:19:54,695 --> 00:19:57,998 +in my app here, +and the best part is, I didn't have to do + +306 +00:19:58,031 --> 00:20:01,435 +anything special - the +templates did all the work for me! + +307 +00:20:03,971 --> 00:20:07,975 +Next, let's switch over to my +navigation app, Space Roads. + +308 +00:20:08,008 --> 00:20:10,911 +We'll launch the app... + +309 +00:20:10,944 --> 00:20:14,781 +and we'll start navigation... + +310 +00:20:14,815 --> 00:20:16,750 +and then I press go. + +311 +00:20:16,783 --> 00:20:18,352 +Boom! + +312 +00:20:18,385 --> 00:20:22,055 +My app now shows a live map view +on both the center console + +313 +00:20:22,089 --> 00:20:24,658 +and in the instrument cluster! + +314 +00:20:24,691 --> 00:20:29,429 +It's great having the live map +right in my line of sight as a driver. + +315 +00:20:29,463 --> 00:20:33,800 +I'm certain drivers using +your navigation app will love it, too. + +316 +00:20:33,834 --> 00:20:35,802 +Well, that's all I have for you today. + +317 +00:20:35,836 --> 00:20:40,407 +For more information, be sure to check out +the CarPlay developer portal + +318 +00:20:40,440 --> 00:20:44,011 +at developer.apple.com/carplay + +319 +00:20:44,044 --> 00:20:47,114 +Thank you, and happy roads, everyone! ♪♪ + diff --git a/eng/2022 Session 10017 Explore the machine learning development experience en.srt b/eng/2022 Session 10017 Explore the machine learning development experience en.srt new file mode 100644 index 0000000..4624eac --- /dev/null +++ b/eng/2022 Session 10017 Explore the machine learning development experience en.srt @@ -0,0 +1,1193 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,943 --> 00:00:12,246 +Ciao, my name is Geppy Parziale, + +3 +00:00:12,279 --> 00:00:15,148 +and I am a machine learning engineer +here at Apple. + +4 +00:00:15,182 --> 00:00:18,952 +Today, I want to walk you through +the journey of building an app + +5 +00:00:18,986 --> 00:00:21,355 +that uses machine learning +to solve a problem + +6 +00:00:21,388 --> 00:00:25,893 +that would usually require an expert +to perform some very specialized work. + +7 +00:00:27,394 --> 00:00:31,098 +This journey gives me the opportunity +to show you how to add + +8 +00:00:31,131 --> 00:00:33,600 +open source machine learning models +to your apps + +9 +00:00:33,634 --> 00:00:37,471 +and create fantastic new experiences. + +10 +00:00:37,504 --> 00:00:39,640 +During the journey, I will also highlight + +11 +00:00:39,673 --> 00:00:44,411 +a few of the many tools, frameworks, +and APIs available for you + +12 +00:00:44,444 --> 00:00:48,749 +in the Apple development ecosystem +to build apps using machine learning. + +13 +00:00:49,983 --> 00:00:53,654 +When building an app, you, the developer, +go through a series of decisions + +14 +00:00:53,687 --> 00:00:57,824 +that hopefully will bring +the best experience to your users. + +15 +00:00:57,858 --> 00:01:02,396 +And this is also true when adding machine +learning functionality to applications. + +16 +00:01:04,031 --> 00:01:07,067 +During the development, you could ask: + +17 +00:01:07,100 --> 00:01:10,637 +should I use machine learning +to build this feature? + +18 +00:01:10,671 --> 00:01:14,408 +How can I get a machine learning model? + +19 +00:01:14,441 --> 00:01:18,745 +How do I make that model +compatible with Apple platforms? + +20 +00:01:18,779 --> 00:01:22,716 +Will that model work +for my specific use case? + +21 +00:01:22,749 --> 00:01:26,320 +Does it run on the Apple Neural Engine? + +22 +00:01:26,353 --> 00:01:29,590 +So let's take this journey together. + +23 +00:01:29,623 --> 00:01:33,193 +I want to build an app that allows me +to add realistic colors + +24 +00:01:33,227 --> 00:01:38,198 +to my family black and white photos +that I found in an old box in my basement. + +25 +00:01:39,666 --> 00:01:44,338 +Of course, a professional photographer +could do this with some manual work, + +26 +00:01:44,371 --> 00:01:47,908 +spending some time +in a photo editing tool. + +27 +00:01:47,941 --> 00:01:50,744 +Instead, what if I want +to automate this process + +28 +00:01:50,777 --> 00:01:54,515 +and apply the colorization +in just a few seconds? + +29 +00:01:54,548 --> 00:01:57,584 +This seems to be a perfect task +for machine learning. + +30 +00:01:58,919 --> 00:02:02,155 +Apple offers a tremendous amount +of frameworks and tools + +31 +00:02:02,189 --> 00:02:06,026 +that can help you build and integrate +ML functionality in your apps. + +32 +00:02:06,960 --> 00:02:09,296 +They provide everything +from data processing + +33 +00:02:09,329 --> 00:02:11,999 +to model training and inference. + +34 +00:02:12,032 --> 00:02:15,669 +For this journey, +I am going to use a few of them. + +35 +00:02:15,702 --> 00:02:19,006 +But remember, you have many to choose from + +36 +00:02:19,039 --> 00:02:22,910 +depending on the specific machine learning +task that you are developing. + +37 +00:02:22,943 --> 00:02:25,712 +The process I use when developing +a machine learning feature + +38 +00:02:25,746 --> 00:02:28,582 +in my apps goes through a set of stages. + +39 +00:02:29,650 --> 00:02:32,753 +First, I search for the right +machine learning model + +40 +00:02:32,786 --> 00:02:36,723 +in either scientific publication +or specialized website. + +41 +00:02:38,058 --> 00:02:40,360 +I searched for photo colorization, + +42 +00:02:40,394 --> 00:02:45,332 +and I found a model called Colorizer +that may work for what I need. + +43 +00:02:46,233 --> 00:02:50,337 +Here is an example of the colorization +I can get using this model. + +44 +00:02:53,473 --> 00:02:55,175 +Here is another one. + +45 +00:02:56,643 --> 00:03:00,547 +And here is another one. Really great. + +46 +00:03:00,581 --> 00:03:03,083 +Let me show you how it works. + +47 +00:03:03,116 --> 00:03:07,821 +The Colorizer model expects +a black and white image as input. + +48 +00:03:07,855 --> 00:03:14,228 +The Python source code I found converts +any RGB image to a LAB color space image. + +49 +00:03:15,362 --> 00:03:18,031 +This color space has 3 channels: + +50 +00:03:18,065 --> 00:03:22,102 +one represents the image lightness +or L channel, + +51 +00:03:22,135 --> 00:03:25,806 +and the other two represent +the color components. + +52 +00:03:25,839 --> 00:03:27,875 +The color components are discarded + +53 +00:03:27,908 --> 00:03:31,378 +while the lightness becomes +the input of the colorizer model. + +54 +00:03:32,646 --> 00:03:35,516 +The model then estimates +two new color components + +55 +00:03:35,549 --> 00:03:40,053 +that, combined with the input L channel, +provide the resulting image with color. + +56 +00:03:41,154 --> 00:03:45,459 +It's time now to make this model +compatible with my app. + +57 +00:03:45,492 --> 00:03:49,496 +To achieve this, I can convert +the original PyTorch model + +58 +00:03:49,530 --> 00:03:53,300 +to Core ML format using coremltools. + +59 +00:03:53,333 --> 00:03:58,071 +Here is the simple Python script I used +to convert the PyTorch model to Core ML. + +60 +00:03:59,439 --> 00:04:02,910 +First, I import the PyTorch model +architecture and weights. + +61 +00:04:04,378 --> 00:04:07,114 +Then I trace the imported model. + +62 +00:04:07,147 --> 00:04:11,285 +Finally, I convert the PyTorch model +to Core ML and save it. + +63 +00:04:12,953 --> 00:04:15,389 +Once the model is in the Core ML format, + +64 +00:04:15,422 --> 00:04:18,992 +I need to verify +that the conversion worked correctly. + +65 +00:04:19,026 --> 00:04:23,130 +I can do that directly in Python +again using coremltools. + +66 +00:04:23,163 --> 00:04:25,499 +And this is easy. + +67 +00:04:25,532 --> 00:04:28,702 +I import the image in RGB color space + +68 +00:04:28,735 --> 00:04:31,238 +and convert it to Lab color space. + +69 +00:04:32,940 --> 00:04:37,144 +Then I separate the lightness +from the color channels and discard them. + +70 +00:04:38,745 --> 00:04:41,515 +I run the prediction +using the Core ML model. + +71 +00:04:42,616 --> 00:04:44,918 +And finally, compose the input lightness + +72 +00:04:44,952 --> 00:04:48,355 +with the estimated color components +and convert to RGB. + +73 +00:04:49,723 --> 00:04:53,794 +This allows me to verify +that functionality of the converted model + +74 +00:04:53,827 --> 00:04:57,464 +matches the functionality +of the original PyTorch model. + +75 +00:04:57,497 --> 00:05:01,301 +I call this stage Model Verification. + +76 +00:05:01,335 --> 00:05:05,305 +However, there is another +important check to be done. + +77 +00:05:05,339 --> 00:05:10,644 +I need to understand if this model +can run fast enough on my target device. + +78 +00:05:10,677 --> 00:05:13,747 +So I would need to assess +the model on device + +79 +00:05:13,780 --> 00:05:17,851 +and make sure it would provide +the best user experience. + +80 +00:05:17,885 --> 00:05:22,322 +The new Core ML Performance report, +available now in Xcode 14, + +81 +00:05:22,356 --> 00:05:26,393 +performs a time-based analysis +of a Core ML model. + +82 +00:05:26,426 --> 00:05:29,630 +I just need to drag and drop +the model into Xcode + +83 +00:05:29,663 --> 00:05:32,699 +and create a performance report +in a few seconds. + +84 +00:05:33,834 --> 00:05:37,871 +Using this tool, +I can see that estimated prediction time + +85 +00:05:37,905 --> 00:05:43,577 +is almost 90 milliseconds +on an iPad Pro with M1 and iPadOS 16. + +86 +00:05:44,578 --> 00:05:48,215 +And this is perfect +for my photo colorization app. + +87 +00:05:48,248 --> 00:05:51,718 +If you want to know more about +Performance Report in Xcode, + +88 +00:05:51,752 --> 00:05:56,957 +I suggest you to watch this year’s session +"Optimize your Core ML usage". + +89 +00:05:56,990 --> 00:06:01,128 +So Performance Report can help you +make an assessment of the model + +90 +00:06:01,161 --> 00:06:04,798 +and make sure it provides +the best on-device user experience. + +91 +00:06:05,966 --> 00:06:09,937 +Now that I am sure about the functionality +and performance of my model, + +92 +00:06:09,970 --> 00:06:11,872 +let me integrate it into my app. + +93 +00:06:13,507 --> 00:06:18,178 +The integration process is identical +to what I have done until now in Python, + +94 +00:06:18,212 --> 00:06:20,848 +but this time I can do it seamlessly in Swift + +95 +00:06:20,881 --> 00:06:24,151 +using Xcode and all the other tools +you are familiar with. + +96 +00:06:26,286 --> 00:06:29,289 +Remember the model, now in Core ML format, + +97 +00:06:29,323 --> 00:06:33,227 +expects a single channel image +representing its lightness. + +98 +00:06:34,695 --> 00:06:37,798 +So similarly to what I did +previously in Python, + +99 +00:06:37,831 --> 00:06:43,203 +I need to convert any RGB input image +to an image using the Lab color space. + +100 +00:06:45,839 --> 00:06:48,408 +I could write this transformation +in multiple ways: + +101 +00:06:48,442 --> 00:06:52,212 +directly in Swift with vImage, +or using Metal. + +102 +00:06:53,514 --> 00:06:57,784 +Exploring deeper in the documentation, +I found that the Core Image framework + +103 +00:06:57,818 --> 00:07:00,554 +provides something +that can help me with this. + +104 +00:07:02,656 --> 00:07:06,360 +So let me show you how to achieve +the RGB to LAB conversion + +105 +00:07:06,393 --> 00:07:08,929 +and run the prediction +using the Core ML model. + +106 +00:07:10,864 --> 00:07:13,667 +Here is the Swift code +to extract the lightness + +107 +00:07:13,700 --> 00:07:17,538 +from an RGB image +and pass it to the Core ML model. + +108 +00:07:17,571 --> 00:07:22,509 +First, I convert the RGB image +into LAB and extract the lightness. + +109 +00:07:23,944 --> 00:07:27,014 +Then, I convert lightness to a CGImage + +110 +00:07:27,047 --> 00:07:30,050 +and prepare the input +for the Core ML model. + +111 +00:07:31,585 --> 00:07:33,921 +Finally, I run the prediction. + +112 +00:07:33,954 --> 00:07:37,658 +To extract the L channel +from the input RGB image, + +113 +00:07:37,691 --> 00:07:41,094 +I first convert the RGB image +into a LAB image, + +114 +00:07:41,128 --> 00:07:45,365 +using the new CIFilter convertRGBtoLab. + +115 +00:07:45,399 --> 00:07:49,570 +The values of the lightness +are set between 0 and 100. + +116 +00:07:51,038 --> 00:07:54,708 +Then, I multiply the Lab image +with a color matrix + +117 +00:07:54,741 --> 00:07:59,613 +and discard the color channels +and return the lightness to the caller. + +118 +00:07:59,646 --> 00:08:02,983 +Let's now analyze what happens +at the output of the model. + +119 +00:08:04,751 --> 00:08:07,921 +The Core ML model returns +two MLShapedArrays + +120 +00:08:07,955 --> 00:08:10,624 +containing the estimated color components. + +121 +00:08:12,326 --> 00:08:18,031 +So after the prediction, I convert +the two MLShapedArray into two CIImages. + +122 +00:08:19,533 --> 00:08:23,937 +Finally, I combine them +with the model input lightness. + +123 +00:08:23,971 --> 00:08:28,075 +This generates a new LAB image +that I convert to RGB and return it. + +124 +00:08:30,344 --> 00:08:34,715 +To convert the two MLShapedArrays +into two CIImages, + +125 +00:08:34,748 --> 00:08:38,752 +I first extract the values +from each shaped array. + +126 +00:08:38,785 --> 00:08:42,356 +Then, I create two core images +representing the two color channels + +127 +00:08:42,389 --> 00:08:44,958 +and return them. + +128 +00:08:44,992 --> 00:08:47,961 +To combine the lightness +with the estimated color channels, + +129 +00:08:47,995 --> 00:08:51,498 +I use a custom CIKernel that takes + +130 +00:08:51,532 --> 00:08:55,068 +the three channels as input +and returns a CIImage. + +131 +00:08:56,503 --> 00:09:00,107 +Then, I use the new CIFilter +convertLabToRGB + +132 +00:09:00,140 --> 00:09:04,978 +to convert the Lab image to RGB +and return it to the caller. + +133 +00:09:05,012 --> 00:09:08,015 +This is the source code +for the custom CIKernel I use + +134 +00:09:08,048 --> 00:09:12,052 +to combine the lightness +with the two estimated color channels + +135 +00:09:12,085 --> 00:09:13,754 +in a single CIImage. + +136 +00:09:14,888 --> 00:09:19,359 +For more information about +the new CI filters to convert RGB images + +137 +00:09:19,393 --> 00:09:23,664 +to LAB images and vice versa, +please refer to the session + +138 +00:09:23,697 --> 00:09:27,734 +"Display EDR content with Core Image, +Metal, and SwiftUI." + +139 +00:09:29,002 --> 00:09:32,472 +Now that I completed the integration +of this ML feature in my app, + +140 +00:09:32,506 --> 00:09:34,541 +let's see it in action. + +141 +00:09:34,575 --> 00:09:36,443 +But wait. + +142 +00:09:36,476 --> 00:09:41,014 +How will I colorize my old family photos +in real time with my application? + +143 +00:09:41,048 --> 00:09:45,519 +I could spend some time to digitize +each of them and import them in my app. + +144 +00:09:46,653 --> 00:09:48,956 +I think I have a better idea. + +145 +00:09:48,989 --> 00:09:51,925 +What if I use my iPad camera +to scan these photos + +146 +00:09:51,959 --> 00:09:54,061 +and colorize them live? + +147 +00:09:54,094 --> 00:09:58,465 +I think it would be really fun, and I have +everything I need to accomplish this. + +148 +00:09:58,498 --> 00:10:01,535 +But first, I have to solve a problem. + +149 +00:10:02,970 --> 00:10:06,607 +My model needs 90 milliseconds +to process an image. + +150 +00:10:06,640 --> 00:10:09,877 +If I want to process a video, +I would need something faster. + +151 +00:10:11,111 --> 00:10:13,013 +For a smooth user experience, + +152 +00:10:13,046 --> 00:10:16,850 +I'd like to run the device camera +at least 30 fps. + +153 +00:10:17,885 --> 00:10:22,256 +That means the camera will produce +a frame about every 30 milliseconds. + +154 +00:10:24,191 --> 00:10:28,529 +But since the model needs about +90 milliseconds to colorize a video frame, + +155 +00:10:28,562 --> 00:10:32,566 +I am going to lose 2 or 3 frames +during each colorization. + +156 +00:10:35,269 --> 00:10:39,406 +The total prediction time of a model +is a function of both its architecture + +157 +00:10:39,439 --> 00:10:44,111 +as well as the compute units operations +it gets mapped to. + +158 +00:10:44,144 --> 00:10:48,515 +Looking at the performance report again, +I notice that my model has a total + +159 +00:10:48,549 --> 00:10:54,188 +of 61 operations running on a combination +of the neural engine and CPU. + +160 +00:10:55,622 --> 00:10:59,726 +If I want a faster prediction time, +I will have to change the model. + +161 +00:11:00,694 --> 00:11:03,530 +I decided to experiment +with the model's architecture + +162 +00:11:03,564 --> 00:11:06,967 +and explore some alternatives +that may be faster. + +163 +00:11:07,000 --> 00:11:11,972 +However, a change in the architecture +means I need to retrain the network. + +164 +00:11:13,941 --> 00:11:15,609 +Apple offers different solutions + +165 +00:11:15,642 --> 00:11:19,112 +that allow me to train machine learning +models directly on my Mac. + +166 +00:11:20,514 --> 00:11:25,018 +In my case, since the original model +was developed in PyTorch, + +167 +00:11:25,052 --> 00:11:27,888 +I decided to use the new PyTorch on Metal, + +168 +00:11:27,921 --> 00:11:31,625 +so I can take advantage +of the tremendous hardware acceleration + +169 +00:11:31,658 --> 00:11:33,660 +provided by Apple Silicon. + +170 +00:11:35,696 --> 00:11:40,167 +If you are interested in knowing more +about the PyTorch accelerated with Metal, + +171 +00:11:40,200 --> 00:11:43,937 +please check the session, +"Accelerate machine learning with Metal" + +172 +00:11:46,173 --> 00:11:49,810 +Because of this change, +our journey needs to take a step back. + +173 +00:11:50,878 --> 00:11:53,914 +After retraining, +I will have to convert the result + +174 +00:11:53,947 --> 00:11:57,317 +to Core ML format +and run the verification again. + +175 +00:11:59,052 --> 00:12:01,455 +This time, model integration consists + +176 +00:12:01,488 --> 00:12:04,725 +of simply swapping the old model +with the new one. + +177 +00:12:04,758 --> 00:12:08,195 +After retraining a few candidate +alternative models, + +178 +00:12:08,228 --> 00:12:11,932 +I have verified one +that will meet my requirements. + +179 +00:12:11,965 --> 00:12:16,537 +Here it is with +the corresponding performance report. + +180 +00:12:16,570 --> 00:12:18,672 +It runs entirely on the neural engine + +181 +00:12:18,705 --> 00:12:22,843 +and the prediction time is now +around 16 milliseconds, + +182 +00:12:22,876 --> 00:12:24,545 +which works for video. + +183 +00:12:27,347 --> 00:12:32,186 +But Performance Report tells me only one +aspect of the performance of my app. + +184 +00:12:33,353 --> 00:12:36,723 +Indeed, after running my app, +I notice immediately + +185 +00:12:36,757 --> 00:12:40,294 +that colorization is not as smooth +as I expect. + +186 +00:12:40,327 --> 00:12:43,697 +So what happens in my app at runtime? + +187 +00:12:45,132 --> 00:12:50,103 +In order to understand that, I can use +the new Core ML template in Instruments. + +188 +00:12:52,706 --> 00:12:55,576 +Analyzing the initial portion +of the Core ML trace, + +189 +00:12:55,609 --> 00:13:00,514 +after loading the model, I notice +that the app accumulates predictions. + +190 +00:13:00,547 --> 00:13:02,549 +And this is unexpected. + +191 +00:13:02,583 --> 00:13:06,587 +Instead, I'd expect to have +a single prediction per frame. + +192 +00:13:08,121 --> 00:13:12,826 +Zooming in the trace +and checking the very first predictions, + +193 +00:13:12,860 --> 00:13:16,296 +I observe that the app requests +a second Core ML prediction + +194 +00:13:16,330 --> 00:13:18,565 +before the first one is finished. + +195 +00:13:19,800 --> 00:13:23,904 +Here, the Neural Engine is +still working on the first request + +196 +00:13:23,937 --> 00:13:26,440 +when the second is given to Core ML. + +197 +00:13:27,574 --> 00:13:30,310 +Similarly, the third prediction starts + +198 +00:13:30,344 --> 00:13:33,413 +while still processing the second one. + +199 +00:13:33,447 --> 00:13:35,549 +Even after four predictions, + +200 +00:13:35,582 --> 00:13:39,052 +the lag between the request and execution + +201 +00:13:39,086 --> 00:13:42,055 +is already about 20 milliseconds. + +202 +00:13:42,089 --> 00:13:45,759 +Instead, I need to make sure +that a new prediction starts + +203 +00:13:45,792 --> 00:13:50,330 +only if the previous one is finished +to avoid cascading these lags. + +204 +00:13:51,798 --> 00:13:55,936 +While fixing this problem, +I also found out that I accidentally set + +205 +00:13:55,969 --> 00:14:01,175 +the camera frame rate to 60 fps +instead of the desired 30fps. + +206 +00:14:03,377 --> 00:14:06,513 +After making sure that the apps process +a new frame + +207 +00:14:06,547 --> 00:14:08,849 +after the previous prediction completes + +208 +00:14:08,882 --> 00:14:12,219 +and setting the camera frame rate +to 30 fps, + +209 +00:14:12,252 --> 00:14:15,923 +I can see that Core ML dispatches +correctly a single prediction + +210 +00:14:15,956 --> 00:14:20,160 +to the Apple Neural Engine, +and now the app runs smoothly. + +211 +00:14:22,362 --> 00:14:24,731 +So we reached the end of our journey. + +212 +00:14:26,066 --> 00:14:28,869 +Let's test the app +on my old family photos. + +213 +00:14:34,508 --> 00:14:38,512 +Here are my black and white photos +that I found in my basement. + +214 +00:14:38,545 --> 00:14:42,583 +They capture some of the places +in Italy I visited a long time ago. + +215 +00:14:49,389 --> 00:14:52,426 +Here is a great photo +of the Colosseum in Rome. + +216 +00:14:53,760 --> 00:14:56,730 +The color of the walls +and the sky are so realistic. + +217 +00:14:59,266 --> 00:15:01,034 +Let's check this one. + +218 +00:15:03,370 --> 00:15:06,240 +This is Castel del Monte +in the South of Italy. + +219 +00:15:06,273 --> 00:15:07,741 +Really nice. + +220 +00:15:09,643 --> 00:15:12,513 +And this is my hometown, Grottaglie. + +221 +00:15:12,546 --> 00:15:15,949 +Adding colors to these images +triggered so many memories. + +222 +00:15:17,184 --> 00:15:20,554 +Notice that I am applying +the colorization only to the photo + +223 +00:15:20,587 --> 00:15:23,390 +while keeping the rest of the scene +black and white. + +224 +00:15:26,326 --> 00:15:29,796 +Here, I am taking advantage +of the rectangle detection algorithm + +225 +00:15:29,830 --> 00:15:32,432 +available in the Vision framework. + +226 +00:15:32,466 --> 00:15:37,204 +Using VNDetectRectangleRequest, +I can isolate the photo in the scene + +227 +00:15:37,237 --> 00:15:39,806 +and use it as input +to the Colorizer model. + +228 +00:15:41,041 --> 00:15:43,277 +And now let me recap. + +229 +00:15:44,645 --> 00:15:48,982 +During our journey, I explored +the enormous amount of frameworks, + +230 +00:15:49,016 --> 00:15:52,819 +APIs, and tools Apple provides +to prepare, integrate, + +231 +00:15:52,853 --> 00:15:56,423 +and evaluate machine learning +functionality for your apps. + +232 +00:15:56,456 --> 00:15:59,660 +I started this journey +identifying a problem + +233 +00:15:59,693 --> 00:16:02,863 +that required an open source +machine learning model to solve it. + +234 +00:16:03,964 --> 00:16:06,800 +I found an open source model +with desired functionality + +235 +00:16:06,834 --> 00:16:10,304 +and made it compatible +with Apple platforms. + +236 +00:16:10,337 --> 00:16:13,440 +I assessed the model performance +directly on the device + +237 +00:16:13,473 --> 00:16:16,276 +using the new Performance Report. + +238 +00:16:16,310 --> 00:16:19,379 +I integrated the model in my app +using the tools + +239 +00:16:19,413 --> 00:16:21,548 +and the frameworks you are familiar with. + +240 +00:16:22,983 --> 00:16:27,154 +I optimized the model using +the new Core ML Template in Instruments. + +241 +00:16:27,187 --> 00:16:30,524 +With Apple tools and frameworks, +I can take care of each phase + +242 +00:16:30,557 --> 00:16:34,828 +of the development process +directly on Apple devices and platforms + +243 +00:16:34,862 --> 00:16:38,866 +from data preparation, training, +integration, and optimization. + +244 +00:16:40,901 --> 00:16:44,938 +Today, we just scratched the surface +of what you, the developer, + +245 +00:16:44,972 --> 00:16:49,042 +can achieve with the frameworks +and tools Apple provides you. + +246 +00:16:49,076 --> 00:16:52,446 +Please, refer to previous sessions, +linked to this, + +247 +00:16:52,479 --> 00:16:56,950 +for additional inspiring ideas +to bring machine learning to your apps. + +248 +00:16:56,984 --> 00:16:59,786 +Explore and try frameworks and tools. + +249 +00:16:59,820 --> 00:17:03,724 +Take advantage of the great synergy +between software and hardware + +250 +00:17:03,757 --> 00:17:06,026 +to accelerate +your machine learning features + +251 +00:17:06,059 --> 00:17:09,196 +and enrich the user experience +of your apps. + +252 +00:17:09,229 --> 00:17:12,332 +Have a great WWDC, and arrivederci. ♪ ♪ + diff --git a/eng/2022 Session 10018 Bring Continuity Camera to your macOS app en.srt b/eng/2022 Session 10018 Bring Continuity Camera to your macOS app en.srt new file mode 100644 index 0000000..6699010 --- /dev/null +++ b/eng/2022 Session 10018 Bring Continuity Camera to your macOS app en.srt @@ -0,0 +1,1669 @@ +1 +00:00:00,000 --> 00:00:03,837 +♪ instrumental hip hop music ♪ + +2 +00:00:03,837 --> 00:00:09,409 +♪ + +3 +00:00:09,409 --> 00:00:11,879 +Hi, my name +is Karen Xing. + +4 +00:00:11,879 --> 00:00:14,414 +I'm an engineer +in the Camera Software team. + +5 +00:00:14,414 --> 00:00:16,750 +Welcome to +"Bring Continuity Camera support + +6 +00:00:16,750 --> 00:00:19,186 +to your macOS app" + +7 +00:00:19,186 --> 00:00:21,688 +To start this session +I will talk about, + +8 +00:00:21,688 --> 00:00:24,124 +what is Continuity Camera? + +9 +00:00:24,124 --> 00:00:26,760 +Next, I will discuss +how your application + +10 +00:00:26,760 --> 00:00:29,529 +can build an automatic +camera selection experience + +11 +00:00:29,529 --> 00:00:32,165 +with Continuity Camera. + +12 +00:00:32,165 --> 00:00:34,935 +And finally, +I will walk through the APIs + +13 +00:00:34,935 --> 00:00:40,474 +that are new in macOS 13 +for Continuity Camera. + +14 +00:00:40,474 --> 00:00:42,109 +With Continuity Camera, + +15 +00:00:42,109 --> 00:00:45,279 +you can now use iPhone +as your webcam. + +16 +00:00:45,279 --> 00:00:46,680 +Setup is seamless; + +17 +00:00:46,680 --> 00:00:49,116 +just bring your iPhone +close to your Mac. + +18 +00:00:49,116 --> 00:00:54,621 +And it works wirelessly +so you can quickly join a call. + +19 +00:00:54,621 --> 00:00:57,190 +Your iPhone +will appear on your Mac + +20 +00:00:57,190 --> 00:00:59,927 +as an external camera +and microphone + +21 +00:00:59,927 --> 00:01:01,962 +under several conditions. + +22 +00:01:01,962 --> 00:01:07,334 +First, you must be running +macOS 13 and iOS 16. + +23 +00:01:07,334 --> 00:01:10,971 +Both Mac and iPhone must be +signed into the same Apple ID + +24 +00:01:10,971 --> 00:01:13,941 +using two-factor authentication. + +25 +00:01:13,941 --> 00:01:15,642 +For wired connection, + +26 +00:01:15,642 --> 00:01:20,113 +the phone needs to be +connected to Mac over USB. + +27 +00:01:20,113 --> 00:01:22,382 +Or for wireless connection, + +28 +00:01:22,382 --> 00:01:24,952 +the two devices need to be +in proximity + +29 +00:01:24,952 --> 00:01:28,622 +and have both Bluetooth +and Wi-Fi turned on. + +30 +00:01:28,622 --> 00:01:31,625 +Rather than talking through it, +let me show you right away + +31 +00:01:31,625 --> 00:01:35,595 +how magical Continuity Camera +looks on devices. + +32 +00:01:35,595 --> 00:01:38,765 +Here I have a MacBook Pro +and iPhone 13 Pro. + +33 +00:01:38,765 --> 00:01:42,569 +Both devices are signed in +to the same Apple ID. + +34 +00:01:47,107 --> 00:01:50,444 +The phone is placed on a stand +attached to my MacBook. + +35 +00:01:50,444 --> 00:01:52,679 +I will be joining +a video conferencing call + +36 +00:01:52,679 --> 00:01:54,381 +with my colleague Eric today + +37 +00:01:54,381 --> 00:01:58,919 +and show you how we can use +Continuity Camera in Zoom. + +38 +00:02:04,257 --> 00:02:07,094 +The app is launched using +the built-in camera first, + +39 +00:02:07,094 --> 00:02:09,830 +and then an onboarding dialogue +shows up + +40 +00:02:09,830 --> 00:02:12,866 +describing what you can do +with the new camera. + +41 +00:02:12,866 --> 00:02:14,835 +The dialogue shows up one time + +42 +00:02:14,835 --> 00:02:18,105 +after your Mac is upgraded +to macOS 13 + +43 +00:02:18,105 --> 00:02:20,841 +when you open a camera +application for the first time + +44 +00:02:20,841 --> 00:02:25,879 +and there's an iPhone eligible +for Continuity Camera. + +45 +00:02:27,881 --> 00:02:30,283 +Hi, Eric! + +46 +00:02:30,283 --> 00:02:35,155 +Eric: Oh, Karen! Hi! + +47 +00:02:35,155 --> 00:02:38,258 +Karen: After the onboarding +dialogue is shown on the system, + +48 +00:02:38,258 --> 00:02:40,927 +Continuity Camera +and microphone devices + +49 +00:02:40,927 --> 00:02:45,532 +will become available +in all applications. + +50 +00:02:51,872 --> 00:02:55,542 +Let's switch to use this camera +and see how it looks. + +51 +00:03:01,114 --> 00:03:05,318 +Continuity Camera uses the rear +camera system on the iPhone, + +52 +00:03:05,318 --> 00:03:07,354 +so you get the same +great video quality + +53 +00:03:07,354 --> 00:03:09,122 +that you expect from iPhone. + +54 +00:03:09,122 --> 00:03:13,060 +And it works with all four +orientations of the phone. + +55 +00:03:16,897 --> 00:03:19,733 +The portrait orientation +gives you a more zoomed in + +56 +00:03:19,733 --> 00:03:23,637 +field of view compared +to landscape orientation. + +57 +00:03:27,307 --> 00:03:30,477 +Continuity Camera +also lets you do things + +58 +00:03:30,477 --> 00:03:33,647 +that were never before +possible with a webcam, + +59 +00:03:33,647 --> 00:03:37,184 +including several new +video effects. + +60 +00:03:37,184 --> 00:03:38,985 +You're probably already familiar + +61 +00:03:38,985 --> 00:03:41,354 +with Center Stage +and Portrait video effects + +62 +00:03:41,354 --> 00:03:45,992 +introduced in iOS 14.5 +and macOS 12.3. + +63 +00:03:45,992 --> 00:03:48,462 +If not, I highly recommend +watching the + +64 +00:03:48,462 --> 00:03:53,333 +"What's new in Camera Capture" +session from WWDC 2021 + +65 +00:03:53,333 --> 00:03:55,635 +to learn more about +system video effects + +66 +00:03:55,635 --> 00:04:00,307 +and how to interact with them +in applications. + +67 +00:04:00,307 --> 00:04:03,710 +Let's go to Control Center +and enable system video effects + +68 +00:04:03,710 --> 00:04:06,379 +on Continuity Camera. + +69 +00:04:09,549 --> 00:04:15,255 +Center Stage keeps you in frame +as you move around in the scene. + +70 +00:04:17,491 --> 00:04:19,359 +Portrait blurs the background + +71 +00:04:19,359 --> 00:04:22,195 +and naturally +puts the focus on you. + +72 +00:04:22,195 --> 00:04:25,365 +Portrait is only supported +on Apple silicon Macs, + +73 +00:04:25,365 --> 00:04:27,100 +but with Continuity Camera, + +74 +00:04:27,100 --> 00:04:32,072 +it is now available on all +Intel and Apple silicon Macs. + +75 +00:04:35,909 --> 00:04:38,378 +Studio Light +is a new system video effect + +76 +00:04:38,378 --> 00:04:40,547 +available on macOS 13. + +77 +00:04:40,547 --> 00:04:42,782 +It is supported +by Continuity Camera + +78 +00:04:42,782 --> 00:04:45,218 +when using iPhone 12 or newer. + +79 +00:04:45,218 --> 00:04:48,588 +Enable this when you want +to look your best on screen. + +80 +00:04:48,588 --> 00:04:50,957 +It provides a stunning +lighting effect + +81 +00:04:50,957 --> 00:04:54,794 +that dims the background +and illuminates your face. + +82 +00:04:54,794 --> 00:04:58,131 +Studio Light is perfect +for tough lighting situations, + +83 +00:04:58,131 --> 00:05:00,967 +like when you're +in front of a window. + +84 +00:05:00,967 --> 00:05:03,970 +Even though I'm showing you +each video effect separately + +85 +00:05:03,970 --> 00:05:08,475 +for a clear comparison, +all of them work well together. + +86 +00:05:13,480 --> 00:05:15,515 +And any combination +of the effects + +87 +00:05:15,515 --> 00:05:19,119 +can be applied at the same time. + +88 +00:05:25,125 --> 00:05:28,328 +Here's another exciting feature +I really want to show you + +89 +00:05:28,328 --> 00:05:30,630 +for Continuity Camera. + +90 +00:05:30,630 --> 00:05:34,034 +When you want to work together +and share what's on your desk, + +91 +00:05:34,034 --> 00:05:36,102 +you can now use Desk View. + +92 +00:05:36,102 --> 00:05:38,972 +The Desk View app +comes with macOS 13 + +93 +00:05:38,972 --> 00:05:42,776 +and can be launched right here +in Control Center. + +94 +00:05:48,448 --> 00:05:51,051 +It works like an overhead +camera setup, + +95 +00:05:51,051 --> 00:05:54,054 +without needing +all the complicated equipment. + +96 +00:05:54,054 --> 00:05:57,257 +iPhone will split the Ultra Wide +camera feed in two, + +97 +00:05:57,257 --> 00:06:00,560 +showing off your desk and face +both at the same time, + +98 +00:06:00,560 --> 00:06:02,963 +so you can collaborate +on a school project + +99 +00:06:02,963 --> 00:06:05,398 +or teach a friend +a knitting stitch. + +100 +00:06:05,398 --> 00:06:08,134 +It leverages the extended +vertical field of view + +101 +00:06:08,134 --> 00:06:10,737 +of our Ultra Wide angle +camera, + +102 +00:06:10,737 --> 00:06:14,374 +applies perspective distortion +correction onto cropped frames, + +103 +00:06:14,374 --> 00:06:18,011 +and then rotates the frames +to create this Desk View. + +104 +00:06:18,011 --> 00:06:21,014 +You can use the share window +function available + +105 +00:06:21,014 --> 00:06:24,851 +in most video conferencing apps +to share this Desk View feed, + +106 +00:06:24,851 --> 00:06:29,256 +running in parallel with +the main video camera feed. + +107 +00:06:52,279 --> 00:06:54,281 +Desk View can also +be used alone + +108 +00:06:54,281 --> 00:06:57,017 +without streaming from the main +camera at the same time. + +109 +00:06:57,017 --> 00:06:58,285 +But when you do stream + +110 +00:06:58,285 --> 00:07:00,453 +from both Desk View +and the main camera, + +111 +00:07:00,453 --> 00:07:04,057 +we recommend enabling +Center Stage on the main camera + +112 +00:07:04,057 --> 00:07:07,560 +for a better framing +to capture face and body there. + +113 +00:07:07,560 --> 00:07:09,696 +The feature is supported +when the phone is placed + +114 +00:07:09,696 --> 00:07:12,732 +in either landscape +or portrait orientation. + +115 +00:07:12,732 --> 00:07:16,069 +The portrait orientation +provides the most versatility, + +116 +00:07:16,069 --> 00:07:19,172 +as there's a larger +vertical field of view. + +117 +00:07:19,172 --> 00:07:22,008 +There's also +a Desk View camera API + +118 +00:07:22,008 --> 00:07:24,277 +to provide customized +integration + +119 +00:07:24,277 --> 00:07:26,413 +suitable for your application. + +120 +00:07:26,413 --> 00:07:29,883 +I will talk about the API +in a moment. + +121 +00:07:29,883 --> 00:07:32,218 +During a video conferencing call +on your Mac, + +122 +00:07:32,218 --> 00:07:34,454 +we want you +to focus on the session + +123 +00:07:34,454 --> 00:07:35,922 +but we also want to make sure + +124 +00:07:35,922 --> 00:07:38,358 +you are not missing +anything important. + +125 +00:07:38,358 --> 00:07:40,760 +When Continuity Camera +is in use, + +126 +00:07:40,760 --> 00:07:43,763 +all notifications on your phone +will be silenced + +127 +00:07:43,763 --> 00:07:49,169 +and important call notifications +will be forwarded on your Mac. + +128 +00:07:49,169 --> 00:07:50,670 +Bye, Eric! + +129 +00:07:50,670 --> 00:07:53,273 +Eric: Bye, Karen! + +130 +00:07:58,845 --> 00:08:00,246 +Karen: We've just talked about + +131 +00:08:00,246 --> 00:08:03,416 +all the great experiences +available to users + +132 +00:08:03,416 --> 00:08:07,153 +without writing a single line +of new code in your application. + +133 +00:08:07,153 --> 00:08:09,923 +But with some adoption +of new APIs, + +134 +00:08:09,923 --> 00:08:12,559 +you can make +the Continuity Camera experience + +135 +00:08:12,559 --> 00:08:16,596 +even more magical and polished +in your app. + +136 +00:08:16,596 --> 00:08:18,164 +Now that most users will get + +137 +00:08:18,164 --> 00:08:20,834 +at least two camera devices +on the Mac, + +138 +00:08:20,834 --> 00:08:24,404 +we've thought more on how +cameras should be managed. + +139 +00:08:24,404 --> 00:08:28,475 +Prior to macOS 13, when a device +is either unplugged + +140 +00:08:28,475 --> 00:08:31,845 +or a better camera becomes +available on the system, + +141 +00:08:31,845 --> 00:08:33,213 +a manual selection step + +142 +00:08:33,213 --> 00:08:36,383 +is usually required +in applications. + +143 +00:08:36,383 --> 00:08:39,953 +We'd like to offer customers +a magical experience by + +144 +00:08:39,953 --> 00:08:43,423 +switching cameras automatically +in applications. + +145 +00:08:43,423 --> 00:08:46,960 +We've added two new APIs +in the AVFoundation framework + +146 +00:08:46,960 --> 00:08:49,963 +to help you build this function +in your app: + +147 +00:08:49,963 --> 00:08:52,832 +the class properties +userPreferredCamera + +148 +00:08:52,832 --> 00:08:56,970 +and systemPreferredCamera +on AVCaptureDevice. + +149 +00:08:56,970 --> 00:09:00,407 +userPreferredCamera +is a read/write property. + +150 +00:09:00,407 --> 00:09:02,008 +You will need +to set this property + +151 +00:09:02,008 --> 00:09:05,645 +whenever a user picks a camera +in the application. + +152 +00:09:05,645 --> 00:09:08,081 +This allows +the AVCaptureDevice class + +153 +00:09:08,081 --> 00:09:11,117 +to learn users' preference, +store a list of cameras + +154 +00:09:11,117 --> 00:09:15,021 +for each application +across app launches and reboots, + +155 +00:09:15,021 --> 00:09:18,625 +and use that information +to suggest a camera. + +156 +00:09:18,625 --> 00:09:21,060 +It also takes into account +whether any camera + +157 +00:09:21,060 --> 00:09:25,198 +becomes connected +or disconnected. + +158 +00:09:25,198 --> 00:09:28,034 +This property +is key-value observable + +159 +00:09:28,034 --> 00:09:31,104 +and intelligently returns +the best selection + +160 +00:09:31,104 --> 00:09:33,273 +based on user preference. + +161 +00:09:33,273 --> 00:09:36,910 +When the most recent preferred +device becomes disconnected, + +162 +00:09:36,910 --> 00:09:38,812 +it spontaneously changes + +163 +00:09:38,812 --> 00:09:41,815 +to the next available camera +in the list. + +164 +00:09:41,815 --> 00:09:44,384 +Even when there's +no user selection history + +165 +00:09:44,384 --> 00:09:47,487 +or none of the preferred devices +are connected, + +166 +00:09:47,487 --> 00:09:49,756 +the property will always +try to return + +167 +00:09:49,756 --> 00:09:52,125 +a camera device +that's ready to use + +168 +00:09:52,125 --> 00:09:56,463 +and prioritize cameras that have +been previously streamed. + +169 +00:09:56,463 --> 00:09:59,666 +It only returns nil +when there's no camera available + +170 +00:09:59,666 --> 00:10:03,102 +on the system. + +171 +00:10:03,102 --> 00:10:06,806 +systemPreferredCamera +is a read-only property. + +172 +00:10:06,806 --> 00:10:08,842 +It incorporates +userPreferredCamera + +173 +00:10:08,842 --> 00:10:11,277 +as well as a few other factors + +174 +00:10:11,277 --> 00:10:15,515 +to suggest the best choice of +cameras present on the system. + +175 +00:10:15,515 --> 00:10:19,152 +For example, this property +will return a different value + +176 +00:10:19,152 --> 00:10:22,755 +than userPreferredCamera when +a Continuity Camera shows up + +177 +00:10:22,755 --> 00:10:26,526 +signaling that it should be +automatically chosen. + +178 +00:10:26,526 --> 00:10:29,963 +The property also tracks +device suspensions internally + +179 +00:10:29,963 --> 00:10:34,801 +so it prioritizes unsuspended +devices over suspended ones. + +180 +00:10:34,801 --> 00:10:37,937 +This is helpful for building +automatic switching behavior + +181 +00:10:37,937 --> 00:10:40,640 +to change to another camera +if the built-in camera + +182 +00:10:40,640 --> 00:10:44,544 +gets suspended +from closing the MacBook lid. + +183 +00:10:44,544 --> 00:10:48,681 +Continuity Camera signals itself +to be automatically chosen + +184 +00:10:48,681 --> 00:10:51,584 +when the phone is placed +on a stationary stand + +185 +00:10:51,584 --> 00:10:54,787 +in landscape orientation, +the screen is off, + +186 +00:10:54,787 --> 00:10:57,757 +and either connected over USB +to the Mac + +187 +00:10:57,757 --> 00:11:00,994 +or within a close range +of the Mac. + +188 +00:11:00,994 --> 00:11:04,364 +In this scenario, +the user's intention is clear + +189 +00:11:04,364 --> 00:11:08,034 +that the device should be used +as Continuity Camera. + +190 +00:11:10,703 --> 00:11:13,606 +When adopting +systemPreferredCamera API, + +191 +00:11:13,606 --> 00:11:16,409 +you should always +key-value observe this property + +192 +00:11:16,409 --> 00:11:18,545 +and update your +AVCaptureSession's + +193 +00:11:18,545 --> 00:11:20,980 +video input device accordingly + +194 +00:11:20,980 --> 00:11:25,118 +to offer a magic +camera selection experience. + +195 +00:11:25,118 --> 00:11:27,820 +userPreferredCamera +and systemPreferredCamera + +196 +00:11:27,820 --> 00:11:31,591 +are already adopted +by first-party applications. + +197 +00:11:31,591 --> 00:11:35,295 +With more and more applications +adopting these APIs, + +198 +00:11:35,295 --> 00:11:37,564 +we will be able to provide +customers + +199 +00:11:37,564 --> 00:11:41,167 +a universal and consistent +method of camera selection + +200 +00:11:41,167 --> 00:11:43,870 +on Apple devices. + +201 +00:11:43,870 --> 00:11:46,039 +Let me show you a demo +to illustrate + +202 +00:11:46,039 --> 00:11:48,908 +how automatic switching +with Continuity Camera + +203 +00:11:48,908 --> 00:11:51,244 +looks like in FaceTime. + +204 +00:11:56,416 --> 00:11:57,684 +Here in FaceTime, + +205 +00:11:57,684 --> 00:12:00,753 +I'm in the +Automatic Camera Selection mode. + +206 +00:12:00,753 --> 00:12:02,689 +For applications +that want to offer both + +207 +00:12:02,689 --> 00:12:06,993 +manual and automatic behavior, +we recommend adding a new UI + +208 +00:12:06,993 --> 00:12:10,396 +for enabling and disabling +auto mode. + +209 +00:12:13,266 --> 00:12:16,936 +FaceTime is currently streaming +from the built-in camera. + +210 +00:12:16,936 --> 00:12:19,172 +When I pick up the phone +from the desk + +211 +00:12:19,172 --> 00:12:23,209 +and place it on a stand +behind the MacBook... + +212 +00:12:26,179 --> 00:12:30,183 +...FaceTime switches to stream +from the Continuity Camera + +213 +00:12:30,183 --> 00:12:32,118 +seamlessly. + +214 +00:12:32,118 --> 00:12:33,920 +That is where +the new class property + +215 +00:12:33,920 --> 00:12:36,122 +systemPreferredCamera +comes in: + +216 +00:12:36,122 --> 00:12:39,125 +the property value +changes to Continuity Camera + +217 +00:12:39,125 --> 00:12:43,630 +when the phone is in a position +ready to stream. + +218 +00:12:43,630 --> 00:12:47,233 +You might want to build your +application in a similar way. + +219 +00:12:47,233 --> 00:12:49,869 +Here's my recipe +for how to implement + +220 +00:12:49,869 --> 00:12:55,041 +Automatic Camera Selection +and manual selection mode. + +221 +00:12:55,041 --> 00:12:57,910 +When Automatic Camera Selection +is on, + +222 +00:12:57,910 --> 00:13:02,715 +start key-value observing the +systemPreferredCamera property. + +223 +00:13:02,715 --> 00:13:05,652 +Follow the systemPreferredCamera +whenever it changes + +224 +00:13:05,652 --> 00:13:09,022 +by updating your session's +input device. + +225 +00:13:09,022 --> 00:13:11,190 +In auto mode, +we highly recommend + +226 +00:13:11,190 --> 00:13:12,392 +still providing options + +227 +00:13:12,392 --> 00:13:15,962 +to let users pick a camera +by themselves. + +228 +00:13:15,962 --> 00:13:17,830 +When a different camera +gets picked, + +229 +00:13:17,830 --> 00:13:20,500 +set the userPreferredCamera +to that device, + +230 +00:13:20,500 --> 00:13:22,168 +which then gets reflected + +231 +00:13:22,168 --> 00:13:26,572 +in systemPreferredCamera +property value. + +232 +00:13:26,572 --> 00:13:29,208 +When Automatic Camera +Selection is off, + +233 +00:13:29,208 --> 00:13:33,813 +stop key-value observing the +systemPreferredCamera property. + +234 +00:13:33,813 --> 00:13:36,416 +Instead of following +systemPreferredCamera, + +235 +00:13:36,416 --> 00:13:39,252 +you will need to update +session's input device + +236 +00:13:39,252 --> 00:13:42,488 +with the user-picked camera +in manual mode. + +237 +00:13:42,488 --> 00:13:44,724 +But same as auto mode, +you still need to set + +238 +00:13:44,724 --> 00:13:46,559 +the userPreferredCamera +property + +239 +00:13:46,559 --> 00:13:49,562 +every time a user +picks a different camera, + +240 +00:13:49,562 --> 00:13:53,032 +so we maintain the user's +history of preferred cameras + +241 +00:13:53,032 --> 00:13:55,201 +and suggest the right camera +when getting back + +242 +00:13:55,201 --> 00:13:59,072 +to Automatic Camera Selection +mode. + +243 +00:13:59,072 --> 00:14:03,276 +For best practices on how to +incorporate userPreferredCamera + +244 +00:14:03,276 --> 00:14:06,279 +and systemPreferredCamera APIs, +please check out + +245 +00:14:06,279 --> 00:14:10,850 +the new sample app, +"Continuity Camera Sample." + +246 +00:14:10,850 --> 00:14:14,187 +Besides bringing a magical +webcam experience to the Mac, + +247 +00:14:14,187 --> 00:14:17,990 +Continuity Camera also presents +you with new opportunities + +248 +00:14:17,990 --> 00:14:21,794 +to harness the power of +iPhone-specific camera features + +249 +00:14:21,794 --> 00:14:23,629 +in your Mac app. + +250 +00:14:23,629 --> 00:14:27,934 +We've added a few +AVCapture APIs on macOS 13 + +251 +00:14:27,934 --> 00:14:30,103 +to help applications +better utilize + +252 +00:14:30,103 --> 00:14:33,206 +Continuity Camera devices. + +253 +00:14:33,206 --> 00:14:36,476 +We're bringing the amazing +quality of iPhone photo captures + +254 +00:14:36,476 --> 00:14:39,912 +to macOS, +thanks to Continuity Camera. + +255 +00:14:39,912 --> 00:14:44,050 +First off, we support capturing +high-resolution photos. + +256 +00:14:44,050 --> 00:14:47,653 +Previously, macOS has only +supported photo captures + +257 +00:14:47,653 --> 00:14:49,388 +at video resolution. + +258 +00:14:49,388 --> 00:14:52,425 +Starting with macOS 13, +you will be able to capture + +259 +00:14:52,425 --> 00:14:56,796 +up to 12 megapixel photos +with Continuity Camera. + +260 +00:14:56,796 --> 00:14:59,332 +This can be enabled +by first setting + +261 +00:14:59,332 --> 00:15:01,901 +highResolutionCaptureEnabled +to true + +262 +00:15:01,901 --> 00:15:05,037 +on AVCapturePhotoOutput +object before starting + +263 +00:15:05,037 --> 00:15:08,007 +a capture session, +and then setting the + +264 +00:15:08,007 --> 00:15:10,910 +highResolutionPhotoEnabled +property to true + +265 +00:15:10,910 --> 00:15:16,349 +on your photoSettings object +for each capture. + +266 +00:15:16,349 --> 00:15:19,318 +In addition to capturing +high-res photos, + +267 +00:15:19,318 --> 00:15:23,122 +Continuity Camera supports +controlling how photo quality + +268 +00:15:23,122 --> 00:15:25,591 +should be prioritized +against speed + +269 +00:15:25,591 --> 00:15:30,029 +by first setting the maximum +photo quality prioritization + +270 +00:15:30,029 --> 00:15:32,165 +on the photoOutput object, + +271 +00:15:32,165 --> 00:15:36,602 +then choosing the prioritization +for each capture by setting + +272 +00:15:36,602 --> 00:15:39,005 +photoQualityPrioritization +property + +273 +00:15:39,005 --> 00:15:44,110 +on the AVCapturePhotoSettings +object. + +274 +00:15:44,110 --> 00:15:47,280 +To learn more about choosing +the right prioritization + +275 +00:15:47,280 --> 00:15:49,515 +for your application, +please check out + +276 +00:15:49,515 --> 00:15:52,251 +"Capture high-quality photos +using video formats" + +277 +00:15:52,251 --> 00:15:58,157 +in WWDC2021. + +278 +00:15:58,157 --> 00:16:01,694 +Another photo-related feature +is flash capture. + +279 +00:16:01,694 --> 00:16:04,931 +You can now set flashMode +on your photoSettings object + +280 +00:16:04,931 --> 00:16:08,067 +to control whether flash +should be on, off, + +281 +00:16:08,067 --> 00:16:09,869 +or automatically chosen + +282 +00:16:09,869 --> 00:16:14,841 +based on the scene +and lighting conditions. + +283 +00:16:14,841 --> 00:16:17,410 +We are also making +AVCaptureMetadataOutput + +284 +00:16:17,410 --> 00:16:22,048 +available on macOS to allow +processing timed metadata + +285 +00:16:22,048 --> 00:16:25,184 +produced by a capture session. + +286 +00:16:25,184 --> 00:16:27,653 +You can now stream +face metadata objects + +287 +00:16:27,653 --> 00:16:32,091 +and human body metadata +objects from iPhone. + +288 +00:16:32,091 --> 00:16:34,160 +Let's go through +how to setup a session + +289 +00:16:34,160 --> 00:16:37,530 +to receive +face metadata objects. + +290 +00:16:37,530 --> 00:16:39,065 +After setting up the session + +291 +00:16:39,065 --> 00:16:41,467 +with proper video +input and output, + +292 +00:16:41,467 --> 00:16:44,971 +you will need to create +an AVCaptureMetadataOutput + +293 +00:16:44,971 --> 00:16:48,841 +and call addOutput +to add it to the session. + +294 +00:16:48,841 --> 00:16:51,711 +To receive face metadata +in particular, + +295 +00:16:51,711 --> 00:16:54,413 +set your object types array +on the output + +296 +00:16:54,413 --> 00:16:57,783 +to include the face object type. + +297 +00:16:57,783 --> 00:17:01,220 +Make sure the metadata types +requested are supported + +298 +00:17:01,220 --> 00:17:02,154 +by checking + +299 +00:17:02,154 --> 00:17:06,025 +availableMetadataObjectTypes +property. + +300 +00:17:06,025 --> 00:17:10,730 +Then setup the delegate +to receive metadata callbacks. + +301 +00:17:10,730 --> 00:17:12,565 +After the session +starts running, + +302 +00:17:12,565 --> 00:17:13,866 +you will get callbacks + +303 +00:17:13,866 --> 00:17:18,571 +with face metadata objects +produced in real time. + +304 +00:17:18,571 --> 00:17:22,608 +Besides AVCapturePhotoOutput +and AVCaptureMetadataOutput + +305 +00:17:22,608 --> 00:17:24,043 +we just talked about, + +306 +00:17:24,043 --> 00:17:27,713 +Continuity Camera also +supports video data output, + +307 +00:17:27,713 --> 00:17:33,319 +movie file output, +and AVCaptureVideoPreviewLayer. + +308 +00:17:33,319 --> 00:17:37,056 +Here's a list of video formats +supported by Continuity Camera + +309 +00:17:37,056 --> 00:17:38,691 +that you'll want to be aware of + +310 +00:17:38,691 --> 00:17:42,128 +when integrating this camera +into your application. + +311 +00:17:42,128 --> 00:17:45,164 +It supports three +16 by 9 formats -- + +312 +00:17:45,164 --> 00:17:48,935 +from 640 by 480 to 1080p -- + +313 +00:17:48,935 --> 00:17:53,639 +and one 4 by 3 format: +1920 by 1440. + +314 +00:17:53,639 --> 00:17:54,941 +You can choose between formats + +315 +00:17:54,941 --> 00:17:57,710 +supporting up to +30 frames per second + +316 +00:17:57,710 --> 00:18:01,180 +or 60 frames per second, +based on the need. + +317 +00:18:01,180 --> 00:18:05,284 +Another major addition +is Desk View device API. + +318 +00:18:05,284 --> 00:18:10,089 +Desk View camera is exposed +as a separate AVCaptureDevice. + +319 +00:18:10,089 --> 00:18:12,792 +There are two ways +you can find this device. + +320 +00:18:12,792 --> 00:18:14,460 +First one is by looking up + +321 +00:18:14,460 --> 00:18:17,129 +AVCaptureDeviceType +DeskViewCamera + +322 +00:18:17,129 --> 00:18:20,967 +in device discovery session. + +323 +00:18:20,967 --> 00:18:23,135 +Alternatively, +if you already know + +324 +00:18:23,135 --> 00:18:26,539 +the AVCaptureDevice object +of the main video camera, + +325 +00:18:26,539 --> 00:18:28,908 +you can use +the companionDeskViewCamera + +326 +00:18:28,908 --> 00:18:33,012 +property on that device +to access a Desk View device. + +327 +00:18:33,012 --> 00:18:35,915 +This API will be helpful +to pair main camera + +328 +00:18:35,915 --> 00:18:37,616 +and Desk View device +when there are + +329 +00:18:37,616 --> 00:18:42,054 +multiple Continuity Camera +devices around. + +330 +00:18:42,054 --> 00:18:44,457 +Once you have +the AVCaptureDevice object + +331 +00:18:44,457 --> 00:18:46,892 +of the desired +Desk View camera, + +332 +00:18:46,892 --> 00:18:50,896 +you can use it with an AVCapture +video data output, + +333 +00:18:50,896 --> 00:18:52,565 +movie file output, + +334 +00:18:52,565 --> 00:18:55,868 +or video preview layer +in the capture session + +335 +00:18:55,868 --> 00:19:00,673 +just as you can +with other camera devices. + +336 +00:19:00,673 --> 00:19:03,542 +Desk View device currently +supports one streaming format + +337 +00:19:03,542 --> 00:19:06,178 +in 420v pixel format. + +338 +00:19:06,178 --> 00:19:10,349 +The resolution of the format +is 1920 by 1440, + +339 +00:19:10,349 --> 00:19:14,153 +and the maximum frame rate +supported is 30 fps. + +340 +00:19:14,153 --> 00:19:16,422 +This is the end +of the session. + +341 +00:19:16,422 --> 00:19:18,491 +You learned about +Continuity Camera, + +342 +00:19:18,491 --> 00:19:22,161 +how to build a magical camera +selection on macOS, + +343 +00:19:22,161 --> 00:19:25,698 +and a handful of new APIs +to integrate Continuity Camera + +344 +00:19:25,698 --> 00:19:27,967 +in your Mac application. + +345 +00:19:27,967 --> 00:19:31,270 +I'm excited to see you +adopting all these APIs, + +346 +00:19:31,270 --> 00:19:34,206 +and have a great rest of WWDC. + +347 +00:19:34,206 --> 00:19:38,644 +♪ + diff --git a/eng/2022 Session 10019 Get to know Create ML Components en.srt b/eng/2022 Session 10019 Get to know Create ML Components en.srt new file mode 100644 index 0000000..678e258 --- /dev/null +++ b/eng/2022 Session 10019 Get to know Create ML Components en.srt @@ -0,0 +1,2439 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,276 +♪ + +3 +00:00:09,276 --> 00:00:11,011 +Hello, I'm Alejandro. + +4 +00:00:11,011 --> 00:00:13,247 +I'm an engineer +on the CreateML team. + +5 +00:00:13,247 --> 00:00:15,582 +Today I'm going to talk about +a brand-new API + +6 +00:00:15,582 --> 00:00:19,453 +for building machine learning +models using components. + +7 +00:00:19,453 --> 00:00:21,655 +Create ML +provides a simple API + +8 +00:00:21,655 --> 00:00:23,957 +for training +machine learning models. + +9 +00:00:23,957 --> 00:00:25,826 +It is based on +a set of supported tasks + +10 +00:00:25,826 --> 00:00:31,198 +like image classification, +sound classification, and so on. + +11 +00:00:31,198 --> 00:00:33,567 +At WWDC 2021, + +12 +00:00:33,567 --> 00:00:36,937 +we presented two great talks +on the Create ML framework. + +13 +00:00:36,937 --> 00:00:39,873 +Make sure to check those out +if you haven't. + +14 +00:00:39,873 --> 00:00:42,943 +But I want to talk about +going beyond predefined tasks. + +15 +00:00:42,943 --> 00:00:44,611 +What if you wanted +to customize a task + +16 +00:00:44,611 --> 00:00:48,115 +to your particular problem +beyond what Create ML offers? + +17 +00:00:48,115 --> 00:00:51,752 +Or what if you wanted to build +a different type of task? + +18 +00:00:51,752 --> 00:00:54,254 +Using components, +you can now compose tasks + +19 +00:00:54,254 --> 00:00:56,056 +in new and creative ways. + +20 +00:00:56,056 --> 00:00:58,325 +Let's dig in. + +21 +00:00:58,325 --> 00:01:00,427 +I'll start by breaking up +an ML task + +22 +00:01:00,427 --> 00:01:03,397 +and explaining what +each component does. + +23 +00:01:03,397 --> 00:01:07,234 +Then, I'll talk about how you +can piece components together. + +24 +00:01:07,234 --> 00:01:10,404 +Followed with an example +of a custom image task. + +25 +00:01:10,404 --> 00:01:13,507 +Then, I'll talk about +tabular tasks. + +26 +00:01:13,507 --> 00:01:16,610 +And I'll end with +deployment strategies. + +27 +00:01:16,610 --> 00:01:18,378 +Let me start by exploring +the insides + +28 +00:01:18,378 --> 00:01:20,781 +of a machine learning task +so that you understand + +29 +00:01:20,781 --> 00:01:23,216 +what goes into it +and how it works. + +30 +00:01:23,216 --> 00:01:25,652 +This way, when we start +building custom tasks, + +31 +00:01:25,652 --> 00:01:27,421 +you know what I'm talking about. + +32 +00:01:27,421 --> 00:01:31,158 +I'm going to use an image +classifier as an example. + +33 +00:01:31,158 --> 00:01:34,094 +An image classifier +uses a list of labeled images + +34 +00:01:34,094 --> 00:01:36,096 +to train a model. + +35 +00:01:36,096 --> 00:01:38,799 +In this example, +I have images of cats and dogs + +36 +00:01:38,799 --> 00:01:41,234 +with their respective labels. + +37 +00:01:41,234 --> 00:01:45,339 +But let's explore how images +are transformed at each step. + +38 +00:01:45,339 --> 00:01:48,208 +To do that, I'll expand +the image classification task + +39 +00:01:48,208 --> 00:01:50,711 +to see what's inside. + +40 +00:01:50,711 --> 00:01:53,280 +Conceptually, an image +classifier is very simple. + +41 +00:01:53,280 --> 00:01:56,650 +It consists of a feature +extractor and a classifier. + +42 +00:01:56,650 --> 00:01:59,987 +But the important part +is that Create ML components + +43 +00:01:59,987 --> 00:02:02,889 +gives you access to these +components independently. + +44 +00:02:02,889 --> 00:02:07,761 +You can add, remove, or switch +components to compose new tasks. + +45 +00:02:07,761 --> 00:02:10,664 +I'm going to represent +components as boxes. + +46 +00:02:10,664 --> 00:02:12,899 +Arrows represent +the flow of data. + +47 +00:02:12,899 --> 00:02:15,669 +Let's zoom into the first step +of the image classifier: + +48 +00:02:15,669 --> 00:02:18,271 +feature extraction. + +49 +00:02:18,271 --> 00:02:21,308 +Generally, feature extractors +reduce the dimensionality + +50 +00:02:21,308 --> 00:02:24,277 +of the input by keeping +only the interesting parts -- + +51 +00:02:24,277 --> 00:02:25,645 +the features. + +52 +00:02:25,645 --> 00:02:26,980 +In the case of images, + +53 +00:02:26,980 --> 00:02:31,018 +a feature extractor +looks for patterns in the image. + +54 +00:02:31,018 --> 00:02:33,687 +Create ML uses +Vision Feature Print, + +55 +00:02:33,687 --> 00:02:36,223 +which is an excellent +image-feature extractor + +56 +00:02:36,223 --> 00:02:39,459 +provided +by the Vision Framework. + +57 +00:02:39,459 --> 00:02:41,528 +Now, let's talk about +the second piece: + +58 +00:02:41,528 --> 00:02:42,929 +the classifier. + +59 +00:02:42,929 --> 00:02:45,198 +A classifier +uses a set of examples + +60 +00:02:45,198 --> 00:02:47,667 +to learn a classification. + +61 +00:02:47,667 --> 00:02:50,771 +Some common implementations +are logistic regression, + +62 +00:02:50,771 --> 00:02:54,241 +boosted trees, +and neural networks. + +63 +00:02:54,241 --> 00:02:57,711 +So training an image classifier +starts with annotated images, + +64 +00:02:57,711 --> 00:03:02,249 +goes to annotated features, +and ends with the classifier. + +65 +00:03:02,249 --> 00:03:05,118 +But why do we want +to break it into pieces? + +66 +00:03:05,118 --> 00:03:08,688 +The reason is we want +to expand the possibilities. + +67 +00:03:08,688 --> 00:03:10,657 +Maybe you want +to do some preprocessing + +68 +00:03:10,657 --> 00:03:12,859 +by increasing the contrast. + +69 +00:03:12,859 --> 00:03:15,062 +Or maybe you want +to normalize all images + +70 +00:03:15,062 --> 00:03:19,533 +so they have uniform brightness +before you extract features. + +71 +00:03:19,533 --> 00:03:22,803 +Or maybe you want to try +a different feature extractor. + +72 +00:03:22,803 --> 00:03:25,639 +Or maybe you want +to try a different classifier. + +73 +00:03:25,639 --> 00:03:27,974 +The possibilities are endless. + +74 +00:03:27,974 --> 00:03:30,644 +These are just +a few of the options. + +75 +00:03:30,644 --> 00:03:33,113 +That's why we've added +support for ML components + +76 +00:03:33,113 --> 00:03:37,717 +in macOS, iOS, iPadOS, and tvOS. + +77 +00:03:37,717 --> 00:03:40,053 +Our hope is that you can +compose new models + +78 +00:03:40,053 --> 00:03:41,721 +using some of the components +we provide + +79 +00:03:41,721 --> 00:03:43,457 +together with +your own components, + +80 +00:03:43,457 --> 00:03:46,226 +or even components built +by others in the community. + +81 +00:03:46,226 --> 00:03:49,729 +And you can leverage it +across all of our platforms. + +82 +00:03:49,729 --> 00:03:54,835 +Here are some of the components +built into Create ML Components. + +83 +00:03:54,835 --> 00:03:58,238 +But let me take a step back +and introduce some concepts. + +84 +00:03:58,238 --> 00:03:59,706 +There are two types +of components: + +85 +00:03:59,706 --> 00:04:02,175 +transformers and estimators. + +86 +00:04:02,175 --> 00:04:04,010 +A transformer is simply a type + +87 +00:04:04,010 --> 00:04:07,013 +that is able to perform +some transformation. + +88 +00:04:07,013 --> 00:04:10,350 +It defines an input type +and an output type. + +89 +00:04:10,350 --> 00:04:14,221 +For example, an image-feature +extractor takes an input image + +90 +00:04:14,221 --> 00:04:17,691 +and produces a shaped +array of features. + +91 +00:04:17,691 --> 00:04:21,628 +An estimator, on the other hand, +needs to learn from data. + +92 +00:04:21,628 --> 00:04:25,098 +It takes input examples, +does some processing, + +93 +00:04:25,098 --> 00:04:27,267 +and produces a transformer. + +94 +00:04:27,267 --> 00:04:30,370 +We call this process "fitting." + +95 +00:04:30,370 --> 00:04:33,140 +Great. With those concepts +out of the way, + +96 +00:04:33,140 --> 00:04:35,609 +let me talk about +how Create ML Components + +97 +00:04:35,609 --> 00:04:37,711 +lets you build +an image classifier + +98 +00:04:37,711 --> 00:04:41,181 +from its individual components +using composition. + +99 +00:04:41,181 --> 00:04:44,084 +This is an image classifier +using components. + +100 +00:04:44,084 --> 00:04:46,953 +It has ImageFeaturePrint +as the feature extractor + +101 +00:04:46,953 --> 00:04:50,290 +and LogisticRegressionClassifier +as the classifier. + +102 +00:04:50,290 --> 00:04:52,159 +Regardless of +whether a component + +103 +00:04:52,159 --> 00:04:54,427 +is a transformer +or an estimator, + +104 +00:04:54,427 --> 00:04:58,999 +you combine them using +the appending method. + +105 +00:04:58,999 --> 00:05:02,502 +And this is where components +provide unlimited possibilities. + +106 +00:05:02,502 --> 00:05:05,272 +You can use a fully connected +neural network as a classifier + +107 +00:05:05,272 --> 00:05:09,442 +instead of logistic regression +with a simple change. + +108 +00:05:09,442 --> 00:05:13,413 +Or you can use a custom feature +extractor in a CoreML model. + +109 +00:05:13,413 --> 00:05:16,449 +For example, +the headless ResNet-50 model + +110 +00:05:16,449 --> 00:05:19,786 +you can find +in the model gallery. + +111 +00:05:19,786 --> 00:05:21,688 +When composing two components, + +112 +00:05:21,688 --> 00:05:23,390 +the output +of the first component + +113 +00:05:23,390 --> 00:05:25,759 +must match +the input of the second. + +114 +00:05:25,759 --> 00:05:27,994 +In the case +of our image classifier, + +115 +00:05:27,994 --> 00:05:30,830 +the output of the feature +extractor is a shaped array, + +116 +00:05:30,830 --> 00:05:32,666 +from the CoreML framework. + +117 +00:05:32,666 --> 00:05:36,503 +Which is also the input of a +logistic regression classifier. + +118 +00:05:36,503 --> 00:05:39,739 +If you get a compiler error +when using the appending method, + +119 +00:05:39,739 --> 00:05:41,908 +this is the first thing +to check. + +120 +00:05:41,908 --> 00:05:44,444 +Make sure the types match. + +121 +00:05:44,444 --> 00:05:48,415 +But let me clarify an important +point around fitting. + +122 +00:05:48,415 --> 00:05:50,550 +I said before that fitting +is the process + +123 +00:05:50,550 --> 00:05:53,486 +of going from an estimator +to a transformer. + +124 +00:05:53,486 --> 00:05:55,088 +Let's look at this +from the perspective + +125 +00:05:55,088 --> 00:05:57,524 +of a composed estimator. + +126 +00:05:57,524 --> 00:05:58,825 +When your composed estimator + +127 +00:05:58,825 --> 00:06:01,061 +has both transformers +and estimators, + +128 +00:06:01,061 --> 00:06:03,330 +like in the case +of the image classifier, + +129 +00:06:03,330 --> 00:06:05,932 +only the estimator pieces +are fitted. + +130 +00:06:05,932 --> 00:06:09,002 +But the transformers are +an important part of the process + +131 +00:06:09,002 --> 00:06:11,271 +because they are used +to feed the correct features + +132 +00:06:11,271 --> 00:06:14,307 +to the estimator's +fitted method. + +133 +00:06:14,307 --> 00:06:15,508 +Here is the code. + +134 +00:06:15,508 --> 00:06:16,876 +The image classifier + +135 +00:06:16,876 --> 00:06:19,446 +needs a collection +of annotated features + +136 +00:06:19,446 --> 00:06:24,251 +where the features are images +and the annotations are strings. + +137 +00:06:24,251 --> 00:06:25,885 +We'll talk about +loading the features + +138 +00:06:25,885 --> 00:06:28,955 +when we go into the demo. + +139 +00:06:28,955 --> 00:06:32,325 +Once I have the data, +I can call the fitted method. + +140 +00:06:32,325 --> 00:06:37,264 +This returns the trained model, +a transformer. + +141 +00:06:37,264 --> 00:06:39,933 +And it's important to note +that the types used + +142 +00:06:39,933 --> 00:06:42,269 +when fitting are related +but different + +143 +00:06:42,269 --> 00:06:44,971 +from the types +of the resulting transformer. + +144 +00:06:44,971 --> 00:06:47,774 +In particular, the types used +in the fitted method + +145 +00:06:47,774 --> 00:06:49,643 +are always collections. + +146 +00:06:49,643 --> 00:06:52,045 +And in the case +of supervised estimators, + +147 +00:06:52,045 --> 00:06:55,415 +the features must include +the annotations. + +148 +00:06:55,415 --> 00:06:58,485 +Create ML Components +uses the AnnotatedFeature type + +149 +00:06:58,485 --> 00:07:03,056 +to represent a feature +along with its annotation. + +150 +00:07:03,056 --> 00:07:06,226 +Once I have the model, +I can do predictions. + +151 +00:07:06,226 --> 00:07:08,461 +It doesn't matter +if it's a model I just fitted, + +152 +00:07:08,461 --> 00:07:11,765 +or if I'm loading +the parameters from a disk. + +153 +00:07:11,765 --> 00:07:15,702 +The API is the same +in both cases. + +154 +00:07:15,702 --> 00:07:17,570 +Since I am training +a classifier, + +155 +00:07:17,570 --> 00:07:20,874 +the result is +a classification distribution. + +156 +00:07:20,874 --> 00:07:25,312 +The distribution includes +a probability for each label. + +157 +00:07:25,312 --> 00:07:27,681 +In this case, I'm just printing +the most likely label + +158 +00:07:27,681 --> 00:07:30,817 +for the image. + +159 +00:07:30,817 --> 00:07:33,320 +The fitted method +also provides a mechanism + +160 +00:07:33,320 --> 00:07:37,457 +to observe training events, +including validation metrics. + +161 +00:07:37,457 --> 00:07:39,826 +In this example, +I'm passing validation data + +162 +00:07:39,826 --> 00:07:43,096 +and printing +the validation accuracy. + +163 +00:07:43,096 --> 00:07:44,798 +Note that only +supervised estimators + +164 +00:07:44,798 --> 00:07:48,401 +provide validation metrics. + +165 +00:07:48,401 --> 00:07:50,003 +Once you train a model, + +166 +00:07:50,003 --> 00:07:52,372 +you can save +the learned parameters, + +167 +00:07:52,372 --> 00:07:56,009 +either to reuse later +or to deploy to an app. + +168 +00:07:56,009 --> 00:07:58,545 +You do this using +the write method. + +169 +00:07:58,545 --> 00:08:02,248 +Later, you can read +using the read method. + +170 +00:08:02,248 --> 00:08:04,184 +And that's composition. + +171 +00:08:04,184 --> 00:08:06,653 +This is where +it gets interesting. + +172 +00:08:06,653 --> 00:08:08,755 +Let me talk about +writing a new task, + +173 +00:08:08,755 --> 00:08:11,758 +something that Create ML +didn't support until now. + +174 +00:08:13,727 --> 00:08:17,597 +What if you wanted to train +a model to score images? + +175 +00:08:17,597 --> 00:08:19,632 +Let's say you have +photos of fruit, + +176 +00:08:19,632 --> 00:08:21,601 +but instead of +classifying the fruit, + +177 +00:08:21,601 --> 00:08:23,670 +you wanted to rate it. + +178 +00:08:23,670 --> 00:08:26,606 +Give it a score +based on how ripe it is. + +179 +00:08:26,606 --> 00:08:28,808 +To do this, +you need to do regression + +180 +00:08:28,808 --> 00:08:30,944 +instead of classification. + +181 +00:08:30,944 --> 00:08:33,046 +So let me write +an image regressor + +182 +00:08:33,046 --> 00:08:37,550 +that gives a score to images +of bananas based on ripeness. + +183 +00:08:37,550 --> 00:08:43,089 +I'll give each image a ripeness +value between one and 10. + +184 +00:08:43,089 --> 00:08:47,594 +An image regressor is very +similar to an image classifier. + +185 +00:08:47,594 --> 00:08:49,696 +The only difference +is that our estimator + +186 +00:08:49,696 --> 00:08:54,167 +is going to be a regressor +instead of a classifier. + +187 +00:08:54,167 --> 00:08:55,869 +As you may have +already guessed, + +188 +00:08:55,869 --> 00:08:58,238 +this is going to be easy. + +189 +00:08:58,238 --> 00:09:01,875 +To refresh your memory, +here is our image classifier. + +190 +00:09:01,875 --> 00:09:04,411 +And this is an image regressor. + +191 +00:09:04,411 --> 00:09:07,180 +I substituted the logistic +regression classifier + +192 +00:09:07,180 --> 00:09:09,582 +with a linear regressor. + +193 +00:09:09,582 --> 00:09:12,786 +This simple change +also changes the expected input + +194 +00:09:12,786 --> 00:09:15,121 +to the fitted method. + +195 +00:09:15,121 --> 00:09:17,791 +Before, it expected +images and labels. + +196 +00:09:17,791 --> 00:09:20,960 +Now, it expects +images and scores. + +197 +00:09:20,960 --> 00:09:22,429 +But enough about concepts. + +198 +00:09:22,429 --> 00:09:25,899 +Let me demo this +with some actual code. + +199 +00:09:28,501 --> 00:09:31,538 +Let me show you how to write +a custom image regressor. + +200 +00:09:31,538 --> 00:09:33,640 +I'll start by defining +an ImageRegressor struct + +201 +00:09:33,640 --> 00:09:36,443 +to encapsulate the code. + +202 +00:09:38,445 --> 00:09:40,547 +I have a folder +with images of bananas + +203 +00:09:40,547 --> 00:09:42,715 +at different levels of ripeness. + +204 +00:09:42,715 --> 00:09:46,119 +I'm going to start +by defining that URL. + +205 +00:09:48,121 --> 00:09:51,124 +The next step +is to add a train method. + +206 +00:09:51,124 --> 00:09:52,292 +This is where you use + +207 +00:09:52,292 --> 00:09:56,229 +training data +to produce a model. + +208 +00:09:56,229 --> 00:09:59,265 +I'm going to use the "some" +keyword on the return type + +209 +00:09:59,265 --> 00:10:00,967 +so that the return type +doesn't change + +210 +00:10:00,967 --> 00:10:05,138 +as I add or modify steps +in the composed estimator. + +211 +00:10:05,138 --> 00:10:07,340 +Now, I'm going to define +the estimator. + +212 +00:10:07,340 --> 00:10:09,042 +It's simply +the feature extractor + +213 +00:10:09,042 --> 00:10:14,047 +with the linear regressor +appended. + +214 +00:10:14,047 --> 00:10:16,316 +And now, I need to load +the training images + +215 +00:10:16,316 --> 00:10:17,584 +with their score. + +216 +00:10:17,584 --> 00:10:19,819 +I can use AnnotatedFiles, +which is a collection + +217 +00:10:19,819 --> 00:10:24,157 +of AnnotatedFeatures containing +URLs and string labels. + +218 +00:10:24,157 --> 00:10:29,729 +It provides a convenience +initializer that fits my needs. + +219 +00:10:29,729 --> 00:10:32,599 +My files consist of a name, +followed by a dash, + +220 +00:10:32,599 --> 00:10:34,200 +followed by the ripeness value. + +221 +00:10:34,200 --> 00:10:37,604 +So I'm going to specify +that the separator is a dash + +222 +00:10:37,604 --> 00:10:39,305 +and the annotation +is at index: 1 + +223 +00:10:39,305 --> 00:10:41,307 +of the filename components. + +224 +00:10:41,307 --> 00:10:42,542 +I'm also going to request + +225 +00:10:42,542 --> 00:10:46,145 +only image files +by using the type argument. + +226 +00:10:46,145 --> 00:10:49,682 +Now that I have URLs, +I need to load the images. + +227 +00:10:49,682 --> 00:10:51,384 +I can use +the mapFeatures method + +228 +00:10:51,384 --> 00:10:56,689 +and the ImageReader to do this. + +229 +00:10:56,689 --> 00:10:58,725 +I also need +to convert the scores + +230 +00:10:58,725 --> 00:11:01,661 +from strings +to floating point values. + +231 +00:11:01,661 --> 00:11:05,598 +I can use the mapAnnotations +method to do this. + +232 +00:11:08,935 --> 00:11:12,005 +And with that, +I have the training data. + +233 +00:11:12,005 --> 00:11:15,174 +But I want to put some of it +aside for validation. + +234 +00:11:15,174 --> 00:11:17,911 +I can use the randomSplit +method to do this. + +235 +00:11:17,911 --> 00:11:19,445 +I'll keep 80 percent +for training + +236 +00:11:19,445 --> 00:11:25,218 +and use the rest for validation. + +237 +00:11:25,218 --> 00:11:27,220 +Now, I'm ready to fit. + +238 +00:11:29,956 --> 00:11:32,025 +And I'm going to save +the trained parameters + +239 +00:11:32,025 --> 00:11:33,826 +so that I can deploy to my app. + +240 +00:11:33,826 --> 00:11:36,129 +I'll choose a location +to save to. + +241 +00:11:40,033 --> 00:11:42,235 +And I'll call the write method. + +242 +00:11:44,671 --> 00:11:46,940 +Finally, +I'll return the transformer. + +243 +00:11:50,944 --> 00:11:53,513 +This is the essence +of defining and training a model + +244 +00:11:53,513 --> 00:11:55,248 +using components. + +245 +00:11:55,248 --> 00:11:57,650 +I defined +my composed estimator, + +246 +00:11:57,650 --> 00:12:00,920 +I loaded my training data, +I called the fitted method, + +247 +00:12:00,920 --> 00:12:03,256 +and I used write +to save the parameters. + +248 +00:12:03,256 --> 00:12:05,592 +But there are some things +I can improve. + +249 +00:12:05,592 --> 00:12:08,561 +For starters, I am passing +a validation data set + +250 +00:12:08,561 --> 00:12:11,230 +but not observing +the validation error, + +251 +00:12:11,230 --> 00:12:12,932 +so I'll do that. + +252 +00:12:12,932 --> 00:12:15,134 +The fitted method +takes an event handler + +253 +00:12:15,134 --> 00:12:17,236 +that you can use +to gather metrics. + +254 +00:12:21,374 --> 00:12:23,776 +For now, I'll just print +both the training + +255 +00:12:23,776 --> 00:12:26,913 +and validation +maximum-error values. + +256 +00:12:26,913 --> 00:12:30,750 +I also want the mean absolute +error for the final model. + +257 +00:12:33,987 --> 00:12:36,356 +I compute that by applying +the fitted transformer + +258 +00:12:36,356 --> 00:12:37,991 +to the validation features + +259 +00:12:37,991 --> 00:12:40,593 +and then passing that along +with the actual scores + +260 +00:12:40,593 --> 00:12:44,564 +to the meanAbsoluteError +function. + +261 +00:12:44,564 --> 00:12:46,799 +I ran this but I didn't get +a great model - + +262 +00:12:46,799 --> 00:12:49,135 +the error was high. + +263 +00:12:49,135 --> 00:12:52,205 +This is because I don't have +that many images of bananas. + +264 +00:12:52,205 --> 00:12:54,807 +I should get more images, +but before I do that, + +265 +00:12:54,807 --> 00:12:57,243 +I can try augmenting my dataset. + +266 +00:12:57,243 --> 00:13:00,513 +I can rotate and scale my images +to get more examples. + +267 +00:13:00,513 --> 00:13:02,482 +To do this, I'm going to write +a new method + +268 +00:13:02,482 --> 00:13:05,585 +that takes an annotated image +and augments it. + +269 +00:13:05,585 --> 00:13:08,154 +It returns an array +of annotated images. + +270 +00:13:13,826 --> 00:13:17,664 +The first augmentation +I'm going to do is rotation. + +271 +00:13:20,800 --> 00:13:23,703 +I'll randomly choose an angle +between -pi and pi + +272 +00:13:23,703 --> 00:13:26,072 +and use it to rotate the image. + +273 +00:13:26,072 --> 00:13:28,541 +I'll also do a random scale. + +274 +00:13:31,144 --> 00:13:32,712 +And I'll return three images: + +275 +00:13:32,712 --> 00:13:36,082 +the original, the rotated one, +and the scaled one. + +276 +00:13:39,118 --> 00:13:40,687 +Now that I have +my augment function, + +277 +00:13:40,687 --> 00:13:44,691 +I'll use it to augment my +training images using flatMap. + +278 +00:13:49,429 --> 00:13:53,366 +Each element of my dataset +will be converted to an array. + +279 +00:13:53,366 --> 00:13:56,936 +FlatMap flattens that array +of arrays into a single array, + +280 +00:13:56,936 --> 00:13:59,605 +which is what I need +for the fitted method. + +281 +00:13:59,605 --> 00:14:02,408 +Note that augmentations +only apply when fitting, + +282 +00:14:02,408 --> 00:14:04,944 +not when doing predictions. + +283 +00:14:04,944 --> 00:14:07,480 +OK, this increased my accuracy. + +284 +00:14:07,480 --> 00:14:09,382 +But let me talk about +one more improvement + +285 +00:14:09,382 --> 00:14:12,218 +that is going to make +my model even better. + +286 +00:14:12,218 --> 00:14:13,619 +I want to use +the Vision framework + +287 +00:14:13,619 --> 00:14:16,923 +to crop the images +to the salient object. + +288 +00:14:16,923 --> 00:14:19,325 +This is one of the images +in my training data. + +289 +00:14:19,325 --> 00:14:23,096 +Someone is holding bananas with +other fruits in the background. + +290 +00:14:23,096 --> 00:14:27,300 +The model may get confused by +the other objects in the photo. + +291 +00:14:27,300 --> 00:14:29,268 +Using the Vision framework API, + +292 +00:14:29,268 --> 00:14:31,003 +I can automatically +crop the image + +293 +00:14:31,003 --> 00:14:33,573 +to the most salient object. + +294 +00:14:33,573 --> 00:14:38,745 +To do this, please check out +the Vision talk from WWDC 2019. + +295 +00:14:38,745 --> 00:14:41,948 +I can easily apply this +transformation to all my images, + +296 +00:14:41,948 --> 00:14:44,183 +both when fitting +and when getting predictions + +297 +00:14:44,183 --> 00:14:46,419 +if I write a custom transformer. + +298 +00:14:46,419 --> 00:14:47,987 +Let me show you how. + +299 +00:14:47,987 --> 00:14:49,422 +The only thing I need to do + +300 +00:14:49,422 --> 00:14:51,424 +to conform to +a transformer protocol + +301 +00:14:51,424 --> 00:14:53,659 +is implement the applied method. + +302 +00:14:53,659 --> 00:14:55,895 +And in this case, +I want it to take an image + +303 +00:14:55,895 --> 00:14:57,697 +and return an image. + +304 +00:14:57,697 --> 00:14:59,265 +I'm not going to go +into this code, + +305 +00:14:59,265 --> 00:15:02,235 +except to say that if I don't +get a salient object, + +306 +00:15:02,235 --> 00:15:06,305 +I'll just return +the original image. + +307 +00:15:06,305 --> 00:15:08,141 +Now that I have +my custom transformer, + +308 +00:15:08,141 --> 00:15:10,076 +I'll add it +to my image regressor. + +309 +00:15:16,415 --> 00:15:18,217 +I just need to use +my custom transformer + +310 +00:15:18,217 --> 00:15:19,986 +before feature extraction. + +311 +00:15:28,694 --> 00:15:31,430 +Now that saliency is part of +my task definition, + +312 +00:15:31,430 --> 00:15:34,133 +it will be used to crop +every training image, + +313 +00:15:34,133 --> 00:15:37,069 +and it will also be used +when doing inference. + +314 +00:15:37,069 --> 00:15:39,705 +This is one of the advantages +of sharing the task definition + +315 +00:15:39,705 --> 00:15:42,141 +between training and inference. + +316 +00:15:42,141 --> 00:15:44,377 +Before we go on +to the next task, + +317 +00:15:44,377 --> 00:15:46,813 +let me highlight +some important points. + +318 +00:15:46,813 --> 00:15:50,650 +Using components, +I can now create custom tasks. + +319 +00:15:50,650 --> 00:15:53,452 +I did this by using +the appending method. + +320 +00:15:53,452 --> 00:15:55,955 +I used AnnotatedFiles +to load my files + +321 +00:15:55,955 --> 00:15:58,124 +with annotated file names, + +322 +00:15:58,124 --> 00:16:01,427 +but you can also load files +annotated by directories. + +323 +00:16:01,427 --> 00:16:04,664 +I mapped the URL to images +using ImageReader + +324 +00:16:04,664 --> 00:16:08,601 +and mapped the annotations +from strings to values. + +325 +00:16:08,601 --> 00:16:12,238 +I used randomSplit to set aside +a validation dataset, + +326 +00:16:12,238 --> 00:16:15,408 +and I saved the trained +parameters for use later. + +327 +00:16:15,408 --> 00:16:18,277 +Then I added augmentations +and defined a custom transformer + +328 +00:16:18,277 --> 00:16:20,313 +to improve my model. + +329 +00:16:20,313 --> 00:16:23,249 +But this works for more +than just images. + +330 +00:16:23,249 --> 00:16:26,319 +I'll switch gears and talk about +another type of task: + +331 +00:16:26,319 --> 00:16:28,187 +tabular tasks. + +332 +00:16:28,187 --> 00:16:30,957 +These are tasks +that use tabular data. + +333 +00:16:30,957 --> 00:16:33,492 +Tabular data is characterized +by having multiple features + +334 +00:16:33,492 --> 00:16:35,294 +of different types. + +335 +00:16:35,294 --> 00:16:37,129 +It can include +both numerical data + +336 +00:16:37,129 --> 00:16:39,332 +as well as categorical data. + +337 +00:16:39,332 --> 00:16:42,401 +A popular example is +house-pricing data. + +338 +00:16:42,401 --> 00:16:44,904 +You have things +like area and age, + +339 +00:16:44,904 --> 00:16:46,505 +but also things +like neighborhood, + +340 +00:16:46,505 --> 00:16:48,741 +type of building, et cetera. + +341 +00:16:48,741 --> 00:16:51,110 +And you want to learn +to predict a value; + +342 +00:16:51,110 --> 00:16:53,779 +for example, the sale price. + +343 +00:16:53,779 --> 00:16:58,351 +In 2021, we introduced +the TabularData framework. + +344 +00:16:58,351 --> 00:17:00,152 +Now you can use +the TabularData framework + +345 +00:17:00,152 --> 00:17:02,355 +together with +Create ML Components + +346 +00:17:02,355 --> 00:17:06,926 +to build and train tabular +classifiers and regressors. + +347 +00:17:06,926 --> 00:17:09,695 +I also recommend the tech talk +on TabularData. + +348 +00:17:09,695 --> 00:17:12,231 +It's a great introduction +to data exploration, + +349 +00:17:12,231 --> 00:17:15,268 +which you will likely need +when building a tabular task. + +350 +00:17:15,268 --> 00:17:17,970 +Let's dive in. + +351 +00:17:17,970 --> 00:17:21,407 +When dealing with tabular data, +each column of the table + +352 +00:17:21,407 --> 00:17:23,876 +will have a different +type of feature. + +353 +00:17:23,876 --> 00:17:26,412 +And you may want to process +each column differently, + +354 +00:17:26,412 --> 00:17:28,447 +based on what type of +information it contains; + +355 +00:17:28,447 --> 00:17:30,149 +the distribution, +range of values, + +356 +00:17:30,149 --> 00:17:32,351 +and other factors. + +357 +00:17:32,351 --> 00:17:36,322 +Create ML Components lets you do +this using the ColumnSelector. + +358 +00:17:36,322 --> 00:17:38,491 +Here is an example. + +359 +00:17:38,491 --> 00:17:42,028 +I mentioned house prices, +but those are ridiculous. + +360 +00:17:42,028 --> 00:17:44,764 +I'm going to use +avocado prices instead. + +361 +00:17:44,764 --> 00:17:47,266 +I have this table +of avocado prices. + +362 +00:17:47,266 --> 00:17:49,235 +I want to build +a tabular regressor + +363 +00:17:49,235 --> 00:17:52,104 +to predict avocado prices +based on this. + +364 +00:17:52,104 --> 00:17:54,240 +It contains columns +with numeric data + +365 +00:17:54,240 --> 00:17:56,642 +such as bags, year, and volume + +366 +00:17:56,642 --> 00:18:01,714 +and columns with categorical +data such as type and region. + +367 +00:18:01,714 --> 00:18:03,449 +Some regressors +benefit from having + +368 +00:18:03,449 --> 00:18:06,619 +a better representation +of these values. + +369 +00:18:06,619 --> 00:18:08,087 +For instance, + +370 +00:18:08,087 --> 00:18:11,657 +this is the distribution of +volume values in the dataset. + +371 +00:18:11,657 --> 00:18:14,093 +It is close to +a normal distribution, + +372 +00:18:14,093 --> 00:18:17,697 +but with large values +centered around 15,000. + +373 +00:18:17,697 --> 00:18:20,232 +I think this is +a great example of a dataset + +374 +00:18:20,232 --> 00:18:23,035 +that could benefit +from normalization. + +375 +00:18:23,035 --> 00:18:27,273 +So the first thing I want to do +is normalize these values. + +376 +00:18:27,273 --> 00:18:30,977 +To do this, I can pass the +column names I want to normalize + +377 +00:18:30,977 --> 00:18:35,081 +to the ColumnSelector +and then use a standard scaler. + +378 +00:18:35,081 --> 00:18:37,083 +Here is the code. + +379 +00:18:37,083 --> 00:18:39,518 +First I create +a column selector. + +380 +00:18:39,518 --> 00:18:42,788 +Then I pass the column names +I want to scale. + +381 +00:18:42,788 --> 00:18:45,491 +All columns must contain +the same type of element; + +382 +00:18:45,491 --> 00:18:47,760 +in this case, Double. + +383 +00:18:47,760 --> 00:18:50,129 +Then I unwrap optionals. + +384 +00:18:50,129 --> 00:18:53,132 +I can do this because I know +there are no missing values. + +385 +00:18:53,132 --> 00:18:56,902 +But I could also use an imputer +which replaces missing values. + +386 +00:18:56,902 --> 00:18:58,804 +And then I append +the StandardScaler + +387 +00:18:58,804 --> 00:19:01,307 +to the unwrapper. + +388 +00:19:01,307 --> 00:19:02,675 +So I started with this table + +389 +00:19:02,675 --> 00:19:05,611 +where bags numbers +were in the tens of thousands + +390 +00:19:05,611 --> 00:19:08,414 +and volumes were in +the hundreds of thousands. + +391 +00:19:08,414 --> 00:19:10,082 +And after scaling those columns, + +392 +00:19:10,082 --> 00:19:13,185 +I end up with values that now +have a magnitude close to one, + +393 +00:19:13,185 --> 00:19:16,722 +which could improve +the performance of my model. + +394 +00:19:16,722 --> 00:19:20,426 +To be more specific, my values +now have a mean of zero + +395 +00:19:20,426 --> 00:19:24,063 +and a standard deviation of one. + +396 +00:19:24,063 --> 00:19:27,066 +Here is a similar example, +but in this example, + +397 +00:19:27,066 --> 00:19:29,602 +I'm selecting the type +and region columns, + +398 +00:19:29,602 --> 00:19:34,073 +which are of type string and +performing a one-hot encoding. + +399 +00:19:34,073 --> 00:19:37,376 +One-hot encoding refers +to encoding categorical data + +400 +00:19:37,376 --> 00:19:42,148 +using an array to indicate +the presence of each category. + +401 +00:19:42,148 --> 00:19:44,450 +In this example, +I have three categories: + +402 +00:19:44,450 --> 00:19:47,586 +Bronze, Silver, and Gold. + +403 +00:19:47,586 --> 00:19:50,156 +Each gets a unique position +within the array, + +404 +00:19:50,156 --> 00:19:54,193 +indicated by a one +in that position. + +405 +00:19:54,193 --> 00:19:57,329 +An alternative is to use +an ordinal encoder, + +406 +00:19:57,329 --> 00:20:01,033 +which gives a consecutive +number to each category. + +407 +00:20:01,033 --> 00:20:04,203 +Use a one-hot encoder when +there are only a few categories + +408 +00:20:04,203 --> 00:20:07,940 +and an ordinal encoder +otherwise. + +409 +00:20:07,940 --> 00:20:13,446 +Now let me put all this together +and build a tabular regressor. + +410 +00:20:17,216 --> 00:20:19,452 +As before, +I'll start creating a struct + +411 +00:20:19,452 --> 00:20:23,956 +and defining the data URL +and the parameters URL. + +412 +00:20:25,791 --> 00:20:27,760 +I also want to define +a column ID + +413 +00:20:27,760 --> 00:20:30,362 +for the column +I want to predict: price. + +414 +00:20:32,965 --> 00:20:35,701 +I'll define my task separately +so that I can use it + +415 +00:20:35,701 --> 00:20:38,537 +both from the train method +and the predict method. + +416 +00:20:41,207 --> 00:20:44,009 +As I mentioned, I'm going +to normalize the volume. + +417 +00:20:46,779 --> 00:20:49,048 +Then I'm going to use +a boosted tree regressor + +418 +00:20:49,048 --> 00:20:53,152 +to predict the price. + +419 +00:20:53,152 --> 00:20:55,187 +It takes the name +of the annotation column -- + +420 +00:20:55,187 --> 00:20:58,023 +which is also the column +of the resulting predictions -- + +421 +00:20:58,023 --> 00:21:01,760 +and it takes the names +of all three feature columns. + +422 +00:21:01,760 --> 00:21:03,929 +I'll start with +these three columns. + +423 +00:21:03,929 --> 00:21:07,366 +Then I'll combine the pieces +using the appending method + +424 +00:21:07,366 --> 00:21:08,834 +and return the task. + +425 +00:21:13,539 --> 00:21:15,474 +Now that I have +my task definition, + +426 +00:21:15,474 --> 00:21:17,643 +I'll add a train method +as before. + +427 +00:21:20,546 --> 00:21:23,082 +And as before, I want to make +sure that the return type + +428 +00:21:23,082 --> 00:21:26,452 +doesn't depend +the specifics of my model. + +429 +00:21:26,452 --> 00:21:32,391 +The first step is to load +the CSV file into a data frame. + +430 +00:21:32,391 --> 00:21:35,027 +I'm using the TabularData +framework to do this. + +431 +00:21:35,027 --> 00:21:37,363 +And as before, I want to split +off some of the data + +432 +00:21:37,363 --> 00:21:39,398 +for validation. + +433 +00:21:43,702 --> 00:21:46,038 +I'll pass the training +and validation datasets + +434 +00:21:46,038 --> 00:21:47,606 +to the fitted method. + +435 +00:21:50,509 --> 00:21:53,512 +I'll also report validation +error as before, + +436 +00:21:53,512 --> 00:21:55,881 +and I'll save the trained +parameters for use later. + +437 +00:21:59,552 --> 00:22:01,754 +Finally, I'll return +the transformer. + +438 +00:22:04,890 --> 00:22:06,625 +Once I have +a trained transformer, + +439 +00:22:06,625 --> 00:22:09,762 +I can use it to make price +predictions on data frames. + +440 +00:22:09,762 --> 00:22:13,966 +I'm going to write +a predict method to do this. + +441 +00:22:17,403 --> 00:22:19,738 +I'll start by loading the model +from the task definition + +442 +00:22:19,738 --> 00:22:22,107 +and the parameters URL. + +443 +00:22:24,977 --> 00:22:27,580 +I need to make sure the data +frame I use for predictions + +444 +00:22:27,580 --> 00:22:30,883 +has the columns +I used as features: + +445 +00:22:30,883 --> 00:22:35,154 +type, region, and volume. + +446 +00:22:35,154 --> 00:22:38,324 +The predicted value +will be in the price column. + +447 +00:22:38,324 --> 00:22:40,659 +I'll use the column ID +I defined at the top. + +448 +00:22:44,563 --> 00:22:46,699 +And that concludes +my tabular regressor. + +449 +00:22:46,699 --> 00:22:48,934 +I have a train method, +that I only need to call once + +450 +00:22:48,934 --> 00:22:50,369 +to produce my trained +parameters, + +451 +00:22:50,369 --> 00:22:52,605 +and a predict method +that returns the avocado price, + +452 +00:22:52,605 --> 00:22:54,573 +predictions based on the type, + +453 +00:22:54,573 --> 00:22:56,976 +region, and the +volume of avocados. + +454 +00:22:56,976 --> 00:22:59,345 +That's all I need +to use this in my app. + +455 +00:22:59,345 --> 00:23:01,013 +Here are some things +to keep in mind + +456 +00:23:01,013 --> 00:23:03,349 +when working on tabular tasks. + +457 +00:23:03,349 --> 00:23:04,984 +You can use +ColumnSelector operations + +458 +00:23:04,984 --> 00:23:07,286 +to process specific columns. + +459 +00:23:07,286 --> 00:23:10,256 +It's worth noting that +tree classifiers and regressors + +460 +00:23:10,256 --> 00:23:13,959 +are all tabular, but you can +also use a nontabular estimator, + +461 +00:23:13,959 --> 00:23:15,527 +such as a linear regressor, + +462 +00:23:15,527 --> 00:23:19,365 +in a tabular task using +AnnotatedFeatureProvider. + +463 +00:23:19,365 --> 00:23:22,167 +Please refer +to the documentation. + +464 +00:23:22,167 --> 00:23:23,469 +When doing predictions, + +465 +00:23:23,469 --> 00:23:26,038 +build a data frame +with the required columns, + +466 +00:23:26,038 --> 00:23:29,141 +making sure to use +the correct types. + +467 +00:23:29,141 --> 00:23:31,744 +Now that you know +how to build a custom task, + +468 +00:23:31,744 --> 00:23:34,780 +let's talk about deployment. + +469 +00:23:34,780 --> 00:23:38,784 +So far, I've used the same API +for training and inference. + +470 +00:23:38,784 --> 00:23:41,654 +I want to point out that when +using Create ML Components, + +471 +00:23:41,654 --> 00:23:43,656 +your model is your code. + +472 +00:23:43,656 --> 00:23:45,057 +You need the task definition, + +473 +00:23:45,057 --> 00:23:48,961 +even when loading the trained +parameters from a file. + +474 +00:23:48,961 --> 00:23:50,996 +This is useful +in some situations, + +475 +00:23:50,996 --> 00:23:55,167 +but sometimes you may want +to use Core ML for deployment. + +476 +00:23:55,167 --> 00:23:58,103 +When using Core ML, +you leave the code behind. + +477 +00:23:58,103 --> 00:24:01,507 +The model is fully represented +by a model file. + +478 +00:24:01,507 --> 00:24:03,208 +If you are +all ready using Core ML, + +479 +00:24:03,208 --> 00:24:05,044 +this may be a good workflow. + +480 +00:24:05,044 --> 00:24:08,747 +And it has the advantage +of optimized tensor operations. + +481 +00:24:08,747 --> 00:24:10,049 +But there are +some considerations + +482 +00:24:10,049 --> 00:24:12,251 +you should keep in mind. + +483 +00:24:12,251 --> 00:24:15,054 +Not all operations +are supported in Core ML. + +484 +00:24:15,054 --> 00:24:17,556 +Specifically, custom +transformers and estimators + +485 +00:24:17,556 --> 00:24:19,291 +are not supported. + +486 +00:24:19,291 --> 00:24:21,260 +And Core ML only supports +a few types + +487 +00:24:21,260 --> 00:24:24,129 +like images and shaped arrays. + +488 +00:24:24,129 --> 00:24:25,864 +If you are using custom types, + +489 +00:24:25,864 --> 00:24:27,833 +you may need to convert +those in your app + +490 +00:24:27,833 --> 00:24:30,102 +when using the Core ML model. + +491 +00:24:30,102 --> 00:24:33,639 +This is how you can export your +transformer as a Core ML model. + +492 +00:24:33,639 --> 00:24:36,375 +If your transformer contains +unsupported operations, + +493 +00:24:36,375 --> 00:24:39,078 +this will throw an error. + +494 +00:24:39,078 --> 00:24:41,747 +If you'd rather stick with +deploying your task definition + +495 +00:24:41,747 --> 00:24:43,582 +along with +the trained parameters, + +496 +00:24:43,582 --> 00:24:46,885 +you should consider bundling +them in a Swift package. + +497 +00:24:46,885 --> 00:24:49,555 +This way, you can provide simple +methods to load the parameters + +498 +00:24:49,555 --> 00:24:51,457 +and perform a prediction. + +499 +00:24:51,457 --> 00:24:53,926 +For more information +on Swift package resources, + +500 +00:24:53,926 --> 00:24:58,197 +check out the Swift packages +talk from WWDC 2020. + +501 +00:24:58,197 --> 00:24:59,832 +That's all I have. + +502 +00:24:59,832 --> 00:25:01,233 +The main thing to remember + +503 +00:25:01,233 --> 00:25:04,636 +is that you can now create +custom tasks with composition. + +504 +00:25:04,636 --> 00:25:06,672 +The possibilities are endless. + +505 +00:25:06,672 --> 00:25:08,774 +I look forward to seeing +what you build. + +506 +00:25:08,774 --> 00:25:09,942 +For more advanced techniques, + +507 +00:25:09,942 --> 00:25:12,578 +including audio and video tasks, +check out + +508 +00:25:12,578 --> 00:25:15,514 +"Compose advanced models +with Create ML Components" + +509 +00:25:15,514 --> 00:25:17,249 +where my colleague David +will present + +510 +00:25:17,249 --> 00:25:20,285 +more advanced custom tasks. + +511 +00:25:20,285 --> 00:25:24,289 +Thank you and enjoy the rest +of WWDC 2022! + +512 +00:25:24,289 --> 00:25:29,061 +♪ + diff --git a/eng/2022 Session 10020 Compose advanced models with Create ML Components en.srt b/eng/2022 Session 10020 Compose advanced models with Create ML Components en.srt new file mode 100644 index 0000000..b79159c --- /dev/null +++ b/eng/2022 Session 10020 Compose advanced models with Create ML Components en.srt @@ -0,0 +1,1388 @@ +1 +00:00:00,200 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,676 +♪ + +3 +00:00:09,676 --> 00:00:11,612 +Hi, my name is David Findlay, + +4 +00:00:11,612 --> 00:00:14,014 +and I'm an engineer +on the Create ML team. + +5 +00:00:14,014 --> 00:00:16,583 +This session is all about +Create ML Components, + +6 +00:00:16,583 --> 00:00:20,487 +a powerful new way to build +your own machine learning tasks. + +7 +00:00:20,487 --> 00:00:23,023 +My colleague Alejandro gave +an introduction in the session + +8 +00:00:23,023 --> 00:00:25,092 +"Get to know +Create ML Components." + +9 +00:00:25,092 --> 00:00:27,394 +He explores deconstructing +Create ML tasks + +10 +00:00:27,394 --> 00:00:29,863 +into components +and revealed how easy it is + +11 +00:00:29,863 --> 00:00:31,865 +to build custom models. + +12 +00:00:31,865 --> 00:00:35,002 +Transformers and estimators +are the main building blocks + +13 +00:00:35,002 --> 00:00:37,938 +that you can compose together +to build custom models + +14 +00:00:37,938 --> 00:00:40,140 +like image regression. + +15 +00:00:40,140 --> 00:00:43,377 +In this session, I want to go +way beyond the basics + +16 +00:00:43,377 --> 00:00:46,613 +and demonstrate what's possible +with Create ML Components. + +17 +00:00:46,613 --> 00:00:49,917 +Let's go over the agenda; +there's lots to cover. + +18 +00:00:49,917 --> 00:00:53,186 +I'll start by talking about +video data and go into detail + +19 +00:00:53,186 --> 00:00:57,090 +about new components designed +to handle values over time. + +20 +00:00:57,090 --> 00:00:58,792 +Then I'll put +those concepts to work + +21 +00:00:58,792 --> 00:01:01,061 +and build a human action +repetition counter + +22 +00:01:01,061 --> 00:01:03,430 +using only transformers. + +23 +00:01:03,430 --> 00:01:05,232 +Finally, +I'll move on to training + +24 +00:01:05,232 --> 00:01:07,200 +a custom sound +classifier model. + +25 +00:01:07,200 --> 00:01:09,469 +I'll discuss incremental fitting +which allows you + +26 +00:01:09,469 --> 00:01:11,271 +to update your model +with batches, + +27 +00:01:11,271 --> 00:01:13,941 +stop training early, +and checkpoint your model. + +28 +00:01:13,941 --> 00:01:17,144 +There's so much opportunity +with this level of flexibility. + +29 +00:01:17,144 --> 00:01:19,012 +I can't wait to dive in. + +30 +00:01:19,012 --> 00:01:21,081 +Let's get started. + +31 +00:01:21,081 --> 00:01:24,551 +At WWDC 2020, we introduced +Action Classification + +32 +00:01:24,551 --> 00:01:26,153 +in Create ML, + +33 +00:01:26,153 --> 00:01:28,989 +which allows you to classify +actions from videos. + +34 +00:01:28,989 --> 00:01:31,625 +And we demonstrated how you can +create a fitness classifier + +35 +00:01:31,625 --> 00:01:34,127 +to recognize a person's +workout routines, + +36 +00:01:34,127 --> 00:01:37,998 +such as jumping jacks, +lunges, and squats. + +37 +00:01:37,998 --> 00:01:40,500 +For example, you can use +the action classifier + +38 +00:01:40,500 --> 00:01:44,037 +to recognize the action in +this video as a jumping jack. + +39 +00:01:44,037 --> 00:01:47,441 +But what if you wanted +to count your jumping jacks? + +40 +00:01:47,441 --> 00:01:49,109 +The first thing +you need to consider + +41 +00:01:49,109 --> 00:01:52,079 +is that a jumping jack +spans consecutive frames, + +42 +00:01:52,079 --> 00:01:55,182 +and you'll need a way +to handle values over time. + +43 +00:01:55,182 --> 00:01:57,050 +Thankfully, +Swift's AsyncSequence + +44 +00:01:57,050 --> 00:01:59,386 +makes this really easy. + +45 +00:01:59,386 --> 00:02:01,888 +If you're unfamiliar +with async sequences, + +46 +00:02:01,888 --> 00:02:06,259 +you should check out the session +"Meet AsyncSequence”. + +47 +00:02:06,259 --> 00:02:09,062 +With Create ML Components, +you can read your video + +48 +00:02:09,062 --> 00:02:13,133 +as an async sequence of frames, +using the video reader. + +49 +00:02:13,133 --> 00:02:16,169 +And AsyncSequence provides a +way of iterating over the frames + +50 +00:02:16,169 --> 00:02:19,840 +as they are received +from the video. + +51 +00:02:19,840 --> 00:02:22,576 +For example, +I can easily transform + +52 +00:02:22,576 --> 00:02:26,146 +each video frame asynchronously +using the map method. + +53 +00:02:26,146 --> 00:02:30,550 +This is useful when you want +to process frames one at a time. + +54 +00:02:30,550 --> 00:02:32,085 +But what if you wanted +to process + +55 +00:02:32,085 --> 00:02:34,254 +multiple frames at a time? + +56 +00:02:34,254 --> 00:02:36,857 +That's where +temporal transformers come in. + +57 +00:02:36,857 --> 00:02:39,626 +For example, you may want +to downsample frames + +58 +00:02:39,626 --> 00:02:42,095 +to speed-up an action +in a video. + +59 +00:02:42,095 --> 00:02:44,064 +You can use +a downsampler for that + +60 +00:02:44,064 --> 00:02:45,766 +which takes an async sequence + +61 +00:02:45,766 --> 00:02:48,802 +and returns a downsampled +async sequence. + +62 +00:02:48,802 --> 00:02:51,505 +Or you may want to group +frames into windows, + +63 +00:02:51,505 --> 00:02:54,708 +which is important +for counting action repetitions. + +64 +00:02:54,708 --> 00:02:58,111 +That's where you can use +a sliding window transformer. + +65 +00:02:58,111 --> 00:03:00,881 +You can specify a window length, +which is how many frames + +66 +00:03:00,881 --> 00:03:03,450 +you want to group in a window, +and a stride, + +67 +00:03:03,450 --> 00:03:06,586 +which is how you control +the sliding interval. + +68 +00:03:06,586 --> 00:03:09,956 +The input is, again, +an async sequence, + +69 +00:03:09,956 --> 00:03:15,662 +and the output in this case is +a windowed async sequence. + +70 +00:03:15,662 --> 00:03:17,964 +Generally speaking, +a temporal transformer + +71 +00:03:17,964 --> 00:03:20,600 +provides a way to process +an async sequence + +72 +00:03:20,600 --> 00:03:22,903 +into a new async sequence. + +73 +00:03:22,903 --> 00:03:25,772 +So let's put +these concepts to work. + +74 +00:03:25,772 --> 00:03:27,941 +I don't know about you, +but when I'm working out, + +75 +00:03:27,941 --> 00:03:30,143 +I always lose count of my reps. + +76 +00:03:30,143 --> 00:03:32,112 +So I decided +to shake things up a bit + +77 +00:03:32,112 --> 00:03:34,214 +and build an action +repetition counter + +78 +00:03:34,214 --> 00:03:36,383 +with Create ML Components. + +79 +00:03:36,383 --> 00:03:39,920 +In this example, I'll go over +how to compose transformers + +80 +00:03:39,920 --> 00:03:42,255 +and temporal transformers +together. + +81 +00:03:42,255 --> 00:03:45,525 +Let's start with +pose extraction. + +82 +00:03:45,525 --> 00:03:49,296 +I can extract poses using +the human body pose extractor. + +83 +00:03:49,296 --> 00:03:50,831 +The input is an image + +84 +00:03:50,831 --> 00:03:54,401 +and the output is an array +of human body poses. + +85 +00:03:54,401 --> 00:03:56,837 +Behind the scenes, +we leverage the Vision framework + +86 +00:03:56,837 --> 00:03:59,573 +to extract the poses. + +87 +00:03:59,573 --> 00:04:02,375 +Note that images +can contain multiple people, + +88 +00:04:02,375 --> 00:04:05,011 +which is common +for group workouts. + +89 +00:04:05,011 --> 00:04:08,415 +That's why the output +is an array of poses. + +90 +00:04:08,415 --> 00:04:11,184 +But I'm only interested in +counting action repetitions + +91 +00:04:11,184 --> 00:04:13,520 +for one person at a time. + +92 +00:04:13,520 --> 00:04:16,356 +So I'll compose +the human body pose extractor + +93 +00:04:16,356 --> 00:04:19,659 +with a pose selector. + +94 +00:04:19,659 --> 00:04:22,129 +A pose selector +takes an array of poses + +95 +00:04:22,129 --> 00:04:26,833 +as well as a selection strategy +and returns a single pose. + +96 +00:04:26,833 --> 00:04:29,236 +There's a few selection +strategies to choose from, + +97 +00:04:29,236 --> 00:04:30,604 +but for this example, + +98 +00:04:30,604 --> 00:04:33,507 +I'll use the +rightMostJointLocation strategy. + +99 +00:04:33,507 --> 00:04:38,311 +The next step is to group +the poses into windows. + +100 +00:04:38,311 --> 00:04:42,015 +I'll append a sliding window +transformer for that. + +101 +00:04:42,015 --> 00:04:44,851 +And I'll use a window length +and stride of 90, + +102 +00:04:44,851 --> 00:04:47,120 +which will generate +nonoverlapping windows + +103 +00:04:47,120 --> 00:04:49,823 +of 90 poses. + +104 +00:04:49,823 --> 00:04:52,826 +Recall that a sliding window +transformer is temporal, + +105 +00:04:52,826 --> 00:04:55,729 +which makes +the whole task temporal, + +106 +00:04:55,729 --> 00:05:00,367 +and the expected input is now +an async sequence of frames. + +107 +00:05:00,367 --> 00:05:05,639 +Finally, I'll append +a human body action counter. + +108 +00:05:05,639 --> 00:05:06,740 +This temporal transformer + +109 +00:05:06,740 --> 00:05:10,043 +consumes a windowed +async sequence of poses + +110 +00:05:10,043 --> 00:05:13,013 +and returns a cumulative count +of the action repetitions + +111 +00:05:13,013 --> 00:05:14,514 +so far. + +112 +00:05:14,514 --> 00:05:15,749 +By now, you may have noticed + +113 +00:05:15,749 --> 00:05:18,285 +that the count is +a floating-point number. + +114 +00:05:18,285 --> 00:05:21,821 +And that's because the task +counts partial actions too. + +115 +00:05:21,821 --> 00:05:23,323 +It's that easy. + +116 +00:05:23,323 --> 00:05:25,625 +Now I can count my reps +in my workout videos + +117 +00:05:25,625 --> 00:05:27,961 +and make sure +I'm not cheating. + +118 +00:05:27,961 --> 00:05:31,264 +But it would be even better +to count repetitions live + +119 +00:05:31,264 --> 00:05:35,168 +in an app, so that I can keep +track of my current workouts. + +120 +00:05:35,168 --> 00:05:38,071 +Let me show you +how you can do that. + +121 +00:05:38,071 --> 00:05:40,607 +First, I'll use +the readCamera method + +122 +00:05:40,607 --> 00:05:42,409 +which takes +a camera configuration + +123 +00:05:42,409 --> 00:05:45,879 +and returns an async sequence +of camera frames. + +124 +00:05:45,879 --> 00:05:49,149 +Next, I'll adjust the stride +parameter to 15 frames + +125 +00:05:49,149 --> 00:05:52,285 +so that I get an updated count +more often. + +126 +00:05:52,285 --> 00:05:55,422 +If my camera captures frames at +a rate of 30 frames per second, + +127 +00:05:55,422 --> 00:05:58,858 +then I get counts +every half second. + +128 +00:05:58,858 --> 00:06:03,396 +Now I can workout and not worry +about missing a rep. + +129 +00:06:03,396 --> 00:06:06,132 +So far, I've explored +temporal components + +130 +00:06:06,132 --> 00:06:08,668 +for transforming +async sequences. + +131 +00:06:08,668 --> 00:06:11,238 +Next, I want to focus on +training custom models + +132 +00:06:11,238 --> 00:06:14,641 +that rely on temporal data. + +133 +00:06:14,641 --> 00:06:18,445 +In 2019, we demonstrated how +you can train a sound classifier + +134 +00:06:18,445 --> 00:06:19,946 +in Create ML. + +135 +00:06:19,946 --> 00:06:22,716 +Then in 2021, +we introduced enhancements + +136 +00:06:22,716 --> 00:06:24,951 +to sound classification. + +137 +00:06:24,951 --> 00:06:26,419 +I want to go even further + +138 +00:06:26,419 --> 00:06:30,657 +and train a custom sound +classifier incrementally. + +139 +00:06:30,657 --> 00:06:33,393 +The MLSoundClassifier +in the Create ML framework + +140 +00:06:33,393 --> 00:06:34,694 +is still the easiest way + +141 +00:06:34,694 --> 00:06:36,896 +to train a custom +sound classifier model. + +142 +00:06:36,896 --> 00:06:39,933 +But when you need more +customizability and control, + +143 +00:06:39,933 --> 00:06:42,168 +you can use the components +under the hood. + +144 +00:06:42,168 --> 00:06:46,406 +In its simplest form, the sound +classifier has two components: + +145 +00:06:46,406 --> 00:06:49,075 +an Audio Feature Print +feature extractor + +146 +00:06:49,075 --> 00:06:51,745 +and a classifier of your choice. + +147 +00:06:51,745 --> 00:06:54,247 +AudioFeaturePrint is +a temporal transformer + +148 +00:06:54,247 --> 00:06:56,850 +that extracts audio features +from an async sequence + +149 +00:06:56,850 --> 00:06:59,252 +of audio buffers. + +150 +00:06:59,252 --> 00:07:01,755 +Similar to +a sliding window transformer, + +151 +00:07:01,755 --> 00:07:04,357 +AudioFeaturePrint windows +the async sequence + +152 +00:07:04,357 --> 00:07:07,327 +then extracts features. + +153 +00:07:07,327 --> 00:07:09,529 +There are a few classifiers +to choose from, + +154 +00:07:09,529 --> 00:07:14,034 +but for this example, I'll use +a logistic regression classifier + +155 +00:07:14,034 --> 00:07:16,836 +and then compose it together +with the feature extractor + +156 +00:07:16,836 --> 00:07:20,573 +to build a custom +sound classifier. + +157 +00:07:20,573 --> 00:07:23,643 +The next step is to fit +the custom sound classifier + +158 +00:07:23,643 --> 00:07:25,378 +to labeled training data. + +159 +00:07:25,378 --> 00:07:27,881 +For more information about +collecting training data, + +160 +00:07:27,881 --> 00:07:30,150 +the "Get to know Create ML +Components" session + +161 +00:07:30,150 --> 00:07:32,085 +is a good place to start. + +162 +00:07:32,085 --> 00:07:34,688 +So far, I've covered +the happy path. + +163 +00:07:34,688 --> 00:07:36,623 +But building +machine learning models + +164 +00:07:36,623 --> 00:07:39,659 +can be an iterative process. + +165 +00:07:39,659 --> 00:07:43,029 +For example, you may discover +and collect new training data + +166 +00:07:43,029 --> 00:07:46,333 +over time and want +to refresh your model. + +167 +00:07:46,333 --> 00:07:49,502 +It's possible that you can +improve the model quality. + +168 +00:07:49,502 --> 00:07:53,340 +But retraining your model +from scratch is time-consuming. + +169 +00:07:53,340 --> 00:07:55,942 +That's because you need +to redo feature extraction + +170 +00:07:55,942 --> 00:07:58,044 +for all of your previous data. + +171 +00:07:58,044 --> 00:08:00,714 +Let me give you an example +of how you can save time + +172 +00:08:00,714 --> 00:08:04,184 +when training your models +with newly discovered data. + +173 +00:08:04,184 --> 00:08:06,686 +The key is to preprocess +your training data + +174 +00:08:06,686 --> 00:08:09,456 +separately from +fitting your model. + +175 +00:08:09,456 --> 00:08:12,792 +In this example, I can extract +audio features separately + +176 +00:08:12,792 --> 00:08:15,428 +from the classifier fitting. + +177 +00:08:15,428 --> 00:08:17,530 +And this works in general too. + +178 +00:08:17,530 --> 00:08:19,966 +Whenever you have +a series of transformers + +179 +00:08:19,966 --> 00:08:21,634 +followed by an estimator, + +180 +00:08:21,634 --> 00:08:24,637 +you can preprocess the input +through the transformers + +181 +00:08:24,637 --> 00:08:26,506 +leading up to the estimator. + +182 +00:08:26,506 --> 00:08:30,143 +All you need to do +is call the preprocess method + +183 +00:08:30,143 --> 00:08:33,513 +and then fit the model on +the preprocessed features. + +184 +00:08:33,513 --> 00:08:35,849 +I find this convenient +because I didn't need to change + +185 +00:08:35,849 --> 00:08:38,785 +the sound classifier +composition. + +186 +00:08:38,785 --> 00:08:41,087 +Now that I have the features +extracted separately, + +187 +00:08:41,087 --> 00:08:44,057 +I have the flexibility +to only extract audio features + +188 +00:08:44,057 --> 00:08:46,760 +for the new data. + +189 +00:08:46,760 --> 00:08:49,129 +As you discover new +training data for your model, + +190 +00:08:49,129 --> 00:08:51,998 +you can easily preprocess +this data separately. + +191 +00:08:51,998 --> 00:08:54,000 +And then append +the supplemental features + +192 +00:08:54,000 --> 00:08:56,770 +to the previously +extracted ones. + +193 +00:08:56,770 --> 00:08:58,605 +This is just the first example + +194 +00:08:58,605 --> 00:09:01,741 +of where preprocessing +can save you time. + +195 +00:09:01,741 --> 00:09:04,644 +Let's go back to +the model-building lifecycle. + +196 +00:09:04,644 --> 00:09:06,813 +You may need to tune +your estimator parameters + +197 +00:09:06,813 --> 00:09:09,482 +until you're satisfied +with your model's quality. + +198 +00:09:09,482 --> 00:09:12,385 +By separating the feature +extraction from the fitting, + +199 +00:09:12,385 --> 00:09:14,821 +you can extract +your features only once + +200 +00:09:14,821 --> 00:09:18,324 +and then fit your model with +different estimator parameters. + +201 +00:09:18,324 --> 00:09:19,592 +Let's go over an example + +202 +00:09:19,592 --> 00:09:21,261 +of changing +the classifier parameters + +203 +00:09:21,261 --> 00:09:24,531 +without redoing +feature extraction. + +204 +00:09:24,531 --> 00:09:26,933 +Assuming that I've already +extracted features, + +205 +00:09:26,933 --> 00:09:30,770 +I'll modify the classifier's +L2 penalty parameter. + +206 +00:09:30,770 --> 00:09:33,006 +And then I'll need to append +the new classifier + +207 +00:09:33,006 --> 00:09:35,375 +to the old feature extractor. + +208 +00:09:35,375 --> 00:09:37,944 +It's important not to change +the feature extractor + +209 +00:09:37,944 --> 00:09:40,747 +when tuning your estimator, +because that would invalidate + +210 +00:09:40,747 --> 00:09:43,049 +the previously +extracted features. + +211 +00:09:43,049 --> 00:09:47,420 +Let's move on to incrementally +fitting your model with batches. + +212 +00:09:47,420 --> 00:09:49,322 +Machine learning models +in general + +213 +00:09:49,322 --> 00:09:51,724 +benefit from large amounts +of training data. + +214 +00:09:51,724 --> 00:09:55,094 +However, your app may have +limited memory constraints. + +215 +00:09:55,094 --> 00:09:56,729 +So what do you do? + +216 +00:09:56,729 --> 00:09:59,265 +You can use Create ML Components +to train a model + +217 +00:09:59,265 --> 00:10:02,569 +by loading only a batch of data +into memory at a time. + +218 +00:10:02,569 --> 00:10:05,605 +The first thing I need to do +is replace the classifier + +219 +00:10:05,605 --> 00:10:07,707 +with an updatable classifier. + +220 +00:10:07,707 --> 00:10:10,076 +In order to train +a custom model with batches, + +221 +00:10:10,076 --> 00:10:12,679 +your classifier +needs to be updatable. + +222 +00:10:12,679 --> 00:10:16,382 +For example, the fully connected +neural network classifier, + +223 +00:10:16,382 --> 00:10:17,684 +which I can easily use + +224 +00:10:17,684 --> 00:10:20,220 +instead of the logistic +regression classifier + +225 +00:10:20,220 --> 00:10:22,021 +which is not updatable. + +226 +00:10:25,291 --> 00:10:28,795 +All right, now I'll write +a training loop. + +227 +00:10:28,795 --> 00:10:32,031 +I'll start by creating +a default initialized model. + +228 +00:10:32,031 --> 00:10:34,033 +You won't be able +to make predictions yet; + +229 +00:10:34,033 --> 00:10:37,837 +that's because this is just +the starting point for training. + +230 +00:10:37,837 --> 00:10:39,873 +Then I'll extract +the audio features + +231 +00:10:39,873 --> 00:10:41,908 +before the training starts. + +232 +00:10:41,908 --> 00:10:43,109 +This is an important step + +233 +00:10:43,109 --> 00:10:46,913 +because I don't want to extract +features every iteration. + +234 +00:10:46,913 --> 00:10:49,749 +The next step is +to define the training loop + +235 +00:10:49,749 --> 00:10:51,384 +and specify the number +of iterations + +236 +00:10:51,384 --> 00:10:53,853 +you'd like to train for. + +237 +00:10:53,853 --> 00:10:57,891 +Before I continue, I'll import +the algorithm's Swift package. + +238 +00:10:57,891 --> 00:11:01,327 +I'll need it for creating +batches of training data. + +239 +00:11:01,327 --> 00:11:02,795 +Make sure to check out +the session + +240 +00:11:02,795 --> 00:11:05,331 +"Meet the Swift Algorithms +and Collections packages" + +241 +00:11:05,331 --> 00:11:08,701 +from WWDC 2021 +to learn more. + +242 +00:11:10,403 --> 00:11:13,506 +Within the training loop is +where the batching happens. + +243 +00:11:13,506 --> 00:11:15,708 +I'll use the chunks method +to group the features + +244 +00:11:15,708 --> 00:11:18,344 +into batches for training. + +245 +00:11:18,344 --> 00:11:20,346 +The chunk size +is the number of features + +246 +00:11:20,346 --> 00:11:23,116 +that are loaded +into memory at once. + +247 +00:11:23,116 --> 00:11:26,519 +Then, I can update the model +by iterating over the batches + +248 +00:11:26,519 --> 00:11:28,655 +and calling the update method. + +249 +00:11:31,491 --> 00:11:33,293 +When you train your model +incrementally, + +250 +00:11:33,293 --> 00:11:35,495 +you can unlock a few more +training techniques. + +251 +00:11:35,495 --> 00:11:36,596 +For example, + +252 +00:11:36,596 --> 00:11:39,065 +in this training graph, +after about 10 iterations, + +253 +00:11:39,065 --> 00:11:42,669 +the model accuracy +plateaus at 95 percent. + +254 +00:11:42,669 --> 00:11:45,004 +At this point, +the model has converged + +255 +00:11:45,004 --> 00:11:46,639 +and you can stop early. + +256 +00:11:46,639 --> 00:11:50,510 +Let's implement early stopping +in the training loop. + +257 +00:11:50,510 --> 00:11:52,779 +The first thing I need to do +is make predictions + +258 +00:11:52,779 --> 00:11:54,581 +for my validation set. + +259 +00:11:54,581 --> 00:11:56,349 +I'm using the mapFeatures +method here + +260 +00:11:56,349 --> 00:11:58,818 +because I need to pair +the validation predictions + +261 +00:11:58,818 --> 00:12:01,721 +with its annotations. + +262 +00:12:01,721 --> 00:12:04,691 +The next step is to measure +the quality of the model. + +263 +00:12:04,691 --> 00:12:06,759 +I'll use the built-in metrics +for now, + +264 +00:12:06,759 --> 00:12:08,127 +but there's nothing +stopping you + +265 +00:12:08,127 --> 00:12:10,663 +from implementing +your own custom metrics. + +266 +00:12:10,663 --> 00:12:12,498 +And finally, I'll stop training + +267 +00:12:12,498 --> 00:12:16,269 +when my model has reached +an accuracy of 95 percent. + +268 +00:12:16,269 --> 00:12:19,505 +Outside of the training loop, +I'll write the model out to disk + +269 +00:12:19,505 --> 00:12:22,842 +so that I can use it later +to make predictions. + +270 +00:12:22,842 --> 00:12:24,377 +In addition to stopping early, + +271 +00:12:24,377 --> 00:12:26,779 +I'd like to talk about +model checkpointing. + +272 +00:12:28,548 --> 00:12:30,984 +You can save your model's +progress during training + +273 +00:12:30,984 --> 00:12:33,152 +rather than waiting +until the end. + +274 +00:12:33,152 --> 00:12:34,787 +And you can even use +checkpointing + +275 +00:12:34,787 --> 00:12:37,423 +in order to resume training, +which is convenient + +276 +00:12:37,423 --> 00:12:41,327 +especially when your model +takes a long time to train. + +277 +00:12:41,327 --> 00:12:45,031 +All you need to do is write out +your model in the training loop. + +278 +00:12:45,031 --> 00:12:47,500 +We recommend doing this +every few iterations + +279 +00:12:47,500 --> 00:12:49,469 +by defining +a checkpoint interval. + +280 +00:12:49,469 --> 00:12:51,604 +It's that easy. + +281 +00:12:51,604 --> 00:12:54,674 +In this session, I introduced +temporal components, + +282 +00:12:54,674 --> 00:12:56,643 +a new way to build +machine learning tasks + +283 +00:12:56,643 --> 00:13:00,246 +with temporal data +like audio and video. + +284 +00:13:00,246 --> 00:13:02,915 +I composed +temporal components together + +285 +00:13:02,915 --> 00:13:05,752 +to make a human action +repetition counter. + +286 +00:13:05,752 --> 00:13:08,921 +And finally, I talked about +incremental fitting. + +287 +00:13:08,921 --> 00:13:10,723 +This will unlock +new possibilities for you + +288 +00:13:10,723 --> 00:13:13,426 +to build machine learning +into your apps. + +289 +00:13:13,426 --> 00:13:16,763 +Thanks for joining me +and enjoy the rest of WWDC. + +290 +00:13:16,763 --> 00:13:21,300 +♪ + diff --git a/eng/2022 Session 10022 Create camera extensions with Core Media IO en.srt b/eng/2022 Session 10022 Create camera extensions with Core Media IO en.srt new file mode 100644 index 0000000..5323c87 --- /dev/null +++ b/eng/2022 Session 10022 Create camera extensions with Core Media IO en.srt @@ -0,0 +1,2205 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,676 --> 00:00:11,211 +Hello and welcome. + +3 +00:00:11,245 --> 00:00:15,382 +I'm Brad Ford from +the Camera Software Engineering team. + +4 +00:00:15,415 --> 00:00:18,452 +In this session, I'll be introducing you +to camera extensions + +5 +00:00:18,485 --> 00:00:22,022 +with CoreMedia IO, which is +a modern camera driver architecture + +6 +00:00:22,055 --> 00:00:25,626 +for macOS +and a replacement for DAL plug-ins. + +7 +00:00:27,094 --> 00:00:30,731 +DAL plug-ins are a technology +that allows you to create camera drivers + +8 +00:00:30,764 --> 00:00:35,335 +for hardware that plugs into a Mac, +or virtual cameras. + +9 +00:00:35,369 --> 00:00:39,540 +They've been around for a very long time– +since macOS 10.7. + +10 +00:00:40,707 --> 00:00:45,712 +DAL plug-ins provide the power +to extend macOS as a rich media platform, + +11 +00:00:45,746 --> 00:00:50,284 +bringing support for great third party +camera products to pros and consumers. + +12 +00:00:51,518 --> 00:00:54,254 +It's part of what makes the Mac, the Mac. + +13 +00:00:56,123 --> 00:00:58,859 +But DAL plug-ins have some problems. + +14 +00:00:58,892 --> 00:01:02,196 +They load untrusted code +directly into an app's process, + +15 +00:01:02,229 --> 00:01:05,933 +making it vulnerable to crashes +if the plug-in has bugs + +16 +00:01:05,966 --> 00:01:08,869 +or to malware attack. + +17 +00:01:08,902 --> 00:01:11,872 +For this reason, +they don't work with Apple apps + +18 +00:01:11,905 --> 00:01:16,176 +such as FaceTime, +QuickTime Player, and PhotoBooth. + +19 +00:01:16,210 --> 00:01:19,213 +They also don't work +with many third party camera apps, + +20 +00:01:19,246 --> 00:01:23,283 +unless those apps intentionally +disable library validation, + +21 +00:01:23,317 --> 00:01:26,787 +or the user turns off +system integrity protection. + +22 +00:01:26,820 --> 00:01:29,022 +Neither of these are practices recommended + +23 +00:01:29,056 --> 00:01:33,360 +as they make the system +less secure and less stable. + +24 +00:01:33,393 --> 00:01:35,562 +They're difficult to develop too. + +25 +00:01:35,596 --> 00:01:38,732 +They carry a C API circa 2011 + +26 +00:01:38,765 --> 00:01:43,537 +and a thick SDK of C++ helper classes +for you to learn. + +27 +00:01:43,570 --> 00:01:46,607 +And on top of all that, +they're sparsely documented. + +28 +00:01:47,875 --> 00:01:50,043 +It's time for an upgrade. + +29 +00:01:50,077 --> 00:01:54,147 +macOS 12.3 introduces +a thoroughly modern replacement + +30 +00:01:54,181 --> 00:01:57,851 +for DAL plug-ins called Camera Extensions… + +31 +00:01:59,553 --> 00:02:03,524 +An architecture that places +user security first. + +32 +00:02:03,557 --> 00:02:05,492 +Let's learn how it works. + +33 +00:02:05,526 --> 00:02:09,096 +First, I'll provide a technology overview. + +34 +00:02:09,129 --> 00:02:13,734 +Next, I'll show you how to build +a camera extension from scratch. + +35 +00:02:13,767 --> 00:02:18,739 +Next, I'll introduce the main classes +and functions of the API. + +36 +00:02:18,772 --> 00:02:23,977 +I'll explain how CoreMedia IO Extensions +can be used as output devices. + +37 +00:02:24,011 --> 00:02:28,882 +And finally, I'll cover +our DAL plug-in deprecation plan. + +38 +00:02:28,916 --> 00:02:31,652 +Let's get started. + +39 +00:02:31,685 --> 00:02:35,856 +Camera extensions, otherwise known +as CoreMedia IO extensions, + +40 +00:02:35,889 --> 00:02:40,060 +are a new way to package and deliver +camera drivers to Mac applications. + +41 +00:02:41,061 --> 00:02:42,462 +They're secure. + +42 +00:02:42,496 --> 00:02:46,300 +Your extension code is cordoned off +into its own daemon process + +43 +00:02:46,333 --> 00:02:50,037 +that's sandboxed and run as a role user. + +44 +00:02:50,070 --> 00:02:53,240 +All the buffers your extension provides +are validated + +45 +00:02:53,273 --> 00:02:56,443 +before being delivered to an app. + +46 +00:02:56,476 --> 00:02:57,911 +They're fast. + +47 +00:02:57,945 --> 00:02:59,947 +The framework handles the IPC layers + +48 +00:02:59,980 --> 00:03:02,749 +between your extension process +and the app, + +49 +00:03:02,783 --> 00:03:05,552 +with an emphasis on performance. + +50 +00:03:05,586 --> 00:03:08,155 +The framework can also take care +of delivering buffers + +51 +00:03:08,188 --> 00:03:11,525 +to multiple simultaneous clients. + +52 +00:03:11,558 --> 00:03:12,960 +They're modern. + +53 +00:03:12,993 --> 00:03:17,064 +Your extension can be written +in either Swift or Objective-c. + +54 +00:03:18,532 --> 00:03:19,900 +They're simple. + +55 +00:03:19,933 --> 00:03:22,002 +There are just a few classes to learn, + +56 +00:03:22,035 --> 00:03:25,305 +a few protocols to implement +in order to get up and running. + +57 +00:03:25,339 --> 00:03:27,774 +The framework takes care +of the boilerplate code. + +58 +00:03:29,476 --> 00:03:31,278 +They're easy to deploy. + +59 +00:03:31,311 --> 00:03:34,014 +You can ship them as apps +in the App Store. + +60 +00:03:36,149 --> 00:03:40,320 +And camera extensions are +100% backward compatible + +61 +00:03:40,354 --> 00:03:43,390 +with existing AVFoundation capture APIs. + +62 +00:03:45,492 --> 00:03:50,364 +Camera extensions shows up just like +the built-in camera in all camera apps, + +63 +00:03:50,397 --> 00:03:52,733 +including Apple apps. + +64 +00:03:52,766 --> 00:03:55,903 +Here's how an example +of how a camera extension might appear + +65 +00:03:55,936 --> 00:03:58,205 +in the FaceTime camera picker. + +66 +00:03:58,238 --> 00:04:02,342 +What kind of experiences can you build +with a camera extension? + +67 +00:04:02,376 --> 00:04:05,812 +Let's study three common uses. + +68 +00:04:05,846 --> 00:04:08,615 +The simplest use +is a software-only camera, + +69 +00:04:08,649 --> 00:04:12,452 +such as a camera that displays color bars, + +70 +00:04:12,486 --> 00:04:15,923 +a unique test pattern, + +71 +00:04:15,956 --> 00:04:22,062 +programmatically generated images +at various frame rates or resolutions, + +72 +00:04:22,095 --> 00:04:25,232 +or a camera that streams +pre-rendered content, + +73 +00:04:25,265 --> 00:04:29,236 +such as frames in a movie, +to test A/V synchronization. + +74 +00:04:30,871 --> 00:04:34,808 +The second use case is a driver +for a camera that you intend + +75 +00:04:34,842 --> 00:04:39,379 +to physically plug into a Mac +or discover wirelessly. + +76 +00:04:39,413 --> 00:04:44,017 +Camera extensions fully support +hot plugging and unplugging. + +77 +00:04:44,051 --> 00:04:47,421 +To address your hardware, +you have a few choices. + +78 +00:04:47,454 --> 00:04:52,259 +The preferred method is to use +a DriverKit Extension, or DEXT, + +79 +00:04:52,292 --> 00:04:55,863 +which runs entirely in user space. + +80 +00:04:55,896 --> 00:04:58,932 +If your hardware must be addressed +at the kernel level, + +81 +00:04:58,966 --> 00:05:03,136 +you can use +the legacy IOVideoFamily kext path. + +82 +00:05:03,170 --> 00:05:05,806 +Development of new kext code +is discouraged + +83 +00:05:05,839 --> 00:05:11,278 +as kexts are inherently less secure +and can contribute to system instability. + +84 +00:05:14,047 --> 00:05:16,783 +Apple provides a class compliant extension + +85 +00:05:16,817 --> 00:05:21,588 +for USB video class, or UVC, cameras. + +86 +00:05:21,622 --> 00:05:25,058 +It works great for cameras +that conform to the UVC spec. + +87 +00:05:26,193 --> 00:05:29,263 +If, however, you need to support +a USB camera + +88 +00:05:29,296 --> 00:05:31,765 +that uses nonstandard protocol, + +89 +00:05:31,798 --> 00:05:35,035 +has additional features +outside the UVC spec, + +90 +00:05:35,068 --> 00:05:40,107 +you can create a camera extension +that overrides Apple's UVC extension, + +91 +00:05:40,140 --> 00:05:44,678 +allowing you to claim +a particular product and vendor ID. + +92 +00:05:44,711 --> 00:05:48,382 +If you're interested in learning more +about it, please refer to the article + +93 +00:05:48,415 --> 00:05:51,585 +at developer.apple.com entitled + +94 +00:05:51,618 --> 00:05:55,489 +"Overriding the default +USB video class extension." + +95 +00:05:55,522 --> 00:05:58,926 +It explains how to create +a minimal DEXT bundle + +96 +00:05:58,959 --> 00:06:04,398 +and which IOKitPersonalities keys +you need to override in your Info.plist. + +97 +00:06:05,432 --> 00:06:08,535 +A third common use is a creative camera, + +98 +00:06:08,569 --> 00:06:11,471 +a hybrid between software and hardware. + +99 +00:06:12,673 --> 00:06:16,376 +Your extension accesses a video stream +from another physical camera + +100 +00:06:16,410 --> 00:06:18,178 +attached to the Mac, + +101 +00:06:18,212 --> 00:06:20,547 +applies an effect to those buffers, + +102 +00:06:20,581 --> 00:06:24,318 +and sends them along to clients +as a new camera stream. + +103 +00:06:26,353 --> 00:06:31,592 +Or a creative camera that accesses +video streams from several cameras, + +104 +00:06:31,625 --> 00:06:35,128 +composites them, +and sends them along to the app. + +105 +00:06:37,164 --> 00:06:40,534 +A creative camera like this +might use a configuration app + +106 +00:06:40,567 --> 00:06:44,438 +to control the compositing +or parameterize filters. + +107 +00:06:44,471 --> 00:06:48,075 +The possibilities for creative camera +are really endless. + +108 +00:06:49,243 --> 00:06:51,879 +Now that we've explored +the primary use cases, + +109 +00:06:51,912 --> 00:06:56,183 +let's look at the anatomy +of a CoreMedia IO Extension. + +110 +00:06:56,216 --> 00:06:59,119 +First the "CoreMedia IO" part. + +111 +00:07:00,721 --> 00:07:03,824 +CoreMedia IO is a low level framework + +112 +00:07:03,857 --> 00:07:07,794 +for publishing +or discovering camera drivers. + +113 +00:07:07,828 --> 00:07:11,431 +You already know +that it contains the legacy DAL API + +114 +00:07:11,465 --> 00:07:15,736 +and the new camera extension API +that replaces it. + +115 +00:07:15,769 --> 00:07:19,673 +But it also contains a powerful set +of low level C APIs + +116 +00:07:19,706 --> 00:07:24,244 +for app developers to find +and inspect cameras on the system. + +117 +00:07:25,612 --> 00:07:28,282 +Now, how about that "Extension" part? + +118 +00:07:29,783 --> 00:07:35,088 +CoreMedia IO Extensions are built +on top of the SystemExtensions framework + +119 +00:07:35,122 --> 00:07:38,525 +which first appeared in macOS Catalina. + +120 +00:07:38,559 --> 00:07:41,728 +It obviates the need +for a throw-away installer. + +121 +00:07:41,762 --> 00:07:45,799 +Instead, you ship your extension +inside an app. + +122 +00:07:45,832 --> 00:07:49,803 +The extension executable +lives within the app bundle. + +123 +00:07:49,837 --> 00:07:52,973 +By making calls +into the SystemExtensions framework, + +124 +00:07:53,006 --> 00:07:58,078 +your app can install, upgrade, +or downgrade your extension + +125 +00:07:58,111 --> 00:08:01,048 +for all users on the system. + +126 +00:08:01,081 --> 00:08:03,717 +And uninstalling is a snap. + +127 +00:08:03,750 --> 00:08:06,553 +Delete the app +and the SystemExtensions framework + +128 +00:08:06,587 --> 00:08:10,357 +uninstalls your camera extension +for all users. + +129 +00:08:10,390 --> 00:08:13,627 +This delivery mechanism is +approved for App Store use, + +130 +00:08:13,660 --> 00:08:18,098 +making it easy to deploy +your camera extension to a wide audience. + +131 +00:08:19,900 --> 00:08:22,636 +To learn more about +the system extensions framework, + +132 +00:08:22,669 --> 00:08:26,840 +you can read the documentation +at developer.apple.com/ + +133 +00:08:26,874 --> 00:08:29,877 +documentation/systemextensions. + +134 +00:08:31,745 --> 00:08:36,817 +And be sure to check out +the WWDC 2019 video entitled + +135 +00:08:36,850 --> 00:08:39,520 +"System Extensions and DriverKit." + +136 +00:08:40,654 --> 00:08:44,224 +That's it for our technology overview +of camera extensions. + +137 +00:08:44,258 --> 00:08:46,527 +Now, let's actually build one. + +138 +00:08:46,560 --> 00:08:50,397 +Here's a quick demo of how to get +a camera extension up and running + +139 +00:08:50,430 --> 00:08:52,099 +in a matter of minutes. + +140 +00:08:53,634 --> 00:08:58,906 +I've created a single window macOS +application in Xcode, called ExampleCam. + +141 +00:08:58,939 --> 00:09:02,042 +At this point, +I've only added a few lines of code. + +142 +00:09:03,844 --> 00:09:07,247 +The App Delegate is unchanged. + +143 +00:09:07,281 --> 00:09:11,518 +In the main storyboard, +I've added two buttons, + +144 +00:09:11,552 --> 00:09:15,055 +one to install +and one to uninstall the extension, + +145 +00:09:15,088 --> 00:09:17,991 +plus a text field to display status. + +146 +00:09:19,293 --> 00:09:23,130 +In the ViewController class, +I've added IBActions + +147 +00:09:23,163 --> 00:09:27,100 +to hook up the install +and uninstall buttons. + +148 +00:09:28,635 --> 00:09:31,939 +These functions create +OSSystemExtensionRequests + +149 +00:09:31,972 --> 00:09:34,975 +to either activate + +150 +00:09:35,008 --> 00:09:39,980 +or deactivate the extension +found within the app's bundle. + +151 +00:09:40,013 --> 00:09:43,650 +At the bottom, +I've added skeletal implementations + +152 +00:09:43,684 --> 00:09:48,956 +of the OSSystemExtensionRequestDelegate +functions that log status. + +153 +00:09:50,591 --> 00:09:56,063 +The app's entitlements file +has the usual App Sandbox=YES + +154 +00:09:56,096 --> 00:09:58,699 +and it defines an AppGroup. + +155 +00:10:00,734 --> 00:10:05,038 +I've only added one new key here, +the "System Extension" key, + +156 +00:10:05,072 --> 00:10:09,176 +which is required if your app +installs system extensions. + +157 +00:10:09,209 --> 00:10:13,714 +At this point, if I run the app + +158 +00:10:13,747 --> 00:10:17,417 +and click on the Install Extension button, + +159 +00:10:17,451 --> 00:10:20,287 +I'll just get a fatal error, +since the app is looking + +160 +00:10:20,320 --> 00:10:23,724 +for an extension in the bundle +that doesn't exist yet. + +161 +00:10:27,394 --> 00:10:33,567 +To create and embed a system extension, +I go to File, + +162 +00:10:33,600 --> 00:10:37,571 +New, Target, + +163 +00:10:37,604 --> 00:10:41,074 +and under macOS, +I scroll all the way down to the bottom + +164 +00:10:41,108 --> 00:10:44,211 +where the System Extensions are located. + +165 +00:10:44,244 --> 00:10:48,982 +Then I pick "Camera Extension," hit next, + +166 +00:10:49,016 --> 00:10:53,987 +give it a name–I'll choose "Extension"– + +167 +00:10:54,021 --> 00:10:58,559 +I'll make sure that +"Embedded in Application" is set, + +168 +00:10:58,592 --> 00:11:01,528 +and then I click finish. + +169 +00:11:01,562 --> 00:11:06,366 +Inside the new extension folder, +I get four new files. + +170 +00:11:06,400 --> 00:11:10,737 +The Info.plist identifies it +as a CMIOExtension + +171 +00:11:10,771 --> 00:11:13,640 +by defining its MachServiceName. + +172 +00:11:15,008 --> 00:11:17,177 +This is a critical piece of information. + +173 +00:11:17,211 --> 00:11:22,549 +CoreMedia IO's registerassistant will not +launch your extension unless it's present. + +174 +00:11:25,285 --> 00:11:29,590 +While we're here, +let's give it a usage description + +175 +00:11:29,623 --> 00:11:32,292 +for the system extension. + +176 +00:11:32,326 --> 00:11:36,997 +The entitlements file shows +that it's app sandboxed. + +177 +00:11:37,030 --> 00:11:41,635 +And I need to ensure here +that my extension's app group is prefixed + +178 +00:11:41,668 --> 00:11:45,739 +by the MachServiceName +in order for it to pass validation. + +179 +00:11:47,140 --> 00:11:51,478 +So I'll copy and paste that over +from the app extension + +180 +00:11:51,512 --> 00:11:56,283 +to the extensions entitlements file. + +181 +00:11:56,316 --> 00:11:57,651 +And that's it. + +182 +00:11:59,152 --> 00:12:03,590 +The main.swift file serves +as your extension's entry point + +183 +00:12:03,624 --> 00:12:05,893 +and starts the service. + +184 +00:12:05,926 --> 00:12:09,563 +And the ExtensionProvider.swift file + +185 +00:12:09,596 --> 00:12:12,032 +gives us a fully functional camera. + +186 +00:12:12,065 --> 00:12:15,936 +It contains a DeviceSource, + +187 +00:12:15,969 --> 00:12:19,806 +a StreamSource, and a ProviderSource, + +188 +00:12:19,840 --> 00:12:22,943 +all that you need to create +a pure software camera. + +189 +00:12:22,976 --> 00:12:25,012 +Not a bad little template. + +190 +00:12:26,280 --> 00:12:29,683 +In this file, I'll search + +191 +00:12:29,716 --> 00:12:32,953 +for "SampleCapture" + +192 +00:12:32,986 --> 00:12:38,392 +and I'll replace with "ExampleCam," + +193 +00:12:38,425 --> 00:12:42,229 +so that my camera's name, model, + +194 +00:12:42,262 --> 00:12:45,566 +and manufacturer all have the proper name. + +195 +00:12:48,535 --> 00:12:51,538 +That's it. Let's compile and run it. + +196 +00:12:57,211 --> 00:13:00,881 +When I hit the Install button, + +197 +00:13:00,914 --> 00:13:04,051 +uh-oh, it fails. + +198 +00:13:04,084 --> 00:13:07,387 +That's because system extensions +can only be installed by apps + +199 +00:13:07,421 --> 00:13:09,923 +residing in /Applications. + +200 +00:13:09,957 --> 00:13:12,159 +Let's move it and try again. + +201 +00:13:26,507 --> 00:13:30,410 +This time, it succeeds. + +202 +00:13:30,444 --> 00:13:33,847 +I'm prompted to Allow +the blocked extension to install + +203 +00:13:33,881 --> 00:13:38,385 +by authenticating in System Settings, + +204 +00:13:38,418 --> 00:13:41,788 +where I find Privacy & Security, + +205 +00:13:41,822 --> 00:13:43,757 +and click the Allow button. + +206 +00:13:45,459 --> 00:13:48,462 +I authenticate with my password, + +207 +00:13:48,495 --> 00:13:53,033 +and then I see that my result has changed +to 0 for "no error." + +208 +00:13:53,066 --> 00:13:58,539 +If I use the systemextensionsctl +list tool, + +209 +00:13:58,572 --> 00:14:03,544 +I confirm that I've succeeded, +and now I have one extension active + +210 +00:14:03,577 --> 00:14:05,379 +on my system. + +211 +00:14:05,412 --> 00:14:09,249 +Now I can open any camera app +and find and admire my work. + +212 +00:14:10,918 --> 00:14:13,654 +Let's launch FaceTime. + +213 +00:14:13,687 --> 00:14:16,924 +ExampleCam shows up in the camera picker. + +214 +00:14:16,957 --> 00:14:20,127 +It sort of looks like the old Pong game +from the '70s, + +215 +00:14:20,160 --> 00:14:23,664 +drawing a horizontal white line that moves +up and down the frame + +216 +00:14:23,697 --> 00:14:25,265 +at 60 frames per second. + +217 +00:14:28,836 --> 00:14:32,840 +To get rid of the camera, +all I have to do is delete the app. + +218 +00:14:37,244 --> 00:14:41,648 +The system prompts me to confirm +that I'm also uninstalling the extension + +219 +00:14:41,682 --> 00:14:43,517 +by deleting the app. + +220 +00:14:49,122 --> 00:14:54,228 +The ExampleCam demo shows just how easy +it is make a software camera from scratch. + +221 +00:14:54,261 --> 00:14:58,298 +Now let's take it up a notch +by turning that software camera + +222 +00:14:58,332 --> 00:15:00,133 +into a creative camera. + +223 +00:15:02,736 --> 00:15:06,139 +I call this second example CIFilterCam. + +224 +00:15:06,173 --> 00:15:09,243 +The CI stands for CoreImage, + +225 +00:15:09,276 --> 00:15:11,778 +a framework +with all sorts of effects filters + +226 +00:15:11,812 --> 00:15:14,414 +that you can apply to stills or video. + +227 +00:15:15,549 --> 00:15:19,553 +To create CIFilterCam, +I began with the ExampleCam shell, + +228 +00:15:19,586 --> 00:15:22,456 +but decided to make the app +a configuration app + +229 +00:15:22,489 --> 00:15:24,925 +as well as an installer. + +230 +00:15:24,958 --> 00:15:30,097 +I've added a camera picker button, +a filter picker button, + +231 +00:15:30,130 --> 00:15:32,866 +and an effect bypass button. + +232 +00:15:32,900 --> 00:15:36,570 +I've also added a view +for live video preview. + +233 +00:15:36,603 --> 00:15:40,274 +This is a standard view +backed by an AVCaptureVideoPreviewLayer + +234 +00:15:40,307 --> 00:15:43,610 +to show you +what the Filter Camera is doing. + +235 +00:15:43,644 --> 00:15:46,847 +By unchecking the bypass button, + +236 +00:15:46,880 --> 00:15:50,951 +I can see various filters +applied to the video, + +237 +00:15:50,984 --> 00:15:56,123 +from color effects + +238 +00:15:56,156 --> 00:15:58,325 +to distortion filters. + +239 +00:16:02,563 --> 00:16:05,165 +I'm kind of partial +to the bump distortion. + +240 +00:16:06,533 --> 00:16:09,703 +I can apply these +to the built-in FaceTime camera + +241 +00:16:09,736 --> 00:16:13,273 +or to any physical camera +attached to my Mac. + +242 +00:16:15,309 --> 00:16:19,446 +I've got my iPhone nearby +set up as a Continuity Camera. + +243 +00:16:22,950 --> 00:16:24,618 +Let's use that. + +244 +00:16:29,223 --> 00:16:32,726 +The CIFilterCam app is nothing special +in and of itself. + +245 +00:16:32,759 --> 00:16:34,761 +Just an effects camera app. + +246 +00:16:34,795 --> 00:16:38,065 +Where it really gets interesting, though, +is when you realize that the app + +247 +00:16:38,098 --> 00:16:43,403 +is a front end to a virtual filter camera +that all apps can use. + +248 +00:16:43,437 --> 00:16:46,406 +I'll launch FaceTime and PhotoBooth + +249 +00:16:46,440 --> 00:16:50,811 +and make sure both of them are +pointed at the CIFilterCam. + +250 +00:16:50,844 --> 00:16:55,048 +Now, as I change filters +in my configuration app, + +251 +00:16:55,082 --> 00:16:58,685 +every app using CIFilterCam +changes in tandem. + +252 +00:16:59,887 --> 00:17:04,758 +If I pick a different source camera, +every camera app picks up the change. + +253 +00:17:06,059 --> 00:17:09,997 +Every button click in the app +translates to a simple property call + +254 +00:17:10,030 --> 00:17:15,602 +to the filter cam extension, telling it, +"Hey, extension, use this camera," + +255 +00:17:15,636 --> 00:17:18,872 +or, "Hey, extension, +use this other filter." + +256 +00:17:20,307 --> 00:17:22,342 +Or this other filter. + +257 +00:17:25,779 --> 00:17:27,281 +Or this other filter. + +258 +00:17:31,118 --> 00:17:34,321 +Support for running a hardware camera +inside your extension + +259 +00:17:34,354 --> 00:17:37,357 +requires macOS Ventura. + +260 +00:17:37,391 --> 00:17:42,863 +You also need to add +the com.apple.security.device.camera key + +261 +00:17:42,896 --> 00:17:45,132 +to your extension's entitlements file, + +262 +00:17:45,165 --> 00:17:49,203 +indicating that you will be using +another camera. + +263 +00:17:49,236 --> 00:17:52,639 +And since you'll be using a camera, +the user will be prompted + +264 +00:17:52,673 --> 00:17:56,343 +to grant permission to your extension, +so you must provide + +265 +00:17:56,376 --> 00:18:00,380 +an NSCameraUsageDescription +in your Info.plist. + +266 +00:18:01,715 --> 00:18:05,118 +That wraps up the basics +of building a camera extension. + +267 +00:18:05,152 --> 00:18:07,855 +Now let's move on to the APIs. + +268 +00:18:10,157 --> 00:18:13,493 +At the bottom of the stack +are daemon processes, + +269 +00:18:13,527 --> 00:18:17,264 +one for each first +or third party camera extension. + +270 +00:18:18,498 --> 00:18:22,870 +Within a camera app process, +there are several layers at play, + +271 +00:18:22,903 --> 00:18:25,372 +beginning with the private framework code + +272 +00:18:25,405 --> 00:18:29,343 +that talks to your camera extension +over IPC. + +273 +00:18:29,376 --> 00:18:31,845 +One level up is another private layer + +274 +00:18:31,879 --> 00:18:35,082 +that translates +CoreMedia IO Extension calls + +275 +00:18:35,115 --> 00:18:37,518 +to legacy DAL plug-in calls. + +276 +00:18:38,819 --> 00:18:40,187 +Up again, + +277 +00:18:40,220 --> 00:18:45,692 +we find the public CoreMedia IO APIs +that publish DAL plug-ins. + +278 +00:18:45,726 --> 00:18:48,462 +To the client of this interface, +there's no difference + +279 +00:18:48,495 --> 00:18:52,299 +between CoreMedia IO Extensions +and DAL plug-ins. + +280 +00:18:52,332 --> 00:18:55,435 +Everything looks like a DAL plug-in. + +281 +00:18:55,469 --> 00:18:58,972 +And finally, at the top is AVFoundation, + +282 +00:18:59,006 --> 00:19:01,675 +which is a client of CoreMedia IO. + +283 +00:19:01,708 --> 00:19:05,779 +It re-publishes DAL plug-ins +as AVCaptureDevices. + +284 +00:19:07,848 --> 00:19:12,219 +Contrast this with the legacy +DAL plug-in architecture. + +285 +00:19:12,252 --> 00:19:16,290 +DAL plug-ins may or may not include +a daemon piece, + +286 +00:19:16,323 --> 00:19:20,093 +but all of them run code +loaded by the CoreMedia IO framework + +287 +00:19:20,127 --> 00:19:22,596 +directly in the app process. + +288 +00:19:22,629 --> 00:19:25,732 +This leaves the app vulnerable to malware. + +289 +00:19:25,766 --> 00:19:30,838 +Camera extensions remove +this attack vector completely. + +290 +00:19:30,871 --> 00:19:35,843 +Your extension must be app sandboxed, +or it won't be allowed to run. + +291 +00:19:36,877 --> 00:19:40,147 +Apple's registerassistantservice +identifies it + +292 +00:19:40,180 --> 00:19:44,017 +by its CMIOExtensionMachServiceName + +293 +00:19:44,051 --> 00:19:49,156 +and launches it as a role user account +called _cmiodalassistants. + +294 +00:19:50,057 --> 00:19:54,728 +Sandboxd applies +a custom sandbox profile to your process. + +295 +00:19:54,761 --> 00:19:57,431 +It's tailored for camera use cases. + +296 +00:19:59,266 --> 00:20:02,669 +The custom sandbox profile +allows you to communicate + +297 +00:20:02,703 --> 00:20:06,006 +over the common hardware interfaces +you would expect. + +298 +00:20:06,039 --> 00:20:10,744 +USB, Bluetooth, WiFi– + +299 +00:20:10,777 --> 00:20:15,148 +as a client but not a server +that opens ports– + +300 +00:20:15,182 --> 00:20:17,518 +and even Firewire. + +301 +00:20:17,551 --> 00:20:23,056 +It also allows your extension to read +and write from its own container and tmp. + +302 +00:20:24,424 --> 00:20:29,563 +The camera extension sandbox profile +is more locked down than a regular app. + +303 +00:20:29,596 --> 00:20:33,367 +Some examples of things you can't do are + +304 +00:20:33,400 --> 00:20:38,505 +forking, exec'ing, or posix spawning +a child process, + +305 +00:20:38,539 --> 00:20:41,308 +accessing the window server, + +306 +00:20:41,341 --> 00:20:44,811 +making a connection +to the foreground user account, + +307 +00:20:44,845 --> 00:20:49,349 +or registering your own mach services +in the global namespace. + +308 +00:20:51,118 --> 00:20:54,955 +If, as you develop your extension, +you find the sandbox too restrictive + +309 +00:20:54,988 --> 00:20:57,491 +for a legitimate capture case, + +310 +00:20:57,524 --> 00:21:00,727 +please provide us feedback +through Feedback Assistant + +311 +00:21:00,761 --> 00:21:04,064 +and we'll carefully consider +loosening restrictions. + +312 +00:21:04,097 --> 00:21:06,333 +The earlier architecture diagram + +313 +00:21:06,366 --> 00:21:08,836 +showed your camera extension's +daemon process + +314 +00:21:08,869 --> 00:21:12,239 +passing buffers directly to the app layer. + +315 +00:21:12,272 --> 00:21:15,175 +There's actually one more layer +of security involved. + +316 +00:21:17,044 --> 00:21:20,681 +Between your daemon and the app +is a proxy service + +317 +00:21:20,714 --> 00:21:23,851 +called registerassistantservice. + +318 +00:21:23,884 --> 00:21:29,022 +It enforces transparency, +consent, and control policy. + +319 +00:21:29,056 --> 00:21:32,192 +When an app tries to use a camera +for the first time, + +320 +00:21:32,226 --> 00:21:35,062 +the system asks the user if it's okay. + +321 +00:21:35,095 --> 00:21:37,865 +That consent needs to be granted +for all cameras, + +322 +00:21:37,898 --> 00:21:40,000 +not just the built-in ones. + +323 +00:21:40,033 --> 00:21:43,537 +The proxy service handles +this consent on your behalf. + +324 +00:21:43,570 --> 00:21:46,039 +If the user has denied camera access, + +325 +00:21:46,073 --> 00:21:50,043 +the proxy stops buffers +from going to that app. + +326 +00:21:50,077 --> 00:21:53,747 +It also handles attribution– +it lets the system know + +327 +00:21:53,780 --> 00:21:57,551 +that a particular camera is in use +by a particular app + +328 +00:21:57,584 --> 00:22:00,153 +so that power consumed by your daemon + +329 +00:22:00,187 --> 00:22:03,190 +can be attributed to the app +that's using your camera. + +330 +00:22:04,691 --> 00:22:08,495 +CoreMedia IO Extensions have +four main classes: + +331 +00:22:08,529 --> 00:22:12,499 +Provider, Device, and Stream. + +332 +00:22:13,534 --> 00:22:16,970 +Providers have devices +and devices have streams, + +333 +00:22:17,004 --> 00:22:20,374 +and all three of them can have properties. + +334 +00:22:22,843 --> 00:22:25,345 +You create +each of these three main classes + +335 +00:22:25,379 --> 00:22:27,881 +by providing a source, + +336 +00:22:27,915 --> 00:22:33,187 +respectively, a ProviderSource, +DeviceSource, and StreamSource. + +337 +00:22:35,155 --> 00:22:39,026 +The ExtensionProvider is +your lowest level object. + +338 +00:22:39,059 --> 00:22:44,097 +It lets you add and remove devices +as needed, such as for hot plug events. + +339 +00:22:45,599 --> 00:22:49,403 +It gets informed of the client processes +as they try to connect, + +340 +00:22:49,436 --> 00:22:53,006 +which gives you an opportunity +to limit your device publishing + +341 +00:22:53,040 --> 00:22:54,875 +to certain apps. + +342 +00:22:54,908 --> 00:22:59,580 +It also consults your provider source +object for property implementations. + +343 +00:23:01,882 --> 00:23:05,853 +Here's what your extension's +main entry point might look like. + +344 +00:23:05,886 --> 00:23:08,822 +You create your own +ExtensionProviderSource, + +345 +00:23:08,856 --> 00:23:13,193 +which conforms to +the CMIOExtensionProviderSource protocol + +346 +00:23:13,227 --> 00:23:16,230 +and creates an ExtensionProvider. + +347 +00:23:16,263 --> 00:23:19,499 +To start your service, +you call the provider class method + +348 +00:23:19,533 --> 00:23:23,003 +startService +and pass your provider instance. + +349 +00:23:25,038 --> 00:23:29,209 +ExtensionProvider implements +two read only properties + +350 +00:23:29,243 --> 00:23:32,379 +that do not change +for the life of your extension. + +351 +00:23:32,412 --> 00:23:36,283 +The manufacturer +and the name of your provider. + +352 +00:23:36,316 --> 00:23:38,185 +Both of these are strings. + +353 +00:23:40,087 --> 00:23:44,191 +Next up is the CMIOExtensionDevice. + +354 +00:23:44,224 --> 00:23:48,762 +It manage streams, +adding or removing them as needed. + +355 +00:23:48,795 --> 00:23:51,331 +Your device can present multiple streams, + +356 +00:23:51,365 --> 00:23:56,003 +but be aware that AVFoundation ignores +all but the first input stream. + +357 +00:23:57,237 --> 00:24:01,875 +When you create a device, +you provide a device source, + +358 +00:24:01,909 --> 00:24:07,681 +as well as a localized name, +a deviceID as a UUID, + +359 +00:24:07,714 --> 00:24:11,518 +and, optionally, a legacyID string. + +360 +00:24:11,552 --> 00:24:14,988 +These properties percolate +all the way up to AVFoundation. + +361 +00:24:16,723 --> 00:24:19,726 +Your device's localizedName becomes + +362 +00:24:19,760 --> 00:24:23,730 +the AVCaptureDevice's localizedName. + +363 +00:24:23,764 --> 00:24:29,169 +Your specified deviceID becomes +the AVCaptureDevice's uniqueIdentifier, + +364 +00:24:29,203 --> 00:24:34,608 +unless you also provide a legacyDeviceID. + +365 +00:24:34,641 --> 00:24:38,278 +You only need to provide this +if you're modernizing a DAL plug-in + +366 +00:24:38,312 --> 00:24:40,647 +and need to maintain +backward compatibility + +367 +00:24:40,681 --> 00:24:43,717 +with the uniqueIdentifier +you've previously shipped. + +368 +00:24:44,751 --> 00:24:47,287 +If you provide a legacyDeviceID, + +369 +00:24:47,321 --> 00:24:50,891 +AVCaptureDevice will use it +as the uniqueIdentifier. + +370 +00:24:52,659 --> 00:24:59,066 +You create your CMIOExtensionDevice +with a CMIOExtensionDeviceSource, + +371 +00:24:59,099 --> 00:25:01,969 +which may optionally implement +other properties, + +372 +00:25:02,002 --> 00:25:04,471 +such as deviceModel, + +373 +00:25:04,505 --> 00:25:08,775 +which should be the same +for all cameras of the same model. + +374 +00:25:08,809 --> 00:25:12,379 +isSuspended should be implemented +if your device can enter + +375 +00:25:12,412 --> 00:25:16,950 +a suspended state, +such as if it has a privacy iris. + +376 +00:25:16,984 --> 00:25:20,621 +The built-in cameras on Apple laptops +enter the suspended state + +377 +00:25:20,654 --> 00:25:24,157 +when the clamshell is closed. + +378 +00:25:24,191 --> 00:25:27,961 +Your device's transport type +reveals how it's connected, + +379 +00:25:27,995 --> 00:25:32,332 +such as via USB, Bluetooth, or Firewire. + +380 +00:25:33,967 --> 00:25:37,838 +Lastly, if you have a microphone +physically paired with your camera, + +381 +00:25:37,871 --> 00:25:40,908 +you can expose it as a linked device. + +382 +00:25:40,941 --> 00:25:44,278 +All of these properties are read only. + +383 +00:25:44,311 --> 00:25:48,715 +Next up is the all-important +CMIOExtensionStream, + +384 +00:25:48,749 --> 00:25:52,819 +which does the heavy lifting +in the CMIOExtension. + +385 +00:25:52,853 --> 00:25:58,058 +It publishes video formats +and defines their valid frame rates + +386 +00:25:58,091 --> 00:26:01,461 +and configures the active format. + +387 +00:26:01,495 --> 00:26:04,965 +It uses a standard clock, +such as the host time clock, + +388 +00:26:04,998 --> 00:26:07,534 +or provides its own custom clock + +389 +00:26:07,568 --> 00:26:11,438 +to drive the timing +of each buffer it produces. + +390 +00:26:11,471 --> 00:26:16,043 +And most importantly, +it sends sample buffers to clients. + +391 +00:26:18,445 --> 00:26:21,381 +Your extension stream source publishes + +392 +00:26:21,415 --> 00:26:24,051 +CMIOExtensionStreamFormats. + +393 +00:26:24,084 --> 00:26:28,488 +Those become AVCaptureDeviceFormats. + +394 +00:26:28,522 --> 00:26:31,758 +Clients can read and write +the active format index + +395 +00:26:31,792 --> 00:26:33,827 +to change the active format. + +396 +00:26:35,329 --> 00:26:39,633 +The frame duration, which is equivalent +to max frame rate. + +397 +00:26:39,666 --> 00:26:44,338 +And max frame duration, +which is the same as min frame rate. + +398 +00:26:46,273 --> 00:26:51,912 +The DAL plug-in world exposes +a fourth interface called DAL controls. + +399 +00:26:51,945 --> 00:26:55,949 +Plug-in developers use these to expose +features such as auto exposure, + +400 +00:26:55,983 --> 00:27:00,687 +brightness, sharpness, pan and zoom, +et cetera. + +401 +00:27:00,721 --> 00:27:04,591 +While powerful, +they've been implemented inconsistently, + +402 +00:27:04,625 --> 00:27:08,195 +so it's difficult for app developers +to use them. + +403 +00:27:08,228 --> 00:27:13,233 +In the CMIOExtension architecture, +we don't offer a DAL control replacement. + +404 +00:27:13,267 --> 00:27:15,936 +Instead, everything is a property. + +405 +00:27:17,471 --> 00:27:21,074 +You've already learned about +many standard properties at the provider, + +406 +00:27:21,108 --> 00:27:23,577 +device, and stream level. + +407 +00:27:23,610 --> 00:27:25,746 +You can also make +your own custom properties + +408 +00:27:25,779 --> 00:27:30,784 +and propagate them to the app layer, +just as I did in the CIFilterCam demo. + +409 +00:27:32,019 --> 00:27:36,423 +CoreMedia IO's C property interface +uses a C struct + +410 +00:27:36,456 --> 00:27:41,595 +to identify a property's selector, +scope, and element. + +411 +00:27:41,628 --> 00:27:43,630 +These are considered its address. + +412 +00:27:45,132 --> 00:27:49,169 +The selector is the name of the property +as a four-character code, + +413 +00:27:49,203 --> 00:27:53,040 +such as cust for custom. + +414 +00:27:53,073 --> 00:27:56,944 +The scope can be global, input, or output, + +415 +00:27:56,977 --> 00:28:00,047 +and the element can be +any number you want. + +416 +00:28:00,080 --> 00:28:03,383 +The main element is always zero. + +417 +00:28:03,417 --> 00:28:07,621 +CMIOExtensions let you bridge +your properties to the old world + +418 +00:28:07,654 --> 00:28:12,559 +by coding property address elements +into a custom property name. + +419 +00:28:12,593 --> 00:28:17,397 +First, the characters 4cc_, + +420 +00:28:17,431 --> 00:28:20,234 +then the selector, scope, and element + +421 +00:28:20,267 --> 00:28:24,037 +as four character codes +separated by underscores. + +422 +00:28:24,071 --> 00:28:29,576 +Using this method, you can communicate +any string or data value to the app layer. + +423 +00:28:30,644 --> 00:28:33,547 +AVFoundation doesn't work +with custom properties, + +424 +00:28:33,580 --> 00:28:37,684 +so you must stick +to the CoreMedia IO C API + +425 +00:28:37,718 --> 00:28:41,722 +if your configuration app needs +to work with custom properties. + +426 +00:28:41,755 --> 00:28:44,691 +That's our high-level look at the API. + +427 +00:28:44,725 --> 00:28:47,761 +Now let's talk about output devices. + +428 +00:28:49,329 --> 00:28:53,267 +A lesser known feature of DAL plug-ins +is their ability to present + +429 +00:28:53,300 --> 00:28:56,737 +the opposite of a camera–an output device– + +430 +00:28:56,770 --> 00:29:01,675 +which consumes video from an app +in real time rather than provides it. + +431 +00:29:01,708 --> 00:29:04,611 +This is the "O" part of CoreMedia IO. + +432 +00:29:04,645 --> 00:29:07,648 +Input and Output. + +433 +00:29:07,681 --> 00:29:11,451 +Output devices +are common in the pro video world. + +434 +00:29:11,485 --> 00:29:16,023 +Some common uses are print-to-tape, +where a video signal is sent + +435 +00:29:16,056 --> 00:29:20,427 +to an external recorder, +or real-time preview monitoring, + +436 +00:29:20,460 --> 00:29:23,730 +such as on a pro deck with SDI inputs. + +437 +00:29:25,232 --> 00:29:28,502 +One important thing to note +is that output devices have + +438 +00:29:28,535 --> 00:29:31,672 +no AVFoundation API equivalent. + +439 +00:29:31,705 --> 00:29:33,674 +To send frames to an output device, + +440 +00:29:33,707 --> 00:29:38,011 +you must use the CoreMedia IO C API +directly. + +441 +00:29:39,313 --> 00:29:43,283 +CMIOExtension streams are created +with a direction + +442 +00:29:43,317 --> 00:29:47,254 +of either source or sink. + +443 +00:29:47,287 --> 00:29:50,624 +Sink streams consume data from an app. + +444 +00:29:50,657 --> 00:29:56,296 +Clients feed your sink stream by inserting +sample buffers into a simple queue. + +445 +00:29:56,330 --> 00:30:01,235 +That translates to a consumeSampleBuffer +call in your extension, + +446 +00:30:01,268 --> 00:30:04,404 +and once you've consumed that buffer, +you notify them + +447 +00:30:04,438 --> 00:30:07,741 +with notifyScheduledOutputChanged. + +448 +00:30:09,576 --> 00:30:13,847 +There are a number of stream properties +specific to output devices. + +449 +00:30:13,881 --> 00:30:18,952 +They mainly deal with the queue sizing, +how many frames to buffer before starting, + +450 +00:30:18,986 --> 00:30:22,055 +and signaling +when all data has been consumed. + +451 +00:30:23,557 --> 00:30:27,261 +Now on to our fifth +and final topic of the day. + +452 +00:30:28,929 --> 00:30:31,932 +Earlier in the presentation, +I showed this diagram + +453 +00:30:31,965 --> 00:30:33,867 +of the DAL plug-in architecture + +454 +00:30:33,901 --> 00:30:36,937 +and I highlighted +its many security problems. + +455 +00:30:36,970 --> 00:30:40,140 +We've addressed these shortcomings +with Camera Extensions + +456 +00:30:40,174 --> 00:30:43,243 +and are fully committed +to their continued development. + +457 +00:30:43,277 --> 00:30:46,280 +They are the path forward. + +458 +00:30:46,313 --> 00:30:49,316 +So what does that mean for DAL plug-ins? + +459 +00:30:49,349 --> 00:30:51,552 +It means the end is near. + +460 +00:30:53,253 --> 00:30:58,358 +As of macOS 12.3, +DAL plug-ins are already deprecated, + +461 +00:30:58,392 --> 00:31:01,395 +so you get a compilation warning +when building. + +462 +00:31:01,428 --> 00:31:04,498 +That's a good start, but it's not enough. + +463 +00:31:04,531 --> 00:31:07,367 +As long as legacy DAL plug-ins +are allowed to load, + +464 +00:31:07,401 --> 00:31:09,937 +camera apps will still be at risk. + +465 +00:31:11,872 --> 00:31:15,175 +To fully address security vulnerabilities +and make the system + +466 +00:31:15,209 --> 00:31:20,581 +more robust for all users, +we plan to disable DAL plug-ins entirely + +467 +00:31:20,614 --> 00:31:24,117 +in the next major release +after macOS Ventura. + +468 +00:31:26,320 --> 00:31:28,388 +What does this mean for you? + +469 +00:31:28,422 --> 00:31:30,824 +Well, we hope the message is clear. + +470 +00:31:30,858 --> 00:31:33,060 +If you currently maintain a DAL plug-in, + +471 +00:31:33,093 --> 00:31:37,297 +now is the time to begin +porting your code to a Camera Extension. + +472 +00:31:38,398 --> 00:31:41,602 +And please, +let us know what friction you encounter. + +473 +00:31:41,635 --> 00:31:45,939 +We are eager to address these issues +and provide a rich feature set. + +474 +00:31:45,973 --> 00:31:48,509 +We really look forward +to working with you. + +475 +00:31:48,542 --> 00:31:53,013 +This concludes today's presentation +on camera extensions for macOS. + +476 +00:31:53,046 --> 00:31:56,617 +We can't wait to see what fresh +and creative camera experiences + +477 +00:31:56,650 --> 00:31:58,085 +you'll bring to the Mac. + +478 +00:31:58,118 --> 00:32:00,954 +And hope you have fun doing it. + diff --git a/eng/2022 Session 10023 What's new in the Photos picker en.srt b/eng/2022 Session 10023 What's new in the Photos picker en.srt new file mode 100644 index 0000000..89143de --- /dev/null +++ b/eng/2022 Session 10023 What's new in the Photos picker en.srt @@ -0,0 +1,1237 @@ +1 +00:00:00,067 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,610 +♪ + +3 +00:00:09,610 --> 00:00:11,245 +Hello! I'm Justin, + +4 +00:00:11,245 --> 00:00:13,280 +and I'm an engineer +on the Photos team. + +5 +00:00:13,280 --> 00:00:15,983 +Today, I would like to talk +about some of the improvements + +6 +00:00:15,983 --> 00:00:18,619 +we have made +to the system Photos picker. + +7 +00:00:18,619 --> 00:00:21,388 +The system Photos picker +is the best way for most apps + +8 +00:00:21,388 --> 00:00:24,224 +to access photos +and videos on iOS. + +9 +00:00:24,224 --> 00:00:26,960 +The picker runs out of process, +so your app doesn't need + +10 +00:00:26,960 --> 00:00:29,796 +to request any library access +to use it. + +11 +00:00:29,796 --> 00:00:33,734 +It has an intuitive UI +and an easy-to-use API. + +12 +00:00:33,734 --> 00:00:36,670 +If you aren't familiar +with the PHPicker API, + +13 +00:00:36,670 --> 00:00:39,840 +you can watch our previous +years' WWDC sessions + +14 +00:00:39,840 --> 00:00:43,343 +where we talked +about it in depth. + +15 +00:00:43,343 --> 00:00:46,413 +In today's session, +I'll start with an overview + +16 +00:00:46,413 --> 00:00:50,083 +of the new features +we added to the picker. + +17 +00:00:50,083 --> 00:00:53,353 +Then, I will talk about +additional platforms + +18 +00:00:53,353 --> 00:00:56,857 +and frameworks +the picker now supports. + +19 +00:00:56,857 --> 00:00:59,927 +All right, let's dive in. + +20 +00:00:59,927 --> 00:01:02,863 +The picker supports filtering +between images, videos, + +21 +00:01:02,863 --> 00:01:06,333 +and Live Photos +since its introduction. + +22 +00:01:06,333 --> 00:01:08,669 +However, we understand +that some of your apps + +23 +00:01:08,669 --> 00:01:11,138 +may have some other requirements. + +24 +00:01:11,138 --> 00:01:13,974 +For example, +a screenshot-stitching app + +25 +00:01:13,974 --> 00:01:17,210 +wants to only show +screenshots in the picker. + +26 +00:01:17,210 --> 00:01:20,213 +Now it is possible +with the new screenshots filter + +27 +00:01:20,213 --> 00:01:23,583 +we added this year. + +28 +00:01:23,583 --> 00:01:27,587 +In addition to screenshots, +we added other asset types + +29 +00:01:27,587 --> 00:01:31,725 +like screen recordings +and slo-mo videos. + +30 +00:01:31,725 --> 00:01:34,027 +There is also a way for you +to create a new filter + +31 +00:01:34,027 --> 00:01:38,532 +using PHAsset.PlaybackStyle. + +32 +00:01:38,532 --> 00:01:42,970 +Other than Cinematic videos, +depth effect photos, and bursts, + +33 +00:01:42,970 --> 00:01:45,706 +all of the new filters +are backported. + +34 +00:01:45,706 --> 00:01:48,408 +If your app +is targeting iOS 15, + +35 +00:01:48,408 --> 00:01:50,877 +you can still use them +as long as you are compiling + +36 +00:01:50,877 --> 00:01:53,847 +with the iOS 16 SDK. + +37 +00:01:53,847 --> 00:01:58,218 +For compound filters, in +addition to the existing "any," + +38 +00:01:58,218 --> 00:02:00,954 +now you also use +"all" and "not." + +39 +00:02:00,954 --> 00:02:04,358 +They are also backported +to iOS 15. + +40 +00:02:04,358 --> 00:02:06,994 +Let's look at some +code examples. + +41 +00:02:06,994 --> 00:02:12,299 +To show videos and Live Photos, +you can combine them with "any." + +42 +00:02:12,299 --> 00:02:17,404 +Or you may only want +to show screenshots. + +43 +00:02:17,404 --> 00:02:20,140 +To show all images +without screenshots, + +44 +00:02:20,140 --> 00:02:22,442 +you can combine images +and screenshots filters + +45 +00:02:22,442 --> 00:02:25,612 +using "all" and "not." + +46 +00:02:25,612 --> 00:02:29,149 +And the last example, you can +use .cinematicVideos filter + +47 +00:02:29,149 --> 00:02:32,285 +if you are targeting iOS 16. + +48 +00:02:32,285 --> 00:02:35,122 +Next, let's talk +about improvements + +49 +00:02:35,122 --> 00:02:37,624 +related to the half-height +picker. + +50 +00:02:37,624 --> 00:02:41,995 +In iOS 15, UIKit has a new +UISheetPresentationController + +51 +00:02:41,995 --> 00:02:47,300 +API which you can use to present +the picker in half-height mode. + +52 +00:02:47,300 --> 00:02:50,637 +It already works great +in many cases. + +53 +00:02:50,637 --> 00:02:53,807 +But some of you may want +to implement a custom UI + +54 +00:02:53,807 --> 00:02:56,543 +to adjust selected assets +and have those changes + +55 +00:02:56,543 --> 00:03:00,447 +be reflected back in picker. + +56 +00:03:00,447 --> 00:03:03,650 +In iOS 16, +you can deselect assets + +57 +00:03:03,650 --> 00:03:07,320 +using their asset identifiers. + +58 +00:03:07,320 --> 00:03:10,857 +As shown here, the second photo +is no longer selected + +59 +00:03:10,857 --> 00:03:16,696 +in the picker after +deselectAssets is called. + +60 +00:03:16,696 --> 00:03:21,568 +You can also call the moveAsset +method to reorder assets. + +61 +00:03:21,568 --> 00:03:24,704 +Now we are familiar with +all of the new picker features, + +62 +00:03:24,704 --> 00:03:27,574 +let's talk about +platform support. + +63 +00:03:27,574 --> 00:03:30,577 +Currently, the system Photos +picker can only be used + +64 +00:03:30,577 --> 00:03:33,814 +by iOS and iPadOS apps. + +65 +00:03:33,814 --> 00:03:35,982 +This year, we are bringing +the Photos picker + +66 +00:03:35,982 --> 00:03:41,154 +to two additional platforms: +macOS and watchOS. + +67 +00:03:41,154 --> 00:03:44,157 +The iPadOS picker is also +updated with a new design + +68 +00:03:44,157 --> 00:03:46,493 +just for the iPad. + +69 +00:03:46,493 --> 00:03:49,696 +Let's take a look +at the new iPad UI first. + +70 +00:03:49,696 --> 00:03:51,398 +The picker now shows a sidebar + +71 +00:03:51,398 --> 00:03:55,202 +to take advantage +of the larger iPad display. + +72 +00:03:55,202 --> 00:03:57,070 +The sidebar allows +faster navigation + +73 +00:03:57,070 --> 00:03:59,239 +between different collections. + +74 +00:03:59,239 --> 00:04:01,274 +But if there is not +enough space, + +75 +00:04:01,274 --> 00:04:03,677 +like in Split Screen mode, + +76 +00:04:03,677 --> 00:04:08,215 +we will fall back to the +existing compact picker UI. + +77 +00:04:08,215 --> 00:04:11,718 +Next, macOS. + +78 +00:04:11,718 --> 00:04:16,056 +The macOS picker also has a +sidebar with Mac-style controls. + +79 +00:04:16,056 --> 00:04:20,527 +And just like the iOS picker, +it supports multiple selection, + +80 +00:04:20,527 --> 00:04:23,930 +fluid zooming in the grid, and +has a powerful search feature + +81 +00:04:23,930 --> 00:04:27,434 +which allows you to search +for things like people, places, + +82 +00:04:27,434 --> 00:04:30,070 +and a lot more. + +83 +00:04:30,070 --> 00:04:34,407 +The new picker UI is also +available in NSOpenPanel. + +84 +00:04:34,407 --> 00:04:38,278 +You can use it to select assets +from the system photo library, + +85 +00:04:38,278 --> 00:04:39,846 +and for the first time, + +86 +00:04:39,846 --> 00:04:43,617 +including those assets +stored in iCloud Photos. + +87 +00:04:43,617 --> 00:04:46,086 +Your app will get +the new UI for free + +88 +00:04:46,086 --> 00:04:49,856 +without needing to do +any adoption work. + +89 +00:04:49,856 --> 00:04:54,227 +Drag and drop is supported +in the NSOpenPanel picker. + +90 +00:04:54,227 --> 00:04:57,164 +It's also supported +in the standard picker on iOS, + +91 +00:04:57,164 --> 00:05:00,734 +iPadOS, and macOS. + +92 +00:05:00,734 --> 00:05:04,838 +If your app only needs to select +a few images or videos, + +93 +00:05:04,838 --> 00:05:08,742 +the NSOpenPanel API +may be all you need. + +94 +00:05:08,742 --> 00:05:11,244 +But keep in mind that selected +files in the photo library + +95 +00:05:11,244 --> 00:05:15,115 +may be deleted by the system +at any time. + +96 +00:05:15,115 --> 00:05:18,118 +You should copy them to +a location managed by your app + +97 +00:05:18,118 --> 00:05:23,557 +if you need to ensure their +availability in the long term. + +98 +00:05:23,557 --> 00:05:28,094 +For media-centric macOS apps, +we encourage you to default + +99 +00:05:28,094 --> 00:05:32,699 +to the new Photos picker +for the best user experience. + +100 +00:05:32,699 --> 00:05:37,237 +However, your app should still +provide an alternative way + +101 +00:05:37,237 --> 00:05:39,573 +to select assets +from the file system + +102 +00:05:39,573 --> 00:05:42,842 +using the NSOpenPanel API. + +103 +00:05:42,842 --> 00:05:46,479 +Sometimes your customers +may still want to select assets + +104 +00:05:46,479 --> 00:05:50,984 +outside their photo libraries. + +105 +00:05:50,984 --> 00:05:55,522 +Last but not least, +let's talk about watchOS. + +106 +00:05:55,522 --> 00:05:58,191 +For the first time, +you can have access to images + +107 +00:05:58,191 --> 00:06:01,561 +stored on the watch +using a new API. + +108 +00:06:01,561 --> 00:06:04,264 +The watchOS picker +also runs out of process, + +109 +00:06:04,264 --> 00:06:06,933 +like the iOS +and the macOS picker, + +110 +00:06:06,933 --> 00:06:10,770 +so you don't need to request +any library access to use it. + +111 +00:06:10,770 --> 00:06:13,373 +It has a UI +similar to the iOS picker + +112 +00:06:13,373 --> 00:06:17,177 +but optimized +for the smaller screen. + +113 +00:06:17,177 --> 00:06:21,681 +You can browse your photos +in the grid or by collections. + +114 +00:06:21,681 --> 00:06:24,784 +You can configure the picker +to show the selection order, + +115 +00:06:24,784 --> 00:06:29,356 +as well as specifying +a selection limit. + +116 +00:06:29,356 --> 00:06:32,626 +However, unlike iOS and macOS, + +117 +00:06:32,626 --> 00:06:36,696 +only images will be displayed +in the watchOS picker. + +118 +00:06:36,696 --> 00:06:39,733 +If the device has more +than 500 images, + +119 +00:06:39,733 --> 00:06:43,370 +only the most recent 500 +will be shown. + +120 +00:06:43,370 --> 00:06:45,005 +You may be wondering, + +121 +00:06:45,005 --> 00:06:48,041 +"PHPickerViewController +is UIKit based. + +122 +00:06:48,041 --> 00:06:52,545 +How can I use it for my macOS +or watchOS app?" + +123 +00:06:52,545 --> 00:06:54,547 +As you may have guessed, + +124 +00:06:54,547 --> 00:07:00,687 +new picker APIs are now +available in AppKit and SwiftUI. + +125 +00:07:00,687 --> 00:07:03,690 +Let's take a look +at the AppKit API first. + +126 +00:07:03,690 --> 00:07:07,961 +Actually, it is very similar +to the UIKit API. + +127 +00:07:07,961 --> 00:07:11,264 +You have access to the same +PHPickerConfiguration type + +128 +00:07:11,264 --> 00:07:13,767 +and its properties. + +129 +00:07:13,767 --> 00:07:16,336 +There is only +a small difference: + +130 +00:07:16,336 --> 00:07:19,706 +PHPickerViewController +is a NSViewController subclass + +131 +00:07:19,706 --> 00:07:23,710 +for AppKit apps. + +132 +00:07:23,710 --> 00:07:27,480 +With the introduction +of the AppKit-based PHPicker, + +133 +00:07:27,480 --> 00:07:31,785 +it's time to move away from the +legacy media library browser. + +134 +00:07:31,785 --> 00:07:35,121 +PHPicker is a lot more powerful. + +135 +00:07:35,121 --> 00:07:38,158 +It's also easier to maintain +if you are working on both + +136 +00:07:38,158 --> 00:07:43,730 +UIKit and AppKit apps +at the same time. + +137 +00:07:43,730 --> 00:07:47,734 +Finally, it's time to talk about +the SwiftUI API. + +138 +00:07:49,969 --> 00:07:53,540 +Remember the iOS picker you saw +at the beginning of the session? + +139 +00:07:53,540 --> 00:07:58,044 +It can be presented with only +a few lines of SwiftUI code. + +140 +00:07:58,044 --> 00:08:00,513 +More importantly, +you have access + +141 +00:08:00,513 --> 00:08:03,016 +to the SwiftUI PhotosPicker API + +142 +00:08:03,016 --> 00:08:06,219 +on all picker-supported +platforms: + +143 +00:08:06,219 --> 00:08:10,957 +iOS, iPadOS, macOS, and watchOS. + +144 +00:08:10,957 --> 00:08:13,693 +The picker will automatically +choose the best layout + +145 +00:08:13,693 --> 00:08:17,297 +depending on the platform, +your app's configuration, + +146 +00:08:17,297 --> 00:08:20,133 +and available screen space. + +147 +00:08:20,133 --> 00:08:23,069 +You don't need to worry about +what the picker UI should be, + +148 +00:08:23,069 --> 00:08:26,639 +so you just can focus +on making your app better. + +149 +00:08:29,409 --> 00:08:32,979 +Before we look at the new API +in detail via a demo, + +150 +00:08:32,979 --> 00:08:34,481 +we should talk about how to load + +151 +00:08:34,481 --> 00:08:38,318 +selected photos +and videos first. + +152 +00:08:38,318 --> 00:08:42,055 +The selection you receive +through the SwiftUI binding + +153 +00:08:42,055 --> 00:08:45,458 +only contains +placeholder objects. + +154 +00:08:45,458 --> 00:08:50,029 +You still need to load +actual asset data on demand. + +155 +00:08:50,029 --> 00:08:51,931 +Keep in mind +that some asset data + +156 +00:08:51,931 --> 00:08:54,634 +won't be loaded immediately. + +157 +00:08:54,634 --> 00:08:56,836 +The load operation +could also fail + +158 +00:08:56,836 --> 00:08:58,938 +if an error was encountered, + +159 +00:08:58,938 --> 00:09:02,075 +for example, when the picker +was trying to download data + +160 +00:09:02,075 --> 00:09:05,512 +from iCloud Photos +but the device was not connected + +161 +00:09:05,512 --> 00:09:08,014 +to the internet. + +162 +00:09:08,014 --> 00:09:12,485 +Some large files like videos may +take a long time to download, + +163 +00:09:12,485 --> 00:09:16,456 +so we recommend you to show +a per-item inline loading UI + +164 +00:09:16,456 --> 00:09:21,060 +instead of a blocking +loading indicator. + +165 +00:09:21,060 --> 00:09:23,563 +The PhotosPicker +uses Transferable, + +166 +00:09:23,563 --> 00:09:26,766 +which is a new SwiftUI protocol +for transferring data + +167 +00:09:26,766 --> 00:09:29,903 +between apps and extensions. + +168 +00:09:29,903 --> 00:09:33,506 +You can load SwiftUI Image +via Transferable directly, + +169 +00:09:33,506 --> 00:09:35,809 +but for advanced use cases, + +170 +00:09:35,809 --> 00:09:37,844 +you should define +your own model objects + +171 +00:09:37,844 --> 00:09:40,280 +conforming to +the Transferable protocol + +172 +00:09:40,280 --> 00:09:44,918 +to fully control the type +of data you want to load. + +173 +00:09:44,918 --> 00:09:47,687 +For more information +about Transferable, + +174 +00:09:47,687 --> 00:09:50,890 +you can check out +the "Meet Transferable" session. + +175 +00:09:50,890 --> 00:09:54,861 +If your app needs to deal with +a lot of items at the same time, + +176 +00:09:54,861 --> 00:09:57,530 +or large assets like videos, + +177 +00:09:57,530 --> 00:10:00,633 +it may not be feasible +to load everything in memory + +178 +00:10:00,633 --> 00:10:03,169 +at the same time. + +179 +00:10:03,169 --> 00:10:08,007 +To reduce memory usage, you can +use FileTransferRepresentation + +180 +00:10:08,007 --> 00:10:11,711 +to load selected assets +as files. + +181 +00:10:11,711 --> 00:10:13,646 +When loading assets as files, + +182 +00:10:13,646 --> 00:10:16,216 +keep in mind +that your app is responsible + +183 +00:10:16,216 --> 00:10:19,219 +for managing their lifecycles. + +184 +00:10:19,219 --> 00:10:21,821 +Files should always be copied +to your app directory + +185 +00:10:21,821 --> 00:10:27,227 +when received and deleted +when they are no longer needed. + +186 +00:10:27,227 --> 00:10:30,163 +OK, it's time for the demo! + +187 +00:10:30,163 --> 00:10:31,931 +I have already set up +this demo app + +188 +00:10:31,931 --> 00:10:34,634 +showing an account profile page. + +189 +00:10:34,634 --> 00:10:39,205 +Right now the profile image +is just a placeholder icon. + +190 +00:10:39,205 --> 00:10:42,675 +We want to add an edit button +to change the profile image + +191 +00:10:42,675 --> 00:10:45,011 +using the PhotosPicker API. + +192 +00:10:45,011 --> 00:10:47,013 +The profile image view +can already respond + +193 +00:10:47,013 --> 00:10:50,383 +to the image state +defined in our view model, + +194 +00:10:50,383 --> 00:10:52,719 +so we just need to update +the image state + +195 +00:10:52,719 --> 00:10:56,189 +when a picker selection +is received. + +196 +00:10:56,189 --> 00:10:58,591 +First, let's go +to our view model + +197 +00:10:58,591 --> 00:11:01,494 +and add a new +imageSelection property. + +198 +00:11:01,494 --> 00:11:04,030 +It will be passed +to the PhotosPicker API + +199 +00:11:04,030 --> 00:11:07,300 +as the selection binding. + +200 +00:11:07,300 --> 00:11:10,937 +Now we can go back +to our profile image view, + +201 +00:11:10,937 --> 00:11:14,340 +and add an overlay button +that brings up the picker. + +202 +00:11:19,212 --> 00:11:22,682 +OK, let's pause for a second +and take a look at the code + +203 +00:11:22,682 --> 00:11:24,384 +we just added. + +204 +00:11:24,384 --> 00:11:26,085 +We added a PhotosPicker view, + +205 +00:11:26,085 --> 00:11:29,289 +given it the selection binding +we just defined, + +206 +00:11:29,289 --> 00:11:33,059 +and configured it +to only show images. + +207 +00:11:33,059 --> 00:11:34,294 +The label of the PhotosPicker + +208 +00:11:34,294 --> 00:11:39,098 +is just a pencil glyph +with a circle background. + +209 +00:11:39,098 --> 00:11:42,335 +We can build and run +to see what we have so far. + +210 +00:11:42,335 --> 00:11:46,005 +I can tap the edit button +to bring up the picker. + +211 +00:11:46,005 --> 00:11:49,909 +Tapping an image will +automatically close the picker, + +212 +00:11:49,909 --> 00:11:52,512 +but the profile image +is not updated. + +213 +00:11:52,512 --> 00:11:53,713 +Why? + +214 +00:11:53,713 --> 00:11:55,815 +We still need to connect +the image selection + +215 +00:11:55,815 --> 00:11:57,350 +and the image state. + +216 +00:11:57,350 --> 00:11:59,452 +So, let's do that. + +217 +00:11:59,452 --> 00:12:01,054 +We can go back to the view model + +218 +00:12:01,054 --> 00:12:04,624 +and respond to image +selection did set. + +219 +00:12:04,624 --> 00:12:08,695 +We set the image state to empty +if the image selection is nil. + +220 +00:12:08,695 --> 00:12:12,532 +Otherwise, +we start loading the image. + +221 +00:12:12,532 --> 00:12:15,535 +We are seeing a compiler error +because we haven't implemented + +222 +00:12:15,535 --> 00:12:17,870 +the loadTransferable +method yet. + +223 +00:12:17,870 --> 00:12:19,472 +Let's fix it. + +224 +00:12:23,042 --> 00:12:25,645 +The implementation +is very simple. + +225 +00:12:25,645 --> 00:12:28,247 +We just need to respond +to the completion handler + +226 +00:12:28,247 --> 00:12:29,916 +and update image state + +227 +00:12:29,916 --> 00:12:34,220 +if the request is still +the most recent one. + +228 +00:12:34,220 --> 00:12:37,790 +Let's build and run +to see it in action. + +229 +00:12:37,790 --> 00:12:43,229 +I can tap the edit button +and select an image. + +230 +00:12:43,229 --> 00:12:46,899 +Great! +It works as expected. + +231 +00:12:46,899 --> 00:12:52,338 +Actually, the project is already +set up to run on macOS as well. + +232 +00:12:52,338 --> 00:12:56,209 +Will the code I just added +automatically work on macOS? + +233 +00:12:56,209 --> 00:12:59,445 +Let's build and run to find out. + +234 +00:12:59,445 --> 00:13:01,014 +It compiles! + +235 +00:13:01,014 --> 00:13:04,150 +I can open the picker, +select an image, + +236 +00:13:04,150 --> 00:13:07,954 +and it is reflected in the app. + +237 +00:13:07,954 --> 00:13:09,989 +That's it for the demo. + +238 +00:13:09,989 --> 00:13:13,226 +You just saw the demo +on iOS and macOS, + +239 +00:13:13,226 --> 00:13:17,030 +but the same code will work +on watchOS as well. + +240 +00:13:17,030 --> 00:13:20,433 +However, there are a few things +to keep in mind. + +241 +00:13:23,269 --> 00:13:25,805 +The watchOS picker is designed +for simple flows + +242 +00:13:25,805 --> 00:13:28,274 +and short interactions. + +243 +00:13:28,274 --> 00:13:31,778 +Images are scaled +based on the device size. + +244 +00:13:31,778 --> 00:13:36,616 +Usually, they are synced +from the paired iPhone. + +245 +00:13:36,616 --> 00:13:39,986 +However, Family Setup +lets your family members + +246 +00:13:39,986 --> 00:13:41,487 +who don't have their own iPhone + +247 +00:13:41,487 --> 00:13:46,092 +enjoy the features and benefits +of an Apple Watch. + +248 +00:13:46,092 --> 00:13:49,228 +If a device is in +the Family Setup mode, + +249 +00:13:49,228 --> 00:13:52,899 +the most recent 1000 images in +iCloud Photos can be selected + +250 +00:13:52,899 --> 00:13:55,301 +using the picker. + +251 +00:13:55,301 --> 00:13:59,439 +The picker may need to download +some images from the internet. + +252 +00:13:59,439 --> 00:14:01,207 +And if that's the case, + +253 +00:14:01,207 --> 00:14:04,877 +a loading UI will be shown +in the picker before closing. + +254 +00:14:06,746 --> 00:14:08,715 +Before you go, +I just want to say + +255 +00:14:08,715 --> 00:14:11,350 +that we are committed to making +the system Photos picker + +256 +00:14:11,350 --> 00:14:15,488 +the best way for most apps +to access photos and videos. + +257 +00:14:15,488 --> 00:14:17,590 +We really encourage you +to switch to it + +258 +00:14:17,590 --> 00:14:20,326 +if you are still using +a custom picker. + +259 +00:14:20,326 --> 00:14:23,396 +Thank you, +and have a great WWDC! + +260 +00:14:23,396 --> 00:14:27,533 +♪ + diff --git a/eng/2022 Session 10024 What's new in Vision en.srt b/eng/2022 Session 10024 What's new in Vision en.srt new file mode 100644 index 0000000..a0a71f2 --- /dev/null +++ b/eng/2022 Session 10024 What's new in Vision en.srt @@ -0,0 +1,1632 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,676 --> 00:00:11,645 +Hi, my name is Brett Keating, + +3 +00:00:11,678 --> 00:00:15,549 +and it's my pleasure to be introducing you +to what is new in the Vision framework. + +4 +00:00:15,582 --> 00:00:17,551 +You may be new to Vision. + +5 +00:00:17,584 --> 00:00:21,388 +Perhaps this is the first session +you've seen about the Vision framework. + +6 +00:00:21,421 --> 00:00:23,991 +If so, welcome. + +7 +00:00:24,024 --> 00:00:28,862 +For your benefit, let's briefly recap some +highlights about the Vision framework. + +8 +00:00:28,896 --> 00:00:31,665 +Some Vision framework facts for you. + +9 +00:00:31,698 --> 00:00:34,434 +Vision was first introduced in 2017, + +10 +00:00:34,468 --> 00:00:37,871 +and since then, many thousands +of great apps have been developed + +11 +00:00:37,905 --> 00:00:40,841 +with the technology Vision provides. + +12 +00:00:40,874 --> 00:00:43,510 +Vision is a collection +of computer vision algorithms + +13 +00:00:43,544 --> 00:00:45,646 +that continues to grow over time + +14 +00:00:45,679 --> 00:00:47,781 +and includes such things +as face detection, + +15 +00:00:47,814 --> 00:00:52,019 +image classification, +and contour detection to name a few. + +16 +00:00:52,052 --> 00:00:56,490 +Each of these algorithms is made available +through an easy-to-use, consistent API. + +17 +00:00:56,523 --> 00:00:58,926 +If you know how to run one algorithm +in the Vision framework, + +18 +00:00:58,959 --> 00:01:01,061 +you know how to run them all. + +19 +00:01:01,094 --> 00:01:04,698 +And Vision takes full advantage +of Apple Silicon + +20 +00:01:04,731 --> 00:01:06,934 +on all of the platforms it supports, + +21 +00:01:06,967 --> 00:01:10,771 +to power the machine learning at the core +of many of Vision's algorithms. + +22 +00:01:10,804 --> 00:01:13,473 +Vision is available on tvOS, + +23 +00:01:13,507 --> 00:01:15,709 +iOS, and macOS; + +24 +00:01:15,742 --> 00:01:19,179 +and will fully leverage +Apple Silicon on the Mac. + +25 +00:01:20,113 --> 00:01:23,884 +Some recent additions to the Vision +framework include Person segmentation, + +26 +00:01:23,917 --> 00:01:25,252 +shown here. + +27 +00:01:27,287 --> 00:01:31,358 +Also hand pose estimation, +shown in this demo. + +28 +00:01:34,761 --> 00:01:37,631 +And here is our +Action and Vision sample app, + +29 +00:01:37,664 --> 00:01:41,134 +which uses body pose estimation +and trajectory analysis. + +30 +00:01:42,069 --> 00:01:46,306 +Our agenda today begins +with an overview of some new revisions, + +31 +00:01:46,340 --> 00:01:49,176 +which are updates to existing requests + +32 +00:01:49,209 --> 00:01:54,081 +that may provide increased functionality, +improve performance, or improve accuracy. + +33 +00:01:57,150 --> 00:02:00,454 +First, we have a new revision +for text recognition. + +34 +00:02:00,487 --> 00:02:06,527 +This is the third revision, +given by VNRecognizeTextRequestRevision3. + +35 +00:02:06,560 --> 00:02:10,464 +This is the text recognizer +that powers the amazing Live Text feature. + +36 +00:02:10,497 --> 00:02:13,333 +The text recognizer supports +several languages, + +37 +00:02:13,367 --> 00:02:15,969 +and you may discover +which languages are supported + +38 +00:02:16,003 --> 00:02:19,173 +by calling supportedRecognitionLanguages. + +39 +00:02:19,206 --> 00:02:23,443 +We have now added a few new languages, +and I'll show you a couple examples. + +40 +00:02:23,477 --> 00:02:27,247 +We are now supporting +the Korean language in Vision. + +41 +00:02:27,281 --> 00:02:31,618 +Here is an example of Vision at work +transcribing a Korean receipt. + +42 +00:02:31,652 --> 00:02:35,255 +And here is a corresponding example +for Japanese, + +43 +00:02:35,289 --> 00:02:38,058 +also showing the results +of Vision's text recognition + +44 +00:02:38,091 --> 00:02:40,961 +on this now-supported language. + +45 +00:02:40,994 --> 00:02:46,767 +For text recognition, we have +a new automatic language identification. + +46 +00:02:46,800 --> 00:02:50,170 +You may still specify +the recognition languages to use + +47 +00:02:50,204 --> 00:02:53,740 +using the recognitionLanguages property. + +48 +00:02:53,774 --> 00:02:56,577 +But suppose you don't know +ahead of time which languages + +49 +00:02:56,610 --> 00:02:59,913 +your app user might be trying +to recognize. + +50 +00:02:59,947 --> 00:03:03,417 +Now, but only +for accurate recognition mode, + +51 +00:03:03,450 --> 00:03:07,955 +you may ask the text recognizer +to automatically detect the language + +52 +00:03:07,988 --> 00:03:11,091 +by setting +automaticallyDetectsLanguage to true. + +53 +00:03:12,759 --> 00:03:15,562 +It's best to use this +just for such a situation + +54 +00:03:15,596 --> 00:03:18,398 +where you don't know +which language to recognize, + +55 +00:03:18,432 --> 00:03:21,702 +because the language detection +can occasionally get this wrong. + +56 +00:03:21,735 --> 00:03:25,038 +If you have the prior knowledge +about which language to recognize, + +57 +00:03:25,072 --> 00:03:27,975 +it's still best to specify +these languages to Vision + +58 +00:03:28,008 --> 00:03:31,612 +and leave automaticallyDetectsLanguage +turned off. + +59 +00:03:34,681 --> 00:03:39,119 +Next, we have a new third revision +for our barcode detection, + +60 +00:03:39,152 --> 00:03:43,123 +called VNDetectBarcodesRequestRevision3. + +61 +00:03:43,156 --> 00:03:46,360 +This revision leverages +modern machine learning under the hood, + +62 +00:03:46,393 --> 00:03:49,162 +which is a departure from prior revisions. + +63 +00:03:49,196 --> 00:03:51,431 +Barcodes come in a variety of symbologies, + +64 +00:03:51,465 --> 00:03:55,769 +from barcodes often seen on products +in stores, to QR codes, + +65 +00:03:55,802 --> 00:03:59,239 +to specialty codes +used in healthcare applications. + +66 +00:03:59,273 --> 00:04:01,775 +In order to know +which symbologies Vision supports, + +67 +00:04:01,808 --> 00:04:03,977 +you may call supportedSymbologies. + +68 +00:04:06,113 --> 00:04:08,582 +Let's talk about performance. + +69 +00:04:08,615 --> 00:04:10,617 +Partly because we are using ML, + +70 +00:04:10,651 --> 00:04:15,222 +we are detecting multiple codes +in one shot rather than one at a time, + +71 +00:04:15,255 --> 00:04:20,160 +so the request will be faster +for images containing multiple codes. + +72 +00:04:20,194 --> 00:04:25,465 +Also, more codes are detected +in a given image containing many codes, + +73 +00:04:25,499 --> 00:04:27,167 +due to increased accuracy. + +74 +00:04:27,201 --> 00:04:31,538 +And furthermore, there are few, +if any, duplicate detections. + +75 +00:04:31,572 --> 00:04:34,441 +The bounding boxes are +improved for some codes, + +76 +00:04:34,474 --> 00:04:39,780 +particularly linear codes such as ean13, +for which a line was formerly returned. + +77 +00:04:39,813 --> 00:04:42,850 +Now, the bounding box surrounds +the entire visible code. + +78 +00:04:45,018 --> 00:04:49,022 +Finally, the ML model is more able +to ignore such things as curved surfaces, + +79 +00:04:49,056 --> 00:04:53,393 +reflections, and other artifacts that have +hindered detection accuracy in the past. + +80 +00:04:56,797 --> 00:04:59,733 +Both of these new revisions, +for text recognition + +81 +00:04:59,766 --> 00:05:03,504 +and for barcode detection, +form the technological foundations + +82 +00:05:03,537 --> 00:05:08,075 +for the VisionKit Data Scanner API, +which is a drop-in UI element + +83 +00:05:08,108 --> 00:05:11,879 +that sets up the camera stream +to scan and return barcodes and text. + +84 +00:05:11,912 --> 00:05:14,948 +It's really a fantastic addition +to our SDK, + +85 +00:05:14,982 --> 00:05:19,119 +and I highly recommend you check out +the session about it to learn more. + +86 +00:05:19,152 --> 00:05:22,956 +The final new revision I'll tell you +about today is a new revision + +87 +00:05:22,990 --> 00:05:25,626 +for our optical flow request called + +88 +00:05:25,659 --> 00:05:29,363 +VNGenerateOpticalFlowRequestRevision2. + +89 +00:05:29,396 --> 00:05:32,900 +Like the barcode detector, +this new revision also uses + +90 +00:05:32,933 --> 00:05:35,102 +modern machine learning under the hood. + +91 +00:05:37,471 --> 00:05:41,508 +Although optical flow is one of the +longest studied computer vision problems, + +92 +00:05:41,542 --> 00:05:44,444 +you might not be aware of what it does, +compared to detection of things + +93 +00:05:44,478 --> 00:05:47,981 +which form part of all of our daily lives, +like text and barcodes. + +94 +00:05:48,849 --> 00:05:52,452 +Optical flow analyzes +two consecutive images, + +95 +00:05:52,486 --> 00:05:54,621 +typically frames from a video. + +96 +00:05:54,655 --> 00:05:56,590 +Depending on your use case, +you might look at motion + +97 +00:05:56,623 --> 00:06:00,027 +between two adjacent frames, +or skip a few frames in between, + +98 +00:06:00,060 --> 00:06:02,996 +but in any case, the two images +should be in chronological order. + +99 +00:06:04,831 --> 00:06:09,303 +The analysis provides an estimate of +the direction and magnitude of the motion, + +100 +00:06:09,336 --> 00:06:13,774 +or by how much parts of the first image +need to "move," so to speak, + +101 +00:06:13,807 --> 00:06:17,544 +to be positioned correctly +in the second image. + +102 +00:06:17,578 --> 00:06:20,480 +A VNPixelBufferObservation is the result, + +103 +00:06:20,514 --> 00:06:23,684 +which represents this motion +at all places in the image. + +104 +00:06:23,717 --> 00:06:25,485 +It is a two-channel image. + +105 +00:06:25,519 --> 00:06:28,021 +One channel contains the X magnitude, + +106 +00:06:28,055 --> 00:06:30,791 +and the other contains the Y magnitude. + +107 +00:06:30,824 --> 00:06:34,094 +Together, these form 2D vectors +at each pixel + +108 +00:06:34,127 --> 00:06:37,064 +arranged in this 2D image +so that their locations map + +109 +00:06:37,097 --> 00:06:41,134 +to corresponding locations in the images +that were provided as input. + +110 +00:06:41,168 --> 00:06:43,470 +Let's have a look at this visually. + +111 +00:06:43,504 --> 00:06:47,541 +Suppose you have an incoming video +and several frames are coming in, + +112 +00:06:47,574 --> 00:06:50,210 +but let's look +at these two images in particular. + +113 +00:06:50,244 --> 00:06:52,479 +Here we have a dog running on the beach. + +114 +00:06:52,513 --> 00:06:54,248 +From the left image to the right image, + +115 +00:06:54,281 --> 00:06:57,017 +it appears the dog has moved +a bit to the left. + +116 +00:06:57,050 --> 00:06:59,419 +How would you estimate +and represent this motion? + +117 +00:07:01,255 --> 00:07:03,323 +Well, you would run optical flow + +118 +00:07:03,357 --> 00:07:05,926 +and arrive at something +akin to the image below. + +119 +00:07:05,959 --> 00:07:08,562 +The darker areas are +where motion has been found, + +120 +00:07:08,595 --> 00:07:12,566 +and notice that it does indeed +look just like the shape of the dog. + +121 +00:07:12,599 --> 00:07:16,170 +That's because only the dog +is really moving in this scene. + +122 +00:07:16,203 --> 00:07:19,907 +We are showing the motion vectors +in this image by using "false color," + +123 +00:07:19,940 --> 00:07:23,777 +which maps the x,y +from the vectors into a color palette. + +124 +00:07:23,810 --> 00:07:27,681 +In this false color representation, +"red" hues happen to indicate + +125 +00:07:27,714 --> 00:07:30,150 +movement primarily to the left. + +126 +00:07:30,184 --> 00:07:33,253 +Now that you've seen +an example from one frame, + +127 +00:07:33,287 --> 00:07:35,989 +let's see how it looks +for a whole video clip. + +128 +00:07:36,023 --> 00:07:39,660 +Here we compute optical flow +for a short clip of this dog + +129 +00:07:39,693 --> 00:07:42,196 +fetching a water bottle on a beach. + +130 +00:07:42,229 --> 00:07:45,065 +On the left is the result from revision 1. + +131 +00:07:45,098 --> 00:07:49,002 +On the right is the result +from our new ML-based revision 2. + +132 +00:07:49,036 --> 00:07:52,639 +Hopefully some of the improvements +in revision 2 are clear to see. + +133 +00:07:52,673 --> 00:07:55,142 +For one thing, perhaps most obviously, + +134 +00:07:55,175 --> 00:07:58,712 +the water bottle's motion is captured +much more accurately. + +135 +00:07:58,745 --> 00:08:03,083 +You might also notice improvements in +some of the estimated motion of the dog. + +136 +00:08:03,116 --> 00:08:05,953 +I notice improvements +in the tail most clearly + +137 +00:08:05,986 --> 00:08:10,023 +but also can see the motion of his ears +flapping in the new revision. + +138 +00:08:10,057 --> 00:08:13,293 +The first revision also contains +a bit of background noise motions, + +139 +00:08:13,327 --> 00:08:17,664 +while the second revision more coherently +represents the backgrounds as not moving. + +140 +00:08:17,698 --> 00:08:21,668 +Hopefully that example gave you +a good idea what this technology does. + +141 +00:08:21,702 --> 00:08:25,272 +Now let's dive in a bit +on how you might use it in your app. + +142 +00:08:25,305 --> 00:08:29,910 +Clearly the primary use case is +to discover local motion in a video. + +143 +00:08:29,943 --> 00:08:32,980 +This feeds directly +into security video use cases, + +144 +00:08:33,013 --> 00:08:35,682 +where it's most important to identify +and localize motions + +145 +00:08:35,716 --> 00:08:37,784 +that deviate from the background, + +146 +00:08:37,818 --> 00:08:40,053 +and it should be mentioned +that optical flow does work best + +147 +00:08:40,087 --> 00:08:44,391 +for stationary cameras, +such as most security cameras. + +148 +00:08:44,424 --> 00:08:46,393 +You might want to use +Vision's object tracker + +149 +00:08:46,426 --> 00:08:48,795 +to track objects +that are moving in a video, + +150 +00:08:48,829 --> 00:08:51,265 +but need to know +where to initialize a tracker. + +151 +00:08:51,298 --> 00:08:54,501 +Optical flow can help you there as well. + +152 +00:08:54,535 --> 00:08:58,338 +If you have some computer vision +or image processing savvy of your own, + +153 +00:08:58,372 --> 00:09:00,407 +you might leverage +our optical flow results + +154 +00:09:00,440 --> 00:09:03,076 +to enable further video processing. + +155 +00:09:03,110 --> 00:09:06,747 +Video interpolation, +or video action analysis, + +156 +00:09:06,780 --> 00:09:10,851 +can greatly benefit from +the information optical flow provides. + +157 +00:09:10,884 --> 00:09:13,854 +Let's now dig into some +important additional differences + +158 +00:09:13,887 --> 00:09:16,023 +between revision 1 and revision 2. + +159 +00:09:16,857 --> 00:09:19,459 +Revision 1 always returns +optical flow fields + +160 +00:09:19,493 --> 00:09:22,029 +that have the same resolution +as the input. + +161 +00:09:22,062 --> 00:09:25,032 +Revision 2 will also do this by default. + +162 +00:09:25,065 --> 00:09:28,135 +However, there is a tiny wrinkle: +partially due to the fact + +163 +00:09:28,168 --> 00:09:32,039 +that revision 2 is ML-based, +the output of the underlying model + +164 +00:09:32,072 --> 00:09:36,710 +is relatively low resolution +compared to most input image resolutions. + +165 +00:09:36,743 --> 00:09:40,414 +Therefore, to match +revision 1 default behavior, + +166 +00:09:40,447 --> 00:09:42,382 +some upsampling must be done, + +167 +00:09:42,416 --> 00:09:45,853 +and we are using +bilinear upsampling to do this. + +168 +00:09:45,886 --> 00:09:48,856 +Here is a visual example +explaining what upsampling does. + +169 +00:09:48,889 --> 00:09:52,659 +On the left, we have a zoomed-in portion +of the network output, + +170 +00:09:52,693 --> 00:09:55,863 +which is low resolution +and therefore appears pixelated. + +171 +00:09:55,896 --> 00:10:00,000 +The overall flow field might have +an aspect ratio of 7:5. + +172 +00:10:00,033 --> 00:10:03,504 +On the right, we have a similar region +taken from the same field, + +173 +00:10:03,537 --> 00:10:06,507 +upsampled to the original +image resolution. + +174 +00:10:06,540 --> 00:10:11,645 +Perhaps that image also has +a different aspect ratio, let's say 16:9. + +175 +00:10:11,678 --> 00:10:14,948 +You will notice that the edges +of the flow field are smoothed out + +176 +00:10:14,982 --> 00:10:18,018 +by the bilinear upsampling. + +177 +00:10:18,051 --> 00:10:20,854 +Due to the potential +for the aspect ratios to differ, + +178 +00:10:20,888 --> 00:10:23,524 +keep in mind that as part +of the upsampling process, + +179 +00:10:23,557 --> 00:10:25,893 +the flow image will be stretched + +180 +00:10:25,926 --> 00:10:28,061 +in order to properly correspond +the flow field + +181 +00:10:28,095 --> 00:10:30,230 +to what is happening in the image. + +182 +00:10:30,264 --> 00:10:32,266 +When working +with the network output directly, + +183 +00:10:32,299 --> 00:10:36,136 +you should account for resolution +and aspect ratio in a similar fashion + +184 +00:10:36,170 --> 00:10:39,006 +when mapping flow results +to the original images. + +185 +00:10:41,575 --> 00:10:43,777 +You have the option to skip the upsampling + +186 +00:10:43,810 --> 00:10:47,581 +by turning on keepNetworkOutput +on the request. + +187 +00:10:47,614 --> 00:10:49,983 +This will give you the raw model output. + +188 +00:10:50,017 --> 00:10:54,154 +There are four computationAccuracy +settings you may apply to the request + +189 +00:10:54,188 --> 00:10:57,357 +in order to choose +an available output resolution. + +190 +00:10:57,391 --> 00:11:00,694 +You can see the resolutions +for each accuracy setting in this table, + +191 +00:11:00,727 --> 00:11:03,297 +but be sure to always check +the width and height of the pixel buffer + +192 +00:11:03,330 --> 00:11:05,265 +contained in the observation. + +193 +00:11:06,066 --> 00:11:07,634 +When should you use network output, + +194 +00:11:07,668 --> 00:11:10,404 +and when should you allow Vision +to upsample? + +195 +00:11:10,437 --> 00:11:14,408 +The default behavior is best +if you already are using optical flow + +196 +00:11:14,441 --> 00:11:17,678 +and want the behavior +to remain backward compatible. + +197 +00:11:17,711 --> 00:11:20,280 +It's also a good option +if you want upsampled output, + +198 +00:11:20,314 --> 00:11:24,885 +and bilinear is acceptable to you and +worth the additional memory and latency. + +199 +00:11:24,918 --> 00:11:28,355 +Network output is best +if you don't need full resolution + +200 +00:11:28,388 --> 00:11:33,227 +and can form correspondences on the fly +or just want to initialize a tracker. + +201 +00:11:33,260 --> 00:11:35,262 +Network output may also be +the right choice + +202 +00:11:35,295 --> 00:11:37,364 +if you do need a full resolution flow, + +203 +00:11:37,397 --> 00:11:40,567 +but would prefer to use +your own upsampling methods. + +204 +00:11:40,601 --> 00:11:44,705 +That covers the new algorithm revisions +for this session. + +205 +00:11:44,738 --> 00:11:47,140 +Let's move on to discuss +some spring cleaning we are doing + +206 +00:11:47,174 --> 00:11:50,511 +in the Vision framework +and how it might impact you. + +207 +00:11:50,544 --> 00:11:53,780 +We first introduced face detection +and face landmarks + +208 +00:11:53,814 --> 00:11:56,316 +when Vision was initially released +five years ago, + +209 +00:11:56,350 --> 00:11:59,419 +as "revision 1" for each algorithm. + +210 +00:11:59,453 --> 00:12:03,123 +Since that time +we've released two newer revisions, + +211 +00:12:03,156 --> 00:12:06,593 +which use more efficient +and more accurate technologies. + +212 +00:12:06,627 --> 00:12:10,330 +Therefore, we are removing +the first revisions of these algorithms + +213 +00:12:10,364 --> 00:12:15,035 +from Vision framework, while keeping +the second and third revisions only. + +214 +00:12:15,068 --> 00:12:18,138 +However, if you use revision 1, +never fear. + +215 +00:12:18,172 --> 00:12:21,708 +We will continue to support code +that specifies revision 1 + +216 +00:12:21,742 --> 00:12:26,947 +or code that has been compiled against +SDKs which only contained revision 1. + +217 +00:12:26,980 --> 00:12:28,749 +How is that possible, you may ask? + +218 +00:12:28,782 --> 00:12:32,252 +Revision 1 executes an algorithm +under the hood + +219 +00:12:32,286 --> 00:12:36,256 +that I have called +"the revision 1 detector" in this diagram. + +220 +00:12:36,290 --> 00:12:40,527 +In the same way, revision 2 uses +the revision 2 detector. + +221 +00:12:40,561 --> 00:12:42,796 +What we have done +for this release of Vision + +222 +00:12:42,829 --> 00:12:45,065 +is to satisfy revision 1 requests + +223 +00:12:45,098 --> 00:12:48,101 +with the output +of the revision 2 detector. + +224 +00:12:48,135 --> 00:12:52,506 +Additionally, the revision 1 request +will be marked as deprecated. + +225 +00:12:52,539 --> 00:12:56,376 +This allows us to remove +the old revision 1 detector completely, + +226 +00:12:56,410 --> 00:12:59,613 +allowing the Vision framework +to remain streamlined. + +227 +00:12:59,646 --> 00:13:01,448 +This has several benefits, + +228 +00:13:01,481 --> 00:13:04,318 +not the least of which +is to save space on disk, + +229 +00:13:04,351 --> 00:13:09,256 +which makes our OS releases and SDKs +less expensive to download and install. + +230 +00:13:09,289 --> 00:13:13,760 +All you Vision experts out there might be +saying to yourselves, "But wait a minute, + +231 +00:13:13,794 --> 00:13:18,298 +"revision 2 returns upside down faces +while revision 1 does not. + +232 +00:13:18,332 --> 00:13:21,435 +Couldn't this behavior difference +have an impact on some apps?" + +233 +00:13:21,468 --> 00:13:24,605 +It certainly would, +except we will be taking precautions + +234 +00:13:24,638 --> 00:13:27,541 +to preserve revision 1 behavior. + +235 +00:13:27,574 --> 00:13:32,312 +We will not be returning upside-down faces +from the revision 2 detector. + +236 +00:13:32,346 --> 00:13:35,616 +Similarly, +the revision 2 landmark detector + +237 +00:13:35,649 --> 00:13:40,053 +will return results that match +the revision 1 landmark constellation. + +238 +00:13:40,087 --> 00:13:42,389 +The execution time is on par, + +239 +00:13:42,422 --> 00:13:45,726 +and you ought to experience +a boost in accuracy. + +240 +00:13:45,759 --> 00:13:48,262 +In any case, +this change will not require any apps + +241 +00:13:48,295 --> 00:13:52,332 +to make any modifications to their code, +and things will continue to work. + +242 +00:13:54,134 --> 00:13:57,237 +Still, we have a call to action for you. + +243 +00:13:57,271 --> 00:13:59,573 +You shouldn't be satisfied +with using revision 1 + +244 +00:13:59,606 --> 00:14:02,509 +when we have +much better options available. + +245 +00:14:02,543 --> 00:14:04,645 +We always recommend +using the latest revisions, + +246 +00:14:04,678 --> 00:14:07,548 +and for these requests, +that would be revision 3. + +247 +00:14:08,749 --> 00:14:11,251 +Of course the main reason +for this recommendation + +248 +00:14:11,285 --> 00:14:14,588 +is to use the latest technology, +which provides the highest level + +249 +00:14:14,621 --> 00:14:18,725 +of accuracy and performance available, +and who doesn't want that? + +250 +00:14:18,759 --> 00:14:22,129 +Furthermore, we have established +and communicated several times, + +251 +00:14:22,162 --> 00:14:24,565 +and we reiterate again here, + +252 +00:14:24,598 --> 00:14:28,368 +the best practice of always +explicitly specifying your revisions, + +253 +00:14:28,402 --> 00:14:31,371 +rather than relying +upon default behaviors. + +254 +00:14:31,405 --> 00:14:34,408 +And that's what we've done +for our spring cleaning. + +255 +00:14:34,441 --> 00:14:36,944 +Now let's talk about how we've made it +easier to debug apps + +256 +00:14:36,977 --> 00:14:38,812 +that use the Vision framework. + +257 +00:14:38,846 --> 00:14:41,949 +We've added +Quick Look Preview support to Vision. + +258 +00:14:41,982 --> 00:14:44,685 +What does this mean +for Vision in particular? + +259 +00:14:44,718 --> 00:14:48,255 +Well, now you can mouse over +VNObservations in the debugger, + +260 +00:14:48,288 --> 00:14:52,860 +and with one click, you can visualize +the result on your input image. + +261 +00:14:52,893 --> 00:14:55,863 +We've also made this available +in Xcode Playgrounds. + +262 +00:14:55,896 --> 00:14:59,099 +I think the only way to really explain +how this can benefit your debugging + +263 +00:14:59,132 --> 00:15:00,334 +is to show you. + +264 +00:15:00,367 --> 00:15:02,603 +Let's move to an Xcode demo. + +265 +00:15:04,471 --> 00:15:08,208 +Here we have a simple routine +that will detect face landmarks + +266 +00:15:08,242 --> 00:15:11,345 +and return the face observations. + +267 +00:15:11,378 --> 00:15:15,849 +First, we set up a face landmarks request. + +268 +00:15:15,883 --> 00:15:20,921 +Then, if we have an image +ready to go in our class, we display it. + +269 +00:15:20,954 --> 00:15:24,458 +Then, we declare an array +to hold our results. + +270 +00:15:26,126 --> 00:15:27,995 +Inside the autoreleasepool, + +271 +00:15:28,028 --> 00:15:31,098 +we instantiate a request handler +with that image, + +272 +00:15:31,131 --> 00:15:34,034 +and then perform our request. + +273 +00:15:34,067 --> 00:15:38,338 +Assuming all went well, we can retrieve +the results from the request. + +274 +00:15:39,206 --> 00:15:44,011 +I will run it and get to a breakpoint +after we retrieve the results. + +275 +00:15:44,044 --> 00:15:45,812 +So now I'm in the debugger. + +276 +00:15:45,846 --> 00:15:47,581 +When I mouse over the results, + +277 +00:15:47,614 --> 00:15:50,284 +the overlay shows +I've detected three faces. + +278 +00:15:50,317 --> 00:15:53,754 +That's great. +I do have three faces in my input image. + +279 +00:15:53,787 --> 00:15:56,557 +But how do I know +which observation is which face? + +280 +00:15:56,590 --> 00:15:59,927 +That's where +the Quick Look Preview support comes in. + +281 +00:15:59,960 --> 00:16:04,898 +As I go into this request, +I can click on each "eye" icon + +282 +00:16:04,932 --> 00:16:07,501 +in order to visualize the result. + +283 +00:16:07,534 --> 00:16:12,206 +The image appears with overlays drawn +for the landmarks constellation + +284 +00:16:12,239 --> 00:16:14,408 +and for the face bounding box. + +285 +00:16:15,676 --> 00:16:18,612 +Now you know where the first +observation is in the image. + +286 +00:16:19,780 --> 00:16:23,250 +I can click on the next one to draw +overlays for the second observation + +287 +00:16:23,283 --> 00:16:25,619 +and for the third observation. + +288 +00:16:27,521 --> 00:16:30,924 +Continuing to the next breakpoint, +we run some code + +289 +00:16:30,958 --> 00:16:34,561 +that prints the face observations +to the debug console. + +290 +00:16:34,595 --> 00:16:37,497 +As you can imagine, +here in the debug console + +291 +00:16:37,531 --> 00:16:40,534 +where the face information is printed, +it's pretty hard + +292 +00:16:40,567 --> 00:16:43,570 +to immediately visualize in your mind +which face is which + +293 +00:16:43,604 --> 00:16:46,907 +or whether the results look correct +just from these printed coordinates. + +294 +00:16:48,976 --> 00:16:51,245 +But there is one more thing +to point out here. + +295 +00:16:51,278 --> 00:16:54,515 +Notice that I've somewhat artificially +forced the request handler + +296 +00:16:54,548 --> 00:16:57,918 +out of scope by introducing +an autoreleasepool. + +297 +00:16:57,951 --> 00:17:00,420 +Now that the request handler +is out of scope, + +298 +00:17:00,454 --> 00:17:03,924 +let's use the Quick Look Preview +support again on the results. + +299 +00:17:03,957 --> 00:17:06,693 +Well, what do you know, +the overlays are still drawn, + +300 +00:17:06,727 --> 00:17:08,395 +but the image is not available. + +301 +00:17:09,496 --> 00:17:12,966 +This is something to keep in mind: +the image request handler that was used + +302 +00:17:13,000 --> 00:17:16,737 +to generate the observations +must still be in scope somewhere + +303 +00:17:16,770 --> 00:17:21,108 +in order for Quick Look Preview support +to use the original image for display. + +304 +00:17:21,141 --> 00:17:25,212 +That is because the image request handler +is where your input image resides. + +305 +00:17:25,245 --> 00:17:28,348 +Things will continue to work, +but the image will not be available. + +306 +00:17:28,382 --> 00:17:31,952 +This Quick Look preview support +can be especially useful + +307 +00:17:31,985 --> 00:17:34,054 +in an Xcode Playgrounds session, + +308 +00:17:34,087 --> 00:17:37,691 +while doing quick experiments +to see how things work. + +309 +00:17:37,724 --> 00:17:40,160 +Let's have a look at that now. + +310 +00:17:40,194 --> 00:17:44,865 +Here we have a simple Playground set up +to analyze images for barcodes. + +311 +00:17:44,898 --> 00:17:47,935 +Rather than go through this code, +let's just make a couple modifications + +312 +00:17:47,968 --> 00:17:50,537 +and check out how it impacts the results. + +313 +00:17:50,571 --> 00:17:52,773 +We'll start off by using revision 2 + +314 +00:17:52,806 --> 00:17:56,743 +on an image with two barcodes +of different symbologies. + +315 +00:17:56,777 --> 00:18:00,113 +All the results at once are displayed +if we ask for all the results, + +316 +00:18:00,147 --> 00:18:03,150 +and just the first result +is also displayed at the end. + +317 +00:18:04,718 --> 00:18:07,287 +Notice that revision 2 has +a couple issues. + +318 +00:18:07,321 --> 00:18:10,257 +First, it missed the first barcode. + +319 +00:18:10,290 --> 00:18:13,427 +Also, it detected +the second barcode twice. + +320 +00:18:13,460 --> 00:18:15,329 +And it gives you a line +through the barcode + +321 +00:18:15,362 --> 00:18:17,564 +rather than a complete bounding box. + +322 +00:18:19,566 --> 00:18:23,637 +What happens if we change +to revision 3 now, instead of revision 2? + +323 +00:18:26,106 --> 00:18:28,609 +First of all, we detect both barcodes. + +324 +00:18:28,642 --> 00:18:32,346 +And, instead of a line, +we are given complete bounding boxes. + +325 +00:18:33,313 --> 00:18:35,949 +What is great about this +Quick Look Preview support + +326 +00:18:35,983 --> 00:18:39,553 +is that we've removed the need for you +to write a variety of utility functions + +327 +00:18:39,586 --> 00:18:41,522 +to visualize the results. + +328 +00:18:41,555 --> 00:18:44,625 +They can be overlaid directly +on your images in the debugger + +329 +00:18:44,658 --> 00:18:46,560 +or in an Xcode Playground. + +330 +00:18:49,796 --> 00:18:54,101 +So that is Quick Look +Preview support in Vision. + +331 +00:18:54,134 --> 00:18:58,071 +Now you can more easily know +which observation is which. + +332 +00:18:58,105 --> 00:19:00,407 +Just be sure to keep +the image request handler in scope + +333 +00:19:00,440 --> 00:19:02,976 +in order to use it with your input image, + +334 +00:19:03,010 --> 00:19:06,113 +and hopefully the Xcode Playground support +will make live tuning + +335 +00:19:06,146 --> 00:19:08,482 +of your Vision framework code much easier. + +336 +00:19:08,515 --> 00:19:11,418 +We've covered some important updates +to Vision today. + +337 +00:19:11,451 --> 00:19:14,588 +To quickly review, we've added +some great new revisions + +338 +00:19:14,621 --> 00:19:19,493 +to text recognition, +barcode detection, and optical flow. + +339 +00:19:21,295 --> 00:19:25,999 +As we continue to add updated revisions, +we will also be removing older ones, + +340 +00:19:26,033 --> 00:19:27,768 +so keep your revisions up-to-date + +341 +00:19:27,801 --> 00:19:30,771 +and use the latest +and greatest technology. + +342 +00:19:30,804 --> 00:19:34,241 +We've also made debugging +Vision applications much easier this year + +343 +00:19:34,274 --> 00:19:36,643 +with Quick Look Preview support. + +344 +00:19:36,677 --> 00:19:41,048 +I hope you enjoyed this session, +and have a wonderful WWDC. ♪ ♪ + diff --git a/eng/2022 Session 10025 Capture machine-readable codes and text with VisionKit en.srt b/eng/2022 Session 10025 Capture machine-readable codes and text with VisionKit en.srt new file mode 100644 index 0000000..67f2044 --- /dev/null +++ b/eng/2022 Session 10025 Capture machine-readable codes and text with VisionKit en.srt @@ -0,0 +1,1075 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:13,313 +Ron Santos: Hey, hope you're well. +I'm Ron Santos, an input engineer. + +3 +00:00:13,347 --> 00:00:16,550 +Today I’m here to talk to you +about capturing machine-readable codes + +4 +00:00:16,583 --> 00:00:18,285 +and text from a video feed, + +5 +00:00:18,318 --> 00:00:21,722 +or, as we like to call it, data scanning. + +6 +00:00:21,755 --> 00:00:24,124 +What exactly do we mean by data scanning? + +7 +00:00:24,157 --> 00:00:28,662 +It’s simply a way of using a sensor, +like a camera, to read data. + +8 +00:00:29,763 --> 00:00:32,199 +Typically that data comes +in the form of text. + +9 +00:00:32,232 --> 00:00:35,035 +For example, +a receipt with interesting information + +10 +00:00:35,068 --> 00:00:38,338 +like telephone numbers, dates, and prices. + +11 +00:00:39,706 --> 00:00:42,709 +Or maybe data comes +as a machine-readable code, + +12 +00:00:42,743 --> 00:00:45,612 +like the ubiquitous QR code. + +13 +00:00:45,646 --> 00:00:48,282 +You’ve probably +used a data scanner before, + +14 +00:00:48,315 --> 00:00:51,218 +maybe in the Camera app +or by using the Live Text features + +15 +00:00:51,251 --> 00:00:53,720 +introduced in iOS 15. + +16 +00:00:53,754 --> 00:00:56,323 +And I bet you’ve used apps +in your day-to-day life + +17 +00:00:56,356 --> 00:00:59,760 +with their own custom scanning experience. + +18 +00:00:59,793 --> 00:01:02,596 +But what if you had +to build your own data scanner? + +19 +00:01:02,629 --> 00:01:04,198 +How would you do it? + +20 +00:01:04,231 --> 00:01:07,034 +The iOS SDK has more +than one solution for you, + +21 +00:01:07,067 --> 00:01:08,602 +depending on your needs. + +22 +00:01:09,303 --> 00:01:12,472 +One option is that you could +use the AVFoundation framework + +23 +00:01:12,506 --> 00:01:14,241 +to set up the camera graph, + +24 +00:01:14,274 --> 00:01:17,945 +connecting inputs and outputs +to a session and configuring it + +25 +00:01:17,978 --> 00:01:22,549 +to yield AVMetadataObjects +like machine-readable codes. + +26 +00:01:22,583 --> 00:01:25,752 +If you also wanted to capture text, +another option would be + +27 +00:01:25,786 --> 00:01:29,990 +to combine both the AVFoundation +and the Vision frameworks together. + +28 +00:01:30,023 --> 00:01:32,693 +In this diagram, +instead of metadata output, + +29 +00:01:32,726 --> 00:01:35,662 +you create video data output. + +30 +00:01:35,696 --> 00:01:40,200 +The video data output results +in the delivery of sample buffers + +31 +00:01:40,234 --> 00:01:43,170 +that can be fed +to the Vision framework for use with text + +32 +00:01:43,203 --> 00:01:47,774 +and barcode recognition requests, +resulting in Vision observation objects. + +33 +00:01:47,808 --> 00:01:49,843 +For more on using Vision +for data scanning, + +34 +00:01:49,877 --> 00:01:54,581 +check out the “Extract document data +using Vision” from WWDC21. + +35 +00:01:54,615 --> 00:01:58,519 +Okay, so that’s using AVFoundation +and Vision for data scanning. + +36 +00:01:58,552 --> 00:02:03,724 +In iOS 16, we have a new option +that encapsulates all of that for you. + +37 +00:02:03,757 --> 00:02:07,494 +Introducing the DataScannerViewController +in the VisionKit framework. + +38 +00:02:07,528 --> 00:02:10,964 +It combines the features +of AVFoundation and Vision + +39 +00:02:10,998 --> 00:02:13,934 +specifically for the purpose +of data scanning. + +40 +00:02:13,967 --> 00:02:17,671 +The DataScannerViewController users +are treated to features like + +41 +00:02:17,704 --> 00:02:22,176 +a live camera preview, +helpful guidance labels, + +42 +00:02:22,209 --> 00:02:24,411 +item highlighting, + +43 +00:02:24,444 --> 00:02:29,049 +tap-to-focus +which is also used for selection, + +44 +00:02:29,082 --> 00:02:32,920 +and lastly, +pinch-to-zoom to get a closer look. + +45 +00:02:34,221 --> 00:02:37,090 +And let’s talk about features +for developers like you. + +46 +00:02:37,124 --> 00:02:40,561 +The DataScannerViewController +is a UIViewController subclass + +47 +00:02:40,594 --> 00:02:42,896 +that you can present however you choose. + +48 +00:02:42,930 --> 00:02:46,466 +Coordinates for recognized items +are always in view coordinates, + +49 +00:02:46,500 --> 00:02:48,702 +saving you from converting +from image space, + +50 +00:02:48,735 --> 00:02:51,572 +to Vision coordinates, +to view coordinates. + +51 +00:02:51,605 --> 00:02:54,241 +You’ll also be able to limit +the active portion of the view + +52 +00:02:54,274 --> 00:02:58,979 +by specifying a region-of-interest, +which is also in view coordinates. + +53 +00:02:59,012 --> 00:03:01,815 +For text recognition, +you can specify content types + +54 +00:03:01,849 --> 00:03:04,918 +to limit the type of text you find. + +55 +00:03:04,952 --> 00:03:06,486 +And for machine-readable codes, + +56 +00:03:06,520 --> 00:03:10,290 +you can specify exactly +which symbologies to look for. + +57 +00:03:10,324 --> 00:03:12,359 +I get it; +I use your apps, and I understand + +58 +00:03:12,392 --> 00:03:15,929 +that data scanning is only a small portion +of their functionality. + +59 +00:03:15,963 --> 00:03:18,365 +But it could require a lot of code. + +60 +00:03:18,398 --> 00:03:20,067 +With DataScannerViewController, + +61 +00:03:20,100 --> 00:03:22,369 +our goal is +to perform the common tasks for you, + +62 +00:03:22,402 --> 00:03:24,638 +so you can focus your time elsewhere. + +63 +00:03:24,671 --> 00:03:28,008 +Next, I’ll walk you +through adding it to your app. + +64 +00:03:28,041 --> 00:03:31,011 +Let’s start +with the privacy usage description. + +65 +00:03:31,044 --> 00:03:34,448 +When apps try to capture video, +iOS asks the user + +66 +00:03:34,481 --> 00:03:38,085 +to grant their explicit permission +to access the camera. + +67 +00:03:38,118 --> 00:03:42,256 +You’ll want to provide a descriptive +message justifying your need. + +68 +00:03:42,289 --> 00:03:45,759 +To do that, +add a “privacy - camera usage description” + +69 +00:03:45,792 --> 00:03:48,095 +to your app’s Info.plist file. + +70 +00:03:48,128 --> 00:03:52,966 +Remember, be as descriptive as possible, +so users know what they’re agreeing to. + +71 +00:03:53,000 --> 00:03:55,102 +Now onto the code. + +72 +00:03:55,135 --> 00:03:57,504 +Wherever you would like +to present a data scanner, + +73 +00:03:57,538 --> 00:03:59,773 +start by importing VisionKit. + +74 +00:04:01,008 --> 00:04:05,012 +Next, because data scanning +isn’t supported on all devices, + +75 +00:04:05,045 --> 00:04:08,549 +use the isSupported class property +to hide any buttons or menus + +76 +00:04:08,582 --> 00:04:10,217 +exposing the functionality, + +77 +00:04:10,250 --> 00:04:13,620 +so users aren’t presented +with something they can’t use. + +78 +00:04:14,888 --> 00:04:19,259 +If you’re curious, +any 2018 and newer iPhone and iPad devices + +79 +00:04:19,293 --> 00:04:22,396 +with the Apple Neural Engine +support data scanning. + +80 +00:04:22,429 --> 00:04:24,932 +You’ll also want +to check for availability. + +81 +00:04:24,965 --> 00:04:27,801 +Recall the privacy usage description? + +82 +00:04:27,835 --> 00:04:31,572 +Scanning is available if the user +approves the app for camera access + +83 +00:04:31,605 --> 00:04:34,374 +and if the device is free +of any restrictions, + +84 +00:04:34,408 --> 00:04:37,110 +like the Camera access restriction +set here, + +85 +00:04:37,144 --> 00:04:41,081 +in Screen Time’s Content +& Privacy Restrictions. + +86 +00:04:41,114 --> 00:04:43,584 +Now you’re ready +to configure an instance. + +87 +00:04:43,617 --> 00:04:47,254 +That’s done by first specifying the types +of data you’re interested in. + +88 +00:04:47,287 --> 00:04:51,458 +For example, +you can scan for both QR codes and text. + +89 +00:04:52,559 --> 00:04:55,829 +You can optionally pass a list +of languages for the text recognizer + +90 +00:04:55,863 --> 00:04:59,099 +to use as a hint +for various processing aspects, + +91 +00:04:59,132 --> 00:05:01,201 +like language correction. + +92 +00:05:01,235 --> 00:05:04,805 +If you have an idea what languages +to expect, list them out. + +93 +00:05:04,838 --> 00:05:08,308 +It’s especially useful when two languages +have similar looking scripts. + +94 +00:05:08,342 --> 00:05:10,244 +If you do not provide any languages, + +95 +00:05:10,277 --> 00:05:13,947 +the user’s preferred languages +are used by default. + +96 +00:05:13,981 --> 00:05:17,050 +You can also request +a specific text content type. + +97 +00:05:17,084 --> 00:05:20,554 +In this example, +I want my scanner to look for URLs. + +98 +00:05:20,587 --> 00:05:23,056 +Now that you stated the types +of data to recognize, + +99 +00:05:23,090 --> 00:05:26,193 +you can create your DataScanner instance. + +100 +00:05:26,226 --> 00:05:29,630 +In the previous example, +I specified a barcode symbology, + +101 +00:05:29,663 --> 00:05:33,267 +a recognition language, +and a text content type. + +102 +00:05:33,300 --> 00:05:37,471 +Let me take a moment to explain +the other options for each of those. + +103 +00:05:37,504 --> 00:05:41,108 +For barcode symbologies, +we support all the same symbologies + +104 +00:05:41,141 --> 00:05:43,877 +as Vision’s barcode detector. + +105 +00:05:43,911 --> 00:05:46,980 +In terms of languages, +as part of the LiveText feature, + +106 +00:05:47,014 --> 00:05:50,150 +DataScannerViewController +supports the same exact languages. + +107 +00:05:50,184 --> 00:05:55,455 +And in iOS 16, I’m happy to say we’re +adding support for Japanese and Korean. + +108 +00:05:55,489 --> 00:05:58,325 +Of course, +this can change again in future. + +109 +00:05:58,358 --> 00:06:00,694 +So use the +supportedTextRecognitionLanguages + +110 +00:06:00,727 --> 00:06:04,498 +class property to retrieve +the most up to date list. + +111 +00:06:04,531 --> 00:06:07,868 +Finally, when scanning for text +with specific semantic meaning, + +112 +00:06:07,901 --> 00:06:10,971 +the DataScannerViewController +can find these seven types. + +113 +00:06:11,905 --> 00:06:14,908 +We’re now ready to present +the Data Scanner to the user. + +114 +00:06:14,942 --> 00:06:18,846 +Present it like any other view controller, +going fullscreen, + +115 +00:06:18,879 --> 00:06:22,883 +using a sheet, or adding it +to another view hierarchy altogether. + +116 +00:06:22,916 --> 00:06:24,184 +It’s all up to you. + +117 +00:06:24,218 --> 00:06:26,520 +Afterwards, +when presentation completes, + +118 +00:06:26,553 --> 00:06:29,890 +call startScanning() +to begin looking for data. + +119 +00:06:29,923 --> 00:06:32,626 +So now I want to take a step back +and spend some time going + +120 +00:06:32,659 --> 00:06:35,729 +over Data Scanner’s +initialization parameters. + +121 +00:06:35,762 --> 00:06:38,365 +I used one here, recognizedDataTypes. + +122 +00:06:38,398 --> 00:06:42,169 +But there are others that can +help you customize your experience. + +123 +00:06:43,136 --> 00:06:44,805 +Let’s go through each one. + +124 +00:06:44,838 --> 00:06:49,243 +recognizedDataTypes allows you +to specify what kind of data to recognize. + +125 +00:06:49,276 --> 00:06:52,946 +Text, machine-readable codes, +and what types of each. + +126 +00:06:52,980 --> 00:06:56,183 +qualityLevel can be balanced, +fast, or accurate. + +127 +00:06:56,216 --> 00:06:59,353 +Fast will sacrifice resolution +in favor of speed in scenarios + +128 +00:06:59,386 --> 00:07:02,222 +where you expect large +and easily-legible items, + +129 +00:07:02,256 --> 00:07:04,024 +like text on signs. + +130 +00:07:04,057 --> 00:07:05,959 +Accurate will give you the best accuracy, + +131 +00:07:05,993 --> 00:07:10,998 +even with small items like +micro QR codes or tiny serial numbers. + +132 +00:07:11,031 --> 00:07:15,636 +I recommend starting with balanced, +which should work great for most cases. + +133 +00:07:15,669 --> 00:07:18,572 +recognizesMultipleItems +gives you the option to look + +134 +00:07:18,605 --> 00:07:20,774 +for one or more items in the frame, + +135 +00:07:20,807 --> 00:07:23,877 +like if you want +to scan multiple barcodes at a time. + +136 +00:07:23,911 --> 00:07:26,580 +When it’s false, +the center-most item is recognized + +137 +00:07:26,613 --> 00:07:29,950 +by default until the user taps elsewhere. + +138 +00:07:29,983 --> 00:07:33,153 +Enable high frame rate tracking +when you draw highlights. + +139 +00:07:33,187 --> 00:07:36,190 +It allows the highlights +to follow items as closely as possible + +140 +00:07:36,223 --> 00:07:39,927 +when the camera moves +or the scene changes. + +141 +00:07:39,960 --> 00:07:43,130 +Enable pinch-to-zoom or disable it. + +142 +00:07:43,163 --> 00:07:47,434 +We also have methods you can use +to modify the zoom level yourself. + +143 +00:07:47,467 --> 00:07:49,870 +When you enable guidance, +labels show at the top + +144 +00:07:49,903 --> 00:07:52,806 +of the screen to help direct the user. + +145 +00:07:52,840 --> 00:07:56,877 +And, finally, you can enable +system highlighting if you need it, + +146 +00:07:56,910 --> 00:07:59,546 +or you can disable it to draw +your own custom highlighting. + +147 +00:08:00,447 --> 00:08:02,349 +Now that you know how +to present the data scanner, + +148 +00:08:02,382 --> 00:08:04,985 +let’s talk about how you’d +ingest the recognized items, + +149 +00:08:05,018 --> 00:08:07,888 +and also how you’d +draw your own custom highlights. + +150 +00:08:08,922 --> 00:08:12,326 +First, provide a delegate +to the data scanner. + +151 +00:08:12,359 --> 00:08:13,994 +Now that you have a delegate, + +152 +00:08:14,027 --> 00:08:17,097 +you can implement +the dataScanner didTapOn method, + +153 +00:08:17,130 --> 00:08:20,133 +which is called +when the user taps on an item. + +154 +00:08:20,167 --> 00:08:24,538 +With it, you’ll receive an instance +of this new type RecognizeItem. + +155 +00:08:24,571 --> 00:08:29,776 +RecognizedItem is an enum that holds text +or a barcode as an associated value. + +156 +00:08:29,810 --> 00:08:33,647 +For text, the transcription +property holds the recognized string. + +157 +00:08:33,680 --> 00:08:36,383 +For barcodes, +if its payload contains a string, + +158 +00:08:36,416 --> 00:08:39,720 +you can retrieve it +with the payloadStringValue. + +159 +00:08:39,753 --> 00:08:42,556 +Two other things you should know +about RecognizedItem: + +160 +00:08:42,589 --> 00:08:46,560 +First, each recognized item +has a unique identifier you can use + +161 +00:08:46,593 --> 00:08:48,896 +to track an item throughout its lifetime. + +162 +00:08:48,929 --> 00:08:51,498 +That lifetime starts +when the item is first seen + +163 +00:08:51,532 --> 00:08:54,001 +and ends when it’s no longer in view. + +164 +00:08:54,034 --> 00:08:57,204 +And second, +each RecognizedItem has a bounds property. + +165 +00:08:57,237 --> 00:08:59,907 +The bounds isn’t a rect, +but it consists of four points, + +166 +00:08:59,940 --> 00:09:01,441 +one for each corner. + +167 +00:09:01,475 --> 00:09:04,645 +Next, let’s talk +about three related delegate methods + +168 +00:09:04,678 --> 00:09:07,548 +that are called when recognized items +in the scene change. + +169 +00:09:07,581 --> 00:09:09,516 +The first is didAdd, + +170 +00:09:09,550 --> 00:09:12,753 +called when items +in the scene are newly recognized. + +171 +00:09:12,786 --> 00:09:15,255 +If you wanted to create +your own custom highlight, + +172 +00:09:15,289 --> 00:09:18,292 +you’d create one here for each new item. + +173 +00:09:18,325 --> 00:09:23,030 +You can keep track of the highlights +using the ID from its associated item. + +174 +00:09:23,063 --> 00:09:25,866 +And when adding your new view +to the view hierarchy, + +175 +00:09:25,899 --> 00:09:28,836 +add them +to DataScanner’s overlayContainerView, + +176 +00:09:28,869 --> 00:09:33,941 +so they appear above the camera preview, +but below any other supplemental chrome. + +177 +00:09:35,142 --> 00:09:37,477 +The next delegate method is didUpdate, + +178 +00:09:37,511 --> 00:09:40,614 +which is called when the items move +or the camera moves. + +179 +00:09:40,647 --> 00:09:44,551 +It can also be called when transcription +for recognized text change. + +180 +00:09:44,585 --> 00:09:47,554 +They change because +the longer the scanner sees the text, + +181 +00:09:47,588 --> 00:09:50,791 +the more accurate it’ll be +with its transcription. + +182 +00:09:50,824 --> 00:09:54,027 +Use the IDs from the updated items +to retrieve your highlights + +183 +00:09:54,061 --> 00:09:56,530 +from the dictionary you just created, + +184 +00:09:56,563 --> 00:10:00,767 +and then animate the views +to their newly updated bounds. + +185 +00:10:00,801 --> 00:10:03,437 +And finally, +the didRemove delegate method, + +186 +00:10:03,470 --> 00:10:07,040 +which is called when items +are no longer visible in the scene. + +187 +00:10:07,074 --> 00:10:09,977 +In this method, +you can forget about any highlight views + +188 +00:10:10,010 --> 00:10:12,479 +you associated +with the removed items, + +189 +00:10:12,513 --> 00:10:15,282 +and you can remove them +from the view hierarchy. + +190 +00:10:15,315 --> 00:10:18,218 +In summary, if you draw +your own highlights over items, + +191 +00:10:18,252 --> 00:10:20,320 +those three delegate methods +will be crucial + +192 +00:10:20,354 --> 00:10:23,323 +for you to control animating highlights +into the scene, + +193 +00:10:23,357 --> 00:10:26,927 +animating their movement, +and animating them out. + +194 +00:10:26,960 --> 00:10:29,096 +And for each of those three +previous delegate methods, + +195 +00:10:29,129 --> 00:10:33,333 +you’ll also be given an array +of all the items currently recognized. + +196 +00:10:33,367 --> 00:10:35,702 +That may come in handy +for text recognition + +197 +00:10:35,736 --> 00:10:38,705 +because the items are placed +in their natural reading order, + +198 +00:10:38,739 --> 00:10:41,775 +meaning the user would +read the item at index 0 + +199 +00:10:41,808 --> 00:10:45,846 +before the item at index 1 and so on. + +200 +00:10:45,879 --> 00:10:48,515 +That’s an overview of how +to use the DataScannerViewController. + +201 +00:10:48,549 --> 00:10:53,287 +Before wrapping up, I wanted +to quickly mention a few other features, + +202 +00:10:53,320 --> 00:10:55,189 +like capturing photos. + +203 +00:10:55,222 --> 00:10:57,024 +You can call the capturePhoto method, + +204 +00:10:57,057 --> 00:11:01,428 +which will asynchronously return +a high quality UIImage. + +205 +00:11:02,462 --> 00:11:04,431 +And if you aren’t +creating custom highlights, + +206 +00:11:04,464 --> 00:11:07,234 +you might not need +these three delegate methods. + +207 +00:11:07,267 --> 00:11:10,704 +Instead, you can +use the recognizedItem property. + +208 +00:11:10,737 --> 00:11:15,742 +It’s an AsyncStream that will be +continuously updated as the scene changes. + +209 +00:11:17,845 --> 00:11:19,379 +Thanks for hanging out. + +210 +00:11:19,413 --> 00:11:21,882 +Remember, +the iOS SDK gives you options + +211 +00:11:21,915 --> 00:11:23,684 +for creating computer vision workflows + +212 +00:11:23,717 --> 00:11:26,854 +with the AVFoundation +and Vision frameworks. + +213 +00:11:26,887 --> 00:11:29,122 +But maybe you’re +creating an app that scans text + +214 +00:11:29,156 --> 00:11:32,025 +or machine-readable codes +with a live video feed, + +215 +00:11:32,059 --> 00:11:33,527 +like a Pick-and-pack app, + +216 +00:11:33,560 --> 00:11:36,697 +a back-of-the-warehouse app, +or a point-of-sale app. + +217 +00:11:36,730 --> 00:11:38,932 +If so, then give +the DataScannerViewController + +218 +00:11:38,966 --> 00:11:40,434 +in VisionKit a look. + +219 +00:11:40,467 --> 00:11:42,302 +As I went over today, it has a number + +220 +00:11:42,336 --> 00:11:45,706 +of initialization parameters +and delegate methods that you can use + +221 +00:11:45,739 --> 00:11:49,943 +to provide a custom experience +that matches your app’s style and needs. + +222 +00:11:50,944 --> 00:11:53,113 +And finally, +I wanted to give a shout out + +223 +00:11:53,146 --> 00:11:55,849 +to the “Add Live Text interaction +to your app” session, + +224 +00:11:55,883 --> 00:12:00,487 +where you can learn about VisionKit’s +Live Text abilities for static images. + +225 +00:12:01,555 --> 00:12:03,357 +Until next time, peace. + +226 +00:12:03,390 --> 00:12:08,662 +[upbeat music] + diff --git a/eng/2022 Session 10026 Add Live Text interaction to your app en.srt b/eng/2022 Session 10026 Add Live Text interaction to your app en.srt new file mode 100644 index 0000000..dbc2f1f --- /dev/null +++ b/eng/2022 Session 10026 Add Live Text interaction to your app en.srt @@ -0,0 +1,1484 @@ +1 +00:00:00,100 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,810 +♪ + +3 +00:00:09,810 --> 00:00:11,979 +Hi! My name is Adam Bradford. + +4 +00:00:11,979 --> 00:00:13,981 +I'm an engineer +on the VisionKit team, + +5 +00:00:13,981 --> 00:00:16,316 +and if you're looking to add +Live Text to your app, + +6 +00:00:16,316 --> 00:00:18,652 +you're in the right place. + +7 +00:00:18,652 --> 00:00:21,588 +But first, what is Live Text? + +8 +00:00:21,588 --> 00:00:24,958 +Live Text analyzes an image and +provides features for the users + +9 +00:00:24,958 --> 00:00:29,663 +to interact with its content, +such selecting and copying text, + +10 +00:00:29,663 --> 00:00:33,133 +perform actions +like lookup and translate, + +11 +00:00:33,133 --> 00:00:34,801 +providing data-detection +workflows, + +12 +00:00:34,801 --> 00:00:37,070 +such as mapping an address, +dialing a number, + +13 +00:00:37,070 --> 00:00:39,940 +or jumping to a URL. + +14 +00:00:39,940 --> 00:00:43,176 +Live Text even allows +for QR code interaction. + +15 +00:00:43,176 --> 00:00:45,913 +Imagine how you could +put this to use in your app? + +16 +00:00:45,913 --> 00:00:49,549 +You want to know more? +Well, you're in the right place. + +17 +00:00:49,549 --> 00:00:52,519 +For this session, I'm going +to start with a general overview + +18 +00:00:52,519 --> 00:00:54,721 +of the Live Text API. + +19 +00:00:54,721 --> 00:00:57,724 +Then I will explore +how to implement this API + +20 +00:00:57,724 --> 00:01:00,460 +in an existing application. + +21 +00:01:00,460 --> 00:01:02,930 +Next, I will dive into +some tips and tricks + +22 +00:01:02,930 --> 00:01:06,733 +which may help you when adding +Live Text to your app. + +23 +00:01:06,733 --> 00:01:10,704 +Now for an overview +of the Live Text API. + +24 +00:01:10,704 --> 00:01:14,908 +At a high level, the Live Text +API is available in Swift. + +25 +00:01:14,908 --> 00:01:17,010 +It works beautifully +on static images + +26 +00:01:17,010 --> 00:01:20,414 +and can be adapted to be used +for paused video frames. + +27 +00:01:20,414 --> 00:01:23,250 +If you need to analyze video +in a live camera stream + +28 +00:01:23,250 --> 00:01:25,819 +to search for items +like text or QR codes, + +29 +00:01:25,819 --> 00:01:28,855 +VisionKit also has +a data scanner available. + +30 +00:01:28,855 --> 00:01:33,593 +Check out this session from +my colleague Ron for more info. + +31 +00:01:33,593 --> 00:01:37,230 +The Live Text API is available +starting on iOS 16 + +32 +00:01:37,230 --> 00:01:39,566 +for devices with +an Apple Neural Engine, + +33 +00:01:39,566 --> 00:01:44,104 +and for all devices +that support macOS 13. + +34 +00:01:44,104 --> 00:01:46,239 +It consists of +four main classes. + +35 +00:01:46,239 --> 00:01:49,176 +To use it, +first, you'll need an image. + +36 +00:01:49,176 --> 00:01:51,945 +This image is then fed +into an ImageAnalyzer, + +37 +00:01:51,945 --> 00:01:54,514 +which performs +the async analysis. + +38 +00:01:54,514 --> 00:01:56,216 +Once the analysis is complete, + +39 +00:01:56,216 --> 00:01:58,018 +the resulting +ImageAnalysis object + +40 +00:01:58,018 --> 00:02:00,721 +is provided to either +an ImageAnalysisInteraction + +41 +00:02:00,721 --> 00:02:05,525 +or ImageAnalysisOverlayView, +depending on your platform. + +42 +00:02:05,525 --> 00:02:08,428 +Seems pretty straightforward +so far, right? + +43 +00:02:08,428 --> 00:02:10,564 +Now, I'm going to demonstrate +how one would add it + +44 +00:02:10,564 --> 00:02:13,600 +to an existing application. + +45 +00:02:13,600 --> 00:02:16,003 +And here's our application. + +46 +00:02:16,003 --> 00:02:17,437 +This is a simple image viewer, + +47 +00:02:17,437 --> 00:02:21,174 +which has an image view +inside of a scroll view. + +48 +00:02:21,174 --> 00:02:24,745 +Notice, I can +both zoom and pan. + +49 +00:02:24,745 --> 00:02:27,647 +But try as I might, +I cannot select any of this text + +50 +00:02:27,647 --> 00:02:30,384 +or activate any +of these data detectors. + +51 +00:02:30,384 --> 00:02:33,120 +This simply will not do. + +52 +00:02:33,120 --> 00:02:35,455 +Here's the project in Xcode. + +53 +00:02:35,455 --> 00:02:37,624 +To add Live Text +to this application, + +54 +00:02:37,624 --> 00:02:40,861 +I'll be modifying +a view controller subclass. + +55 +00:02:40,861 --> 00:02:43,964 +First, I'm going to need +an ImageAnalyzer, + +56 +00:02:43,964 --> 00:02:48,235 +and an +ImageAnalysisInteraction. + +57 +00:02:48,235 --> 00:02:50,470 +Here, I'm simply +overriding viewDidLoad + +58 +00:02:50,470 --> 00:02:54,975 +and adding the interaction +to the imageview. + +59 +00:02:54,975 --> 00:02:58,979 +Next, I need to know +when to perform the analysis. + +60 +00:03:01,848 --> 00:03:03,517 +Notice that when +a new image is set, + +61 +00:03:03,517 --> 00:03:05,852 +I first reset the +preferredInteractionTypes + +62 +00:03:05,852 --> 00:03:10,290 +and analysis which were meant +for the old image. + +63 +00:03:10,290 --> 00:03:13,827 +Now everything is ready +for a new analysis. + +64 +00:03:13,827 --> 00:03:17,164 +Next, I'm going to create +the function we will use + +65 +00:03:17,164 --> 00:03:19,833 +and then check +that our image exists. + +66 +00:03:23,003 --> 00:03:28,542 +If so, then create a task. + +67 +00:03:28,542 --> 00:03:31,845 +Next, create a configuration +in order to tell the analyzer + +68 +00:03:31,845 --> 00:03:34,181 +what it should be looking for. + +69 +00:03:34,181 --> 00:03:39,653 +In this case, I'll go with text +and machine-readable codes. + +70 +00:03:39,653 --> 00:03:41,555 +Generating the analysis +can throw, + +71 +00:03:41,555 --> 00:03:43,690 +so handle that as appropriate. + +72 +00:03:43,690 --> 00:03:46,226 +And now finally, +I'm ready to call the method + +73 +00:03:46,226 --> 00:03:48,195 +analyzeImageWithConfiguration, + +74 +00:03:48,195 --> 00:03:51,698 +which will start +the analysis process. + +75 +00:03:51,698 --> 00:03:53,700 +Once the analysis is complete, + +76 +00:03:53,700 --> 00:03:56,169 +an indeterminate +amount of time has passed, + +77 +00:03:56,169 --> 00:03:59,005 +and the state of the application +may have changed, + +78 +00:03:59,005 --> 00:04:01,975 +so I will check that both +the analysis was successful + +79 +00:04:01,975 --> 00:04:05,145 +and that the displayed image +has not changed. + +80 +00:04:05,145 --> 00:04:07,080 +If all of these checks pass, + +81 +00:04:07,080 --> 00:04:09,683 +I can simply set +the analysis on the interaction + +82 +00:04:09,683 --> 00:04:12,219 +and set the +preferredInteractionTypes. + +83 +00:04:12,219 --> 00:04:13,787 +I'm using .automatic here, + +84 +00:04:13,787 --> 00:04:17,824 +which will give me +the default system behavior. + +85 +00:04:17,824 --> 00:04:20,894 +I think this is ready +for a test. + +86 +00:04:20,894 --> 00:04:22,896 +Oh, look at that! + +87 +00:04:22,896 --> 00:04:26,099 +I see the Live Text button +has appeared, and yep, + +88 +00:04:26,099 --> 00:04:28,368 +I can now select text. + +89 +00:04:28,368 --> 00:04:30,303 +Notice how these +interface elements + +90 +00:04:30,303 --> 00:04:32,405 +are positioned for me +automatically, + +91 +00:04:32,405 --> 00:04:35,108 +and keep their position inside +of both the image bounds + +92 +00:04:35,108 --> 00:04:39,779 +and the visible area, +with no work on my part. + +93 +00:04:39,779 --> 00:04:42,282 +OK, notice that tapping +the Live Text button + +94 +00:04:42,282 --> 00:04:44,451 +will both highlight +any selectable items, + +95 +00:04:44,451 --> 00:04:48,021 +underline data detectors, +and show Quick Actions. + +96 +00:04:48,021 --> 00:04:51,024 +I can easily tap this +Quick Action to make a call, + +97 +00:04:51,024 --> 00:04:54,394 +and even see more options +by long-pressing. + +98 +00:04:54,394 --> 00:04:58,665 +You have to admit, +this is pretty cool. + +99 +00:04:58,665 --> 00:05:01,101 +With just these +few lines of code, + +100 +00:05:01,101 --> 00:05:04,404 +I've taken an ordinary image +and brought it to life. + +101 +00:05:04,404 --> 00:05:06,740 +This simple application +now has the ability + +102 +00:05:06,740 --> 00:05:10,210 +to select text on images, +activate data detectors, + +103 +00:05:10,210 --> 00:05:14,114 +QR codes, lookup, +translate text, and more. + +104 +00:05:14,114 --> 00:05:16,716 +Not too shabby from just +this few lines of code, + +105 +00:05:16,716 --> 00:05:18,652 +if you ask me. + +106 +00:05:18,652 --> 00:05:21,354 +And now that you've have seen +how to implement Live Text, + +107 +00:05:21,354 --> 00:05:23,890 +I'm going to go over +a few tips and tricks + +108 +00:05:23,890 --> 00:05:26,560 +that may help you +with your adoption. + +109 +00:05:26,560 --> 00:05:29,095 +I'll start by exploring +interaction types. + +110 +00:05:29,095 --> 00:05:30,931 +Most developers +will want .automatic, + +111 +00:05:30,931 --> 00:05:33,466 +which provides text selection, +but will also highlight + +112 +00:05:33,466 --> 00:05:36,736 +data detectors if the Live Text +button is active. + +113 +00:05:36,736 --> 00:05:40,407 +This will draw a line underneath +any applicable detected items + +114 +00:05:40,407 --> 00:05:43,510 +and allows one-tap access +to activate them. + +115 +00:05:43,510 --> 00:05:45,745 +This is the exact same +behavior you would see + +116 +00:05:45,745 --> 00:05:48,148 +from built-in applications. + +117 +00:05:48,148 --> 00:05:50,884 +If it makes sense for your app +to only have text selection + +118 +00:05:50,884 --> 00:05:54,387 +without data detectors, you may +set the type to .textSelection + +119 +00:05:54,387 --> 00:05:59,159 +and it will not change with the +state of the Live Text button. + +120 +00:05:59,159 --> 00:06:00,827 +If, however, +it makes sense for your app + +121 +00:06:00,827 --> 00:06:04,030 +to only have data detectors +without text selection, + +122 +00:06:04,030 --> 00:06:06,132 +set the type to +.dataDetectors. + +123 +00:06:06,132 --> 00:06:09,269 +Note that in this mode, +since selection is disabled, + +124 +00:06:09,269 --> 00:06:11,204 +you will not see +a Live Text button, + +125 +00:06:11,204 --> 00:06:13,673 +but data detectors will be +underlined and ready + +126 +00:06:13,673 --> 00:06:17,210 +for one-tap access. + +127 +00:06:17,210 --> 00:06:18,812 +Setting the +preferredInteractionTypes + +128 +00:06:18,812 --> 00:06:21,715 +to an empty set +will disable the interaction. + +129 +00:06:21,715 --> 00:06:26,052 +And also, a last note, with text +selection or automatic mode, + +130 +00:06:26,052 --> 00:06:28,622 +you'll find you can +still activate data detectors + +131 +00:06:28,622 --> 00:06:31,258 +by long-pressing. + +132 +00:06:31,258 --> 00:06:32,726 +This is controlled by the + +133 +00:06:32,726 --> 00:06:35,829 +allowLongPressForDataDetectorsIn +TextMode property, + +134 +00:06:35,829 --> 00:06:39,766 +which will be active when set +to true, which the default. + +135 +00:06:39,766 --> 00:06:43,270 +Simply set to false +to disable this if necessary. + +136 +00:06:43,270 --> 00:06:44,437 +I would like +to now take a moment + +137 +00:06:44,437 --> 00:06:46,573 +and talk about these buttons +at the bottom, + +138 +00:06:46,573 --> 00:06:49,309 +collectively known as +the supplementary interface. + +139 +00:06:49,309 --> 00:06:51,278 +This consists of +the Live Text button, + +140 +00:06:51,278 --> 00:06:53,446 +which normally lives in +the bottom right-hand corner, + +141 +00:06:53,446 --> 00:06:56,516 +as well as Quick Actions +which appear on the bottom left. + +142 +00:06:56,516 --> 00:06:59,986 +Quick Actions represent any +data detectors from the analysis + +143 +00:06:59,986 --> 00:07:02,922 +and are visible when +the Live Text button is active. + +144 +00:07:02,922 --> 00:07:05,091 +The size, position, +and visibility + +145 +00:07:05,091 --> 00:07:07,093 +are controlled +by the interaction. + +146 +00:07:07,093 --> 00:07:10,297 +And while the default position +and look matches the system, + +147 +00:07:10,297 --> 00:07:12,365 +your app may have +custom interface elements + +148 +00:07:12,365 --> 00:07:15,101 +which may interfere +or utilize different fonts + +149 +00:07:15,101 --> 00:07:16,469 +and symbol weights. + +150 +00:07:16,469 --> 00:07:20,307 +Let's look at how you can +customize this interface. + +151 +00:07:20,307 --> 00:07:23,877 +First off, the isSupplementary +InterfaceHidden property. + +152 +00:07:23,877 --> 00:07:26,913 +If I wanted to allow my app +to still select text + +153 +00:07:26,913 --> 00:07:29,983 +but I did not want to show +the Live Text button, + +154 +00:07:29,983 --> 00:07:31,751 +if I set +the SupplementaryInterfaceHidden + +155 +00:07:31,751 --> 00:07:34,321 +to true, you will not see +any Live Text button + +156 +00:07:34,321 --> 00:07:37,090 +or Quick Actions. + +157 +00:07:37,090 --> 00:07:40,126 +We also have a content insets +property available. + +158 +00:07:40,126 --> 00:07:42,495 +If you have interface elements +that would overlap + +159 +00:07:42,495 --> 00:07:43,997 +the supplementary interface, + +160 +00:07:43,997 --> 00:07:46,032 +you may adjust +the content insets + +161 +00:07:46,032 --> 00:07:48,768 +so the Live Text button +and Quick Actions adapt nicely + +162 +00:07:48,768 --> 00:07:52,839 +to your existing app content +when visible. + +163 +00:07:52,839 --> 00:07:54,441 +If your app is using +a custom font + +164 +00:07:54,441 --> 00:07:56,276 +you'd like +the interface to adopt, + +165 +00:07:56,276 --> 00:07:58,244 +setting the +supplementaryInterfaceFont + +166 +00:07:58,244 --> 00:08:00,547 +will cause the Live Text +button and Quick Actions + +167 +00:08:00,547 --> 00:08:02,515 +to use the specified font +for text + +168 +00:08:02,515 --> 00:08:04,651 +and font weight for symbols. + +169 +00:08:04,651 --> 00:08:06,986 +Please note that +for button-sizing consistency, + +170 +00:08:06,986 --> 00:08:10,090 +Live Text will ignore +the point size. + +171 +00:08:10,090 --> 00:08:12,025 +Switching gears for a moment, + +172 +00:08:12,025 --> 00:08:14,427 +if you are not using +UIImageview, + +173 +00:08:14,427 --> 00:08:18,531 +you may discover that highlights +do not match up with your image. + +174 +00:08:18,531 --> 00:08:20,600 +This is because +with UIImageView, + +175 +00:08:20,600 --> 00:08:22,969 +VisionKit can use +its ContentMode property + +176 +00:08:22,969 --> 00:08:26,673 +to calculate the contentsRect +automatically for you. + +177 +00:08:26,673 --> 00:08:29,776 +Here, the interaction's view +has a bounds that is bigger + +178 +00:08:29,776 --> 00:08:32,145 +than its image content +but is using + +179 +00:08:32,145 --> 00:08:36,049 +the default's content rect, +which is a unit rectangle. + +180 +00:08:36,049 --> 00:08:39,152 +This is easily solved by +implementing the delegate method + +181 +00:08:39,152 --> 00:08:41,054 +contentsRectForInteraction + +182 +00:08:41,054 --> 00:08:43,590 +and return a rectangle +in unit coordinate space + +183 +00:08:43,590 --> 00:08:46,159 +describing how +the image content relates + +184 +00:08:46,159 --> 00:08:49,829 +to the interaction's bounds +in order to correct this. + +185 +00:08:49,829 --> 00:08:52,132 +For example, returning +a rectangle with these values + +186 +00:08:52,132 --> 00:08:53,600 +would correct the issue, + +187 +00:08:53,600 --> 00:08:55,902 +but please return the correct +normalized rectangle + +188 +00:08:55,902 --> 00:08:59,939 +based on your app's +current content and layout. + +189 +00:08:59,939 --> 00:09:01,841 +contentsRectForInteraction +will be called + +190 +00:09:01,841 --> 00:09:04,811 +whenever the interaction's +bounds change, however, + +191 +00:09:04,811 --> 00:09:06,546 +if your contentsRect +has changed + +192 +00:09:06,546 --> 00:09:08,882 +but your interaction's bounds +have not, + +193 +00:09:08,882 --> 00:09:11,284 +you can ask the interaction +to update by calling + +194 +00:09:11,284 --> 00:09:15,121 +setContentsRectNeedsUpdate(). + +195 +00:09:15,121 --> 00:09:18,825 +Another question you may have +when adopting Live Text may be, + +196 +00:09:18,825 --> 00:09:21,561 +Where is the best place +to put this interaction? + +197 +00:09:21,561 --> 00:09:24,964 +Ideally, Live Text interactions +are placed directly on the view + +198 +00:09:24,964 --> 00:09:27,167 +that hosts your image content. + +199 +00:09:27,167 --> 00:09:30,003 +As mentioned before, +UIImageView will handle + +200 +00:09:30,003 --> 00:09:32,605 +the contentsRect +calculations for you, + +201 +00:09:32,605 --> 00:09:36,075 +and while not necessary, +is preferred. + +202 +00:09:36,075 --> 00:09:38,178 +If you are using UIImageview, + +203 +00:09:38,178 --> 00:09:40,346 +just set the interaction +on the imageView + +204 +00:09:40,346 --> 00:09:43,450 +and VisionKit +will handle the rest. + +205 +00:09:43,450 --> 00:09:45,973 +However, if your ImageView +is located + +206 +00:09:45,973 --> 00:09:47,487 +inside of a ScrollView, + +207 +00:09:47,487 --> 00:09:50,590 +you may be tempted to place the +interaction on the ScrollView, + +208 +00:09:50,590 --> 00:09:54,294 +however, this is not recommended +and could be difficult to manage + +209 +00:09:54,294 --> 00:09:58,765 +as it will have a continually +changing contentsRect. + +210 +00:09:58,765 --> 00:10:00,600 +The solution here is the same, + +211 +00:10:00,600 --> 00:10:02,268 +place the interaction +on the view + +212 +00:10:02,268 --> 00:10:04,103 +that hosts your image content, + +213 +00:10:04,103 --> 00:10:05,839 +even if it is inside +a ScrollView + +214 +00:10:05,839 --> 00:10:08,942 +with magnification applied. + +215 +00:10:08,942 --> 00:10:11,144 +I'm going talk about gestures +for a moment, + +216 +00:10:11,144 --> 00:10:14,547 +Live Text has a very, very +rich set of gesture recognizers, + +217 +00:10:14,547 --> 00:10:16,015 +to say to least. + +218 +00:10:16,015 --> 00:10:18,084 +Depending on how your app +is structured, + +219 +00:10:18,084 --> 00:10:20,653 +it's possible you may find +the interaction + +220 +00:10:20,653 --> 00:10:22,522 +responding to gestures +and events + +221 +00:10:22,522 --> 00:10:25,558 +your app should really handle +or vice versa. + +222 +00:10:25,558 --> 00:10:26,593 +Don't panic. + +223 +00:10:26,593 --> 00:10:29,262 +Here are a few techniques +you can use to help correct + +224 +00:10:29,262 --> 00:10:31,498 +if you see these issues occur. + +225 +00:10:31,498 --> 00:10:33,366 +One common way to correct this + +226 +00:10:33,366 --> 00:10:35,168 +is to implement +the delegate method + +227 +00:10:35,168 --> 00:10:39,005 +interactionShouldBeginAtPointFor +InteractionType. + +228 +00:10:39,005 --> 00:10:42,842 +If you return false, the +action will not be performed. + +229 +00:10:42,842 --> 00:10:44,978 +A good place to start is +to check if the interaction + +230 +00:10:44,978 --> 00:10:47,547 +has an interactive item +at the given point + +231 +00:10:47,547 --> 00:10:50,283 +or if it has an active +text selection. + +232 +00:10:50,283 --> 00:10:52,285 +The text selection check +is used here + +233 +00:10:52,285 --> 00:10:55,488 +so you will be able to have +the ability tap off of the text + +234 +00:10:55,488 --> 00:10:58,258 +in order to deselect it. + +235 +00:10:58,258 --> 00:11:01,194 +On the other hand, +if you find your interaction + +236 +00:11:01,194 --> 00:11:03,296 +doesn't seem +to respond to gestures, + +237 +00:11:03,296 --> 00:11:05,665 +it may be because +there's a gesture recognizer + +238 +00:11:05,665 --> 00:11:08,835 +in your app +that's handling them instead. + +239 +00:11:08,835 --> 00:11:11,738 +In this case, you can craft +a similar solution + +240 +00:11:11,738 --> 00:11:15,675 +using your gestureRecognizer's +gestureRecognizerShouldBegin + +241 +00:11:15,675 --> 00:11:17,043 +delegate method. + +242 +00:11:17,043 --> 00:11:21,014 +Here, I perform a similar check +and return false + +243 +00:11:21,014 --> 00:11:23,249 +if there is an interactive item +at the location + +244 +00:11:23,249 --> 00:11:25,385 +or there's an active +text selection. + +245 +00:11:25,385 --> 00:11:26,519 +On a side note. + +246 +00:11:26,519 --> 00:11:28,588 +In this example, +I'm first converting + +247 +00:11:28,588 --> 00:11:32,025 +the gestureRecognizer's location +to the window's coordinate space + +248 +00:11:32,025 --> 00:11:33,326 +by passing in nil, + +249 +00:11:33,326 --> 00:11:36,663 +and then converting it +to the interaction's view. + +250 +00:11:36,663 --> 00:11:39,098 +This may be necessary +if your interaction + +251 +00:11:39,098 --> 00:11:42,869 +is inside of a ScrollView +with magnification applied. + +252 +00:11:42,869 --> 00:11:44,637 +If you find your points +aren't matching up, + +253 +00:11:44,637 --> 00:11:46,906 +give this technique a try. + +254 +00:11:46,906 --> 00:11:49,342 +Another similar option +I have found to be useful + +255 +00:11:49,342 --> 00:11:52,445 +is to override UIView's +hitTest:WithEvent. + +256 +00:11:52,445 --> 00:11:54,814 +Here, once again, +similar story, + +257 +00:11:54,814 --> 00:11:57,150 +I perform the same +types of checks as before, + +258 +00:11:57,150 --> 00:12:00,720 +and in this case, +return the appropriate view. + +259 +00:12:00,720 --> 00:12:05,158 +As always, we want your app +to be as responsive as possible, + +260 +00:12:05,158 --> 00:12:06,893 +and while the Neural Engine +makes analysis + +261 +00:12:06,893 --> 00:12:10,129 +extremely efficient, +there a few ImageAnalyzer tips + +262 +00:12:10,129 --> 00:12:12,365 +I'd like to share +for best performance. + +263 +00:12:12,365 --> 00:12:14,767 +Ideally, you want +only one ImageAnalyzer + +264 +00:12:14,767 --> 00:12:16,436 +shared in your app. + +265 +00:12:16,436 --> 00:12:19,138 +Also, we support +several types of images. + +266 +00:12:19,138 --> 00:12:21,674 +You should always +minimize image conversions + +267 +00:12:21,674 --> 00:12:24,010 +by passing in the native type +that you have; + +268 +00:12:24,010 --> 00:12:27,113 +however, if you do happen +to have a CVPixelBuffer, + +269 +00:12:27,113 --> 00:12:29,349 +that would be +the most efficient. + +270 +00:12:29,349 --> 00:12:32,885 +Also, in order to best utilize +system resources, + +271 +00:12:32,885 --> 00:12:36,389 +you should begin your analysis +only when, or just before, + +272 +00:12:36,389 --> 00:12:38,958 +an image appears onscreen. + +273 +00:12:38,958 --> 00:12:41,427 +If your app's content scrolls -- + +274 +00:12:41,427 --> 00:12:43,529 +for example, +it has a timeline -- + +275 +00:12:43,529 --> 00:12:47,567 +begin analysis only once +the scrolling has stopped. + +276 +00:12:47,567 --> 00:12:51,004 +Now this API isn't the only +place you'll see Live Text, + +277 +00:12:51,004 --> 00:12:52,739 +support is provided +automatically + +278 +00:12:52,739 --> 00:12:56,542 +in a few frameworks across the +system your app may already use. + +279 +00:12:56,542 --> 00:13:00,546 +For example, +UITextField or UITextView + +280 +00:13:00,546 --> 00:13:04,250 +have Live Text support using +Camera for keyboard input. + +281 +00:13:04,250 --> 00:13:08,287 +And Live Text is also supported +in WebKit and Quick Look. + +282 +00:13:08,287 --> 00:13:12,158 +For more information, +please check out these sessions. + +283 +00:13:12,158 --> 00:13:14,160 +New this year for iOS 16, + +284 +00:13:14,160 --> 00:13:16,963 +we've added +Live Text support in AVKit. + +285 +00:13:16,963 --> 00:13:20,400 +AVPlayerView and ViewController +support Live Text + +286 +00:13:20,400 --> 00:13:22,902 +in paused frames automatically + +287 +00:13:22,902 --> 00:13:25,738 +via the allowsVideoFrameAnalysis +property, + +288 +00:13:25,738 --> 00:13:27,707 +which is enabled by default. + +289 +00:13:27,707 --> 00:13:29,542 +Please note, +this is only available + +290 +00:13:29,542 --> 00:13:32,979 +with non-FairPlay +protected content. + +291 +00:13:32,979 --> 00:13:35,281 +If you're using AVPlayerLayer, + +292 +00:13:35,281 --> 00:13:37,684 +then you are responsible +for managing the analysis + +293 +00:13:37,684 --> 00:13:40,987 +and the interaction but it is +very important to use the + +294 +00:13:40,987 --> 00:13:43,222 +currentlyDisplayedPixelBuffer +property + +295 +00:13:43,222 --> 00:13:45,091 +to get the current frame. + +296 +00:13:45,091 --> 00:13:46,659 +This is the only way +to guarantee + +297 +00:13:46,659 --> 00:13:49,829 +the proper frame +is being analyzed. + +298 +00:13:49,829 --> 00:13:51,597 +This will only return +a valid value + +299 +00:13:51,597 --> 00:13:55,968 +if the video play rate is zero, +and this is a shallow copy + +300 +00:13:55,968 --> 00:13:58,671 +and absolutely +not safe to write to. + +301 +00:13:58,671 --> 00:14:00,740 +And once again, +only available + +302 +00:14:00,740 --> 00:14:04,210 +for non-FairPlay +protected content. + +303 +00:14:04,210 --> 00:14:06,746 +We are thrilled to help bring +Live Text functionality + +304 +00:14:06,746 --> 00:14:07,847 +to your app. + +305 +00:14:07,847 --> 00:14:10,850 +On behalf of everybody +on the Live Text team, + +306 +00:14:10,850 --> 00:14:12,952 +thank you for joining us +for this session. + +307 +00:14:12,952 --> 00:14:16,222 +I am stoked to see how you use +it for images in your app. + +308 +00:14:16,222 --> 00:14:18,691 +And as always, have fun! + +309 +00:14:18,691 --> 00:14:23,096 +♪ + diff --git a/eng/2022 Session 10027 Optimize your Core ML usage en.srt b/eng/2022 Session 10027 Optimize your Core ML usage en.srt new file mode 100644 index 0000000..682e116 --- /dev/null +++ b/eng/2022 Session 10027 Optimize your Core ML usage en.srt @@ -0,0 +1,2280 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,610 +♪ + +3 +00:00:09,610 --> 00:00:11,445 +Hi, my name is Ben, + +4 +00:00:11,445 --> 00:00:13,947 +and I'm an engineer +on the Core ML team. + +5 +00:00:13,947 --> 00:00:16,283 +Today I'm going to show some +of the exciting new features + +6 +00:00:16,283 --> 00:00:18,218 +being added to Core ML. + +7 +00:00:18,218 --> 00:00:20,287 +The focus of these features +is to help you + +8 +00:00:20,287 --> 00:00:23,257 +optimize your Core ML usage. + +9 +00:00:23,257 --> 00:00:25,893 +In this session, +I'll go over performance tools + +10 +00:00:25,893 --> 00:00:28,629 +that are now available to give +you the information you need + +11 +00:00:28,629 --> 00:00:31,565 +to understand and optimize +your model's performance + +12 +00:00:31,565 --> 00:00:34,668 +when using Core ML. + +13 +00:00:34,668 --> 00:00:36,803 +Then I'll go over +some enhanced APIs + +14 +00:00:36,803 --> 00:00:40,540 +which will enable you +to make those optimizations. + +15 +00:00:40,540 --> 00:00:42,376 +And lastly, +I'll give an overview + +16 +00:00:42,376 --> 00:00:44,611 +of some additional +Core ML capabilities + +17 +00:00:44,611 --> 00:00:47,581 +and integration options. + +18 +00:00:47,581 --> 00:00:50,484 +Let me begin with +the performance tools. + +19 +00:00:50,484 --> 00:00:51,752 +To give some background, + +20 +00:00:51,752 --> 00:00:53,887 +I'll start by summarizing +the standard workflow + +21 +00:00:53,887 --> 00:00:56,723 +when using Core ML +within your app. + +22 +00:00:56,723 --> 00:00:59,526 +The first step +is to choose your model. + +23 +00:00:59,526 --> 00:01:01,561 +This may be done +in a variety of ways, + +24 +00:01:01,561 --> 00:01:04,197 +such as using Core ML tools +to convert a PyTorch + +25 +00:01:04,197 --> 00:01:06,900 +or TensorFlow model +to Core ML format, + +26 +00:01:06,900 --> 00:01:09,603 +using an already-existing +Core ML model, + +27 +00:01:09,603 --> 00:01:13,140 +or using Create ML +to train and export your model. + +28 +00:01:13,140 --> 00:01:15,008 +For more details +on model conversion + +29 +00:01:15,008 --> 00:01:16,610 +or to learn about Create ML, + +30 +00:01:16,610 --> 00:01:20,280 +I recommend checking out +these sessions. + +31 +00:01:20,280 --> 00:01:23,951 +The next step is to integrate +that model into your app. + +32 +00:01:23,951 --> 00:01:26,586 +This involves bundling the model +with your application + +33 +00:01:26,586 --> 00:01:29,890 +and using the Core ML APIs +to load and run inference + +34 +00:01:29,890 --> 00:01:33,660 +on that model +during your app's execution. + +35 +00:01:33,660 --> 00:01:39,032 +The last step is to optimize +the way you use Core ML. + +36 +00:01:39,032 --> 00:01:41,435 +First, I'll go over +choosing a model. + +37 +00:01:41,435 --> 00:01:42,736 +There are many aspects +of a model + +38 +00:01:42,736 --> 00:01:44,137 +that you may want to consider + +39 +00:01:44,137 --> 00:01:47,407 +when deciding if you should use +that model within your app. + +40 +00:01:47,407 --> 00:01:49,343 +You also may have multiple +candidates of models + +41 +00:01:49,343 --> 00:01:50,777 +you'd like to select from, + +42 +00:01:50,777 --> 00:01:53,347 +but how do you decide +which one to use? + +43 +00:01:53,347 --> 00:01:55,415 +You need to have a model +whose functionality + +44 +00:01:55,415 --> 00:01:58,919 +will match the requirements of +the feature you wish to enable. + +45 +00:01:58,919 --> 00:02:01,321 +This includes understanding +the model's accuracy + +46 +00:02:01,321 --> 00:02:03,790 +as well as its performance. + +47 +00:02:03,790 --> 00:02:05,625 +A great way to learn about +a Core ML model + +48 +00:02:05,625 --> 00:02:07,728 +is by opening it in Xcode. + +49 +00:02:07,728 --> 00:02:09,262 +Just double-click on any model, + +50 +00:02:09,262 --> 00:02:12,432 +and it will bring up +the following. + +51 +00:02:12,432 --> 00:02:15,102 +At the top, +you'll find the model type, + +52 +00:02:15,102 --> 00:02:19,806 +its size, and the operating +system requirements. + +53 +00:02:19,806 --> 00:02:22,542 +In the General tab, +it shows additional details + +54 +00:02:22,542 --> 00:02:24,611 +captured in +the model's metadata, + +55 +00:02:24,611 --> 00:02:26,546 +its compute +and storage precision, + +56 +00:02:26,546 --> 00:02:30,784 +and info, such as class labels +that it can predict. + +57 +00:02:30,784 --> 00:02:33,153 +The Preview tab +is for testing out your model + +58 +00:02:33,153 --> 00:02:37,624 +by providing example inputs +and seeing what it predicts. + +59 +00:02:37,624 --> 00:02:40,660 +The Predictions tab displays +the model's inputs and outputs, + +60 +00:02:40,660 --> 00:02:42,262 +as well as the types and sizes + +61 +00:02:42,262 --> 00:02:45,532 +that Core ML +will expect at runtime. + +62 +00:02:45,532 --> 00:02:48,935 +And finally, the Utilities tab +can help with model encryption + +63 +00:02:48,935 --> 00:02:52,572 +and deployment tasks. + +64 +00:02:52,572 --> 00:02:55,308 +Overall, these views give you +a quick overview + +65 +00:02:55,308 --> 00:02:58,879 +of your model's functionality +and preview of its accuracy. + +66 +00:02:58,879 --> 00:03:02,416 +But what about +your model's performance? + +67 +00:03:02,416 --> 00:03:04,518 +The cost of loading a model, + +68 +00:03:04,518 --> 00:03:07,220 +the amount of time +a single prediction takes, + +69 +00:03:07,220 --> 00:03:09,489 +or what hardware it utilizes, + +70 +00:03:09,489 --> 00:03:12,426 +may be critical factors +for your use case. + +71 +00:03:12,426 --> 00:03:13,660 +You may have hard targets + +72 +00:03:13,660 --> 00:03:16,430 +related to real-time +streaming data constraints + +73 +00:03:16,430 --> 00:03:19,733 +or need to make key design +decisions around user interface + +74 +00:03:19,733 --> 00:03:22,869 +depending +on perceived latency. + +75 +00:03:22,869 --> 00:03:25,372 +One way to get insight +into the model's performance + +76 +00:03:25,372 --> 00:03:27,841 +is to do an initial integration +into your app + +77 +00:03:27,841 --> 00:03:29,743 +or by creating a small prototype + +78 +00:03:29,743 --> 00:03:32,045 +which you can instrument +and measure. + +79 +00:03:32,045 --> 00:03:34,281 +And since performance +is hardware dependent, + +80 +00:03:34,281 --> 00:03:35,849 +you would likely want +to do these measurements + +81 +00:03:35,849 --> 00:03:39,052 +on a variety +of supported hardware. + +82 +00:03:39,052 --> 00:03:41,721 +Xcode and Core ML +can now help you with this task + +83 +00:03:41,721 --> 00:03:45,058 +even before writing +a single line of code. + +84 +00:03:45,058 --> 00:03:47,961 +Core ML now allows you +to create performance reports. + +85 +00:03:47,961 --> 00:03:49,930 +Let me show you. + +86 +00:03:52,566 --> 00:03:53,967 +[CLICK] + +87 +00:03:53,967 --> 00:03:56,036 +I now have +the Xcode model viewer open + +88 +00:03:56,036 --> 00:03:59,272 +for the YOLOv3 +object detection model. + +89 +00:03:59,272 --> 00:04:01,708 +Between the Predictions +and Utilities tabs, + +90 +00:04:01,708 --> 00:04:04,211 +there is now a Performance tab. + +91 +00:04:04,211 --> 00:04:06,012 +To generate +a performance report, + +92 +00:04:06,012 --> 00:04:10,317 +I'll select the plus icon +at the bottom left, + +93 +00:04:10,317 --> 00:04:12,252 +select the device +I'd like to run on -- + +94 +00:04:12,252 --> 00:04:16,756 +which is my iPhone -- +click next, + +95 +00:04:16,756 --> 00:04:20,293 +then select which compute units +I'd like Core ML to use. + +96 +00:04:20,293 --> 00:04:21,695 +I'm going to leave it on All, + +97 +00:04:21,695 --> 00:04:23,830 +to allow Core ML +to optimize for latency + +98 +00:04:23,830 --> 00:04:26,833 +with all available +compute units. + +99 +00:04:26,833 --> 00:04:31,138 +Now I'll finish +by pressing Run Test. + +100 +00:04:31,138 --> 00:04:32,606 +To ensure the test can run, + +101 +00:04:32,606 --> 00:04:36,776 +make sure the selected device +is unlocked. + +102 +00:04:36,776 --> 00:04:38,979 +It shows a spinning icon +while the performance report + +103 +00:04:38,979 --> 00:04:40,847 +is being generated. + +104 +00:04:40,847 --> 00:04:42,215 +To create the report, + +105 +00:04:42,215 --> 00:04:44,451 +the model is sent over +to the device, + +106 +00:04:44,451 --> 00:04:46,820 +then there are several +iterations of compile, + +107 +00:04:46,820 --> 00:04:50,490 +load, and predictions +which are run with the model. + +108 +00:04:50,490 --> 00:04:51,791 +Once those are complete, + +109 +00:04:51,791 --> 00:04:55,562 +the metrics in the performance +report are calculated. + +110 +00:04:55,562 --> 00:04:57,130 +Now it's run the model +on my iPhone, + +111 +00:04:57,130 --> 00:05:00,767 +and it displays +the performance report. + +112 +00:05:00,767 --> 00:05:02,636 +At the top, +it shows some details + +113 +00:05:02,636 --> 00:05:04,838 +about the device +where the test was run + +114 +00:05:04,838 --> 00:05:09,342 +as well as which compute units +were selected. + +115 +00:05:09,342 --> 00:05:12,145 +Next it shows statistics +about the run. + +116 +00:05:12,145 --> 00:05:16,116 +The median prediction time +was 22.19 milliseconds + +117 +00:05:16,116 --> 00:05:20,153 +and the median load time +was about 400 ms. + +118 +00:05:20,153 --> 00:05:23,390 +Also, if you plan to compile +your model on-device, + +119 +00:05:23,390 --> 00:05:28,795 +this shows the compilation time +was about 940 ms. + +120 +00:05:28,795 --> 00:05:32,399 +A prediction time of around +22 ms tells me that this model + +121 +00:05:32,399 --> 00:05:34,668 +can support about +45 frames per second + +122 +00:05:34,668 --> 00:05:36,670 +if I want to run it +in real time. + +123 +00:05:39,773 --> 00:05:41,775 +Since this model contains +a neural network, + +124 +00:05:41,775 --> 00:05:43,643 +there's a layer view +displayed towards the bottom + +125 +00:05:43,643 --> 00:05:45,812 +of the performance report. + +126 +00:05:45,812 --> 00:05:49,015 +This shows the name +and type of all of the layers, + +127 +00:05:49,015 --> 00:05:53,853 +as well as which compute unit +each layer ran on. + +128 +00:05:53,853 --> 00:05:56,423 +A filled-in checkmark means +that the layer was executed + +129 +00:05:56,423 --> 00:05:59,492 +on that compute unit. + +130 +00:05:59,492 --> 00:06:02,062 +An unfilled checkmark means +that the layer is supported + +131 +00:06:02,062 --> 00:06:03,330 +on that compute unit, + +132 +00:06:03,330 --> 00:06:06,866 +but Core ML did not choose +to run it there. + +133 +00:06:06,866 --> 00:06:09,502 +And an empty diamond means +that the layer is not supported + +134 +00:06:09,502 --> 00:06:12,272 +on that compute unit. + +135 +00:06:12,272 --> 00:06:16,009 +In this case, +54 layers were run on the GPU, + +136 +00:06:16,009 --> 00:06:19,312 +and 32 layers +were run on the Neural Engine. + +137 +00:06:19,312 --> 00:06:20,614 +You can also filter the layers + +138 +00:06:20,614 --> 00:06:23,917 +by a compute unit +by clicking on it. + +139 +00:06:29,923 --> 00:06:31,958 +That was how +you can use Xcode 14 + +140 +00:06:31,958 --> 00:06:35,428 +to generate performance reports +for your Core ML models. + +141 +00:06:35,428 --> 00:06:37,464 +This was shown for running +on an iPhone, + +142 +00:06:37,464 --> 00:06:40,367 +but it will allow you to test +on multiple operating system + +143 +00:06:40,367 --> 00:06:42,235 +and hardware combinations, + +144 +00:06:42,235 --> 00:06:45,505 +without having to write +a single line of code. + +145 +00:06:45,505 --> 00:06:47,274 +Now that you've chosen +your model, + +146 +00:06:47,274 --> 00:06:50,944 +the next step is to integrate +this model into your app. + +147 +00:06:50,944 --> 00:06:53,280 +This involves bundling +the model with your app + +148 +00:06:53,280 --> 00:06:56,316 +and making use of Core ML APIs +to load the model + +149 +00:06:56,316 --> 00:06:59,252 +and make predictions with it. + +150 +00:06:59,252 --> 00:07:01,521 +In this case, I've built an app + +151 +00:07:01,521 --> 00:07:05,325 +that uses Core ML style transfer +models to perform style transfer + +152 +00:07:05,325 --> 00:07:08,061 +on frames from +a live camera session. + +153 +00:07:08,061 --> 00:07:10,764 +It's working properly; +however, the frame rate + +154 +00:07:10,764 --> 00:07:15,101 +is slower than I'd expect, +and I'd like to understand why. + +155 +00:07:15,101 --> 00:07:17,237 +This is where you'd move on +to step three, + +156 +00:07:17,237 --> 00:07:20,674 +which is to optimize +your Core ML usage. + +157 +00:07:20,674 --> 00:07:23,109 +Generating a performance report +can show the performance + +158 +00:07:23,109 --> 00:07:26,846 +a model is capable of achieving +in a stand-alone environment; + +159 +00:07:26,846 --> 00:07:29,816 +however, you also need a way +to profile the performance + +160 +00:07:29,816 --> 00:07:32,952 +of a model that's running +live in your app. + +161 +00:07:32,952 --> 00:07:35,522 +For this, you can now use +the Core ML Instrument + +162 +00:07:35,522 --> 00:07:38,858 +found in the Instruments app +in Xcode 14. + +163 +00:07:38,858 --> 00:07:41,294 +This Instrument allows you +to visualize the performance + +164 +00:07:41,294 --> 00:07:43,563 +of your model when it runs +live in your app, + +165 +00:07:43,563 --> 00:07:46,933 +and helps you identify +potential performance issues. + +166 +00:07:46,933 --> 00:07:50,837 +Let me show +how it can be used. + +167 +00:07:50,837 --> 00:07:52,005 +So I'm in Xcode + +168 +00:07:52,005 --> 00:07:54,240 +with my style transfer app +workspace open, + +169 +00:07:54,240 --> 00:07:56,543 +and I'm ready +to profile the app. + +170 +00:07:56,543 --> 00:07:57,944 +I'll force-click +on the Run button + +171 +00:07:57,944 --> 00:07:59,946 +and select Profile. + +172 +00:08:02,482 --> 00:08:04,517 +This will install +the latest version of the code + +173 +00:08:04,517 --> 00:08:06,820 +on my device +and open Instruments for me + +174 +00:08:06,820 --> 00:08:10,290 +with my targeted device +and app selected. + +175 +00:08:10,290 --> 00:08:12,659 +Since I want to profile +my Core ML usage, + +176 +00:08:12,659 --> 00:08:17,163 +I'm going to select +the Core ML template. + +177 +00:08:17,163 --> 00:08:19,366 +This template includes +the Core ML Instrument, + +178 +00:08:19,366 --> 00:08:21,501 +as well as several +other useful Instruments + +179 +00:08:21,501 --> 00:08:24,771 +which will help you profile +your Core ML usage. + +180 +00:08:24,771 --> 00:08:28,575 +To capture a trace, +I'll simply press Record. + +181 +00:08:32,579 --> 00:08:35,081 +The app is now running +on my iPhone. + +182 +00:08:35,081 --> 00:08:36,916 +I will let it run +for a few seconds + +183 +00:08:36,916 --> 00:08:39,285 +and use a few different styles. + +184 +00:08:39,285 --> 00:08:42,922 +And now I'll end the trace +by pressing the Stop button. + +185 +00:08:42,922 --> 00:08:44,891 +Now I have my Instruments trace. + +186 +00:08:44,891 --> 00:08:48,328 +I'm going to focus on +the Core ML Instrument. + +187 +00:08:48,328 --> 00:08:50,830 +The Core ML Instrument +shows all of the Core ML events + +188 +00:08:50,830 --> 00:08:53,032 +that were captured in the trace. + +189 +00:08:53,032 --> 00:08:56,636 +The initial view groups all +of the events into three lanes: + +190 +00:08:56,636 --> 00:09:01,674 +Activity, Data, and Compute. + +191 +00:09:01,674 --> 00:09:04,577 +The Activity lane shows +top-level Core ML events + +192 +00:09:04,577 --> 00:09:06,312 +which have a one-to-one +relationship + +193 +00:09:06,312 --> 00:09:09,849 +with the actual Core ML APIs +that you would call directly, + +194 +00:09:09,849 --> 00:09:14,387 +such as loads and predictions. + +195 +00:09:14,387 --> 00:09:16,589 +The Data lane shows events +in which Core ML + +196 +00:09:16,589 --> 00:09:19,426 +is performing data checks +or data transformations + +197 +00:09:19,426 --> 00:09:20,960 +to make sure that +it can safely work + +198 +00:09:20,960 --> 00:09:25,165 +with the model's inputs +and outputs. + +199 +00:09:25,165 --> 00:09:27,267 +The Compute lane shows +when Core ML sends + +200 +00:09:27,267 --> 00:09:30,069 +compute requests +to specific compute units, + +201 +00:09:30,069 --> 00:09:33,807 +such as the Neural Engine, +or the GPU. + +202 +00:09:33,807 --> 00:09:36,910 +You can also select +the Ungrouped view + +203 +00:09:36,910 --> 00:09:42,148 +where there is an individual +lane for each event type. + +204 +00:09:42,148 --> 00:09:46,386 +At the bottom, there's the +Model Activity Aggregation view. + +205 +00:09:46,386 --> 00:09:49,489 +This view provides aggregate +statistics for all of the events + +206 +00:09:49,489 --> 00:09:51,524 +displayed in the trace. + +207 +00:09:51,524 --> 00:09:53,159 +For example, in this trace, + +208 +00:09:53,159 --> 00:09:57,063 +the average model load +took 17.17 ms, + +209 +00:09:57,063 --> 00:10:02,101 +and the average prediction +took 7.2 ms. + +210 +00:10:02,101 --> 00:10:05,772 +Another note is that it can sort +the events by duration. + +211 +00:10:05,772 --> 00:10:08,608 +Here, the list is telling me +that more time is being spent + +212 +00:10:08,608 --> 00:10:12,078 +loading the model than actually +making predictions with it, + +213 +00:10:12,078 --> 00:10:15,114 +at a total of 6.41 seconds +of loads, + +214 +00:10:15,114 --> 00:10:19,352 +compared to only 2.69 seconds +of predictions. + +215 +00:10:19,352 --> 00:10:22,689 +Perhaps this has something +to with the low frame rate. + +216 +00:10:22,689 --> 00:10:26,025 +Let me try to find where all +of these loads are coming from. + +217 +00:10:28,027 --> 00:10:30,630 +I am noticing that +I am reloading my Core ML model + +218 +00:10:30,630 --> 00:10:33,099 +prior to calling +each prediction. + +219 +00:10:33,099 --> 00:10:35,134 +This is generally +not good practice + +220 +00:10:35,134 --> 00:10:38,638 +as I can just load the model +once and hold it in memory. + +221 +00:10:38,638 --> 00:10:42,442 +I'm going to jump back into +my code and try to fix this. + +222 +00:10:47,080 --> 00:10:50,083 +I found the area of code +where I load my model. + +223 +00:10:50,083 --> 00:10:52,886 +The issue here is that +this is a computed properly, + +224 +00:10:52,886 --> 00:10:54,654 +which means that each time +I reference the + +225 +00:10:54,654 --> 00:10:58,625 +styleTransferModel variable, +it will recompute the property, + +226 +00:10:58,625 --> 00:11:01,594 +which means reloading the model, +in this case. + +227 +00:11:01,594 --> 00:11:02,762 +I can quickly fix this + +228 +00:11:02,762 --> 00:11:05,932 +by changing this +to be a lazy variable. + +229 +00:11:14,340 --> 00:11:17,010 +Now I'll reprofile the app +to check if this has fixed + +230 +00:11:17,010 --> 00:11:19,178 +the repeated loads issue. + +231 +00:11:27,186 --> 00:11:30,890 +I'll once again select +the Core ML template + +232 +00:11:30,890 --> 00:11:34,227 +and capture a trace. + +233 +00:11:34,227 --> 00:11:36,996 +This is much more in line +with what I'd expect. + +234 +00:11:36,996 --> 00:11:38,164 +The count column tells me + +235 +00:11:38,164 --> 00:11:40,233 +that there are +five load events total, + +236 +00:11:40,233 --> 00:11:42,969 +which matches the number +of styles I used in the app, + +237 +00:11:42,969 --> 00:11:45,605 +and the total duration of loads +is much smaller + +238 +00:11:45,605 --> 00:11:49,509 +than the total duration +of predictions. + +239 +00:11:49,509 --> 00:11:54,747 +Also, as I scroll through... + +240 +00:11:54,747 --> 00:11:57,250 +...it correctly shows repeated +prediction events + +241 +00:11:57,250 --> 00:11:59,252 +without loads +in between each one. + +242 +00:12:02,088 --> 00:12:04,824 +Another note is that so far, +I've only looked at the views + +243 +00:12:04,824 --> 00:12:07,827 +that show all +Core ML model activity. + +244 +00:12:07,827 --> 00:12:11,097 +In this app, there is one +Core ML model per style, + +245 +00:12:11,097 --> 00:12:14,734 +so I may want to breakdown +the Core ML activity by model. + +246 +00:12:14,734 --> 00:12:17,270 +The Instrument +makes this easy to do. + +247 +00:12:17,270 --> 00:12:23,409 +In the main graph, you can click +the arrow at the top left, + +248 +00:12:23,409 --> 00:12:24,978 +and it will make one subtrack + +249 +00:12:24,978 --> 00:12:27,580 +for each model used +in the trace. + +250 +00:12:27,580 --> 00:12:29,782 +Here it displays all of the +different style transfer models + +251 +00:12:29,782 --> 00:12:32,352 +that were used. + +252 +00:12:32,352 --> 00:12:35,822 +The Aggregation view also offers +similar functionality + +253 +00:12:35,822 --> 00:12:39,726 +by allowing you to break down +the statistics by model. + +254 +00:12:43,730 --> 00:12:47,133 +Next I'd like to dive into +a prediction on one of my models + +255 +00:12:47,133 --> 00:12:50,303 +to get a better idea +of how it's being run. + +256 +00:12:50,303 --> 00:12:53,139 +I'll look deeper into +the Watercolor model. + +257 +00:12:54,874 --> 00:12:57,410 +In this prediction, +the Compute lane is telling me + +258 +00:12:57,410 --> 00:12:59,312 +that my model was run +on a combination + +259 +00:12:59,312 --> 00:13:03,016 +of the Neural Engine +and the GPU. + +260 +00:13:03,016 --> 00:13:06,219 +Core ML is sending these compute +requests asynchronously, + +261 +00:13:06,219 --> 00:13:08,821 +so if I'm interested +to see when these compute units + +262 +00:13:08,821 --> 00:13:10,657 +are actively running +the model, + +263 +00:13:10,657 --> 00:13:12,759 +I can combine +the Core ML Instrument + +264 +00:13:12,759 --> 00:13:16,462 +with the GPU Instrument and the +new Neural Engine Instrument. + +265 +00:13:16,462 --> 00:13:19,465 +To do this, I have the three +Instruments pinned here. + +266 +00:13:23,202 --> 00:13:25,705 +The Core ML Instrument shows me +the entire region + +267 +00:13:25,705 --> 00:13:28,641 +where the model ran. + +268 +00:13:33,646 --> 00:13:36,249 +And within this region, +the Neural Engine Instrument + +269 +00:13:36,249 --> 00:13:41,421 +shows the compute first running +on the Neural Engine, + +270 +00:13:41,421 --> 00:13:43,456 +then the GPU Instrument +shows the model + +271 +00:13:43,456 --> 00:13:45,291 +was handed off +from the Neural Engine + +272 +00:13:45,291 --> 00:13:47,994 +to finish running on the GPU. + +273 +00:13:47,994 --> 00:13:50,096 +This gives me a better idea +of how my model + +274 +00:13:50,096 --> 00:13:54,167 +is actually being executed +on the hardware. + +275 +00:13:54,167 --> 00:13:58,671 +To recap, I used the Core ML +Instrument in Xcode 14 + +276 +00:13:58,671 --> 00:14:00,406 +to learn about my model's +performance + +277 +00:14:00,406 --> 00:14:02,742 +when running live in my app. + +278 +00:14:02,742 --> 00:14:04,143 +I then identified an issue + +279 +00:14:04,143 --> 00:14:07,246 +in which I was too frequently +reloading my model. + +280 +00:14:07,246 --> 00:14:10,917 +I fixed the issue in my code, +reprofiled the application, + +281 +00:14:10,917 --> 00:14:14,153 +and verified that the issue +had been fixed. + +282 +00:14:14,153 --> 00:14:17,423 +I was also able to combine +the Core ML, GPU, + +283 +00:14:17,423 --> 00:14:20,226 +and new Neural Engine Instrument +to get more details + +284 +00:14:20,226 --> 00:14:24,931 +on how my model was actually +run on different compute units. + +285 +00:14:24,931 --> 00:14:26,532 +That was an overview +of the new tools + +286 +00:14:26,532 --> 00:14:29,135 +to help you understand +performance. + +287 +00:14:29,135 --> 00:14:31,337 +Next, I'll go over +some enhanced APIs + +288 +00:14:31,337 --> 00:14:34,307 +that can help optimize +that performance. + +289 +00:14:34,307 --> 00:14:36,375 +Let me start by going over +how Core ML + +290 +00:14:36,375 --> 00:14:39,679 +handles model inputs +and outputs. + +291 +00:14:39,679 --> 00:14:41,748 +When you create a Core ML model, + +292 +00:14:41,748 --> 00:14:44,617 +that model has a set +of input and output features, + +293 +00:14:44,617 --> 00:14:47,220 +each with a type and size. + +294 +00:14:47,220 --> 00:14:50,590 +At runtime, you use Core ML APIs +to provide inputs + +295 +00:14:50,590 --> 00:14:52,792 +that conform with +the model's interface + +296 +00:14:52,792 --> 00:14:55,862 +and get outputs +after running inference. + +297 +00:14:55,862 --> 00:14:57,730 +Let me focus on images +and MultiArrays + +298 +00:14:57,730 --> 00:15:00,933 +in a bit more detail. + +299 +00:15:00,933 --> 00:15:04,203 +For images, Core ML supports +8-bit grayscale + +300 +00:15:04,203 --> 00:15:08,207 +and 32-bit color images +with 8 bits per component. + +301 +00:15:08,207 --> 00:15:10,143 +And for multidimensional arrays, + +302 +00:15:10,143 --> 00:15:13,880 +Core ML supports Int32, +Double, and Float32 + +303 +00:15:13,880 --> 00:15:16,082 +as the scalar types. + +304 +00:15:16,082 --> 00:15:18,317 +If your app is already working +with these types, + +305 +00:15:18,317 --> 00:15:21,354 +it's simply a matter of +connecting them to the model. + +306 +00:15:21,354 --> 00:15:24,056 +However, sometimes +your types may differ. + +307 +00:15:24,056 --> 00:15:26,325 +Let me show an example. + +308 +00:15:26,325 --> 00:15:28,661 +I'd like to add a new filter +to my image processing + +309 +00:15:28,661 --> 00:15:30,329 +and style app. + +310 +00:15:30,329 --> 00:15:31,998 +This filter works +to sharpen images + +311 +00:15:31,998 --> 00:15:35,134 +by operating on +a single-channel image. + +312 +00:15:35,134 --> 00:15:37,603 +My app has some pre- +and post-processing operations + +313 +00:15:37,603 --> 00:15:40,439 +on the GPU and represents +this single channel + +314 +00:15:40,439 --> 00:15:43,376 +in Float16 precision. + +315 +00:15:43,376 --> 00:15:46,312 +To do this, +I used coremltools to convert + +316 +00:15:46,312 --> 00:15:51,083 +an image-sharpening torch model +to Core ML format as shown here. + +317 +00:15:51,083 --> 00:15:54,954 +The model was set up to use +Float16 precision computation. + +318 +00:15:54,954 --> 00:15:59,525 +Also, it takes image inputs +and produces image outputs. + +319 +00:15:59,525 --> 00:16:02,628 +I got a model +that looks like this. + +320 +00:16:02,628 --> 00:16:04,831 +Note that it takes +grayscale images + +321 +00:16:04,831 --> 00:16:07,433 +which are 8-bit for Core ML. + +322 +00:16:07,433 --> 00:16:09,802 +To make this work, +I had to write some code + +323 +00:16:09,802 --> 00:16:12,972 +to downcast my input +from OneComponent16Half + +324 +00:16:12,972 --> 00:16:16,042 +to OneComponent8 +and then upcast the output + +325 +00:16:16,042 --> 00:16:19,946 +from OneComponent8 +to OneComponent16Half. + +326 +00:16:19,946 --> 00:16:22,815 +However, +this isn't the whole story. + +327 +00:16:22,815 --> 00:16:25,451 +Since the model was set up +to perform computation + +328 +00:16:25,451 --> 00:16:28,321 +in Float16 precision, +at some point, + +329 +00:16:28,321 --> 00:16:33,359 +Core ML needs to convert +these 8-bit inputs to Float16. + +330 +00:16:33,359 --> 00:16:35,127 +It does the conversion +efficiently, + +331 +00:16:35,127 --> 00:16:36,729 +but when looking +at an Instruments trace + +332 +00:16:36,729 --> 00:16:39,999 +with the app running, +it shows this. + +333 +00:16:39,999 --> 00:16:42,201 +Notice the data steps +Core ML is performing + +334 +00:16:42,201 --> 00:16:47,506 +before and after +Neural Engine computation. + +335 +00:16:47,506 --> 00:16:49,408 +When zooming in +on the Data lane, + +336 +00:16:49,408 --> 00:16:51,377 +it shows Core ML is copying data + +337 +00:16:51,377 --> 00:16:54,280 +to prepare it for computation +on the Neural Engine, + +338 +00:16:54,280 --> 00:16:57,817 +which means converting it +to Float16, in this case. + +339 +00:16:57,817 --> 00:16:59,952 +This seems unfortunate +since the original data + +340 +00:16:59,952 --> 00:17:02,555 +was already Float16. + +341 +00:17:02,555 --> 00:17:05,892 +Ideally, these data +transformations can be avoided + +342 +00:17:05,892 --> 00:17:09,562 +both in-app and inside Core ML +by making the model work + +343 +00:17:09,562 --> 00:17:12,832 +directly with Float16 +inputs and outputs. + +344 +00:17:12,832 --> 00:17:16,002 +Starting in iOS 16 +and macOS Ventura, + +345 +00:17:16,002 --> 00:17:17,770 +Core ML will have native support + +346 +00:17:17,770 --> 00:17:20,573 +for one OneComponent16Half +grayscale images, + +347 +00:17:20,573 --> 00:17:24,543 +and Float16 MultiArrays. + +348 +00:17:24,543 --> 00:17:27,179 +You can create a model +that accepts Float16 inputs + +349 +00:17:27,179 --> 00:17:30,650 +and outputs by specifying +a new color layout for images + +350 +00:17:30,650 --> 00:17:32,685 +or a new data type +for MultiArrays, + +351 +00:17:32,685 --> 00:17:36,088 +while invoking the coremltools +convert method. + +352 +00:17:36,088 --> 00:17:38,991 +In this case, I'm specifying +the input and output + +353 +00:17:38,991 --> 00:17:43,729 +of my model to be grayscale +Float16 images. + +354 +00:17:43,729 --> 00:17:45,798 +Since Float16 support +is available + +355 +00:17:45,798 --> 00:17:49,101 +starting in iOS 16 +and macOS Ventura, + +356 +00:17:49,101 --> 00:17:50,569 +these features +are only available + +357 +00:17:50,569 --> 00:17:56,375 +when the minimum deployment +target is specified as iOS 16. + +358 +00:17:56,375 --> 00:17:59,412 +This is how the reconverted +version of the model looks. + +359 +00:17:59,412 --> 00:18:04,183 +Note that the inputs and outputs +are marked as Grayscale16Half. + +360 +00:18:04,183 --> 00:18:05,918 +With this Float16 support, + +361 +00:18:05,918 --> 00:18:09,288 +my app can directly feed +Float16 images to Core ML, + +362 +00:18:09,288 --> 00:18:11,891 +which will avoid the need +for downcasting the inputs + +363 +00:18:11,891 --> 00:18:16,295 +and upcasting the outputs +in the app. + +364 +00:18:16,295 --> 00:18:18,798 +This is how it looks +in the code. + +365 +00:18:18,798 --> 00:18:20,800 +Since I have my input data +in the form + +366 +00:18:20,800 --> 00:18:24,103 +of a OneComponent16Half +CVPixelBuffer, + +367 +00:18:24,103 --> 00:18:25,771 +I can simply send +the pixel buffer + +368 +00:18:25,771 --> 00:18:27,773 +directly to Core ML. + +369 +00:18:27,773 --> 00:18:31,877 +This does not incur any +data copy or transformation. + +370 +00:18:31,877 --> 00:18:36,782 +I then get a OneComponent16Half +CVPixelBuffer as the output. + +371 +00:18:36,782 --> 00:18:38,384 +This results in simpler code, + +372 +00:18:38,384 --> 00:18:42,188 +and no data transformations +required. + +373 +00:18:42,188 --> 00:18:44,190 +There's also another cool thing +you can do, + +374 +00:18:44,190 --> 00:18:47,259 +and that's to ask Core ML to +fill your preallocated buffers + +375 +00:18:47,259 --> 00:18:50,062 +for outputs instead of having +Core ML allocate + +376 +00:18:50,062 --> 00:18:53,299 +a new buffer +for each prediction. + +377 +00:18:53,299 --> 00:18:56,302 +You can do this by allocating +an output backing buffer + +378 +00:18:56,302 --> 00:18:59,138 +and setting it +on the prediction options. + +379 +00:18:59,138 --> 00:19:02,675 +For my app, I wrote a function +called outputBackingBuffer + +380 +00:19:02,675 --> 00:19:06,679 +which returns a OneComponent1 +HalfCVPixelBuffer. + +381 +00:19:06,679 --> 00:19:08,714 +I then set this on +the prediction options, + +382 +00:19:08,714 --> 00:19:11,317 +and finally call the prediction +method on my model + +383 +00:19:11,317 --> 00:19:14,220 +with those prediction options. + +384 +00:19:14,220 --> 00:19:16,022 +By specifying output backings, + +385 +00:19:16,022 --> 00:19:18,524 +you can gain better control +over the buffer management + +386 +00:19:18,524 --> 00:19:21,427 +for model outputs. + +387 +00:19:21,427 --> 00:19:24,230 +So with those changes made, +to recap, + +388 +00:19:24,230 --> 00:19:26,198 +here's what was shown +in the Instruments trace + +389 +00:19:26,198 --> 00:19:28,334 +when using the original version +of the model + +390 +00:19:28,334 --> 00:19:31,837 +that had 8-bit inputs +and outputs. + +391 +00:19:31,837 --> 00:19:34,340 +And here's how the +final Instruments trace looks + +392 +00:19:34,340 --> 00:19:35,741 +after modifying the code + +393 +00:19:35,741 --> 00:19:38,844 +to provide IOSurface-backed +Float16 buffers + +394 +00:19:38,844 --> 00:19:42,415 +to the new Float16 version +of the model. + +395 +00:19:42,415 --> 00:19:44,617 +The data transformations +that were previously shown + +396 +00:19:44,617 --> 00:19:46,385 +in the Data lane are now gone, + +397 +00:19:46,385 --> 00:19:49,655 +since Core ML no longer +needs to perform them. + +398 +00:19:49,655 --> 00:19:53,659 +To summarize, Core ML now has +end-to-end native support + +399 +00:19:53,659 --> 00:19:55,828 +for Float16 data. + +400 +00:19:55,828 --> 00:19:59,365 +This means you can provide +Float16 inputs to Core ML + +401 +00:19:59,365 --> 00:20:03,135 +and have Core ML +give you back Float16 outputs. + +402 +00:20:03,135 --> 00:20:05,838 +You can also use the new +output backing API + +403 +00:20:05,838 --> 00:20:09,175 +to have Core ML fill up +your preallocated output buffers + +404 +00:20:09,175 --> 00:20:11,644 +instead of making new ones. + +405 +00:20:11,644 --> 00:20:13,212 +And lastly, we recommend + +406 +00:20:13,212 --> 00:20:16,415 +using IOSurface-backed buffers +whenever possible, + +407 +00:20:16,415 --> 00:20:18,818 +as this allows Core ML +to transfer the data + +408 +00:20:18,818 --> 00:20:21,654 +between different compute units +without data copies + +409 +00:20:21,654 --> 00:20:25,357 +by taking advantage +of the unified memory. + +410 +00:20:25,357 --> 00:20:26,826 +Next, I'll go through +a quick tour + +411 +00:20:26,826 --> 00:20:28,661 +of some of the additional +capabilities + +412 +00:20:28,661 --> 00:20:31,430 +being added to Core ML. + +413 +00:20:31,430 --> 00:20:33,732 +First is weight compression. + +414 +00:20:33,732 --> 00:20:35,768 +Compressing the weights +of your model may allow you + +415 +00:20:35,768 --> 00:20:39,939 +to achieve similar accuracy +while having a smaller model. + +416 +00:20:39,939 --> 00:20:43,976 +In iOS 12, Core ML introduced +post-training weight compression + +417 +00:20:43,976 --> 00:20:45,778 +which allows you +to reduce the size + +418 +00:20:45,778 --> 00:20:48,681 +of Core ML +neural network models. + +419 +00:20:48,681 --> 00:20:51,650 +We are now extending +16- and 8-bit support + +420 +00:20:51,650 --> 00:20:54,720 +to the ML Program model type, +and additionally, + +421 +00:20:54,720 --> 00:20:56,689 +introducing a new option +to store weights + +422 +00:20:56,689 --> 00:20:59,625 +in a sparse representation. + +423 +00:20:59,625 --> 00:21:01,527 +With coremltools utilities, + +424 +00:21:01,527 --> 00:21:04,396 +you will now be able +to quantize, palettize, + +425 +00:21:04,396 --> 00:21:09,535 +and sparsify the weights +for your ML Program models. + +426 +00:21:09,535 --> 00:21:12,905 +Next is a new +compute unit option. + +427 +00:21:12,905 --> 00:21:15,741 +Core ML always aims to minimize +inference latency + +428 +00:21:15,741 --> 00:21:18,110 +for the given +compute unit preference. + +429 +00:21:18,110 --> 00:21:20,346 +Apps can specify +this preference by setting + +430 +00:21:20,346 --> 00:21:24,450 +the MLModelConfiguration +computeUnits property. + +431 +00:21:24,450 --> 00:21:27,353 +In addition to the three +existing compute unit options, + +432 +00:21:27,353 --> 00:21:31,357 +there is now a new one +called cpuAndNeuralEngine. + +433 +00:21:31,357 --> 00:21:34,994 +This tells Core ML to not +dispatch computation on the GPU, + +434 +00:21:34,994 --> 00:21:37,363 +which can be helpful +when the app uses the GPU + +435 +00:21:37,363 --> 00:21:40,332 +for other computation +and, hence, prefers Core ML + +436 +00:21:40,332 --> 00:21:44,937 +to limit its focus to +the CPU and the Neural Engine. + +437 +00:21:44,937 --> 00:21:47,540 +Next, we are adding +a new way to initialize + +438 +00:21:47,540 --> 00:21:51,310 +your Core ML model instance that +provides additional flexibility + +439 +00:21:51,310 --> 00:21:54,780 +in terms of model serialization. + +440 +00:21:54,780 --> 00:21:56,982 +This allows you to encrypt +your model data + +441 +00:21:56,982 --> 00:21:58,617 +with custom encryption schemes + +442 +00:21:58,617 --> 00:22:01,387 +and decrypt it +just before loading. + +443 +00:22:01,387 --> 00:22:04,089 +With these new APIs, +you can compile and load + +444 +00:22:04,089 --> 00:22:06,792 +an in-memory +Core ML model specification + +445 +00:22:06,792 --> 00:22:11,931 +without requiring the compiled +model to be on disk. + +446 +00:22:11,931 --> 00:22:14,066 +The last update +is about Swift packages + +447 +00:22:14,066 --> 00:22:16,502 +and how they work with Core ML. + +448 +00:22:16,502 --> 00:22:18,704 +Packages are a great way +to bundle and distribute + +449 +00:22:18,704 --> 00:22:20,573 +reusable code. + +450 +00:22:20,573 --> 00:22:23,342 +With Xcode 14, +you can include Core ML models + +451 +00:22:23,342 --> 00:22:24,877 +in your Swift packages, + +452 +00:22:24,877 --> 00:22:27,012 +and now when someone +imports your package, + +453 +00:22:27,012 --> 00:22:28,881 +your model will just work. + +454 +00:22:28,881 --> 00:22:32,351 +Xcode will compile and bundle +your Core ML model automatically + +455 +00:22:32,351 --> 00:22:34,620 +and create the same +code-generation interface + +456 +00:22:34,620 --> 00:22:36,822 +you're used to working with. + +457 +00:22:36,822 --> 00:22:38,324 +We're excited about this change, + +458 +00:22:38,324 --> 00:22:40,693 +as it'll make it a lot easier +to distribute your models + +459 +00:22:40,693 --> 00:22:43,429 +in the Swift ecosystem. + +460 +00:22:43,429 --> 00:22:46,465 +That brings us to the end +of this session. + +461 +00:22:46,465 --> 00:22:49,868 +Core ML performance reports +and Instrument in Xcode 14 + +462 +00:22:49,868 --> 00:22:51,971 +are here to help you +analyze and optimize + +463 +00:22:51,971 --> 00:22:53,939 +the performance +of the ML-powered features + +464 +00:22:53,939 --> 00:22:56,175 +in your apps. + +465 +00:22:56,175 --> 00:22:59,111 +New Float16 support +and output backing APIs + +466 +00:22:59,111 --> 00:23:01,714 +gives you more control +of how data flows in and out + +467 +00:23:01,714 --> 00:23:03,949 +of Core ML. + +468 +00:23:03,949 --> 00:23:05,918 +Extended support +for weight compression + +469 +00:23:05,918 --> 00:23:09,288 +can help you minimize +the size of your models. + +470 +00:23:09,288 --> 00:23:12,024 +And with in-memory models +and Swift package support, + +471 +00:23:12,024 --> 00:23:13,993 +you have even more options +when it comes + +472 +00:23:13,993 --> 00:23:18,664 +to how you represent, integrate, +and share Core ML models. + +473 +00:23:18,664 --> 00:23:20,499 +This was Ben +from the Core ML team, + +474 +00:23:20,499 --> 00:23:22,334 +and have a great +rest of WWDC. + +475 +00:23:22,334 --> 00:23:26,405 +♪ + diff --git a/eng/2022 Session 10028 Create custom catalogs at scale with ShazamKit en.srt b/eng/2022 Session 10028 Create custom catalogs at scale with ShazamKit en.srt new file mode 100644 index 0000000..365d7d2 --- /dev/null +++ b/eng/2022 Session 10028 Create custom catalogs at scale with ShazamKit en.srt @@ -0,0 +1,1397 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:14,548 +Hello, I'm Neil Foley, +an engineer on the ShazamKit team. + +3 +00:00:14,581 --> 00:00:19,253 +In 2021, we introduced ShazamKit, +allowing you to match audio + +4 +00:00:19,286 --> 00:00:22,623 +against Shazam's +vast catalog of recorded music. + +5 +00:00:22,656 --> 00:00:25,759 +We also introduced custom catalog +matching, + +6 +00:00:25,792 --> 00:00:28,595 +giving developers the ability +to match their own audio + +7 +00:00:28,629 --> 00:00:31,532 +and provide synced experiences. + +8 +00:00:31,565 --> 00:00:34,134 +Now we have some important +updates that streamline + +9 +00:00:34,168 --> 00:00:37,304 +working with custom catalogs at scale. + +10 +00:00:37,337 --> 00:00:42,376 +In this session, I'm going to use +some of the existing ShazamKit concepts + +11 +00:00:42,409 --> 00:00:46,713 +such as signatures, +catalogs, and media items. + +12 +00:00:46,747 --> 00:00:48,715 +If you're not already familiar with those, + +13 +00:00:48,749 --> 00:00:50,951 +check out the "Explore ShazamKit” + +14 +00:00:50,984 --> 00:00:54,421 +and "Create custom audio experiences +with ShazamKit" talks + +15 +00:00:54,454 --> 00:00:57,357 +from WWDC21. + +16 +00:00:57,391 --> 00:01:01,929 +But as quick overview, +ShazamKit lets you convert audio + +17 +00:01:01,962 --> 00:01:04,198 +into a special format that can be matched. + +18 +00:01:04,231 --> 00:01:07,067 +We call these signatures. + +19 +00:01:07,100 --> 00:01:10,971 +Signatures can be combined +with media items containing metadata + +20 +00:01:11,004 --> 00:01:13,607 +to form a reference signature. + +21 +00:01:13,640 --> 00:01:16,743 +And reference signatures can +be stored together in a file + +22 +00:01:16,777 --> 00:01:19,279 +that we call a custom catalog. + +23 +00:01:19,313 --> 00:01:21,248 +Now that we are all caught up, + +24 +00:01:21,281 --> 00:01:24,384 +I'll take you through building +custom catalogs at scale, + +25 +00:01:24,418 --> 00:01:29,356 +and then I'll talk about some tips +and tricks to make great catalogs. + +26 +00:01:29,389 --> 00:01:32,025 +In today's custom catalog workflow, + +27 +00:01:32,059 --> 00:01:35,629 +if you have a small amount +of content you want to be matched, + +28 +00:01:35,662 --> 00:01:39,099 +working with custom catalogs +can be a simple task. + +29 +00:01:39,132 --> 00:01:42,236 +You just need to follow these steps. + +30 +00:01:42,269 --> 00:01:45,772 +Record your audio in a format +that ShazamKit accepts. + +31 +00:01:47,441 --> 00:01:50,811 +Use the signature generator +to transform it into a signature. + +32 +00:01:52,279 --> 00:01:54,381 +Annotate it with your metadata, + +33 +00:01:54,414 --> 00:01:57,117 +and then store it in a custom catalog. + +34 +00:01:57,150 --> 00:02:00,387 +And that's it, +you can provide a Shazam experience. + +35 +00:02:00,420 --> 00:02:02,890 +But some of those steps can be daunting, + +36 +00:02:02,923 --> 00:02:06,260 +especially if you're not familiar +with audio programming. + +37 +00:02:06,293 --> 00:02:09,997 +Dealing with sample rates and buffers +can be tricky + +38 +00:02:10,030 --> 00:02:12,399 +even for the most experienced developer. + +39 +00:02:12,432 --> 00:02:15,836 +And what happens when you have +a vast amount of content + +40 +00:02:15,869 --> 00:02:19,940 +you'd like to make Shazamable, +like 10 seasons of a TV show? + +41 +00:02:21,808 --> 00:02:24,144 +This workflow can become painful. + +42 +00:02:25,379 --> 00:02:29,616 +And if you have large amounts of content, +it can quickly become unmanageable. + +43 +00:02:30,150 --> 00:02:33,120 +If you're thinking of improving +this workflow for yourself, + +44 +00:02:33,153 --> 00:02:37,291 +you'll probably need to write code to +transform audio into signatures, + +45 +00:02:37,324 --> 00:02:40,961 +more code to load +and associate media items, + +46 +00:02:40,994 --> 00:02:44,865 +and each time you change your content, +you'll have to repeat the work. + +47 +00:02:44,898 --> 00:02:48,502 +This is a big investment +when you just want to match some audio. + +48 +00:02:48,902 --> 00:02:51,972 +And then if you want to sync content +with ShazamKit, + +49 +00:02:52,005 --> 00:02:56,143 +you need complicated logic to figure +out what should be shown and when. + +50 +00:02:56,176 --> 00:03:01,181 +I'll introduce some great enhancements to +ShazamKit that streamline this workflow. + +51 +00:03:01,215 --> 00:03:03,650 +But first a quick demo. + +52 +00:03:03,684 --> 00:03:08,722 +Here I have the FoodMath app that +Alex demonstrated in 2021 + +53 +00:03:08,755 --> 00:03:11,725 +that syncs a maths quiz +with an on screen lesson. + +54 +00:03:11,758 --> 00:03:14,628 +I've updated it with +the latest ShazamKit features, + +55 +00:03:14,661 --> 00:03:19,199 +and I'm going play back the FoodMath +video to see how it syncs. + +56 +00:03:21,502 --> 00:03:23,937 +Skip to 26 seconds. + +57 +00:03:24,872 --> 00:03:29,109 +2, 3 green apples. + +58 +00:03:29,142 --> 00:03:31,044 +How many apples do I have in total? + +59 +00:03:31,078 --> 00:03:34,281 +Your timer starts…now. + +60 +00:03:34,314 --> 00:03:38,685 +[light music] + +61 +00:03:38,719 --> 00:03:41,488 +Okay, time's up. +Let's see how you did. + +62 +00:03:41,522 --> 00:03:44,992 +Skip to 56 seconds. + +63 +00:03:45,025 --> 00:03:47,394 +Today, to spice it up a bit, + +64 +00:03:47,427 --> 00:03:51,164 +when I went to the shop, +started with 2 red apples... + +65 +00:03:51,198 --> 00:03:55,002 +and I bought 2 green apples. + +66 +00:03:55,035 --> 00:03:58,572 +How many apples did I have +in total this time? + +67 +00:03:58,605 --> 00:04:01,208 +Your timer starts…now. + +68 +00:04:07,214 --> 00:04:09,449 +Okay, time's up. + +69 +00:04:09,483 --> 00:04:10,417 +Seems to be working great. + +70 +00:04:11,351 --> 00:04:14,354 +There's rich content synced with the +video and when I said "now," + +71 +00:04:14,388 --> 00:04:17,658 +the menu appeared +at exactly the right time. + +72 +00:04:17,691 --> 00:04:22,496 +Also, when content was no longer +relevant, it disappeared right on cue. + +73 +00:04:22,529 --> 00:04:23,730 +But how does it work? + +74 +00:04:23,764 --> 00:04:26,333 +Let's have a look at the code. + +75 +00:04:26,366 --> 00:04:27,768 +There's just a simple loop. + +76 +00:04:27,801 --> 00:04:31,772 +It uses an AsyncSequence on the session +instead of the delegate callbacks + +77 +00:04:31,805 --> 00:04:33,774 +that we used before. + +78 +00:04:33,807 --> 00:04:38,679 +The sequence returns an enum +representing match, no match, or error. + +79 +00:04:38,712 --> 00:04:44,585 +I'm only interested in matches, so I've +restricted the loop to just that case. + +80 +00:04:44,618 --> 00:04:46,720 +And to build the result for display, + +81 +00:04:46,753 --> 00:04:49,857 +I reduce the media items +to the content that I need. + +82 +00:04:52,226 --> 00:04:54,595 +There's actually +not much more to see in the app, + +83 +00:04:54,628 --> 00:04:58,232 +just SwiftUI views that are driven by +the matchResult that we create. + +84 +00:04:58,265 --> 00:05:03,604 +There's no complicated logic or +timing code and it syncs perfectly. + +85 +00:05:03,637 --> 00:05:08,242 +So the question remains, +how does it sync so well? + +86 +00:05:08,275 --> 00:05:13,480 +FoodMaths' secret is the rich +custom catalog that drives the experience. + +87 +00:05:13,514 --> 00:05:19,052 +I created the catalog with a simple tool +that we've built to complement ShazamKit, + +88 +00:05:19,086 --> 00:05:24,091 +and you can use it too to create +rich experiences in your own apps. + +89 +00:05:24,124 --> 00:05:28,495 +The Shazam CLI ships as part of +macOS 13 + +90 +00:05:28,529 --> 00:05:32,065 +and provides an easy way to sync content. + +91 +00:05:32,099 --> 00:05:34,935 +It can help to automate +some of the repetitive tasks + +92 +00:05:34,968 --> 00:05:37,738 +associated with creating custom catalogs. + +93 +00:05:37,771 --> 00:05:41,308 +Let's update the custom catalog +that I just showed you. + +94 +00:05:41,341 --> 00:05:43,177 +Time for another demo. + +95 +00:05:45,812 --> 00:05:49,983 +Here's a folder containing +the FoodMath video file, + +96 +00:05:50,017 --> 00:05:52,719 +and here's my terminal in the same folder. + +97 +00:05:52,753 --> 00:05:56,256 +I'll use the CLI to convert +the video into a signature + +98 +00:05:56,290 --> 00:05:58,258 +using the signature command. + +99 +00:06:02,663 --> 00:06:07,701 +I just pass the video file as input +and specify our signature output. + +100 +00:06:11,104 --> 00:06:11,939 +Okay. There's our signature. + +101 +00:06:13,073 --> 00:06:16,176 +Now I want combine this signature +with media items + +102 +00:06:16,210 --> 00:06:18,612 +to make a custom catalog. + +103 +00:06:18,645 --> 00:06:21,715 +The CLI accepts a simple +comma separated file + +104 +00:06:21,748 --> 00:06:24,484 +for describing media items +that I'll copy here. + +105 +00:06:33,493 --> 00:06:36,496 +It describes everything +that I need to sync my content. + +106 +00:06:38,999 --> 00:06:43,237 +Here's where I've specified my titles, + +107 +00:06:43,270 --> 00:06:47,508 +and here's a custom JSON field +I've defined for the equation. + +108 +00:06:47,541 --> 00:06:50,644 +The headers map to media item +properties. + +109 +00:06:50,677 --> 00:06:55,249 +For details on the mapping, +run the custom catalog create command + +110 +00:06:55,282 --> 00:06:57,150 +with the help flag. + +111 +00:07:02,222 --> 00:07:07,928 +It describes the relationship between +the csv headers and media item properties. + +112 +00:07:07,961 --> 00:07:12,199 +Now I want to combine them +together into a custom catalog. + +113 +00:07:12,232 --> 00:07:14,701 +So I'll run the create command. + +114 +00:07:19,573 --> 00:07:24,745 +I pass in the signature file and the csv +file and it outputs a catalog. + +115 +00:07:28,282 --> 00:07:30,217 +Okay, now we have our catalog. + +116 +00:07:30,250 --> 00:07:34,788 +Excitingly, I have early access to +the latest FoodMath episode, + +117 +00:07:34,821 --> 00:07:37,691 +so I want to add that to our catalog file. + +118 +00:07:37,724 --> 00:07:39,793 +Let me copy the files here. + +119 +00:07:44,932 --> 00:07:47,501 +Here's our media items +for our new episode. + +120 +00:07:51,004 --> 00:07:54,107 +I'll run the update command +passing in the video, + +121 +00:07:54,141 --> 00:07:58,912 +the new media, and the catalog to update. + +122 +00:08:02,015 --> 00:08:05,052 +Okay, we've updated our catalog. + +123 +00:08:05,085 --> 00:08:08,255 +That's a quick overview +of how to create catalogs, + +124 +00:08:08,288 --> 00:08:12,659 +but if you're like me, +you'll really you'll want to script this. + +125 +00:08:20,701 --> 00:08:24,338 +The FoodMath app +actually has quite a few new episodes, + +126 +00:08:24,371 --> 00:08:27,174 +and I want to +add them all to this catalog. + +127 +00:08:27,207 --> 00:08:31,245 +I've written a really simple script +that loops through all the episode folders + +128 +00:08:31,278 --> 00:08:33,847 +and combines them into a custom catalog. + +129 +00:08:33,881 --> 00:08:35,582 +I'll run it now. + +130 +00:08:49,162 --> 00:08:50,931 +There we go. + +131 +00:08:50,964 --> 00:08:54,535 +We now have one catalog representing +every FoodMath episode + +132 +00:08:54,568 --> 00:09:00,507 +and the script used the display command +to detail what's inside the catalog. + +133 +00:09:00,541 --> 00:09:02,276 +I think we have everything. + +134 +00:09:02,309 --> 00:09:05,078 +The foodmath project is already +referencing our new catalog. + +135 +00:09:05,112 --> 00:09:08,549 +So let's build and run +so that we can enjoy doing some maths. + +136 +00:09:11,852 --> 00:09:14,655 +Skip to 30 seconds. + +137 +00:09:14,688 --> 00:09:16,723 +How many apples do I have in total? + +138 +00:09:16,757 --> 00:09:19,927 +Your timer starts…now. + +139 +00:09:19,960 --> 00:09:24,698 +[upbeat music] + +140 +00:09:24,731 --> 00:09:28,202 +Okay, time's up. +Let's see how you did. + +141 +00:09:28,235 --> 00:09:30,938 +I like that guy. +That's a great episode. + +142 +00:09:30,971 --> 00:09:34,107 +What about a new episode? +Let's try that. + +143 +00:09:37,311 --> 00:09:41,048 +Skip to 15 seconds. + +144 +00:09:41,081 --> 00:09:45,419 +Over the years, I explored what makes +a guacamole truly delicious, + +145 +00:09:45,452 --> 00:09:49,223 +and I wrote down my favorite +guacamole recipe. + +146 +00:09:49,256 --> 00:09:51,825 +It calls for 4 avocados. + +147 +00:09:51,859 --> 00:09:53,527 +Tomorrow +my friend is visiting. + +148 +00:09:53,560 --> 00:09:57,364 +So for the two of us, I only need +to make half of the portion. + +149 +00:09:57,397 --> 00:09:59,466 +How many avocados do I need? + +150 +00:09:59,499 --> 00:10:02,769 +The timer starts…now. + +151 +00:10:02,803 --> 00:10:07,541 +[rapid beeping] + +152 +00:10:10,077 --> 00:10:12,913 +That's correct. +You need two avocados. + +153 +00:10:12,946 --> 00:10:16,049 +Let's make this guacamole together. + +154 +00:10:16,083 --> 00:10:22,256 +[upbeat music] + +155 +00:10:24,358 --> 00:10:26,260 +Let's give this a try. + +156 +00:10:30,631 --> 00:10:33,734 +Mmm. That turned out to be great. +I hope you had some fun + +157 +00:10:33,767 --> 00:10:36,236 +and see you next time. + +158 +00:10:40,140 --> 00:10:41,341 +Oh! + +159 +00:10:41,375 --> 00:10:43,644 +They have a new host. +Interesting. + +160 +00:10:43,677 --> 00:10:48,415 +Anyway, I've created a rich synced +experience in no time at all. + +161 +00:10:48,448 --> 00:10:51,451 +The Shazam CLI supports +a rich set of commands. + +162 +00:10:51,485 --> 00:10:52,819 +Let's go over them. + +163 +00:10:54,021 --> 00:10:56,657 +You can create a signature +from any media file + +164 +00:10:56,690 --> 00:10:58,759 +that has an audio track. + +165 +00:10:58,792 --> 00:11:00,294 +You can create custom catalogs + +166 +00:11:00,327 --> 00:11:03,564 +by combining signatures and media items. + +167 +00:11:03,597 --> 00:11:06,700 +You can display a catalog's content. + +168 +00:11:06,733 --> 00:11:09,503 +Add, remove, and export both signatures + +169 +00:11:09,536 --> 00:11:11,939 +and media items. + +170 +00:11:11,972 --> 00:11:14,107 +Next, on to how the CLI created + +171 +00:11:14,141 --> 00:11:16,476 +the signatures from the FoodMath videos. + +172 +00:11:18,745 --> 00:11:23,984 +SHSignatureGenerator now has +a method signatureFromAsset + +173 +00:11:24,017 --> 00:11:26,954 +that's available on all platforms. + +174 +00:11:26,987 --> 00:11:31,592 +With this method, there's no more manually +pulling audio buffers from media. + +175 +00:11:31,625 --> 00:11:37,531 +Simply pass an AVAsset with an +audio track to turn it into a signature. + +176 +00:11:37,564 --> 00:11:40,801 +If you have multiple tracks in +your asset, they'll be mixed together + +177 +00:11:40,834 --> 00:11:44,037 +ensuring the signature +captures everything. + +178 +00:11:44,071 --> 00:11:48,075 +Okay, now that I have a signature +that represents the media, + +179 +00:11:48,108 --> 00:11:51,545 +how did I accurately sync content? + +180 +00:11:51,578 --> 00:11:54,414 +I used the Timed MediaItem API. + +181 +00:11:54,448 --> 00:11:58,752 +Attaching a time range to the media +item makes it easy to specify + +182 +00:11:58,785 --> 00:12:01,321 +when it starts and when it ends. + +183 +00:12:01,355 --> 00:12:04,491 +Media items can also have +multiple time ranges + +184 +00:12:04,525 --> 00:12:08,862 +to target more than +one portion of a signature. + +185 +00:12:08,896 --> 00:12:12,966 +Imagine that you have a media item +that targets the chorus of a song. + +186 +00:12:13,000 --> 00:12:15,802 +You can add a time range +for each place it's sung. + +187 +00:12:16,970 --> 00:12:19,840 +Specifying the time ranges is only useful + +188 +00:12:19,873 --> 00:12:23,610 +if you're notified when they start +and when they end. + +189 +00:12:23,644 --> 00:12:27,481 +ShazamKit will deliver a match +callback synced with the time range, + +190 +00:12:27,514 --> 00:12:30,784 +one when it starts and one when it ends. + +191 +00:12:30,817 --> 00:12:35,689 +Signatures can contain many media items, +so this callback will contain only + +192 +00:12:35,722 --> 00:12:40,661 +the media items that are in range +at that specific point in time. + +193 +00:12:40,694 --> 00:12:44,398 +There's a few simple rules for which +media items will be returned + +194 +00:12:44,431 --> 00:12:47,901 +in a callback and their order, +so let's go over them. + +195 +00:12:49,269 --> 00:12:54,174 +Media items outside of their time range +will not be returned. + +196 +00:12:54,208 --> 00:12:57,444 +Media items within their +time range will be returned, + +197 +00:12:57,477 --> 00:13:00,013 +with the most recent events +coming first. + +198 +00:13:01,348 --> 00:13:06,420 +And finally, media items with no time +ranges will always be returned last, + +199 +00:13:06,453 --> 00:13:09,056 +but they will be unordered. + +200 +00:13:09,089 --> 00:13:11,592 +Media items that have no time range +can be a great place + +201 +00:13:11,625 --> 00:13:15,963 +to store global information that applies +to the whole reference signature. + +202 +00:13:15,996 --> 00:13:20,501 +In my FoodMath example, I used it +to store the name of the episode. + +203 +00:13:20,534 --> 00:13:23,904 +It appears when no other +media items are in range. + +204 +00:13:25,939 --> 00:13:30,577 +One final point, if all your media items +have time ranges + +205 +00:13:30,611 --> 00:13:32,212 +and none of them are in scope, + +206 +00:13:32,246 --> 00:13:37,351 +ShazamKit will always return a +media item with basic match information. + +207 +00:13:37,384 --> 00:13:40,254 +This way, you will always +get important properties, + +208 +00:13:40,287 --> 00:13:45,259 +such as the predictedCurrentMatch offset +and the frequencySkew. + +209 +00:13:46,026 --> 00:13:49,062 +And in code, it's easy too. + +210 +00:13:49,096 --> 00:13:52,099 +Timed media items +are created by specifying + +211 +00:13:52,132 --> 00:13:55,035 +the timeRanges media item property. + +212 +00:13:55,068 --> 00:13:58,405 +It's an array of Swift ranges. + +213 +00:13:58,438 --> 00:14:02,409 +It can also be read back +using the timeRanges property. + +214 +00:14:02,442 --> 00:14:05,946 +And for Objective-C programmers, +there's a new SHRange class + +215 +00:14:05,979 --> 00:14:08,482 +as a drop in replacement. + +216 +00:14:08,515 --> 00:14:10,717 +Now that you've +seen how to build them, + +217 +00:14:10,751 --> 00:14:15,989 +let's explore some tips and tricks +to make great custom catalogs. + +218 +00:14:16,023 --> 00:14:21,028 +Avoid creating many small signatures +for one piece of media. + +219 +00:14:21,061 --> 00:14:25,399 +A signature is a one to one +mapping to the media that it represents, + +220 +00:14:25,432 --> 00:14:30,737 +so for each piece of audio you have, +be it from a song or video, + +221 +00:14:30,771 --> 00:14:33,774 +create one signature +for the entire duration. + +222 +00:14:34,975 --> 00:14:38,445 +A longer signature provides more +opportunities for ShazamKit to match + +223 +00:14:38,478 --> 00:14:41,615 +audio peaks, resulting in better +accuracy. + +224 +00:14:42,149 --> 00:14:45,052 +It also avoids issues +with query signatures + +225 +00:14:45,085 --> 00:14:48,488 +overlapping multiple reference signatures. + +226 +00:14:50,023 --> 00:14:52,593 +Using the new Timed MediaItem API, + +227 +00:14:52,626 --> 00:14:55,529 +you can target synced content +to individual areas. + +228 +00:14:55,562 --> 00:15:01,101 +There's no need to divide a piece +of audio into multiple signatures. + +229 +00:15:01,134 --> 00:15:04,204 +I showed an example where we +had one piece of media, + +230 +00:15:04,238 --> 00:15:06,373 +but with multiple media items. + +231 +00:15:06,406 --> 00:15:09,443 +But what should we do if we have +a huge amount of content + +232 +00:15:09,476 --> 00:15:11,979 +that we want to make Shazamable? + +233 +00:15:12,012 --> 00:15:14,414 +How should we split it up? + +234 +00:15:14,448 --> 00:15:18,285 +There's a trade-off you need +to make when splitting your content + +235 +00:15:18,318 --> 00:15:20,621 +across custom catalogs. + +236 +00:15:20,654 --> 00:15:23,924 +If you create individual catalogs +for each media asset, + +237 +00:15:23,957 --> 00:15:27,060 +you'll need to know +which piece of audio is being played + +238 +00:15:27,094 --> 00:15:30,197 +so that you can load the correct catalog. + +239 +00:15:30,230 --> 00:15:32,833 +And if you put them all together +in one catalog, + +240 +00:15:32,866 --> 00:15:35,869 +you'll have a larger download +and use more memory, + +241 +00:15:35,903 --> 00:15:39,239 +but you can match +many more pieces of audio. + +242 +00:15:39,273 --> 00:15:44,244 +Our advice is to keep the catalog +files you create tightly focused. + +243 +00:15:44,278 --> 00:15:48,615 +For example, a catalog per music +track or the whole album, + +244 +00:15:48,649 --> 00:15:51,485 +but not the artist's whole discography. + +245 +00:15:51,518 --> 00:15:56,757 +Keeping things separate means that you +can decide what to load at runtime. + +246 +00:15:56,790 --> 00:16:00,627 +You can do that with the custom +catalog add API. + +247 +00:16:02,062 --> 00:16:06,867 +Try it out +and see if helps with your use case. + +248 +00:16:06,900 --> 00:16:11,572 +If you have multiple audio +assets that sound the same, + +249 +00:16:11,605 --> 00:16:15,709 +maybe a show that always starts with +the same intro music, and you want to + +250 +00:16:15,742 --> 00:16:18,312 +provide a custom experience +for each episode, + +251 +00:16:18,345 --> 00:16:21,648 +or a song that's sampled in another track, + +252 +00:16:21,682 --> 00:16:25,285 +maybe consider using +frequency skew as a differentiator. + +253 +00:16:25,319 --> 00:16:30,190 +Skewing audio is raising or +lowing the frequencies in the recording. + +254 +00:16:30,224 --> 00:16:33,427 +When you do this, +you affect how the audio sounds, + +255 +00:16:33,460 --> 00:16:37,965 +but if you do it by a small enough amount, +it can be noticed by ShazamKit + +256 +00:16:37,998 --> 00:16:39,833 +but not by the average human ear. + +257 +00:16:40,801 --> 00:16:42,503 +So if we take an audio recording, + +258 +00:16:42,536 --> 00:16:45,672 +make a custom catalog from it, +and then play it back + +259 +00:16:45,706 --> 00:16:50,143 +with the frequencies slightly shifted: +ShazamKit will still match the audio, + +260 +00:16:50,177 --> 00:16:54,681 +and it will also report the skew amount +through the frequencySkew property. + +261 +00:16:54,715 --> 00:16:56,817 +Here's how to do that in code. + +262 +00:16:58,452 --> 00:17:01,088 +There are limits to how much +you can skew audio + +263 +00:17:01,121 --> 00:17:05,025 +without the change becoming +either noticeable to the human ear + +264 +00:17:05,058 --> 00:17:05,893 +or unrecognizable to ShazamKit. + +265 +00:17:08,595 --> 00:17:11,899 +Keeping the skew +to less than 5 percent should be safe + +266 +00:17:11,932 --> 00:17:16,970 +and provide enough room to +differentiate multiple skewed recordings. + +267 +00:17:17,004 --> 00:17:21,608 +To really take advantage of this, +use the frequencySkew ranges. + +268 +00:17:21,642 --> 00:17:25,012 +Media items will only be +returned if they fall inside + +269 +00:17:25,045 --> 00:17:26,446 +the specified skew ranges. + +270 +00:17:27,781 --> 00:17:29,883 +The range specifies as a percentage + +271 +00:17:29,917 --> 00:17:33,153 +how much the audio +differs from the original. + +272 +00:17:33,187 --> 00:17:38,425 +A value of 0 indicates the +audio is unskewed and a value of .01 + +273 +00:17:38,458 --> 00:17:40,928 +indicates a 1 percent skew. + +274 +00:17:41,528 --> 00:17:46,300 +You can access the property on media items +using the frequencySkewRanges property. + +275 +00:17:48,268 --> 00:17:51,471 +I'll go over the steps to +get this working in your app: + +276 +00:17:51,505 --> 00:17:56,376 +First create a reference signature +of your original audio recording. + +277 +00:17:56,410 --> 00:18:03,183 +Then take a media item and restrict it +by frequency skew to 3 to 4 percent. + +278 +00:18:03,217 --> 00:18:04,618 +Place this inside your custom catalog. + +279 +00:18:05,986 --> 00:18:10,390 +Now play back the audio skewed +by 3 to 4 percent, + +280 +00:18:10,424 --> 00:18:13,260 +and your media item will be returned. + +281 +00:18:13,293 --> 00:18:16,463 +Playing back the audio unskewed +or skewed outside of the range + +282 +00:18:16,496 --> 00:18:18,665 +will not return your media item. + +283 +00:18:18,699 --> 00:18:20,667 +That's frequency skewing. + +284 +00:18:22,002 --> 00:18:25,439 +Now that you've seen the exciting updates +to ShazamKit this year, + +285 +00:18:25,472 --> 00:18:29,009 +you're ready to make +some amazing synced experiences. + +286 +00:18:29,042 --> 00:18:31,311 +So remember these best practices: + +287 +00:18:31,345 --> 00:18:35,182 +First, create one signature +per media asset. + +288 +00:18:35,215 --> 00:18:41,054 +You'll get better accuracy from +ShazamKit and simpler creation pipeline. + +289 +00:18:41,088 --> 00:18:44,191 +Create your signatures +with SHSignatureGenerators + +290 +00:18:44,224 --> 00:18:46,560 +signatureFromAsset. + +291 +00:18:46,593 --> 00:18:48,929 +It accepts a wide variety of media, + +292 +00:18:48,962 --> 00:18:52,566 +meaning you no longer have +to deal with low level audio details. + +293 +00:18:54,568 --> 00:19:00,507 +Target synced content to areas of interest +with the new Timed MediaItem API. + +294 +00:19:00,541 --> 00:19:05,179 +It combines a simple API +with excellent accuracy. + +295 +00:19:05,212 --> 00:19:10,851 +And finally let the Shazam CLI streamline +the way you create custom catalogs. + +296 +00:19:10,884 --> 00:19:15,622 +It's been designed take away the hassle +of dealing with vast amounts of media + +297 +00:19:15,656 --> 00:19:20,694 +and let you focus on the great +experiences you want to make instead. + +298 +00:19:20,727 --> 00:19:23,664 +I hope you enjoyed the latest updates +to ShazamKit, + +299 +00:19:23,697 --> 00:19:27,034 +and I'm excited to see you +make everything Shazamable. + +300 +00:19:27,067 --> 00:19:30,771 +All of the information we discussed +and links to documentation + +301 +00:19:30,804 --> 00:19:32,639 +are attached to this session. + +302 +00:19:32,673 --> 00:19:37,211 +Thanks for joining. +Enjoy the rest of WWDC22. ♪ ♪ + diff --git a/eng/2022 Session 10032 Dive into App Intents en.srt b/eng/2022 Session 10032 Dive into App Intents en.srt new file mode 100644 index 0000000..7e975f9 --- /dev/null +++ b/eng/2022 Session 10032 Dive into App Intents en.srt @@ -0,0 +1,3079 @@ +1 +00:00:00,000 --> 00:00:03,737 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,737 --> 00:00:09,543 +♪ + +3 +00:00:09,543 --> 00:00:11,745 +Hi, folks. +My name is Michael Gorbach + +4 +00:00:11,745 --> 00:00:13,714 +from Shortcuts Engineering. + +5 +00:00:13,714 --> 00:00:17,117 +Thanks for tuning in for this +deep dive into App Intents, + +6 +00:00:17,117 --> 00:00:18,118 +our new framework + +7 +00:00:18,118 --> 00:00:22,489 +for exposing your app's +functionality to the system. + +8 +00:00:22,489 --> 00:00:24,224 +Here's the plan for our dive. + +9 +00:00:24,224 --> 00:00:26,159 +After a quick introduction, + +10 +00:00:26,159 --> 00:00:28,729 +I'll talk about intents +and their parameters, + +11 +00:00:28,729 --> 00:00:31,431 +and how to define entities. + +12 +00:00:31,431 --> 00:00:33,834 +I'll go over some powerful +finding and filtering features + +13 +00:00:33,834 --> 00:00:35,102 +you can build, + +14 +00:00:35,102 --> 00:00:38,472 +and how your intents +can interact with the user. + +15 +00:00:38,472 --> 00:00:43,277 +Lastly, I'll cover App Intents +architecture and lifecycle. + +16 +00:00:43,277 --> 00:00:45,913 +Let's start at the beginning. + +17 +00:00:45,913 --> 00:00:49,683 +In iOS 10, we introduced +the SiriKit Intents framework, + +18 +00:00:49,683 --> 00:00:51,952 +which lets you hook up +your app's functionality + +19 +00:00:51,952 --> 00:00:56,690 +to Siri domains like messaging, +workouts, and payments. + +20 +00:00:56,690 --> 00:00:59,192 +Now we're introducing +a new framework + +21 +00:00:59,192 --> 00:01:01,528 +called App Intents. + +22 +00:01:01,528 --> 00:01:04,097 +It has three key components. + +23 +00:01:04,097 --> 00:01:06,400 +Intents are actions +built into your app + +24 +00:01:06,400 --> 00:01:08,602 +that can be used +throughout the system. + +25 +00:01:08,602 --> 00:01:12,906 +Intents use entities to +represent your app's concepts. + +26 +00:01:12,906 --> 00:01:15,175 +App Shortcuts wrap your intents + +27 +00:01:15,175 --> 00:01:18,378 +to make them automatic +and discoverable. + +28 +00:01:18,378 --> 00:01:19,980 +Let's talk about +a couple of the ways + +29 +00:01:19,980 --> 00:01:22,582 +that App Intents can make your +app's functionality available + +30 +00:01:22,582 --> 00:01:26,653 +in more places, +and benefit your customers. + +31 +00:01:26,653 --> 00:01:28,755 +With App Shortcuts, +everyone can use + +32 +00:01:28,755 --> 00:01:31,591 +your app's features +with their voice through Siri, + +33 +00:01:31,591 --> 00:01:34,928 +without needing +to set anything up first. + +34 +00:01:34,928 --> 00:01:38,398 +The same adoption also makes +your intents appear in Spotlight + +35 +00:01:38,398 --> 00:01:42,135 +when people search for your app +and when your app's suggested. + +36 +00:01:42,135 --> 00:01:45,872 +This will put your work +front and center. + +37 +00:01:45,872 --> 00:01:49,576 +Using App Intents, you can also +build Focus Filters, + +38 +00:01:49,576 --> 00:01:53,947 +letting customers customize +your app for a specific Focus. + +39 +00:01:53,947 --> 00:01:56,450 +For example, they might +set up their Calendar app + +40 +00:01:56,450 --> 00:02:01,021 +to only show their work calendar +while they're actually at work. + +41 +00:02:01,021 --> 00:02:02,923 +Check out this session +to learn more about + +42 +00:02:02,923 --> 00:02:06,660 +how to adopt Focus Filters. + +43 +00:02:06,660 --> 00:02:08,161 +With App Shortcuts, + +44 +00:02:08,161 --> 00:02:11,298 +your intents show up in the +Shortcuts app automatically, + +45 +00:02:11,298 --> 00:02:14,201 +without needing +to be added manually. + +46 +00:02:14,201 --> 00:02:16,737 +Integrating your actions +into Shortcuts + +47 +00:02:16,737 --> 00:02:18,939 +is incredibly valuable +for customers + +48 +00:02:18,939 --> 00:02:20,674 +because they can run shortcuts, + +49 +00:02:20,674 --> 00:02:22,876 +and take advantage +of your app's features, + +50 +00:02:22,876 --> 00:02:25,912 +from so many places +throughout the system. + +51 +00:02:25,912 --> 00:02:28,882 +They can run shortcuts with +a single tap on the Home Screen, + +52 +00:02:28,882 --> 00:02:32,386 +from the menu bar on macOS, +and in many other ways. + +53 +00:02:32,386 --> 00:02:34,955 +They can even set shortcuts up +to run automatically + +54 +00:02:34,955 --> 00:02:37,991 +with automations. + +55 +00:02:37,991 --> 00:02:40,727 +Supporting shortcuts +multiplies the power + +56 +00:02:40,727 --> 00:02:43,296 +and capability of your app +by connecting it + +57 +00:02:43,296 --> 00:02:46,366 +to the entire +Shortcuts ecosystem, + +58 +00:02:46,366 --> 00:02:49,236 +harnessing the power +of an array of apps from Apple + +59 +00:02:49,236 --> 00:02:51,271 +and other developers. + +60 +00:02:51,271 --> 00:02:53,874 +That's because a shortcut +can combine actions + +61 +00:02:53,874 --> 00:02:55,242 +from multiple apps, + +62 +00:02:55,242 --> 00:02:59,046 +letting users invent entirely +new features and capabilities + +63 +00:02:59,046 --> 00:03:02,816 +without you needing +to do any work. + +64 +00:03:02,816 --> 00:03:05,052 +If you want to learn +how to make your actions + +65 +00:03:05,052 --> 00:03:08,655 +work well with others and fit +seamlessly into this ecosystem, + +66 +00:03:08,655 --> 00:03:11,191 +check out our design talk. + +67 +00:03:11,191 --> 00:03:13,026 +Our goal in building App Intents + +68 +00:03:13,026 --> 00:03:16,163 +was to make it a joy +to develop for. + +69 +00:03:16,163 --> 00:03:18,598 +App Intents is concise. + +70 +00:03:18,598 --> 00:03:21,668 +Writing a simple intent can take +only a few lines of code, + +71 +00:03:21,668 --> 00:03:23,870 +but the API also scales + +72 +00:03:23,870 --> 00:03:27,374 +to deeper and more +customizable actions. + +73 +00:03:27,374 --> 00:03:29,276 +App Intents is modern. + +74 +00:03:29,276 --> 00:03:30,644 +We've gone all in on Swift, + +75 +00:03:30,644 --> 00:03:33,213 +leveraging result builders, +property wrappers, + +76 +00:03:33,213 --> 00:03:36,049 +protocol-oriented programming, +and generics. + +77 +00:03:36,049 --> 00:03:37,851 +These APIs just couldn't exist + +78 +00:03:37,851 --> 00:03:41,221 +without cutting-edge +language features. + +79 +00:03:41,221 --> 00:03:43,623 +Adopting App Intents +is also easy, + +80 +00:03:43,623 --> 00:03:45,258 +because it doesn't require +re-architecting + +81 +00:03:45,258 --> 00:03:49,029 +your products and targets +or creating a framework. + +82 +00:03:49,029 --> 00:03:50,697 +It doesn't require an extension + +83 +00:03:50,697 --> 00:03:53,233 +and can be adopted +right in your app. + +84 +00:03:53,233 --> 00:03:55,802 +And App Intents code +is maintainable. + +85 +00:03:55,802 --> 00:03:58,605 +Like SwiftUI, +App Intents uses your code + +86 +00:03:58,605 --> 00:04:00,574 +as the fundamental +source of truth, + +87 +00:04:00,574 --> 00:04:04,578 +avoiding the need for separate +editors or definition files. + +88 +00:04:04,578 --> 00:04:07,881 +This lets you rapidly build +and iterate on your adoption, + +89 +00:04:07,881 --> 00:04:09,082 +and simplifies maintenance + +90 +00:04:09,082 --> 00:04:12,452 +because everything +lives in one place. + +91 +00:04:12,452 --> 00:04:15,388 +With that said, +let's explore these new APIs, + +92 +00:04:15,388 --> 00:04:16,790 +starting with the intent, + +93 +00:04:16,790 --> 00:04:19,960 +the central building block +of our new framework. + +94 +00:04:19,960 --> 00:04:22,662 +An app intent -- +or "intent" for short -- + +95 +00:04:22,662 --> 00:04:25,265 +is a single, isolated unit +of functionality + +96 +00:04:25,265 --> 00:04:27,634 +that your app +exposes to the system. + +97 +00:04:27,634 --> 00:04:30,537 +For example, an intent +could make a new calendar event, + +98 +00:04:30,537 --> 00:04:34,007 +open a particular screen, +or place an order. + +99 +00:04:34,007 --> 00:04:37,277 +An intent can be run +by the user on request -- + +100 +00:04:37,277 --> 00:04:40,180 +like by running a shortcut +or asking Siri -- + +101 +00:04:40,180 --> 00:04:41,348 +or automatically -- + +102 +00:04:41,348 --> 00:04:45,318 +like using Focus filters +or a Shortcuts automation. + +103 +00:04:45,318 --> 00:04:46,586 +When an intent is run, + +104 +00:04:46,586 --> 00:04:51,024 +it will either return a result +or throw an error. + +105 +00:04:51,024 --> 00:04:54,294 +An intent includes +three key pieces: + +106 +00:04:54,294 --> 00:04:56,796 +metadata, +or information about the intent, + +107 +00:04:56,796 --> 00:04:59,065 +including a localized title; + +108 +00:04:59,065 --> 00:05:01,801 +parameters, which are inputs +that the intent can use + +109 +00:05:01,801 --> 00:05:03,036 +when it's run; + +110 +00:05:03,036 --> 00:05:05,572 +and a perform method, +which does the actual work + +111 +00:05:05,572 --> 00:05:09,042 +when the intent is executed. + +112 +00:05:09,042 --> 00:05:11,945 +Our starting point today +is this Library app. + +113 +00:05:11,945 --> 00:05:13,480 +Since I'm a huge bookworm, + +114 +00:05:13,480 --> 00:05:15,515 +it's all about tracking +books I've read, + +115 +00:05:15,515 --> 00:05:18,485 +want to read, +or am currently reading. + +116 +00:05:18,485 --> 00:05:21,421 +Each category is shown +in separate tab of the app + +117 +00:05:21,421 --> 00:05:24,391 +that I call a Shelf. + +118 +00:05:24,391 --> 00:05:26,593 +My users visit +the Currently Reading shelf + +119 +00:05:26,593 --> 00:05:27,694 +all the time, + +120 +00:05:27,694 --> 00:05:29,896 +so I'm going expose +an app intent + +121 +00:05:29,896 --> 00:05:34,034 +to make opening it quicker +and more convenient. + +122 +00:05:34,034 --> 00:05:36,970 +I'll create an +OpenCurrentlyReading intent here + +123 +00:05:36,970 --> 00:05:38,471 +by defining a Swift struct + +124 +00:05:38,471 --> 00:05:41,975 +that conforms to +the AppIntent protocol. + +125 +00:05:41,975 --> 00:05:45,478 +I need to implement +only one method, called perform. + +126 +00:05:45,478 --> 00:05:48,949 +In my app, I've already got +a navigator that can open tabs, + +127 +00:05:48,949 --> 00:05:50,317 +so implementing +the intent for me + +128 +00:05:50,317 --> 00:05:52,852 +is only a few lines of code. + +129 +00:05:52,852 --> 00:05:55,755 +I'll annotate the perform method +with @MainActor, + +130 +00:05:55,755 --> 00:05:59,859 +since my Navigator +expects the main thread. + +131 +00:05:59,859 --> 00:06:02,662 +My intent also needs a title. + +132 +00:06:02,662 --> 00:06:04,631 +Like all the other strings +I'll be showing you today, + +133 +00:06:04,631 --> 00:06:06,299 +this will get localized +automatically + +134 +00:06:06,299 --> 00:06:09,135 +if I add the key +to my strings files. + +135 +00:06:09,135 --> 00:06:13,907 +This is all I need to do to get +a basic app intent working. + +136 +00:06:13,907 --> 00:06:15,775 +Now that it's defined +in my code, + +137 +00:06:15,775 --> 00:06:19,179 +it will automatically appear +in the Shortcuts editor, + +138 +00:06:19,179 --> 00:06:24,084 +where my user +can add it to a shortcut. + +139 +00:06:24,084 --> 00:06:27,621 +Just exposing this intent +provides huge leverage, + +140 +00:06:27,621 --> 00:06:30,423 +because once customers turn +this intent into a shortcut, + +141 +00:06:30,423 --> 00:06:33,293 +it can be used from +a ton of places in the system, + +142 +00:06:33,293 --> 00:06:36,129 +including all of these. + +143 +00:06:36,129 --> 00:06:39,099 +To make my new intent +easy to use and discover, + +144 +00:06:39,099 --> 00:06:41,635 +I'll also add support +for App Shortcuts. + +145 +00:06:41,635 --> 00:06:43,003 +With a little bit of code, + +146 +00:06:43,003 --> 00:06:46,239 +I can make my intent show up +automatically in Spotlight + +147 +00:06:46,239 --> 00:06:47,841 +and the Shortcuts app, + +148 +00:06:47,841 --> 00:06:51,044 +and I can define a phrase +that people can say to Siri + +149 +00:06:51,044 --> 00:06:53,980 +to use this intent +with their voice. + +150 +00:06:53,980 --> 00:06:56,516 +Check out the "Implement App +Shortcuts with App Intents" + +151 +00:06:56,516 --> 00:07:00,487 +session to get all the details. + +152 +00:07:00,487 --> 00:07:02,322 +So far, I've exposed an intent + +153 +00:07:02,322 --> 00:07:05,025 +to open the Currently Reading +shelf. + +154 +00:07:05,025 --> 00:07:06,860 +Next, let's generalize it, + +155 +00:07:06,860 --> 00:07:12,432 +adding a parameter so it can +open any of the shelves. + +156 +00:07:12,432 --> 00:07:15,235 +I have an enum +that represents shelves. + +157 +00:07:15,235 --> 00:07:17,671 +In order for it to be used +as an intent parameter, + +158 +00:07:17,671 --> 00:07:21,174 +I need to conform it +to the AppEnum protocol. + +159 +00:07:21,174 --> 00:07:24,044 +AppEnum requires +a String raw value, + +160 +00:07:24,044 --> 00:07:26,413 +so I'll add that first. + +161 +00:07:26,413 --> 00:07:28,915 +It also requires +that I provide localizable, + +162 +00:07:28,915 --> 00:07:32,752 +human-readable titles +for each of my enum cases. + +163 +00:07:32,752 --> 00:07:35,188 +These must be provided +as a dictionary literal, + +164 +00:07:35,188 --> 00:07:39,626 +since the compiler will +read this code at build time. + +165 +00:07:39,626 --> 00:07:42,095 +Finally, I'll add +a typeDisplayName: + +166 +00:07:42,095 --> 00:07:44,397 +a user-visible, localizable name + +167 +00:07:44,397 --> 00:07:46,999 +for this enum type +as a whole. + +168 +00:07:46,999 --> 00:07:49,436 +I'll use "Shelf." + +169 +00:07:49,436 --> 00:07:51,938 +In an intent, +each parameter is declared + +170 +00:07:51,938 --> 00:07:54,407 +using an @Parameter +property wrapper, + +171 +00:07:54,407 --> 00:07:57,177 +which is initialized with +information about the parameter, + +172 +00:07:57,177 --> 00:07:58,845 +like the title. + +173 +00:07:58,845 --> 00:08:01,514 +Here, I define +a new shelf parameter, + +174 +00:08:01,514 --> 00:08:04,818 +which I read +in my perform method. + +175 +00:08:04,818 --> 00:08:06,820 +Parameters support +all of these types, + +176 +00:08:06,820 --> 00:08:10,023 +including numbers, strings, +files, and more, + +177 +00:08:10,023 --> 00:08:13,593 +as well as entities and enums +from your app. + +178 +00:08:13,593 --> 00:08:17,297 +Here's how this intent looks +in the Shortcuts editor. + +179 +00:08:17,297 --> 00:08:21,201 +Note that the shelf parameter +appears in a table row. + +180 +00:08:21,201 --> 00:08:23,069 +I can make the UI +more streamlined, + +181 +00:08:23,069 --> 00:08:24,938 +and make it fit better +into Shortcuts, + +182 +00:08:24,938 --> 00:08:29,008 +by using the +ParameterSummary API. + +183 +00:08:29,008 --> 00:08:31,077 +The Parameter Summary +is a sentence + +184 +00:08:31,077 --> 00:08:33,680 +that represents your intent +and its parameters + +185 +00:08:33,680 --> 00:08:37,684 +in the editor, +like "Open [Shelf]." + +186 +00:08:37,684 --> 00:08:39,619 +For best results in Shortcuts, + +187 +00:08:39,619 --> 00:08:41,988 +you should always provide +a Parameter Summary + +188 +00:08:41,988 --> 00:08:45,458 +for every intent you create. + +189 +00:08:45,458 --> 00:08:47,026 +You can also define +which parameters + +190 +00:08:47,026 --> 00:08:50,497 +show up below the fold +and which are hidden. + +191 +00:08:50,497 --> 00:08:53,166 +These APIs can +do some pretty cool stuff, + +192 +00:08:53,166 --> 00:08:56,102 +like varying the summary +based on the actual values + +193 +00:08:56,102 --> 00:08:58,371 +of any parameter of your intent, + +194 +00:08:58,371 --> 00:09:00,807 +using the When +and Otherwise APIs, + +195 +00:09:00,807 --> 00:09:05,512 +or the Switch, Case, +and Default APIs. + +196 +00:09:05,512 --> 00:09:06,946 +To add a parameter summary, + +197 +00:09:06,946 --> 00:09:09,649 +I implement +this static property. + +198 +00:09:09,649 --> 00:09:11,718 +Here I'll return the string +"Open", + +199 +00:09:11,718 --> 00:09:15,755 +and interpolate +the shelf parameter. + +200 +00:09:15,755 --> 00:09:18,758 +The last thing I need to do +to get Open Shelf working + +201 +00:09:18,758 --> 00:09:21,494 +is make sure that the intent +opens the Library app + +202 +00:09:21,494 --> 00:09:25,865 +when it's run, +like this. + +203 +00:09:25,865 --> 00:09:28,835 +Opening the app by is controlled +by the static property, + +204 +00:09:28,835 --> 00:09:30,837 +openAppWhenRun. + +205 +00:09:30,837 --> 00:09:33,940 +It defaults to false, +which is great for most intents. + +206 +00:09:33,940 --> 00:09:36,176 +But for intents that open +something in the UI + +207 +00:09:36,176 --> 00:09:40,213 +like this one, +I'll need to set it to true. + +208 +00:09:40,213 --> 00:09:43,082 +I just created an intent +to open shelves. + +209 +00:09:43,082 --> 00:09:47,187 +This is super simple because +the set of shelves is fixed. + +210 +00:09:47,187 --> 00:09:50,256 +But what if I wanted to build +an intent that opens Books, + +211 +00:09:50,256 --> 00:09:53,193 +the set of which is dynamic, +not fixed? + +212 +00:09:53,193 --> 00:09:55,728 +For that, I'll need entities. + +213 +00:09:55,728 --> 00:09:59,666 +An entity is a concept that +your app exposes to App Intents. + +214 +00:09:59,666 --> 00:10:02,202 +You should use an entity +instead of an enum + +215 +00:10:02,202 --> 00:10:05,071 +when the values are dynamic, +or user-defined, + +216 +00:10:05,071 --> 00:10:09,209 +like a note in Notes +or a photo or album in Photos. + +217 +00:10:09,209 --> 00:10:11,511 +To provide instances +of entities, + +218 +00:10:11,511 --> 00:10:13,513 +your app can implement queries, + +219 +00:10:13,513 --> 00:10:17,817 +and return entities +as results from intents. + +220 +00:10:17,817 --> 00:10:21,321 +I'll start by making an intent +to open a book in the app. + +221 +00:10:21,321 --> 00:10:25,158 +In the Shortcuts editor, +it should look like this. + +222 +00:10:25,158 --> 00:10:27,260 +When people tap +on the Book parameter, + +223 +00:10:27,260 --> 00:10:29,262 +they'll get a picker +to choose a book, + +224 +00:10:29,262 --> 00:10:30,964 +including a set +of suggested entities + +225 +00:10:30,964 --> 00:10:34,400 +that my app has provided. + +226 +00:10:34,400 --> 00:10:36,703 +They can also find +any book in their library + +227 +00:10:36,703 --> 00:10:41,374 +with this search field +at the top of the picker. + +228 +00:10:41,374 --> 00:10:43,343 +Before I build +the intent itself, + +229 +00:10:43,343 --> 00:10:45,311 +I'll need to create +a book entity + +230 +00:10:45,311 --> 00:10:47,614 +and the corresponding query. + +231 +00:10:47,614 --> 00:10:50,483 +An entity contains +at least three things: + +232 +00:10:50,483 --> 00:10:53,152 +an identifier, +a display representation, + +233 +00:10:53,152 --> 00:10:56,189 +and an entity type name. + +234 +00:10:56,189 --> 00:10:58,925 +To add an entity, +start by conforming a struct + +235 +00:10:58,925 --> 00:11:01,528 +to the AppEntity protocol. + +236 +00:11:01,528 --> 00:11:03,997 +Here, I'll define a new struct +for the BookEntity, + +237 +00:11:03,997 --> 00:11:08,568 +but I could also conform +an existing type from my model. + +238 +00:11:08,568 --> 00:11:11,371 +You provide an identifier +by conforming your entity + +239 +00:11:11,371 --> 00:11:14,040 +to the Identifiable protocol. + +240 +00:11:14,040 --> 00:11:17,110 +App Intents uses this identifier +to refer to your entity + +241 +00:11:17,110 --> 00:11:20,880 +as it's passed between your app +and other parts of the system. + +242 +00:11:20,880 --> 00:11:23,716 +The identifier should be +stable and persistent, + +243 +00:11:23,716 --> 00:11:25,752 +since it might be saved +in a shortcut + +244 +00:11:25,752 --> 00:11:29,155 +created by your customers. + +245 +00:11:29,155 --> 00:11:31,057 +The display representation +is used + +246 +00:11:31,057 --> 00:11:33,560 +to show this entity to the user. + +247 +00:11:33,560 --> 00:11:35,595 +This can be as simple +as a string of text, + +248 +00:11:35,595 --> 00:11:37,530 +like a book title. + +249 +00:11:37,530 --> 00:11:41,634 +You could also provide +a subtitle and an image. + +250 +00:11:41,634 --> 00:11:44,070 +The typeDisplayName +is a human-readable string + +251 +00:11:44,070 --> 00:11:46,439 +representing +the type of an entity. + +252 +00:11:46,439 --> 00:11:49,208 +In this example, +it's "Book." + +253 +00:11:49,208 --> 00:11:51,544 +Now, to round out +the book entity, + +254 +00:11:51,544 --> 00:11:54,180 +I need to add a query. + +255 +00:11:54,180 --> 00:11:56,616 +A query gives the system +an interface + +256 +00:11:56,616 --> 00:12:00,019 +for retrieving entities +from your app. + +257 +00:12:00,019 --> 00:12:03,156 +Queries can look up entities +in a few ways. + +258 +00:12:03,156 --> 00:12:05,291 +All queries need to be able +to look up entities + +259 +00:12:05,291 --> 00:12:07,360 +based on an identifier. + +260 +00:12:07,360 --> 00:12:09,796 +String queries support search. + +261 +00:12:09,796 --> 00:12:11,898 +And later, you'll run into +property queries, + +262 +00:12:11,898 --> 00:12:13,933 +which are more flexible. + +263 +00:12:13,933 --> 00:12:17,337 +All queries can also provide +suggested entities, + +264 +00:12:17,337 --> 00:12:21,708 +which allow the users +to pick from a list. + +265 +00:12:21,708 --> 00:12:24,611 +Every entity should be +associated with a query + +266 +00:12:24,611 --> 00:12:29,015 +so the system can look up +instances of that entity. + +267 +00:12:29,015 --> 00:12:31,584 +You provide a query +by making a Swift struct + +268 +00:12:31,584 --> 00:12:35,221 +that conforms to +the EntityQuery protocol. + +269 +00:12:35,221 --> 00:12:38,291 +The basic query has only +one required method, + +270 +00:12:38,291 --> 00:12:39,959 +which you implement +to resolve entities + +271 +00:12:39,959 --> 00:12:42,862 +given an array of identifiers. + +272 +00:12:42,862 --> 00:12:45,665 +I've implemented this +by going to my model database + +273 +00:12:45,665 --> 00:12:49,969 +and finding any books +matching those identifiers. + +274 +00:12:49,969 --> 00:12:53,373 +Now, I need to hook up +the query to the entity. + +275 +00:12:53,373 --> 00:12:56,409 +I do this by implementing +the defaultQuery static property + +276 +00:12:56,409 --> 00:12:58,277 +on the BookEntity type + +277 +00:12:58,277 --> 00:13:03,182 +and returning an instance +of my BookQuery. + +278 +00:13:03,182 --> 00:13:05,151 +When the user picks a book, + +279 +00:13:05,151 --> 00:13:08,121 +its identifier will be saved +into the shortcut. + +280 +00:13:08,121 --> 00:13:09,555 +When the shortcut is run, + +281 +00:13:09,555 --> 00:13:12,492 +App Intents will pass +the identifier to my query + +282 +00:13:12,492 --> 00:13:16,796 +to retrieve +the BookEntity instance. + +283 +00:13:16,796 --> 00:13:18,197 +Now that the BookEntity type + +284 +00:13:18,197 --> 00:13:20,667 +conforms to +the AppEntity protocol, + +285 +00:13:20,667 --> 00:13:24,671 +I can use it as a parameter +in my OpenBook intent. + +286 +00:13:24,671 --> 00:13:26,939 +The perform method +uses my Navigator + +287 +00:13:26,939 --> 00:13:28,941 +to navigate to the book. + +288 +00:13:32,545 --> 00:13:34,647 +In order to support +the book picker, + +289 +00:13:34,647 --> 00:13:38,484 +my query also needs +to provide suggested results. + +290 +00:13:38,484 --> 00:13:42,021 +To do that, I need to implement +one more method on query, + +291 +00:13:42,021 --> 00:13:45,091 +returning all the books +added to my Library app. + +292 +00:13:45,091 --> 00:13:49,996 +Shortcuts will fill the picker +with these results. + +293 +00:13:49,996 --> 00:13:53,700 +Notice that the Shortcuts UI +has a search field on top. + +294 +00:13:53,700 --> 00:13:55,668 +My app could have +a lot of book entities, + +295 +00:13:55,668 --> 00:13:58,738 +so I should really run +the search in my app process, + +296 +00:13:58,738 --> 00:14:00,907 +against my database directly. + +297 +00:14:00,907 --> 00:14:04,410 +The StringQuery API +lets me do that. + +298 +00:14:04,410 --> 00:14:06,813 +Adopting the StringQuery +subprotocol gives me + +299 +00:14:06,813 --> 00:14:08,514 +one more method to implement, + +300 +00:14:08,514 --> 00:14:10,717 +called +entities (matching string:), + +301 +00:14:10,717 --> 00:14:13,286 +to return results +given a string. + +302 +00:14:13,286 --> 00:14:16,689 +Here, I've implemented it as +a simple case-insensitive match + +303 +00:14:16,689 --> 00:14:18,391 +against the title of the book, + +304 +00:14:18,391 --> 00:14:19,959 +but I could have done +fancier stuff + +305 +00:14:19,959 --> 00:14:22,395 +like searching through +the author or series name, + +306 +00:14:22,395 --> 00:14:24,597 +for example. + +307 +00:14:24,597 --> 00:14:28,167 +If I have a huge list of books, +and a smaller list of favorites, + +308 +00:14:28,167 --> 00:14:31,237 +I could return just the +favorites in suggestedEntities, + +309 +00:14:31,237 --> 00:14:33,573 +and rely on +entities (matching string:) + +310 +00:14:33,573 --> 00:14:38,911 +to allow my users to search +across the longer list. + +311 +00:14:38,911 --> 00:14:41,881 +Now I've exposed a way +to open books in my app, + +312 +00:14:41,881 --> 00:14:45,485 +and built a book entity +and book query in the process. + +313 +00:14:45,485 --> 00:14:49,322 +I can use the same entity and +query to create more intents. + +314 +00:14:49,322 --> 00:14:51,758 +My next task +is to build an intent + +315 +00:14:51,758 --> 00:14:53,893 +to add books to the library. + +316 +00:14:53,893 --> 00:14:56,629 +Customers can quickly add books +while browsing online + +317 +00:14:56,629 --> 00:14:58,231 +using a share sheet shortcut, + +318 +00:14:58,231 --> 00:15:00,533 +or they can tell Siri on HomePod +to add a book + +319 +00:15:00,533 --> 00:15:03,002 +without even looking +at a screen. + +320 +00:15:03,002 --> 00:15:06,672 +Building intents like this that +manipulate your model directly + +321 +00:15:06,672 --> 00:15:11,878 +without showing your UI +can really empower your users. + +322 +00:15:11,878 --> 00:15:14,714 +Here's the implementation +of my AddBook intent, + +323 +00:15:14,714 --> 00:15:16,816 +taking as parameters +the title of the book + +324 +00:15:16,816 --> 00:15:19,519 +and an optional +name of the author. + +325 +00:15:19,519 --> 00:15:21,621 +It also includes +an optional note + +326 +00:15:21,621 --> 00:15:24,791 +to record which friend +recommended the book. + +327 +00:15:24,791 --> 00:15:27,727 +The perform method +will add the book to the library + +328 +00:15:27,727 --> 00:15:31,764 +by looking it up with +an API call using async/await. + +329 +00:15:31,764 --> 00:15:36,669 +It will throw an error +if it can't find a match. + +330 +00:15:36,669 --> 00:15:39,472 +To localize this error, +I conform my error type to + +331 +00:15:39,472 --> 00:15:43,943 +the CustomLocalizedString +ResourceConvertible protocol. + +332 +00:15:43,943 --> 00:15:47,213 +I'll return a localized +string key from this property, + +333 +00:15:47,213 --> 00:15:50,950 +and add the key +to my strings files. + +334 +00:15:50,950 --> 00:15:54,287 +This Add Book intent +is incredibly useful as is, + +335 +00:15:54,287 --> 00:15:56,856 +with Siri, widgets, +and more. + +336 +00:15:56,856 --> 00:15:58,758 +But it gets even more flexible + +337 +00:15:58,758 --> 00:16:01,627 +if it can be combined +with other intents. + +338 +00:16:01,627 --> 00:16:02,895 +With a little bit of work, + +339 +00:16:02,895 --> 00:16:05,164 +I can allow combining +my Add Book intent + +340 +00:16:05,164 --> 00:16:07,633 +with the Open Book intent +I built earlier, + +341 +00:16:07,633 --> 00:16:10,937 +passing the result +from one to the other. + +342 +00:16:10,937 --> 00:16:13,806 +To do so, I'll have +the Add Book intent + +343 +00:16:13,806 --> 00:16:17,109 +return a value +as part of its result. + +344 +00:16:17,109 --> 00:16:19,212 +Notice that my perform method's +return type + +345 +00:16:19,212 --> 00:16:20,980 +has picked up a new protocol + +346 +00:16:20,980 --> 00:16:23,950 +to represent the value +I'm returning. + +347 +00:16:23,950 --> 00:16:27,420 +Now, users can connect +the result value of this intent + +348 +00:16:27,420 --> 00:16:31,858 +to other intents that take +a book entity as a parameter. + +349 +00:16:31,858 --> 00:16:34,927 +The Add Book intent +and the Open Book intent + +350 +00:16:34,927 --> 00:16:36,395 +pair quite naturally together, + +351 +00:16:36,395 --> 00:16:38,865 +so you can make a shortcut +that adds a book + +352 +00:16:38,865 --> 00:16:41,434 +and then immediately +opens it in the library. + +353 +00:16:41,434 --> 00:16:44,503 +It's a common pattern to return +a result from an intent + +354 +00:16:44,503 --> 00:16:46,873 +and open it in the app. + +355 +00:16:46,873 --> 00:16:49,342 +App intents have a built-in way +to express this + +356 +00:16:49,342 --> 00:16:51,577 +called the openIntent. + +357 +00:16:51,577 --> 00:16:53,880 +If I add an openIntent, +customers will get + +358 +00:16:53,880 --> 00:16:57,483 +a new switch in Shortcuts +called "Open When Run." + +359 +00:16:57,483 --> 00:16:59,085 +If they turn the switch off, + +360 +00:16:59,085 --> 00:17:00,253 +they'll be able +to use this intent + +361 +00:17:00,253 --> 00:17:04,624 +as part of a shortcut in the +background without interruption. + +362 +00:17:04,624 --> 00:17:07,526 +If they leave the switch on, +the newly added book + +363 +00:17:07,526 --> 00:17:12,398 +will be immediately opened +in my Library app. + +364 +00:17:12,398 --> 00:17:15,268 +Adopting openIntent +is as easy + +365 +00:17:15,268 --> 00:17:17,670 +as creating an instance +of the Open Book intent + +366 +00:17:17,670 --> 00:17:20,907 +and returning it +as part of the result. + +367 +00:17:20,907 --> 00:17:22,308 +When this intent is run, + +368 +00:17:22,308 --> 00:17:24,277 +if the Open When Run switch +is on, + +369 +00:17:24,277 --> 00:17:27,146 +the Open Book intent +will automatically be performed + +370 +00:17:27,146 --> 00:17:31,350 +after the Add Book intent +finishes. + +371 +00:17:31,350 --> 00:17:35,054 +There's a lot more you can do +with entities and queries. + +372 +00:17:35,054 --> 00:17:36,622 +With the next set of APIs, + +373 +00:17:36,622 --> 00:17:39,325 +AppIntents opens up +some powerful abilities + +374 +00:17:39,325 --> 00:17:43,496 +you never had before with +the SiriKit Intents framework. + +375 +00:17:43,496 --> 00:17:45,998 +Let's take a look at how +you can expose more information + +376 +00:17:45,998 --> 00:17:47,300 +from your entities, + +377 +00:17:47,300 --> 00:17:52,305 +and allow customers to find +and filter based on that. + +378 +00:17:52,305 --> 00:17:55,174 +So far, I've added +all the basic requirements + +379 +00:17:55,174 --> 00:17:56,676 +to my book entity. + +380 +00:17:56,676 --> 00:17:58,144 +But to let people +integrate books + +381 +00:17:58,144 --> 00:18:00,246 +more deeply +into their shortcuts, + +382 +00:18:00,246 --> 00:18:05,084 +I'm going to need to expose +a bit more about my books. + +383 +00:18:05,084 --> 00:18:07,119 +Entities support properties, + +384 +00:18:07,119 --> 00:18:09,422 +which hold additional +information on the entity + +385 +00:18:09,422 --> 00:18:11,991 +that you want to expose +to users. + +386 +00:18:11,991 --> 00:18:13,993 +In this case, +I'll add the book's author, + +387 +00:18:13,993 --> 00:18:16,996 +publishing date, read date, +and who recommended it, + +388 +00:18:16,996 --> 00:18:22,068 +so that people can use those +properties in their shortcuts. + +389 +00:18:22,068 --> 00:18:24,070 +I add properties +to my BookEntity + +390 +00:18:24,070 --> 00:18:27,340 +using a property wrapper +called @Property. + +391 +00:18:27,340 --> 00:18:30,242 +Properties support all the same +types that parameters do, + +392 +00:18:30,242 --> 00:18:35,514 +and each one +takes a localized title. + +393 +00:18:35,514 --> 00:18:37,249 +With these new properties, + +394 +00:18:37,249 --> 00:18:40,386 +my customers can now use +magic variables in Shortcuts + +395 +00:18:40,386 --> 00:18:42,688 +to pull out each new +piece of information + +396 +00:18:42,688 --> 00:18:45,291 +when working with +a book entity + +397 +00:18:45,291 --> 00:18:47,660 +When using +the earlier Add Book intent, + +398 +00:18:47,660 --> 00:18:49,996 +they can use the author +or publishing date + +399 +00:18:49,996 --> 00:18:52,999 +of a newly added book +in their shortcuts. + +400 +00:18:55,134 --> 00:18:57,670 +When you combine properties +with queries, + +401 +00:18:57,670 --> 00:19:00,606 +your app automatically +gets these incredibly powerful + +402 +00:19:00,606 --> 00:19:03,075 +Find and Filter actions +in Shortcuts, + +403 +00:19:03,075 --> 00:19:06,545 +with this flexible +predicate editor UI. + +404 +00:19:06,545 --> 00:19:09,315 +Now, my customers will be +able to find and filter books + +405 +00:19:09,315 --> 00:19:13,052 +based on date read, title, +author, and more. + +406 +00:19:13,052 --> 00:19:14,553 +It's a piece of cake to find + +407 +00:19:14,553 --> 00:19:18,758 +all the books by Delia Owens, +for example. + +408 +00:19:18,758 --> 00:19:20,659 +Using the Sort by +and Limit options, + +409 +00:19:20,659 --> 00:19:22,728 +you can support even more +advanced queries, + +410 +00:19:22,728 --> 00:19:25,064 +like find the three +most recently published books + +411 +00:19:25,064 --> 00:19:27,867 +by Delia Owens. + +412 +00:19:27,867 --> 00:19:30,002 +A customer can use +these building blocks + +413 +00:19:30,002 --> 00:19:32,371 +to do some pretty cool stuff, +like finding + +414 +00:19:32,371 --> 00:19:36,042 +the three most common authors +in their collection. + +415 +00:19:36,042 --> 00:19:37,276 +To enable all this, + +416 +00:19:37,276 --> 00:19:39,678 +I'll need to adopt +another kind of query + +417 +00:19:39,678 --> 00:19:41,914 +called a property query. + +418 +00:19:41,914 --> 00:19:44,917 +Property queries find entities +not based on a string, + +419 +00:19:44,917 --> 00:19:50,389 +or an identifier, but on the +properties within the entity. + +420 +00:19:50,389 --> 00:19:53,592 +There are three steps to +implementing property queries. + +421 +00:19:53,592 --> 00:19:55,928 +First, you declare +query properties, + +422 +00:19:55,928 --> 00:19:58,364 +which specify how your entity +can be searched + +423 +00:19:58,364 --> 00:20:00,633 +using its properties. + +424 +00:20:00,633 --> 00:20:02,468 +Then, you add sorting options, + +425 +00:20:02,468 --> 00:20:06,372 +which define how query results +can be sorted. + +426 +00:20:06,372 --> 00:20:09,475 +And finally, you implement +entities(matching:) + +427 +00:20:09,475 --> 00:20:12,211 +to run the search. + +428 +00:20:12,211 --> 00:20:13,446 +The query properties + +429 +00:20:13,446 --> 00:20:16,215 +declare every way +AppIntents can search + +430 +00:20:16,215 --> 00:20:19,518 +on the entity associated +with this query. + +431 +00:20:19,518 --> 00:20:21,987 +Each one lists +a property of my entity, + +432 +00:20:21,987 --> 00:20:23,789 +and the comparison operators -- + +433 +00:20:23,789 --> 00:20:26,125 +like contains, equal to, +or less than -- + +434 +00:20:26,125 --> 00:20:28,527 +that are available for it. + +435 +00:20:28,527 --> 00:20:31,530 +Here, I list "less than" +and "greater than" comparators + +436 +00:20:31,530 --> 00:20:33,332 +for my date properties, + +437 +00:20:33,332 --> 00:20:38,671 +and "contains" and "equal to" +for my title property. + +438 +00:20:38,671 --> 00:20:41,507 +Query properties +map each combination + +439 +00:20:41,507 --> 00:20:45,544 +of property and comparator +into a type of your choice, + +440 +00:20:45,544 --> 00:20:48,047 +called +the comparator mapping type. + +441 +00:20:48,047 --> 00:20:51,951 +Here, I am using CoreData, +so I'll use an NSPredicate. + +442 +00:20:51,951 --> 00:20:55,221 +If I was using a custom database +or a REST API, + +443 +00:20:55,221 --> 00:20:57,523 +I could design +my own comparator type + +444 +00:20:57,523 --> 00:21:00,593 +and use that instead. + +445 +00:21:00,593 --> 00:21:04,196 +Here's the code to set up the +query properties for my books. + +446 +00:21:04,196 --> 00:21:08,667 +I conform BooksQuery to the +EntityPropertyQuery protocol. + +447 +00:21:08,667 --> 00:21:11,570 +Then I implement +static var properties + +448 +00:21:11,570 --> 00:21:15,274 +using the QueryProperties +result builder. + +449 +00:21:15,274 --> 00:21:18,144 +Each entry specifies +a keyPath of a Property + +450 +00:21:18,144 --> 00:21:20,379 +that can be queried, +and within it, + +451 +00:21:20,379 --> 00:21:24,183 +each comparator that is +applicable to that property. + +452 +00:21:24,183 --> 00:21:26,685 +For each comparator, +I provide an NSPredicate, + +453 +00:21:26,685 --> 00:21:31,457 +because I've chosen NSPredicate +as my comparator mapping type. + +454 +00:21:31,457 --> 00:21:35,194 +When the system asks my app +to return results for the query, + +455 +00:21:35,194 --> 00:21:37,096 +it will provide back +the NSPredicates + +456 +00:21:37,096 --> 00:21:39,965 +that I'm constructing here. + +457 +00:21:39,965 --> 00:21:42,701 +There's a similar definition +for sorting. + +458 +00:21:42,701 --> 00:21:44,403 +This is a list +of all the properties + +459 +00:21:44,403 --> 00:21:46,639 +my model can sort books by. + +460 +00:21:46,639 --> 00:21:48,841 +In this case, +I allow sorting by title, + +461 +00:21:48,841 --> 00:21:52,178 +date read, +and date published. + +462 +00:21:52,178 --> 00:21:55,214 +Finally, I implement +entities(matching:), + +463 +00:21:55,214 --> 00:21:59,151 +which queries my database +and returns matching entities. + +464 +00:21:59,151 --> 00:22:02,521 +This method takes an array +of the comparator mapping type + +465 +00:22:02,521 --> 00:22:05,925 +I used in the previously defined +query parameters -- + +466 +00:22:05,925 --> 00:22:07,893 +in this case, +NSPredicate. + +467 +00:22:07,893 --> 00:22:11,030 +These predicates +are describing what criteria + +468 +00:22:11,030 --> 00:22:15,034 +on the properties of my entity +I want to query by. + +469 +00:22:15,034 --> 00:22:16,468 +It also takes a mode, + +470 +00:22:16,468 --> 00:22:18,504 +indicating whether +to combine the predicates + +471 +00:22:18,504 --> 00:22:22,541 +with "and" or with "or," +key paths to sort by, + +472 +00:22:22,541 --> 00:22:26,378 +and an optional limit +for the number of results. + +473 +00:22:26,378 --> 00:22:28,647 +My implementation +uses these parameters + +474 +00:22:28,647 --> 00:22:34,453 +to perform a query against +my CoreData database. + +475 +00:22:34,453 --> 00:22:37,389 +What can customers do +with this property query? + +476 +00:22:37,389 --> 00:22:40,960 +They can pick a random book +from their library to read. + +477 +00:22:40,960 --> 00:22:42,494 +They can find all their books + +478 +00:22:42,494 --> 00:22:45,831 +published in the early part +of the twentieth century. + +479 +00:22:45,831 --> 00:22:48,267 +They can leverage +the Shortcuts ecosystem + +480 +00:22:48,267 --> 00:22:51,570 +and make my app more useful +by connecting it to others. + +481 +00:22:51,570 --> 00:22:53,572 +For example, they can use +a spreadsheet app + +482 +00:22:53,572 --> 00:22:57,476 +to export all the books they +read this year to a CSV file. + +483 +00:22:57,476 --> 00:22:59,778 +Or, they can use a graphing app +to make a chart + +484 +00:22:59,778 --> 00:23:03,282 +of how many books they've read +every year over the last 10. + +485 +00:23:03,282 --> 00:23:04,917 +And that's just the beginning. + +486 +00:23:04,917 --> 00:23:07,319 +This kind of deep +App Intents adoption + +487 +00:23:07,319 --> 00:23:09,255 +really lets customers +use your app + +488 +00:23:09,255 --> 00:23:11,323 +to do what they need it to do, + +489 +00:23:11,323 --> 00:23:14,326 +making it a critical part +of their workflow. + +490 +00:23:14,326 --> 00:23:15,861 +Each of these integrations -- + +491 +00:23:15,861 --> 00:23:17,930 +like making graphs, +for example -- + +492 +00:23:17,930 --> 00:23:21,734 +is a feature +you don't have to build. + +493 +00:23:21,734 --> 00:23:23,502 +When your intents +are performed, + +494 +00:23:23,502 --> 00:23:25,771 +your app may need +to interact with the user + +495 +00:23:25,771 --> 00:23:29,141 +to show or speak a result, +or to resolve ambiguity, + +496 +00:23:29,141 --> 00:23:32,945 +whether it's a Siri request +or a shortcut. + +497 +00:23:32,945 --> 00:23:36,515 +App Intents supports +a number of these interactions: + +498 +00:23:36,515 --> 00:23:38,851 +dialog for giving text +and voice feedback + +499 +00:23:38,851 --> 00:23:41,453 +to your users +when an intent has completed, + +500 +00:23:41,453 --> 00:23:43,989 +and snippets +for giving visual feedback. + +501 +00:23:43,989 --> 00:23:46,258 +Request value and disambiguation + +502 +00:23:46,258 --> 00:23:50,362 +for asking the user to clarify +values for intent parameters, + +503 +00:23:50,362 --> 00:23:53,732 +and confirmation for verifying +parameter values + +504 +00:23:53,732 --> 00:23:55,167 +or checking with the user + +505 +00:23:55,167 --> 00:23:59,438 +on intents that are +transactional or destructive. + +506 +00:23:59,438 --> 00:24:02,541 +Dialog provides +a spoken or textual response + +507 +00:24:02,541 --> 00:24:04,576 +to the person +running an intent. + +508 +00:24:04,576 --> 00:24:06,712 +It's really important +to provide dialog + +509 +00:24:06,712 --> 00:24:10,182 +for intents to work well +in a voice experience. + +510 +00:24:10,182 --> 00:24:12,117 +In my Add Book intent +from earlier, + +511 +00:24:12,117 --> 00:24:13,819 +I'll add a needsValueDialog + +512 +00:24:13,819 --> 00:24:16,221 +that's spoken +when asking for a book title + +513 +00:24:16,221 --> 00:24:20,025 +and a result dialog returned +from my perform method. + +514 +00:24:20,025 --> 00:24:22,728 +These will be read or shown +by Shortcuts or Siri + +515 +00:24:22,728 --> 00:24:25,864 +across our many platforms. + +516 +00:24:25,864 --> 00:24:30,402 +You can think of snippets as +the visual equivalent of dialog, + +517 +00:24:30,402 --> 00:24:32,538 +letting you add +a visual representation + +518 +00:24:32,538 --> 00:24:34,940 +to the result of your intent. + +519 +00:24:34,940 --> 00:24:38,210 +To use a snippet, just add +the SwiftUI view of your choice + +520 +00:24:38,210 --> 00:24:42,348 +as a trailing closure +to your intent result. + +521 +00:24:42,348 --> 00:24:46,018 +Like with a widget, your SwiftUI +view will be archived + +522 +00:24:46,018 --> 00:24:51,256 +and sent over +to Shortcuts or Siri. + +523 +00:24:51,256 --> 00:24:54,560 +App Intents also supports +asking the user for a value + +524 +00:24:54,560 --> 00:24:56,729 +by throwing requestValue. + +525 +00:24:56,729 --> 00:24:59,431 +For example, this comes in handy +when you need a value + +526 +00:24:59,431 --> 00:25:02,768 +for a parameter +that is sometimes optional. + +527 +00:25:02,768 --> 00:25:06,105 +Here, requestValue helps me +when my string search + +528 +00:25:06,105 --> 00:25:08,273 +returns more than one book. + +529 +00:25:08,273 --> 00:25:10,542 +In this case, +I prompt and ask for an author + +530 +00:25:10,542 --> 00:25:12,778 +to narrow the book search down. + +531 +00:25:12,778 --> 00:25:15,481 +requestValue gives me +an error I can throw, + +532 +00:25:15,481 --> 00:25:17,015 +which will prompt the user, + +533 +00:25:17,015 --> 00:25:21,954 +and rerun the action +with the updated author name. + +534 +00:25:21,954 --> 00:25:24,022 +Disambiguation, +meanwhile, + +535 +00:25:24,022 --> 00:25:25,924 +is great when you need +the user to choose + +536 +00:25:25,924 --> 00:25:29,328 +between a set of values +for a parameter. + +537 +00:25:29,328 --> 00:25:31,463 +This gives me an even better +way to handle + +538 +00:25:31,463 --> 00:25:35,801 +multiple possible results +in my Add Book action. + +539 +00:25:35,801 --> 00:25:39,204 +Here, I get a list of author +names from the generated books, + +540 +00:25:39,204 --> 00:25:43,075 +and request disambiguation +with those possible values. + +541 +00:25:43,075 --> 00:25:45,077 +The user will be asked +to pick between them, + +542 +00:25:45,077 --> 00:25:49,148 +and I'll get the result back. + +543 +00:25:49,148 --> 00:25:53,685 +Lastly, App Intents supports two +different kinds of confirmation. + +544 +00:25:53,685 --> 00:25:57,623 +The first kind is confirmation +of a parameter value. + +545 +00:25:57,623 --> 00:25:59,191 +You might use this +when you have a guess + +546 +00:25:59,191 --> 00:26:02,161 +at what that value should be +but you want to confirm, + +547 +00:26:02,161 --> 00:26:04,329 +just to make sure. + +548 +00:26:04,329 --> 00:26:05,597 +When adding a book, + +549 +00:26:05,597 --> 00:26:08,834 +sometimes the web service I call +to look up books by title + +550 +00:26:08,834 --> 00:26:10,235 +returns a couple matches, + +551 +00:26:10,235 --> 00:26:13,105 +but one of them is by far +the more popular. + +552 +00:26:13,105 --> 00:26:15,274 +In these cases, +I'm going to assume + +553 +00:26:15,274 --> 00:26:17,976 +that the user meant +to add that popular book, + +554 +00:26:17,976 --> 00:26:21,380 +but I'll add confirmation +to make sure I got it right. + +555 +00:26:21,380 --> 00:26:23,682 +To do that, +I'll call requestConfirmation + +556 +00:26:23,682 --> 00:26:26,285 +on the title parameter. + +557 +00:26:26,285 --> 00:26:28,854 +The second kind +is a confirmation + +558 +00:26:28,854 --> 00:26:31,023 +of the result of an intent. + +559 +00:26:31,023 --> 00:26:33,926 +This is great for placing +orders, for example. + +560 +00:26:33,926 --> 00:26:35,961 +If I wanted to monetize +my Library app + +561 +00:26:35,961 --> 00:26:38,263 +and add ordering +through a bookstore, + +562 +00:26:38,263 --> 00:26:41,400 +I'd want to make sure +that I have the order right. + +563 +00:26:41,400 --> 00:26:44,102 +To do this, I could call +requestConfirmation + +564 +00:26:44,102 --> 00:26:47,706 +on my intent, passing in +the order to be placed. + +565 +00:26:47,706 --> 00:26:52,344 +I'll specify a snippet here too, +showing a preview of the order. + +566 +00:26:52,344 --> 00:26:55,047 +I prefix the call with "try" +because requestConfirmation + +567 +00:26:55,047 --> 00:26:57,716 +will throw an error +if the user cancels + +568 +00:26:57,716 --> 00:27:00,886 +instead of confirming. + +569 +00:27:00,886 --> 00:27:02,955 +Before I leave you, +there are a couple aspects + +570 +00:27:02,955 --> 00:27:05,824 +of the App Intents +architecture I want to cover, + +571 +00:27:05,824 --> 00:27:09,628 +which you should know +as you adopt the framework. + +572 +00:27:09,628 --> 00:27:12,731 +There are actually two ways +to build your App Intents: + +573 +00:27:12,731 --> 00:27:16,034 +within your app +or in a separate extension. + +574 +00:27:16,034 --> 00:27:18,804 +Of these, implementing intents +directly in your app + +575 +00:27:18,804 --> 00:27:20,839 +is the simplest. + +576 +00:27:20,839 --> 00:27:23,108 +This is great because +you don't need a framework + +577 +00:27:23,108 --> 00:27:25,244 +or to duplicate your code, + +578 +00:27:25,244 --> 00:27:28,647 +and you don't need to coordinate +across processes. + +579 +00:27:28,647 --> 00:27:31,483 +Using your app also gives +some higher memory limits, + +580 +00:27:31,483 --> 00:27:34,119 +and it gives you the ability +to do some kinds of work + +581 +00:27:34,119 --> 00:27:36,021 +that are harder +from an extension, + +582 +00:27:36,021 --> 00:27:39,625 +like playing audio. + +583 +00:27:39,625 --> 00:27:41,493 +Your app can be run +in the foreground + +584 +00:27:41,493 --> 00:27:45,631 +if you implement openAppWhenRun +on your intent to return true. + +585 +00:27:45,631 --> 00:27:48,600 +Otherwise, it will be run +in the background. + +586 +00:27:48,600 --> 00:27:50,168 +When running in the background, + +587 +00:27:50,168 --> 00:27:52,070 +your app will launch +in a special mode + +588 +00:27:52,070 --> 00:27:55,574 +without scenes being brought up +to maximize performance. + +589 +00:27:55,574 --> 00:27:58,577 +In fact, if you implement +background app intents + +590 +00:27:58,577 --> 00:27:59,678 +in your app, + +591 +00:27:59,678 --> 00:28:05,450 +we strongly encourage you +to also implement scene support. + +592 +00:28:05,450 --> 00:28:08,820 +Or, you can build your +app intents in an extension. + +593 +00:28:08,820 --> 00:28:10,923 +This has a couple advantages. + +594 +00:28:10,923 --> 00:28:11,823 +It's lighter weight, + +595 +00:28:11,823 --> 00:28:15,260 +because the extension process +only handles app intents + +596 +00:28:15,260 --> 00:28:18,363 +and doesn't require +spinning up your app. + +597 +00:28:18,363 --> 00:28:20,732 +If you are handling +Focus intents, + +598 +00:28:20,732 --> 00:28:23,201 +using an extension also means +that you'll immediately + +599 +00:28:23,201 --> 00:28:25,837 +get intents performed +on your extension + +600 +00:28:25,837 --> 00:28:28,140 +when Focus changes, +without the requirement + +601 +00:28:28,140 --> 00:28:32,044 +that your app is running +in the foreground first. + +602 +00:28:32,044 --> 00:28:33,645 +An extension is a bit more work, + +603 +00:28:33,645 --> 00:28:35,747 +since you'll need +to add a new target, + +604 +00:28:35,747 --> 00:28:37,249 +move some code +into a framework, + +605 +00:28:37,249 --> 00:28:39,251 +and handle coordination +between your app + +606 +00:28:39,251 --> 00:28:43,021 +and the extension. + +607 +00:28:43,021 --> 00:28:45,057 +To create an App Intents +extension, + +608 +00:28:45,057 --> 00:28:47,259 +go to File New Target +in Xcode + +609 +00:28:47,259 --> 00:28:52,064 +and choose +App Intents Extension. + +610 +00:28:52,064 --> 00:28:56,735 +With App Intents, your code +is the only source of truth. + +611 +00:28:56,735 --> 00:28:59,605 +App Intents achieves this +elegant developer experience + +612 +00:28:59,605 --> 00:29:02,941 +by statically extracting +information about your intents, + +613 +00:29:02,941 --> 00:29:06,912 +entities, queries, +and parameters at build time. + +614 +00:29:06,912 --> 00:29:10,048 +Xcode will generate +a metadata file inside your app + +615 +00:29:10,048 --> 00:29:12,651 +or extension bundle +during your build process, + +616 +00:29:12,651 --> 00:29:15,787 +containing information received +from the Swift compiler + +617 +00:29:15,787 --> 00:29:18,290 +as it runs on your code. + +618 +00:29:18,290 --> 00:29:21,393 +To make sure all of this works, +keep your App Intents types + +619 +00:29:21,393 --> 00:29:25,731 +directly in the target or +extension, not in a framework. + +620 +00:29:25,731 --> 00:29:27,466 +Similarly, +your localized strings + +621 +00:29:27,466 --> 00:29:29,401 +should be found +in a strings file + +622 +00:29:29,401 --> 00:29:34,506 +within the same bundle where +your App Intents types live. + +623 +00:29:34,506 --> 00:29:36,775 +For those of you +who have existing apps + +624 +00:29:36,775 --> 00:29:39,778 +with SiriKit Intents +that you want to upgrade, + +625 +00:29:39,778 --> 00:29:42,848 +if you adopt intents +to integrate with widgets, + +626 +00:29:42,848 --> 00:29:45,317 +or domains like messaging +or media, + +627 +00:29:45,317 --> 00:29:48,754 +you should keep using +the SiriKit Intents framework. + +628 +00:29:48,754 --> 00:29:51,823 +But if you add custom intents +for Siri and Shortcuts, + +629 +00:29:51,823 --> 00:29:55,160 +you should go ahead and upgrade +to App Intents. + +630 +00:29:55,160 --> 00:29:57,162 +You can start +the upgrade process by clicking + +631 +00:29:57,162 --> 00:29:59,398 +the Convert to App Intent +button + +632 +00:29:59,398 --> 00:30:04,436 +in your SiriKit Intents +definition file. + +633 +00:30:04,436 --> 00:30:06,938 +Integrating your app into +Shortcuts with App Intents + +634 +00:30:06,938 --> 00:30:10,776 +is a great way to maximize +your leverage as a developer, + +635 +00:30:10,776 --> 00:30:14,212 +because by doing a small amount +of work to adopt App Intents, + +636 +00:30:14,212 --> 00:30:17,783 +you create a large amount +of value for customers. + +637 +00:30:17,783 --> 00:30:18,884 +Thank you for joining! + +638 +00:30:18,884 --> 00:30:21,753 +I really hope that you'll +try out App Intents today + +639 +00:30:21,753 --> 00:30:23,321 +and give us your feedback. + +640 +00:30:23,321 --> 00:30:25,190 +I'm excited about +how this new framework + +641 +00:30:25,190 --> 00:30:27,993 +can help you surprise, +delight, and empower folks + +642 +00:30:27,993 --> 00:30:29,494 +using your apps! + +643 +00:30:29,494 --> 00:30:33,365 +Happy reading and +hope your WWDC is epic! + +644 +00:30:33,365 --> 00:30:37,636 +♪ + diff --git a/eng/2022 Session 10034 Design for Arabic en.srt b/eng/2022 Session 10034 Design for Arabic en.srt new file mode 100644 index 0000000..1e9f880 --- /dev/null +++ b/eng/2022 Session 10034 Design for Arabic en.srt @@ -0,0 +1,1688 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,610 --> 00:00:11,578 +Hi, I am Mohamed Samir, + +3 +00:00:11,612 --> 00:00:14,114 +a designer on the Apple Design team. + +4 +00:00:14,147 --> 00:00:17,117 +Today, I'm going to walk you through +some of the best practices + +5 +00:00:17,150 --> 00:00:19,686 +when it comes to designing an Arabic app. + +6 +00:00:19,720 --> 00:00:23,857 +This session is also available in Arabic, +so feel free to check it out. + +7 +00:00:23,891 --> 00:00:27,027 +And before we get started, +I wanted to touch on why it's important + +8 +00:00:27,060 --> 00:00:30,264 +to consider designing +or optimizing your app or game + +9 +00:00:30,297 --> 00:00:33,333 +to be used by Arab audience. + +10 +00:00:33,367 --> 00:00:37,971 +There is around 660 million people +that uses the Arabic script today, + +11 +00:00:38,005 --> 00:00:40,807 +which makes it the third most written +language in the world + +12 +00:00:40,841 --> 00:00:42,976 +after Latin and Chinese. + +13 +00:00:43,010 --> 00:00:45,179 +People from more than 22 countries, + +14 +00:00:45,212 --> 00:00:49,550 +many cities, and regions would potentially +see and use what you've built. + +15 +00:00:49,583 --> 00:00:52,419 +And If you want to reach even a fraction +of that audience, + +16 +00:00:52,452 --> 00:00:55,889 +you would want to consider optimizing +not only for the language, + +17 +00:00:55,923 --> 00:00:59,526 +but also for the directionality of the UI. + +18 +00:00:59,560 --> 00:01:04,364 +And this is because Arabic is a language +that is written from right to left. + +19 +00:01:04,398 --> 00:01:06,967 +To understand this, +let's look at this example, + +20 +00:01:07,768 --> 00:01:11,104 +that says "The Arabian desert" in Arabic, + +21 +00:01:11,138 --> 00:01:15,108 +And as you see, it's written from +the right to the left of the screen. + +22 +00:01:15,142 --> 00:01:17,611 +But this is not only it. + +23 +00:01:17,644 --> 00:01:20,247 +If this phrase exists in a layout, + +24 +00:01:20,280 --> 00:01:24,084 +the entire layout flows +from top to bottom, right to left, + +25 +00:01:24,117 --> 00:01:28,121 +to match the reading behavior +and direction of the language. + +26 +00:01:28,155 --> 00:01:30,924 +This means that things like titles, +paragraphs, + +27 +00:01:30,958 --> 00:01:35,462 +columns, and even imagery +should flow from right to left. + +28 +00:01:35,495 --> 00:01:40,834 +And this directionality behavior +extends beyond the layout to also the UI. + +29 +00:01:41,969 --> 00:01:44,805 +As in this example from the Pages app, + +30 +00:01:44,838 --> 00:01:49,643 +where the Navigation bar order +and direction is from right to left. + +31 +00:01:49,676 --> 00:01:53,213 +And the icons also flow +in the same direction. + +32 +00:01:53,247 --> 00:01:55,482 +And if you start navigating +through the app, + +33 +00:01:55,516 --> 00:01:58,252 +you would find menus, controls, + +34 +00:01:58,285 --> 00:01:59,953 +graphical elements, + +35 +00:01:59,987 --> 00:02:02,990 +and even tables have been designed +to match the natural flow + +36 +00:02:03,023 --> 00:02:05,893 +and behavior of the language. + +37 +00:02:05,926 --> 00:02:09,229 +Now, you might think that this +could be a lot of work to optimize + +38 +00:02:09,263 --> 00:02:11,765 +your app or game to work with Arabic, + +39 +00:02:11,798 --> 00:02:15,302 +but the good news is, a lot of this +has been taken care of by Apple + +40 +00:02:15,335 --> 00:02:18,238 +if you use our native frameworks, +like Swift UI, + +41 +00:02:18,272 --> 00:02:21,742 +so you can focus on the content +and few other UI details + +42 +00:02:21,775 --> 00:02:24,511 +that could be specific +to your app or game. + +43 +00:02:24,545 --> 00:02:27,581 +And in today's session I will share +with you some of these aspects + +44 +00:02:27,614 --> 00:02:31,485 +that you should be looking at +while doing this optimization. + +45 +00:02:31,518 --> 00:02:33,520 +Starting by the UI directionality + +46 +00:02:33,554 --> 00:02:37,024 +and example components +that you would want to consider. + +47 +00:02:37,057 --> 00:02:40,194 +Then we move to the Arabic script +main features + +48 +00:02:40,227 --> 00:02:44,765 +and the typography support +that Apple provides you for Arabic, + +49 +00:02:44,798 --> 00:02:46,466 +and how iconography is a detail + +50 +00:02:46,500 --> 00:02:49,970 +that enhances +the overall Arabic experience. + +51 +00:02:50,003 --> 00:02:55,175 +And at last, the numeral systems +supported by Apple for Arabic usage. + +52 +00:02:55,209 --> 00:02:58,312 +Let's start by the UI directionality. + +53 +00:02:58,345 --> 00:03:00,781 +This is an example from the App Store. + +54 +00:03:00,814 --> 00:03:04,318 +It is a flow that starts +by the story card in the Today tab, + +55 +00:03:04,351 --> 00:03:08,789 +the story page, and it ends +with the product page of an app. + +56 +00:03:08,822 --> 00:03:11,358 +The best way to think about +layout directionality + +57 +00:03:11,391 --> 00:03:14,161 +is to turn it into wireframes. + +58 +00:03:14,194 --> 00:03:16,530 +For Arabic, +you would want to switch the placement + +59 +00:03:16,563 --> 00:03:19,399 +of the UI components of these screens. + +60 +00:03:19,433 --> 00:03:21,969 +Some of the elements would switch +from right to left, + +61 +00:03:22,002 --> 00:03:25,072 +while others from left to right. + +62 +00:03:25,105 --> 00:03:30,711 +Titles, buttons, and the Navigation bar +should change order and position. + +63 +00:03:30,744 --> 00:03:34,081 +Paragraphs should be +always aligned to the right. + +64 +00:03:34,114 --> 00:03:39,119 +Carousals and swipeable elements +should also flow from right to left. + +65 +00:03:39,152 --> 00:03:41,788 +After changing the placement +of the UI components, + +66 +00:03:41,822 --> 00:03:45,759 +localizing the content, +and keeping the images as they are, + +67 +00:03:45,792 --> 00:03:49,329 +now you have a layout +that flows from right to left. + +68 +00:03:49,363 --> 00:03:52,900 +Changing the layout directionality +is only the beginning of your journey + +69 +00:03:52,933 --> 00:03:55,936 +to create an excellent +right-to-left behavior, + +70 +00:03:55,969 --> 00:03:57,604 +You would want to keep in mind + +71 +00:03:57,638 --> 00:04:01,475 +that the entire flow of the app +is now structured differently. + +72 +00:04:01,508 --> 00:04:07,247 +And the user thinks about the navigation +between these pages in a reverse order. + +73 +00:04:07,281 --> 00:04:10,584 +So mentally, they start +by the today tab on the right, + +74 +00:04:10,617 --> 00:04:13,153 +then they navigate through the story card, + +75 +00:04:13,187 --> 00:04:16,089 +and they end with the product page +on the left side– + +76 +00:04:16,123 --> 00:04:20,694 +as if they are navigating +through an Arabic book from right to left. + +77 +00:04:20,727 --> 00:04:23,764 +And as I mentioned earlier, +all of this happens automatically + +78 +00:04:23,797 --> 00:04:26,500 +if you use our native frameworks. + +79 +00:04:26,533 --> 00:04:28,936 +Now I would like to share with you +some other examples + +80 +00:04:28,969 --> 00:04:31,138 +for areas and components +that could be impacted + +81 +00:04:31,171 --> 00:04:34,241 +by changing the directionality of the UI. + +82 +00:04:35,142 --> 00:04:38,245 +Let's look at this example +from the Weather app. + +83 +00:04:38,278 --> 00:04:40,881 +On the left side +you see the English Layout, + +84 +00:04:40,914 --> 00:04:44,117 +while the Arabic layout +in on the right side. + +85 +00:04:45,586 --> 00:04:48,622 +The first thing that you would +notice here is content like images, + +86 +00:04:48,655 --> 00:04:51,725 +videos, and backgrounds remain the same. + +87 +00:04:51,758 --> 00:04:54,995 +In this example, +the sun always rises from the east + +88 +00:04:55,028 --> 00:04:57,631 +regardless the location or the language, + +89 +00:04:57,664 --> 00:04:59,566 +and you would want to avoid +flipping the content + +90 +00:04:59,600 --> 00:05:03,036 +so it doesn't impact +the overall experience. + +91 +00:05:04,872 --> 00:05:09,243 +The second component here is the carousal +of "the weather across the day". + +92 +00:05:09,276 --> 00:05:13,647 +Both, the interaction and the animation +of this component are inverted + +93 +00:05:13,680 --> 00:05:16,583 +to match the UI direction. + +94 +00:05:16,617 --> 00:05:19,720 +The third component that I would like +to share with you in the Weather app + +95 +00:05:19,753 --> 00:05:21,488 +is the temperature scale. + +96 +00:05:21,522 --> 00:05:24,124 +For Arabic, the lowest temperature +is on the right, + +97 +00:05:24,157 --> 00:05:26,126 +while the highest is on the left. + +98 +00:05:26,159 --> 00:05:28,662 +So as the scale gradient and indicator, + +99 +00:05:28,695 --> 00:05:31,899 +which we inverted +to match this behavior. + +100 +00:05:31,932 --> 00:05:34,101 +And as previously said, +the mental model + +101 +00:05:34,134 --> 00:05:37,204 +of switching between pages +is also reversed. + +102 +00:05:37,237 --> 00:05:39,339 +The primary page is on the far right, + +103 +00:05:39,373 --> 00:05:42,576 +and you navigate to secondary pages +on the left. + +104 +00:05:42,609 --> 00:05:47,281 +And therefore the pagination dots +should flow also from right to left. + +105 +00:05:47,314 --> 00:05:50,150 +And our second example here +is from the Calendar app, + +106 +00:05:50,184 --> 00:05:53,654 +in which the progress +and the flow of dates, months, and years + +107 +00:05:53,687 --> 00:05:56,623 +is from right to left +when used in Arabic, + +108 +00:05:56,657 --> 00:06:01,495 +which matches the progress +of physical calendars in the Arab world. + +109 +00:06:01,528 --> 00:06:05,432 +And it is always important to make sure +that your app is culturally relevant. + +110 +00:06:05,465 --> 00:06:07,968 +In this example here +from the Calendar app, + +111 +00:06:08,001 --> 00:06:12,072 +you may have noticed that there are +red lines beneath some dates. + +112 +00:06:12,105 --> 00:06:15,008 +These are actually used to indicate +the beginning of each month + +113 +00:06:15,042 --> 00:06:16,777 +of the Islamic Lunar Calendar, + +114 +00:06:16,810 --> 00:06:22,015 +which we have available for people +in the Arab and Islamic world. + +115 +00:06:22,049 --> 00:06:26,854 +The last example about directionality +is from the battery status in settings + +116 +00:06:26,887 --> 00:06:30,457 +where toggles, segmented controllers, +design, and interaction + +117 +00:06:30,490 --> 00:06:33,994 +are mirrored in the Arabic layout. + +118 +00:06:34,027 --> 00:06:36,396 +And also Charts is another place + +119 +00:06:36,430 --> 00:06:39,533 +where directionality could be impacted +in the UI– + +120 +00:06:39,566 --> 00:06:41,902 +especially charts that include +a time component, + +121 +00:06:41,935 --> 00:06:45,539 +like days, weeks, months, or years. + +122 +00:06:45,572 --> 00:06:47,140 +In the battery usage graph, + +123 +00:06:47,174 --> 00:06:50,010 +days of the week goes from left to right +in the English UI, + +124 +00:06:50,043 --> 00:06:51,745 +while in Arabic it is preferred + +125 +00:06:51,778 --> 00:06:54,715 +to have the days progress +from right to left + +126 +00:06:54,748 --> 00:06:59,086 +to match the calendar behavior that we +referred to in the previous example. + +127 +00:06:59,119 --> 00:07:02,556 +Which means that the early time would be +on the right, + +128 +00:07:02,589 --> 00:07:05,592 +and the late time is on the left. + +129 +00:07:05,626 --> 00:07:09,062 +And other charts and graphs in general +are dependent on the country, + +130 +00:07:09,096 --> 00:07:11,198 +so you would want to double-check +before deciding + +131 +00:07:11,231 --> 00:07:14,535 +on which direction to use for charts. + +132 +00:07:14,568 --> 00:07:16,904 +That was UI directionality. + +133 +00:07:16,937 --> 00:07:19,540 +Now let's talk about typography. + +134 +00:07:19,573 --> 00:07:23,210 +But before we dive into the typefaces +and the typestyle adjustments + +135 +00:07:23,243 --> 00:07:26,813 +that you would want to consider +while designing your app, + +136 +00:07:26,847 --> 00:07:28,782 +let me take you through +a brief introduction + +137 +00:07:28,815 --> 00:07:32,653 +about the Arabic script +and its main features. + +138 +00:07:32,686 --> 00:07:36,623 +These are four letters +that spell the word "Arabic" in Arabic, + +139 +00:07:36,657 --> 00:07:40,727 +but in writing they actually don't +mean anything on their own + +140 +00:07:40,761 --> 00:07:42,896 +until they get connected. + +141 +00:07:44,498 --> 00:07:45,999 +To understand this even more, + +142 +00:07:46,033 --> 00:07:49,970 +let me share with you how I would write +this word in a full Arabic sentence + +143 +00:07:50,003 --> 00:07:52,439 +using the iOS keyboard. + +144 +00:08:02,449 --> 00:08:04,585 +And as you see, +being connected doesn't mean + +145 +00:08:04,618 --> 00:08:08,222 +that all the letters in a given word +would be linked. + +146 +00:08:08,255 --> 00:08:10,791 +So this word is made of two parts. + +147 +00:08:10,824 --> 00:08:13,794 +Each part has two characters. + +148 +00:08:13,827 --> 00:08:16,864 +And this is one of the main features +of the Arabic script– + +149 +00:08:16,897 --> 00:08:19,299 +the connected nature of it. + +150 +00:08:19,333 --> 00:08:22,202 +And this also means +that the possible glyphs for each letter + +151 +00:08:22,236 --> 00:08:25,138 +and each pair can be many. + +152 +00:08:25,172 --> 00:08:29,209 +If we take the first letter here +in this example, the letter "Ain," + +153 +00:08:29,243 --> 00:08:33,080 +its form changes based on its position +in the word, + +154 +00:08:33,113 --> 00:08:35,182 +whether it's isolated, + +155 +00:08:35,215 --> 00:08:37,217 +at the beginning of the word, + +156 +00:08:37,251 --> 00:08:38,719 +in the middle, + +157 +00:08:38,752 --> 00:08:41,154 +or at the end. + +158 +00:08:41,188 --> 00:08:44,925 +And it's worth noting that this +usually makes Arabic fonts libraries + +159 +00:08:44,958 --> 00:08:46,894 +way bigger than Latin. + +160 +00:08:48,195 --> 00:08:51,365 +Another feature of the Arabic script +that in most cases, + +161 +00:08:51,398 --> 00:08:54,201 +words are more concise than Latin + +162 +00:08:54,234 --> 00:08:56,069 +due to the connected nature +that tends to give + +163 +00:08:56,103 --> 00:09:00,140 +a more condensed feel +to words and phrases. + +164 +00:09:00,174 --> 00:09:02,442 +But it's also slightly taller, + +165 +00:09:02,476 --> 00:09:04,378 +especially with the use of dots, + +166 +00:09:04,411 --> 00:09:07,481 +vocalization, and diacritic marks. + +167 +00:09:07,514 --> 00:09:10,350 +Vocalization marks are used +to emphasize certain letters + +168 +00:09:10,384 --> 00:09:13,187 +or distinguish between words +that sound different + +169 +00:09:13,220 --> 00:09:16,123 +but would otherwise be identical +in writing. + +170 +00:09:16,156 --> 00:09:19,893 +And if your app would include +an intense use of vocalization marks, + +171 +00:09:19,927 --> 00:09:24,431 +make sure to have more vertical spacing +in the UI to avoid clipping. + +172 +00:09:25,199 --> 00:09:28,869 +Now we understand the main differences +between Arabic and Latin. + +173 +00:09:28,902 --> 00:09:33,607 +Let's talk about the typefaces that Apple +provides you if you use our system API. + +174 +00:09:34,541 --> 00:09:38,345 +Apple provides an exclusive +Arabic typeface that is designed carefully + +175 +00:09:38,378 --> 00:09:41,748 +with legibility and functionality in mind. + +176 +00:09:41,782 --> 00:09:45,652 +It is also designed to be consistent +with the Latin SF family style + +177 +00:09:45,686 --> 00:09:49,523 +to make if feel natural +in bilingual contexts. + +178 +00:09:49,556 --> 00:09:52,693 +And similar to Latin, +SF Arabic also provides you + +179 +00:09:52,726 --> 00:09:55,696 +with all possible weights +that you may need in your app. + +180 +00:09:55,729 --> 00:09:58,232 +from Ultralight to Black. + +181 +00:09:58,265 --> 00:10:02,536 +You can see the usage of different weights +in many places in our native apps. + +182 +00:10:02,569 --> 00:10:05,606 +The Clock app is a good example for that; + +183 +00:10:05,639 --> 00:10:08,108 +using Bold in the title, + +184 +00:10:08,141 --> 00:10:10,644 +Regular for the different cities, + +185 +00:10:10,677 --> 00:10:13,280 +and Light for the clock numerals. + +186 +00:10:13,313 --> 00:10:15,582 +You can explore more apps +in our ecosystem + +187 +00:10:15,616 --> 00:10:18,051 +to see how different weights can be used. + +188 +00:10:19,219 --> 00:10:22,189 +Like the Health app where bold, +medium, and regular + +189 +00:10:22,222 --> 00:10:25,359 +are used in titles and body copy. + +190 +00:10:25,392 --> 00:10:30,063 +And the Weather app where multiple weights +are used in numerals and body copy. + +191 +00:10:31,031 --> 00:10:34,935 +SF Arabic also has been made +with scalability in mind. + +192 +00:10:34,968 --> 00:10:38,906 +Which means that its form changes slightly +depending on the point size. + +193 +00:10:38,939 --> 00:10:41,842 +And this is what we call optical size. + +194 +00:10:41,875 --> 00:10:43,710 +You can see +the structural differences here + +195 +00:10:43,744 --> 00:10:47,047 +between the largest +and the smallest point sizes. + +196 +00:10:47,080 --> 00:10:50,417 +Large is usually used +for titles and headings. + +197 +00:10:50,450 --> 00:10:55,923 +And is designed to match the contemporary +grotesque feel of the rest of SF Family. + +198 +00:10:55,956 --> 00:10:57,958 +On the other hand, smaller point sizes, + +199 +00:10:57,991 --> 00:11:00,427 +which are used in paragraphs +and body copy, + +200 +00:11:00,460 --> 00:11:05,766 +are designed to prioritize legibility +and functionality over style. + +201 +00:11:05,799 --> 00:11:09,036 +This happens by adding angularity +to the terminals, + +202 +00:11:09,069 --> 00:11:13,540 +width and contrast +to the overall font structure. + +203 +00:11:13,574 --> 00:11:16,543 +It's worth noting that the system +takes care of this. + +204 +00:11:16,577 --> 00:11:21,048 +It is automatically choosing the +right form depending on the point size. + +205 +00:11:21,081 --> 00:11:24,751 +Here is an example from +the App Store Editorial sheet, + +206 +00:11:24,785 --> 00:11:29,923 +where we use display in the title, +and text is used in paragraphs. + +207 +00:11:29,957 --> 00:11:32,793 +And in both you could see how +Arabic and English typefaces + +208 +00:11:32,826 --> 00:11:35,395 +work seamlessly in this bilingual context. + +209 +00:11:36,530 --> 00:11:40,567 +SF Arabic Scalable typeface +is used across our ecosystem, + +210 +00:11:40,601 --> 00:11:44,204 +but it's also available for you +to use in your app or game. + +211 +00:11:44,238 --> 00:11:48,475 +And this year we're introducing +SF Arabic Rounded to the SF family, + +212 +00:11:48,509 --> 00:11:52,379 +including all its various weights, +from Ultralight to Black. + +213 +00:11:53,480 --> 00:11:55,549 +Here is an example from the Reminders app + +214 +00:11:55,582 --> 00:12:00,621 +where we use SF Arabic Rounded +in the titles and the body text. + +215 +00:12:00,654 --> 00:12:03,924 +And as you see, Rounded could +give your app a more practical, + +216 +00:12:03,957 --> 00:12:07,127 +active, or softer look, +depending on the context. + +217 +00:12:08,629 --> 00:12:12,266 +The usage of the Rounded typeface, +the several weights, + +218 +00:12:12,299 --> 00:12:17,538 +and the scalability of SF Arabic +can all be seen in all of our native apps, + +219 +00:12:17,571 --> 00:12:19,339 +and we can't wait for you to use them + +220 +00:12:19,373 --> 00:12:23,043 +to create the best possible experiences +for the Arab users. + +221 +00:12:23,076 --> 00:12:25,379 +If you would like to learn more +about all the new fonts + +222 +00:12:25,412 --> 00:12:28,782 +that have been announced this year, +including SF Arabic Rounded, + +223 +00:12:28,815 --> 00:12:30,684 +make sure to watch this year's session + +224 +00:12:30,717 --> 00:12:34,388 +"Meet the expanded +San Francisco font family". + +225 +00:12:34,421 --> 00:12:36,857 +Now let's talk about +some typestyle considerations + +226 +00:12:36,890 --> 00:12:39,760 +when using Arabic typefaces. + +227 +00:12:39,793 --> 00:12:42,229 +Arabic is a non-case sensitive script. + +228 +00:12:42,262 --> 00:12:47,301 +Digital Arabic fonts are usually designed +to match the lower case Latin. + +229 +00:12:47,334 --> 00:12:51,171 +But when uppercase is used, +it gives Latin more volume + +230 +00:12:51,205 --> 00:12:54,875 +and it makes Arabic feels smaller +in comparison to Latin. + +231 +00:12:54,908 --> 00:12:58,111 +To compensate for this this +optical size difference in the UI, + +232 +00:12:58,145 --> 00:13:02,716 +you may want to increase +the Arabic font size by 10%. + +233 +00:13:02,749 --> 00:13:05,819 +This subtle difference would also help +with legibility + +234 +00:13:05,853 --> 00:13:10,123 +especially when upper case is used +in smaller point sizes. + +235 +00:13:10,157 --> 00:13:13,093 +The other thing to consider +is letter spacing. + +236 +00:13:13,126 --> 00:13:15,529 +Given the fact that Arabic is connected, + +237 +00:13:15,562 --> 00:13:18,565 +some Arabic typefaces +are not fully optimized + +238 +00:13:18,599 --> 00:13:20,901 +to deal properly with spacing. + +239 +00:13:20,934 --> 00:13:24,204 +This can result +in showing misplaced links, + +240 +00:13:24,238 --> 00:13:25,806 +breaking letters apart, + +241 +00:13:25,839 --> 00:13:28,342 +or showing unnecessary spacing. + +242 +00:13:28,375 --> 00:13:32,312 +If the Arabic typeface you're using +isn't fully optimized for letter spacing, + +243 +00:13:32,346 --> 00:13:34,915 +make sure to use 0 tracking. + +244 +00:13:34,948 --> 00:13:39,419 +or simply, use our system font, +which adds the correct letters linkage. + +245 +00:13:39,453 --> 00:13:42,289 +This linkage in Arabic +is called "Kashida" + +246 +00:13:42,322 --> 00:13:44,858 +and the system adds Kashidas +with different lengths + +247 +00:13:44,892 --> 00:13:49,029 +to have more organic +natural spacing in Arabic. + +248 +00:13:49,062 --> 00:13:52,432 +The last thing to look out for +is transparency. + +249 +00:13:52,466 --> 00:13:55,469 +Sometimes you can see visible joints +between letters. + +250 +00:13:55,502 --> 00:13:58,505 +This often happens +if there is transparency in a font + +251 +00:13:58,539 --> 00:14:02,509 +or a system +that is not fully optimized for Arabic. + +252 +00:14:02,543 --> 00:14:04,478 +Thankfully, if you use our system font, + +253 +00:14:04,511 --> 00:14:06,346 +you don't have to worry about this. + +254 +00:14:06,380 --> 00:14:09,283 +Opacity is applied to the entire word +or phrase + +255 +00:14:09,316 --> 00:14:11,985 +to overcome any potential distortion. + +256 +00:14:12,019 --> 00:14:14,788 +In the typography section, +I talked about the main features + +257 +00:14:14,821 --> 00:14:16,356 +of the Arabic script, + +258 +00:14:16,390 --> 00:14:19,059 +the Arabic typefaces +that we use in our ecosystem, + +259 +00:14:19,092 --> 00:14:22,529 +and some considerations +for certain type treatments in Arabic. + +260 +00:14:22,563 --> 00:14:25,599 +Now let's talk about iconography. + +261 +00:14:25,632 --> 00:14:29,937 +Iconography is one of the UI elements +that could be easily missed, + +262 +00:14:29,970 --> 00:14:33,373 +but they are usually the entry point +to a user flow + +263 +00:14:33,407 --> 00:14:35,742 +or a trigger to a certain action, + +264 +00:14:35,776 --> 00:14:38,745 +which makes having the correct iconography +crucial + +265 +00:14:38,779 --> 00:14:40,914 +to having a seamless experience. + +266 +00:14:40,948 --> 00:14:42,883 +For Arabic, +we are committed to choosing + +267 +00:14:42,916 --> 00:14:46,286 +the most relevant symbols +for our customers. + +268 +00:14:46,320 --> 00:14:50,791 +Let's take the App Store tab bar icons +as an example. + +269 +00:14:50,824 --> 00:14:55,929 +Some symbols have been tweaked, while +others remained the same in the Arabic UI. + +270 +00:14:55,963 --> 00:15:00,000 +And to understand why we made this choice, +let's look at some of them. + +271 +00:15:01,802 --> 00:15:06,406 +The Today tab symbol, for example, +symbolizes text direction, + +272 +00:15:06,440 --> 00:15:11,078 +and it is more relevant for Arabic users +to have the lines aligned to the right + +273 +00:15:11,111 --> 00:15:14,715 +to match the natural reading direction +of the language. + +274 +00:15:14,748 --> 00:15:19,553 +While the magnifying glass's direction +implies the angle of the right hand usage, + +275 +00:15:19,586 --> 00:15:22,823 +which is the behavior of +the majority of users in the world, + +276 +00:15:22,856 --> 00:15:24,491 +regardless their location + +277 +00:15:24,525 --> 00:15:27,594 +so we decided to keep it as it is +in the Arabic UI. + +278 +00:15:29,263 --> 00:15:31,798 +And here some other examples +from other apps + +279 +00:15:31,832 --> 00:15:35,402 +that shows how directionality +can impact the way symbols are treated + +280 +00:15:35,435 --> 00:15:37,604 +in an Arabic UI, + +281 +00:15:37,638 --> 00:15:42,609 +like writing from right to left while +maintaining the angularity of the pen + +282 +00:15:42,643 --> 00:15:46,880 +or changing the speaker's direction +to make it feel natural in the UI + +283 +00:15:46,914 --> 00:15:49,016 +while maintaining the slash direction, + +284 +00:15:49,049 --> 00:15:53,120 +which is consistent +across our Apple ecosystem, + +285 +00:15:53,153 --> 00:15:55,556 +or changing the direction +of the calendar dots + +286 +00:15:55,589 --> 00:15:58,025 +that represent the progress of months + +287 +00:15:58,058 --> 00:16:00,260 +while keeping the clock hands as they are + +288 +00:16:00,294 --> 00:16:03,830 +to match the physical clock representation +in the system. + +289 +00:16:03,864 --> 00:16:07,701 +And on top of directionality, +having more locally relevant symbols + +290 +00:16:07,734 --> 00:16:11,371 +is another layer that we're dedicated +to enhancing over time + +291 +00:16:11,405 --> 00:16:14,908 +to ensure excellence +in our international markets. + +292 +00:16:14,942 --> 00:16:17,778 +And here are few examples +for Arabic specific symbols + +293 +00:16:17,811 --> 00:16:19,680 +from the SF symbols library, + +294 +00:16:19,713 --> 00:16:23,050 +including an exclusively drawn +Arabic signature symbol + +295 +00:16:23,083 --> 00:16:25,953 +and other text formatting ones. + +296 +00:16:25,986 --> 00:16:30,657 +All of this and more than 300 Arabic +and right-to-left symbols can be found + +297 +00:16:30,691 --> 00:16:33,060 +in SF Symbols app. + +298 +00:16:33,093 --> 00:16:35,629 +In the app you can easily choose a symbol + +299 +00:16:35,662 --> 00:16:39,199 +and check the localization section +in the info panel + +300 +00:16:39,233 --> 00:16:43,804 +to see the Arabic local variant +and other non-Latin scripts. + +301 +00:16:43,837 --> 00:16:47,274 +All of the right-to-left and the local +symbols should appear automatically + +302 +00:16:47,307 --> 00:16:50,477 +in your app +if you use our system API. + +303 +00:16:50,511 --> 00:16:55,516 +As we saw, using the right symbols +can transform the entire app experience, + +304 +00:16:55,549 --> 00:16:57,718 +as it becomes more relevant to the users. + +305 +00:16:57,751 --> 00:17:01,355 +And especially for countries +and regions that use a non-Latin script, + +306 +00:17:01,388 --> 00:17:03,123 +we need to pay more attention, + +307 +00:17:03,156 --> 00:17:07,594 +as sometimes we forget about the different +linguistic and cultural nuances. + +308 +00:17:07,628 --> 00:17:09,997 +And I really hope to see your contribution + +309 +00:17:10,030 --> 00:17:13,800 +in creating the most relevant icons +for the Arabic customers. + +310 +00:17:13,834 --> 00:17:16,837 +Now let's talk about Arabic numerals. + +311 +00:17:16,870 --> 00:17:19,406 +The numerals that we're all familiar with + +312 +00:17:19,439 --> 00:17:24,811 +and are used in most countries around +the world are called Arabic numerals. + +313 +00:17:24,845 --> 00:17:27,814 +And this is because they were +invented in the Arab world + +314 +00:17:27,848 --> 00:17:30,751 +and they replaced +the Roman numerals back then. + +315 +00:17:31,852 --> 00:17:34,054 +And you can notice that until today + +316 +00:17:34,087 --> 00:17:37,824 +all mathematical calculations happens +from right to left, + +317 +00:17:37,858 --> 00:17:41,728 +matching the reading behavior +of the Arabic language. + +318 +00:17:41,762 --> 00:17:45,866 +Like this example which starts +the summation process by ones, + +319 +00:17:45,899 --> 00:17:49,670 +then tens, then hundreds. + +320 +00:17:49,703 --> 00:17:55,275 +In today's world this form of numerals +is called Western Arabic numerals, + +321 +00:17:55,309 --> 00:17:58,912 +and that is to contrast it +with the other form of Arabic numerals + +322 +00:17:58,946 --> 00:18:01,114 +which is the Eastern one. + +323 +00:18:01,148 --> 00:18:03,851 +Both forms have been invented +in the Arab world + +324 +00:18:03,884 --> 00:18:07,421 +and are currently used +in different Arab countries. + +325 +00:18:07,454 --> 00:18:10,924 +The Western Arabic numerals is used +in West African Arab countries + +326 +00:18:10,958 --> 00:18:14,795 +like Morocco, Algeria, and Tunisia, + +327 +00:18:14,828 --> 00:18:19,566 +while the Eastern is used in +some Levantine and gulf countries. + +328 +00:18:19,600 --> 00:18:23,337 +Countries like Egypt or Saudi Arabia +uses both versions. + +329 +00:18:24,505 --> 00:18:27,407 +Choosing between both systems +happens automatically + +330 +00:18:27,441 --> 00:18:29,543 +according to the user's country, + +331 +00:18:29,576 --> 00:18:32,513 +and also can be triggered +by the user choice. + +332 +00:18:32,546 --> 00:18:34,581 +And you can see a reflection +of this choice + +333 +00:18:34,615 --> 00:18:38,752 +in the all apps that uses numerals +in our ecosystem. + +334 +00:18:38,785 --> 00:18:41,288 +Including Calculator app, + +335 +00:18:41,321 --> 00:18:43,490 +and the Calendar app, + +336 +00:18:43,524 --> 00:18:46,493 +and the Typograph watch face +that is beautifully designed + +337 +00:18:46,527 --> 00:18:48,862 +in both numeral forms. + +338 +00:18:48,896 --> 00:18:53,400 +And many other watch faces that you can +check in our watch face gallery. + +339 +00:18:53,433 --> 00:18:56,370 +If the app that you're developing +includes numerals, + +340 +00:18:56,403 --> 00:18:58,906 +make sure to count for both forms + +341 +00:18:58,939 --> 00:19:01,041 +or to check which country you're targeting + +342 +00:19:01,074 --> 00:19:03,944 +to validate which form +would be more suitable. + +343 +00:19:03,977 --> 00:19:06,713 +And at last, if you would like to have +more design guidance + +344 +00:19:06,747 --> 00:19:08,682 +for right-to-left languages, + +345 +00:19:08,715 --> 00:19:10,918 +please refer to +our Right to Left guidelines + +346 +00:19:10,951 --> 00:19:14,154 +in the Human Interface Guidelines. + +347 +00:19:14,188 --> 00:19:17,291 +Today I talked about Designing in Arabic, + +348 +00:19:17,324 --> 00:19:22,095 +including the impact that the language +can have on the UI directionality. + +349 +00:19:22,129 --> 00:19:23,931 +And in the typography section, + +350 +00:19:23,964 --> 00:19:27,034 +I gave an introduction +about the Arabic script, + +351 +00:19:27,067 --> 00:19:32,172 +the typefaces, +and some Arabic UI type considerations. + +352 +00:19:32,206 --> 00:19:34,374 +I also talked about iconography + +353 +00:19:34,408 --> 00:19:37,578 +and how it can +transform your app experience, + +354 +00:19:37,611 --> 00:19:40,714 +the Arabic numerals in its both forms, + +355 +00:19:40,747 --> 00:19:42,749 +and the Right to Left Guidelines. + +356 +00:19:43,917 --> 00:19:47,421 +And if you would like to even dive deeper +into how to get all of this right + +357 +00:19:47,454 --> 00:19:49,556 +from a development perspective, + +358 +00:19:49,590 --> 00:19:53,660 +you can also check this year's session +"Get it Right (to Left)". + +359 +00:19:53,694 --> 00:19:56,096 +I hope all of this gave you guidance + +360 +00:19:56,129 --> 00:19:57,831 +and pointed you at the right tools + +361 +00:19:57,865 --> 00:20:02,369 +to start designing or optimizing +your app to be used in the Arab world. + +362 +00:20:02,402 --> 00:20:06,673 +And I can't wait to use all the amazing +Arabic apps that you're going to build. + diff --git a/eng/2022 Session 10035 What's new in MapKit en.srt b/eng/2022 Session 10035 What's new in MapKit en.srt new file mode 100644 index 0000000..cc69258 --- /dev/null +++ b/eng/2022 Session 10035 What's new in MapKit en.srt @@ -0,0 +1,3289 @@ +1 +00:00:01,001 --> 00:00:07,007 +[spacey music] + +2 +00:00:09,309 --> 00:00:12,746 +Eric: Hello! +Welcome to WWDC! + +3 +00:00:12,779 --> 00:00:13,947 +My name is Eric. + +4 +00:00:13,981 --> 00:00:16,250 +I'm an engineer from the Maps team. + +5 +00:00:16,283 --> 00:00:19,786 +Today, I'm joined by my colleague Yingxiu, +and together, + +6 +00:00:19,820 --> 00:00:21,922 +we're going to explore what's new +in MapKit. + +7 +00:00:23,357 --> 00:00:27,427 +It's been 3 years since Apple +Maps introduced its all-new map + +8 +00:00:27,461 --> 00:00:29,663 +and its immersive Look Around experience. + +9 +00:00:31,031 --> 00:00:33,367 +Originally launched with U.S. support, + +10 +00:00:33,400 --> 00:00:36,837 +the all-new map and Look Around coverage +has been expanding since + +11 +00:00:36,870 --> 00:00:38,405 +and now includes Canada, + +12 +00:00:38,438 --> 00:00:41,341 +many European countries, Japan, and more. + +13 +00:00:42,910 --> 00:00:45,712 +Last year, Apple Maps took things +to the next level + +14 +00:00:45,746 --> 00:00:49,983 +by introducing 3D City Experiences, +featuring turn lanes, + +15 +00:00:50,017 --> 00:00:51,885 +crosswalks, bike lanes, + +16 +00:00:51,919 --> 00:00:55,589 +and beautifully handcrafted 3D landmarks +like the Ferry Building. + +17 +00:00:57,457 --> 00:00:59,526 +The additional detail of the map +allows you + +18 +00:00:59,560 --> 00:01:01,728 +to provide context and precision + +19 +00:01:01,762 --> 00:01:04,264 +that was never before possible, + +20 +00:01:04,298 --> 00:01:06,633 +and the addition of 3D terrain elevation + +21 +00:01:06,667 --> 00:01:09,469 +provides a level of realism +like no other map! + +22 +00:01:10,704 --> 00:01:13,674 +This year, MapKit brings +the latest innovations + +23 +00:01:13,707 --> 00:01:15,576 +from Apple Maps to your apps, + +24 +00:01:15,609 --> 00:01:19,012 +letting your users explore the world +in amazing details. + +25 +00:01:20,247 --> 00:01:24,484 +In this talk, we'll be covering +several new MapKit features. + +26 +00:01:24,518 --> 00:01:27,721 +First, we'll talk about adopting +the all-new map + +27 +00:01:27,754 --> 00:01:30,858 +and fully leveraging it using +the Map Configuration API. + +28 +00:01:31,992 --> 00:01:35,863 +Next, we'll cover various improvements +we made to our overlay APIs + +29 +00:01:35,896 --> 00:01:39,533 +to allow for a seamless integration +of your content with the map. + +30 +00:01:40,834 --> 00:01:43,136 +We'll also discuss +our new blend modes support + +31 +00:01:43,170 --> 00:01:47,040 +and show how you can make use of it +to further enhance the presentation + +32 +00:01:47,074 --> 00:01:49,510 +of your map content. + +33 +00:01:49,543 --> 00:01:53,113 +Then, we'll cover ways of making your maps +more interactive + +34 +00:01:53,146 --> 00:01:56,450 +by adopting our brand-new +Selectable Map Features API. + +35 +00:01:57,384 --> 00:02:01,154 +And finally, we'll cover integrating +the immersive Look Around experience + +36 +00:02:01,188 --> 00:02:03,190 +directly into your applications. + +37 +00:02:04,124 --> 00:02:08,395 +We've got a lot to cover, so buckle up +as we head for our first topic: + +38 +00:02:08,428 --> 00:02:12,332 +adopting the all-new map +and using the Map Configuration API. + +39 +00:02:13,600 --> 00:02:16,170 +Adopting the all-new map in your iOS, + +40 +00:02:16,203 --> 00:02:18,505 +macOS, or tvOS application + +41 +00:02:18,539 --> 00:02:20,307 +couldn't be easier. + +42 +00:02:20,340 --> 00:02:23,610 +Simply recompile your app +with the new SDK, + +43 +00:02:23,644 --> 00:02:27,347 +and it will be automatically opted in +to the all-new Apple map, + +44 +00:02:27,381 --> 00:02:30,083 +including the 3D City Experience, +where available. + +45 +00:02:30,717 --> 00:02:34,588 +For many applications, a simple recompile +is all that's needed. + +46 +00:02:36,089 --> 00:02:39,059 +Of course, there may be situations +where you need more control + +47 +00:02:39,092 --> 00:02:40,727 +over the presentation of the map. + +48 +00:02:41,495 --> 00:02:46,033 +In iOS 15, the way you configure the map +is through various properties + +49 +00:02:46,066 --> 00:02:47,267 +on MKMapView. + +50 +00:02:48,202 --> 00:02:53,173 +In iOS 16, however, +we're soft deprecating those properties, + +51 +00:02:53,207 --> 00:02:56,376 +and we're introducing +our new Map Configuration API + +52 +00:02:56,410 --> 00:02:57,344 +as a replacement. + +53 +00:02:58,779 --> 00:03:03,984 +MKMapConfiguration is the central class +of the new Map Configuration API. + +54 +00:03:04,017 --> 00:03:07,454 +MKMapConfiguration is +an abstract base class + +55 +00:03:07,487 --> 00:03:09,489 +with three concrete subclasses. + +56 +00:03:11,058 --> 00:03:16,296 +The imagery map configuration is used +to present satellite-style imagery. + +57 +00:03:16,330 --> 00:03:20,167 +The hybrid map configuration is used +to present an imagery-based map + +58 +00:03:20,200 --> 00:03:24,505 +with added map features such +as road labels and points of interest. + +59 +00:03:26,006 --> 00:03:30,644 +The standard map configuration is used +to present a fully graphics-based map. + +60 +00:03:31,411 --> 00:03:35,549 +These three map configurations may sound +familiar to you, as they're similar + +61 +00:03:35,582 --> 00:03:37,251 +to our existing map types. + +62 +00:03:40,153 --> 00:03:44,691 +The base map configuration class +supports an elevationStyle property, + +63 +00:03:44,725 --> 00:03:47,294 +which can be either flat or realistic. + +64 +00:03:48,562 --> 00:03:52,533 +A flat elevation style means +that the ground appears flat. + +65 +00:03:52,566 --> 00:03:57,137 +Roads, including +bridges and overpasses, also appear flat. + +66 +00:03:57,171 --> 00:03:59,673 +Flat is the default elevation style. + +67 +00:04:00,741 --> 00:04:03,110 +A realistic elevation style means + +68 +00:04:03,143 --> 00:04:06,346 +that the ground terrain reproduces +the real-world elevation + +69 +00:04:06,380 --> 00:04:08,782 +such as hills and mountains. + +70 +00:04:08,815 --> 00:04:12,553 +Roads are depicted +with realistic elevation details. + +71 +00:04:13,754 --> 00:04:17,591 +Now let's take a closer look +at the map configuration subclasses. + +72 +00:04:18,825 --> 00:04:22,496 +The imagery map configuration +only shows satellite imagery + +73 +00:04:22,529 --> 00:04:26,667 +with no additional map features, +so it doesn't have any other properties. + +74 +00:04:28,068 --> 00:04:31,104 +The hybrid map configuration +has additional properties + +75 +00:04:31,138 --> 00:04:33,907 +to control filtering +of point of interest categories + +76 +00:04:33,941 --> 00:04:35,909 +and whether to show traffic or not. + +77 +00:04:37,911 --> 00:04:41,949 +The standard map configuration supports +an emphasisStyle property + +78 +00:04:41,982 --> 00:04:44,051 +which can be either default or muted. + +79 +00:04:45,485 --> 00:04:48,755 +As the name implies, this is +the default emphasis style + +80 +00:04:48,789 --> 00:04:50,324 +unless otherwise stated. + +81 +00:04:51,458 --> 00:04:55,762 +The muted emphasis style softens +the contrasts of the map details, + +82 +00:04:55,796 --> 00:04:59,633 +allowing you to bring more attention +to additional graphical information + +83 +00:04:59,666 --> 00:05:02,102 +you might want to show on top. + +84 +00:05:02,903 --> 00:05:06,540 +The standard map configuration +also has additional properties + +85 +00:05:06,573 --> 00:05:09,343 +to control filtering +of point of interest categories + +86 +00:05:09,376 --> 00:05:11,278 +and whether to show traffic or not. + +87 +00:05:12,379 --> 00:05:15,682 +And that sums up +the available map configuration classes + +88 +00:05:15,716 --> 00:05:16,850 +and their properties. + +89 +00:05:18,819 --> 00:05:21,421 +This new API ensures +you can only configure + +90 +00:05:21,455 --> 00:05:23,824 +supported combinations of options. + +91 +00:05:23,857 --> 00:05:27,661 +It also allows you to change +the map configuration atomically. + +92 +00:05:27,694 --> 00:05:31,331 +We think this is a great improvement +over our existing APIs. + +93 +00:05:32,799 --> 00:05:34,535 +To recap what we just discussed, + +94 +00:05:34,568 --> 00:05:36,370 +here's a table that shows +the correspondence + +95 +00:05:36,403 --> 00:05:40,507 +between the new map configuration classes +and the MKMapType property. + +96 +00:05:42,109 --> 00:05:44,611 +The all-new map +with the 3D City Experience + +97 +00:05:44,645 --> 00:05:46,914 +requires compatible hardware. + +98 +00:05:46,947 --> 00:05:53,020 +On iOS, the new map support requires +A12-based iPhones and iPads or later. + +99 +00:05:53,053 --> 00:05:55,722 +On macOS, the new map support requires + +100 +00:05:55,756 --> 00:05:57,958 +any M1-based computer or later. + +101 +00:05:59,593 --> 00:06:02,829 +In areas where the 3D City Experience is +not available, + +102 +00:06:02,863 --> 00:06:06,266 +the map will automatically fall back +to present the all-new map + +103 +00:06:06,300 --> 00:06:08,001 +with a flat elevation. + +104 +00:06:09,236 --> 00:06:11,772 +On all other devices, the all-new map + +105 +00:06:11,805 --> 00:06:13,807 +will be presented with a flat elevation. + +106 +00:06:15,375 --> 00:06:19,813 +On M1 Macs, Xcode allows you to simulate +both experiences + +107 +00:06:19,847 --> 00:06:21,815 +simply by changing the OS version. + +108 +00:06:22,683 --> 00:06:24,585 +We encourage you to try out both, + +109 +00:06:24,618 --> 00:06:26,753 +to ensure that your app looks great + +110 +00:06:26,787 --> 00:06:28,055 +on all devices! + +111 +00:06:29,423 --> 00:06:31,925 +The 3D City Experience is available + +112 +00:06:31,959 --> 00:06:34,361 +in many metropolitan areas +around the world. + +113 +00:06:35,362 --> 00:06:38,098 +We're continuously adding new cities +to this list, + +114 +00:06:38,131 --> 00:06:41,735 +so I encourage you to check out +the 3D City Experience section + +115 +00:06:41,768 --> 00:06:45,272 +on the feature availability website linked +in the session notes. + +116 +00:06:45,305 --> 00:06:48,942 +This concludes our section on adopting +the all-new map + +117 +00:06:48,976 --> 00:06:51,011 +and using the Map Configuration API. + +118 +00:06:52,646 --> 00:06:55,115 +Now let's dive into overlays. + +119 +00:06:55,148 --> 00:06:59,520 +MapKit has supported overlays +with several styling options for years. + +120 +00:06:59,553 --> 00:07:01,021 +In iOS 16, + +121 +00:07:01,054 --> 00:07:03,257 +we are improving our existing APIs + +122 +00:07:03,290 --> 00:07:06,894 +to allow your overlays to seamlessly +integrate with the map. + +123 +00:07:06,927 --> 00:07:10,163 +Let's start with a quick recap +of overlay levels. + +124 +00:07:11,632 --> 00:07:14,434 +Overlays can be rendered +at two different levels: + +125 +00:07:14,468 --> 00:07:17,004 +above roads and above labels. + +126 +00:07:17,037 --> 00:07:20,374 +You can specify the rendering level +at insertion time + +127 +00:07:20,407 --> 00:07:23,477 +using one of MapKit's +many overlay insertion functions. + +128 +00:07:24,778 --> 00:07:28,949 +Above labels renders your overlay +above everything, including labels. + +129 +00:07:29,716 --> 00:07:32,953 +Since labels +provide important context information, + +130 +00:07:32,986 --> 00:07:36,857 +we encourage you to only use above labels +in those rare cases + +131 +00:07:36,890 --> 00:07:40,661 +where you don't want your data +to interact with the map at all. + +132 +00:07:40,694 --> 00:07:43,263 +If what you're trying to achieve +is for your content + +133 +00:07:43,297 --> 00:07:45,065 +to stand out against the map, + +134 +00:07:45,098 --> 00:07:48,202 +you might want to consider +using the muted map emphasis + +135 +00:07:48,235 --> 00:07:50,938 +or the blend modes, +which we'll talk about later. + +136 +00:07:52,840 --> 00:07:56,610 +Above roads means the overlay +will be shown on top of the terrain, + +137 +00:07:56,643 --> 00:08:00,080 +including roads, land cover, +or bodies of water. + +138 +00:08:00,113 --> 00:08:04,418 +It will, however, be shown below labels +and, to some degree, + +139 +00:08:04,451 --> 00:08:06,119 +trees and buildings. + +140 +00:08:06,153 --> 00:08:07,888 +More on that in a second. + +141 +00:08:07,921 --> 00:08:10,991 +Above roads will be +the new default mode in iOS 16. + +142 +00:08:11,925 --> 00:08:16,230 +Next, let's talk about a new feature +we're introducing in iOS 16 + +143 +00:08:16,263 --> 00:08:17,698 +called transparent buildings. + +144 +00:08:18,832 --> 00:08:21,101 +Regardless of whether your overlay level + +145 +00:08:21,134 --> 00:08:23,504 +is above roads or above labels, + +146 +00:08:23,537 --> 00:08:26,373 +your overlay will always be rendered +on top of buildings + +147 +00:08:26,406 --> 00:08:28,475 +when viewed top-down with no pitch. + +148 +00:08:28,509 --> 00:08:30,711 +However, we've made some improvements + +149 +00:08:30,744 --> 00:08:32,846 +to the experience when using above roads + +150 +00:08:32,880 --> 00:08:34,648 +in combination with a pitched map. + +151 +00:08:35,883 --> 00:08:38,318 +Ground objects such as trees and buildings + +152 +00:08:38,352 --> 00:08:41,088 +are now automatically rendered +with transparency + +153 +00:08:41,121 --> 00:08:43,023 +when appearing above overlays, + +154 +00:08:43,056 --> 00:08:45,592 +so as not to fully obscure them. + +155 +00:08:45,626 --> 00:08:49,730 +The alpha value varies +with the map's pitch angle. + +156 +00:08:49,763 --> 00:08:54,067 +If we revert to showing the map top-down +with a 0º pitch angle, + +157 +00:08:54,101 --> 00:08:57,371 +colliding ground objects effectively +disappear from view, + +158 +00:08:57,404 --> 00:08:59,306 +leaving your overlays fully visible. + +159 +00:09:00,607 --> 00:09:04,811 +Transparent buildings also work +for semi-transparent overlays. + +160 +00:09:04,845 --> 00:09:07,748 +The alpha value of the overlay +will be added to combine + +161 +00:09:07,781 --> 00:09:11,218 +with the alpha value +of the transparent buildings. + +162 +00:09:11,251 --> 00:09:13,921 +There's one more change +we're making to overlays. + +163 +00:09:14,855 --> 00:09:17,024 +When adding an overlay to a map + +164 +00:09:17,057 --> 00:09:18,458 +with realistic terrain, + +165 +00:09:18,492 --> 00:09:22,996 +MapKit will automatically transition +the map to a flat representation. + +166 +00:09:23,030 --> 00:09:26,266 +The map will automatically go back +to realistic when you remove + +167 +00:09:26,300 --> 00:09:27,668 +the last overlay. + +168 +00:09:28,635 --> 00:09:32,506 +One notable exception to this rule +are overlays sourced + +169 +00:09:32,539 --> 00:09:35,609 +through MapKit's directions API. + +170 +00:09:35,642 --> 00:09:39,012 +Those overlays +automatically follow the terrain. + +171 +00:09:39,680 --> 00:09:42,282 +And with that, I'm going +to hand it over to Yingxiu. + +172 +00:09:42,316 --> 00:09:45,452 +Yingxiu: Thank you, Eric. +Hi! I'm Yingxiu. + +173 +00:09:45,485 --> 00:09:47,387 +I'm an engineer on the Maps team. + +174 +00:09:47,421 --> 00:09:50,224 +I'm going to demonstrate +our new MapKit features + +175 +00:09:50,257 --> 00:09:54,094 +and show you how easy it is to build +beautiful map experiences + +176 +00:09:54,127 --> 00:09:56,363 +with our updated APIs. + +177 +00:09:56,396 --> 00:09:57,865 +I will be using our sample app, + +178 +00:09:57,898 --> 00:09:59,499 +which helps users rent scooters + +179 +00:09:59,533 --> 00:10:01,168 +to tour San Francisco. + +180 +00:10:04,571 --> 00:10:06,740 +Our app offers a number of features, + +181 +00:10:06,773 --> 00:10:09,109 +as shown by the rows in this table view. + +182 +00:10:10,077 --> 00:10:14,915 +"Operating Area" allows the user +to see where they can take our scooters. + +183 +00:10:14,948 --> 00:10:19,753 +"Ride" takes the user on a tour +across the Golden Gate Bridge. + +184 +00:10:19,786 --> 00:10:22,689 +"Explore" gives the user +an interactive map + +185 +00:10:22,723 --> 00:10:25,425 +of downtown San Francisco, +which they can use + +186 +00:10:25,459 --> 00:10:28,028 +to explore attractions +near the waterfront. + +187 +00:10:28,662 --> 00:10:32,833 +"Highlights" offers a closer look +at must-see places. + +188 +00:10:33,534 --> 00:10:36,870 +We will implement or upgrade +these features throughout this session. + +189 +00:10:36,904 --> 00:10:38,572 +Let's get started! + +190 +00:10:42,576 --> 00:10:45,412 +First, I'm going to use +the Operating Area feature + +191 +00:10:45,445 --> 00:10:49,316 +to demonstrate how easy it is +to adopt the all-new map, + +192 +00:10:49,349 --> 00:10:50,784 +and then I'll show you + +193 +00:10:50,817 --> 00:10:53,453 +some of the improvements we have made +to overlays. + +194 +00:10:54,254 --> 00:10:57,457 +I already have the project opened +in Xcode. + +195 +00:10:57,491 --> 00:11:00,294 +Let's compile it with iOS 16 SDK + +196 +00:11:00,327 --> 00:11:01,862 +and see how it looks. + +197 +00:11:05,399 --> 00:11:06,333 +Here we are! + +198 +00:11:06,366 --> 00:11:09,403 +We are already opted into the all-new map. + +199 +00:11:09,436 --> 00:11:11,471 +We get a map view with rich details, + +200 +00:11:11,505 --> 00:11:13,540 +including these beautiful lighting + +201 +00:11:13,574 --> 00:11:16,476 +and hill-shading effects on the terrain. + +202 +00:11:16,510 --> 00:11:20,581 +As we zoom in, +you will see buildings, trees... + +203 +00:11:26,053 --> 00:11:27,087 +…and landmarks. + +204 +00:11:29,089 --> 00:11:33,093 +Now let's add a polygon overlay +to visualize our operating area. + +205 +00:11:37,164 --> 00:11:41,869 +Here I already have the polygon data ready +for the operating area. + +206 +00:11:41,902 --> 00:11:47,140 +When the view is loaded, we'll first set +the region and the camera boundary. + +207 +00:11:47,174 --> 00:11:49,309 +Make sure we look at the correct area. + +208 +00:11:50,544 --> 00:11:52,913 +Then, we simply add the overlay. + +209 +00:11:53,914 --> 00:11:55,782 +Let's build it and check it out! + +210 +00:11:57,818 --> 00:12:01,522 +The polygon overlay is opaque +when viewed straight down. + +211 +00:12:01,555 --> 00:12:08,362 +As we zoom in and pitch, + +212 +00:12:08,395 --> 00:12:09,897 +the buildings begin to show, + +213 +00:12:09,930 --> 00:12:13,700 +with the transparency increasing +as we pitch further. + +214 +00:12:13,734 --> 00:12:18,238 +This effect is only available +when using the overlay level AboveRoads. + +215 +00:12:18,272 --> 00:12:21,141 +If you want to leverage +transparent buildings and trees, + +216 +00:12:21,175 --> 00:12:23,510 +make sure you choose +the correct overlay level. + +217 +00:12:25,412 --> 00:12:28,916 +The overlay looks great, +but I'd like the map to show through + +218 +00:12:28,949 --> 00:12:30,651 +even when not pitched. + +219 +00:12:30,684 --> 00:12:33,287 +Let's go back to the code +and make it semi-transparent. + +220 +00:12:37,324 --> 00:12:39,693 +Let's change alpha to 0.8. + +221 +00:12:44,131 --> 00:12:45,999 +Now we have a transparent overlay + +222 +00:12:46,033 --> 00:12:49,736 +and I can see roads and buildings +even when not pitched. + +223 +00:12:49,770 --> 00:12:53,240 +If I zoom in and pitch the map, + +224 +00:12:53,273 --> 00:12:55,642 +I still get an increase in transparency. + +225 +00:12:55,676 --> 00:12:57,411 +This looks great! + +226 +00:12:57,444 --> 00:12:59,680 +That's it for polygon overlays. + +227 +00:12:59,713 --> 00:13:03,417 +Next, I want to show you +how to integrate realistic terrain + +228 +00:13:03,450 --> 00:13:05,719 +and show adding an elevated route line. + +229 +00:13:05,752 --> 00:13:08,355 +This will complete our "Ride" feature, + +230 +00:13:08,388 --> 00:13:10,657 +a tour across the Golden Gate Bridge. + +231 +00:13:14,962 --> 00:13:17,831 +Let's start by configuring the map view. + +232 +00:13:17,865 --> 00:13:20,334 +We can change the elevation style in code, + +233 +00:13:20,367 --> 00:13:23,303 +or we can just open +the Interface Builder inspector + +234 +00:13:23,337 --> 00:13:24,605 +on the right-hand side. + +235 +00:13:26,874 --> 00:13:30,110 +Here are the available map view +configuration settings. + +236 +00:13:30,143 --> 00:13:32,579 +Let's select elevation realistic. + +237 +00:13:34,214 --> 00:13:36,016 +Next, let's work on the route. + +238 +00:13:38,752 --> 00:13:41,121 +For this feature, we want to show a route + +239 +00:13:41,154 --> 00:13:44,324 +when the user toggles +the Show Route switch. + +240 +00:13:44,358 --> 00:13:47,761 +We will also animate the camera +to focus on the route. + +241 +00:13:52,599 --> 00:13:56,069 +Since we want to show a route +across the Golden Gate Bridge, + +242 +00:13:56,103 --> 00:13:59,239 +we'll use the Presidio Park entry +as the start point + +243 +00:13:59,273 --> 00:14:01,909 +and the Battery Spencer as the end point. + +244 +00:14:05,112 --> 00:14:08,015 +When the map view is loaded, +we'll create annotations + +245 +00:14:08,048 --> 00:14:10,651 +to mark the start and destination points. + +246 +00:14:16,557 --> 00:14:18,759 +Set coordinates and title, + +247 +00:14:18,792 --> 00:14:21,428 +append it to the annotation array, + +248 +00:14:21,461 --> 00:14:23,497 +then add them to the map view. + +249 +00:14:31,839 --> 00:14:34,308 +Now, we can focus on the route polyline. + +250 +00:14:37,211 --> 00:14:39,580 +While normal overlays will flatten the +map, + +251 +00:14:39,613 --> 00:14:42,249 +the polyline returned +by MapKit's Directions API + +252 +00:14:42,282 --> 00:14:44,284 +will preserve the realistic terrain. + +253 +00:14:45,986 --> 00:14:50,490 +In this action function, once the switch +is turned on, + +254 +00:14:50,524 --> 00:14:54,027 +we'll create place marks +with the coordinates defined above. + +255 +00:14:57,998 --> 00:15:02,736 +Then we create a direction request, + +256 +00:15:02,769 --> 00:15:06,206 +with source and destination. + +257 +00:15:06,240 --> 00:15:08,675 +Finally, we request the directions. + +258 +00:15:11,812 --> 00:15:13,847 +If the fetch operation succeeds, + +259 +00:15:13,881 --> 00:15:16,483 +simply add the route polyline +as an overlay. + +260 +00:15:21,889 --> 00:15:22,723 +All right. + +261 +00:15:22,756 --> 00:15:24,725 +Let's compile it and see how it looks! + +262 +00:15:29,830 --> 00:15:33,100 +You'll notice the annotations are +automatically upgraded + +263 +00:15:33,133 --> 00:15:35,135 +to the new gradient look. + +264 +00:15:35,169 --> 00:15:38,572 +They are also seamlessly displayed +at the correct elevation. + +265 +00:15:38,605 --> 00:15:40,474 +Once I toggle show route, + +266 +00:15:40,507 --> 00:15:41,909 +the camera will pitch. + +267 +00:15:41,942 --> 00:15:43,977 +Then, you can get a better view of them. + +268 +00:15:46,079 --> 00:15:48,415 +As you can see, the route line follows + +269 +00:15:48,448 --> 00:15:50,083 +the elevated terrain. + +270 +00:15:50,117 --> 00:15:51,518 +This comes in handy + +271 +00:15:51,552 --> 00:15:54,121 +when navigating complex intersections. + +272 +00:15:54,154 --> 00:15:57,157 +It also follows the road +across the bridge. + +273 +00:15:57,191 --> 00:15:59,159 +Also, notice how the route + +274 +00:15:59,193 --> 00:16:01,261 +subtly shows through the bridge pillars. + +275 +00:16:03,830 --> 00:16:06,733 +Finally, when the map is really pitched, + +276 +00:16:06,767 --> 00:16:08,302 +you can see the route show + +277 +00:16:08,335 --> 00:16:11,004 +through the trees +that stand in front of it. + +278 +00:16:11,038 --> 00:16:14,575 +Well, look, the route line color +is washed out here, + +279 +00:16:14,608 --> 00:16:16,176 +where there is no trees. + +280 +00:16:16,210 --> 00:16:18,779 +Let's zoom out and check +what's happening here. + +281 +00:16:22,850 --> 00:16:26,153 +All right, this part of the route goes +through a tunnel, + +282 +00:16:26,186 --> 00:16:28,555 +so we get transparency for it. + +283 +00:16:28,589 --> 00:16:32,259 +ElevationRealisticStyle +and the elevated route line feature + +284 +00:16:32,292 --> 00:16:35,662 +are supported on A12-based iOS devices. + +285 +00:16:35,696 --> 00:16:39,166 +If I run the same app +on an older iOS device, + +286 +00:16:39,199 --> 00:16:44,872 +I will automatically get a 2D route +on a 2D map.. + +287 +00:16:44,905 --> 00:16:47,174 +It's that simple to add an +elevated route line + +288 +00:16:47,207 --> 00:16:49,409 +to a 3D city experience map! + +289 +00:16:49,443 --> 00:16:51,945 +With that, I hand it back to Eric. + +290 +00:16:51,979 --> 00:16:53,247 +Eric: Thanks, Yingxiu! + +291 +00:16:53,280 --> 00:16:55,249 +That scooter ride across +the Golden Gate Bridge + +292 +00:16:55,282 --> 00:16:57,384 +looks absolutely stunning! + +293 +00:16:57,417 --> 00:17:00,821 +So that's just some of what's new +with MapKit overlays. + +294 +00:17:02,556 --> 00:17:05,459 +Next up, let's talk about blend modes. + +295 +00:17:05,492 --> 00:17:09,730 +This new API gives you more control +over the look and feel of your overlays + +296 +00:17:09,763 --> 00:17:12,833 +and unlocks a whole range +of new creative possibilities. + +297 +00:17:14,067 --> 00:17:16,103 +I'm sure many of you are already familiar + +298 +00:17:16,136 --> 00:17:18,705 +with blend modes from photo editing apps + +299 +00:17:18,739 --> 00:17:21,275 +or Apple's CoreGraphics API. + +300 +00:17:21,308 --> 00:17:23,177 +During a blend operation, + +301 +00:17:23,210 --> 00:17:25,078 +two graphical layers are combined + +302 +00:17:25,112 --> 00:17:26,647 +following a set of equations + +303 +00:17:26,680 --> 00:17:28,815 +specified by the blend mode. + +304 +00:17:28,849 --> 00:17:32,486 +Now, let's look at an example +of how we might use blend modes + +305 +00:17:32,519 --> 00:17:34,121 +in a MapKit context. + +306 +00:17:35,522 --> 00:17:38,458 +In this scenario, I want to highlight +the area + +307 +00:17:38,492 --> 00:17:41,128 +of the Presidio National Park +in San Francisco + +308 +00:17:41,161 --> 00:17:43,096 +in the center of this map. + +309 +00:17:43,130 --> 00:17:45,599 +First, I'm going to create an overlay + +310 +00:17:45,632 --> 00:17:47,568 +covering my entire map area, + +311 +00:17:47,601 --> 00:17:50,003 +with a cutout in the shape +of the Presidio. + +312 +00:17:51,338 --> 00:17:53,273 +I'm not using any blend modes yet. + +313 +00:17:53,307 --> 00:17:55,442 +This is just a plain overlay, + +314 +00:17:55,475 --> 00:17:58,011 +in the shape of a big square doughnut. + +315 +00:17:58,045 --> 00:18:00,747 +Next, I assign a hue blend mode + +316 +00:18:00,781 --> 00:18:04,384 +to the overlay, with a gray fill. + +317 +00:18:04,418 --> 00:18:06,320 +This desaturates the map + +318 +00:18:06,353 --> 00:18:08,288 +outside of the Presidio. + +319 +00:18:08,322 --> 00:18:11,091 +Next, I'm going to duplicate that overlay + +320 +00:18:11,124 --> 00:18:13,460 +and assign it a hard light blend mode, + +321 +00:18:13,493 --> 00:18:14,928 +with a dark gray fill. + +322 +00:18:16,263 --> 00:18:18,732 +This has the effect of darkening the area + +323 +00:18:18,765 --> 00:18:20,567 +around the Presidio. + +324 +00:18:20,601 --> 00:18:22,236 +This already looks nice, + +325 +00:18:22,269 --> 00:18:23,937 +but lets add another overlay. + +326 +00:18:23,971 --> 00:18:27,641 +This time, we'll add an overlay +in the shape of the Presidio + +327 +00:18:27,674 --> 00:18:29,810 +and assign a saturation blend mode, + +328 +00:18:29,843 --> 00:18:31,311 +with a yellow fill. + +329 +00:18:32,279 --> 00:18:36,316 +Whoa, that's not exactly the effect +I was aiming for here. + +330 +00:18:36,350 --> 00:18:38,886 +The colors are way too bright. + +331 +00:18:38,919 --> 00:18:40,687 +Let's try something else. + +332 +00:18:40,721 --> 00:18:43,924 +We'll apply a color burn blend mode +with a gray fill. + +333 +00:18:44,992 --> 00:18:47,327 +There, that's a little more subtle. + +334 +00:18:47,361 --> 00:18:48,795 +Perfect! + +335 +00:18:48,829 --> 00:18:50,697 +Now, let's look at some code. + +336 +00:18:52,099 --> 00:18:54,434 +To enable the kind of effects we just saw, + +337 +00:18:54,468 --> 00:18:57,738 +we added a property to MKOverlayRenderer, + +338 +00:18:57,771 --> 00:18:59,573 +called blendMode. + +339 +00:18:59,606 --> 00:19:01,341 +All you need to do is set + +340 +00:19:01,375 --> 00:19:05,245 +the desired CoreGraphics blend mode +on the overlay renderer, and you're done! + +341 +00:19:06,113 --> 00:19:09,917 +As previously mentioned, blend modes +are order dependent. + +342 +00:19:09,950 --> 00:19:11,885 +The overlay at the bottom of the stack + +343 +00:19:11,919 --> 00:19:13,720 +is blended with the map, + +344 +00:19:13,754 --> 00:19:16,290 +the second-to-last overlay is then blended + +345 +00:19:16,323 --> 00:19:18,926 +with the result +of the previous blend operation, + +346 +00:19:18,959 --> 00:19:21,128 +and so on. + +347 +00:19:21,161 --> 00:19:23,764 +In MapKit, the order of overlays is + +348 +00:19:23,797 --> 00:19:25,799 +determined at insertion time. + +349 +00:19:25,832 --> 00:19:29,403 +You can use either absolute +or relative positioning + +350 +00:19:29,436 --> 00:19:33,106 +using one of MKMapView's +many overlay insertion functions. + +351 +00:19:35,576 --> 00:19:38,278 +MapKit supports a wide range +of blend modes + +352 +00:19:38,312 --> 00:19:41,114 +and we can't possibly cover +all of them today. + +353 +00:19:41,148 --> 00:19:42,783 +I encourage you try them out. + +354 +00:19:43,317 --> 00:19:45,752 +And that's it for blend modes! + +355 +00:19:45,786 --> 00:19:47,087 +As you've just seen, + +356 +00:19:47,120 --> 00:19:49,456 +blend modes are a powerful tool to control + +357 +00:19:49,489 --> 00:19:51,258 +the styling of your map. + +358 +00:19:51,291 --> 00:19:54,328 +You can use it +to highlight geographical areas, + +359 +00:19:54,361 --> 00:19:57,397 +deemphasize the map to make +your content stand out, + +360 +00:19:57,431 --> 00:19:58,599 +and so much more. + +361 +00:20:00,133 --> 00:20:03,637 +Next up, let's talk +about Selectable Map Features! + +362 +00:20:03,670 --> 00:20:06,006 +This has been a highly requested feature, + +363 +00:20:06,039 --> 00:20:07,941 +and I'm really excited to show it to you! + +364 +00:20:09,943 --> 00:20:12,846 +If you are using MapKit in your app today, + +365 +00:20:12,880 --> 00:20:16,683 +you're likely leveraging annotations +to show the location of cities, + +366 +00:20:16,717 --> 00:20:19,353 +points of interest, or physical objects. + +367 +00:20:20,554 --> 00:20:22,890 +Unless you're using POI filtering, + +368 +00:20:22,923 --> 00:20:25,893 +you are placing those annotations +on a map which already contains + +369 +00:20:25,926 --> 00:20:28,862 +a number of similar annotations +provided by Apple. + +370 +00:20:29,830 --> 00:20:32,866 +Up until now, your users +could only interact + +371 +00:20:32,900 --> 00:20:34,868 +with the annotations you provided. + +372 +00:20:35,636 --> 00:20:39,506 +In iOS 16, we're going to change that. + +373 +00:20:39,540 --> 00:20:42,643 +Using our new Selectable Map Features API, + +374 +00:20:42,676 --> 00:20:47,181 +you'll now have the option to let +your users select features on the map. + +375 +00:20:48,715 --> 00:20:51,952 +Selectable map features include +points of interest, + +376 +00:20:51,985 --> 00:20:55,689 +such as stores, restaurants, +and landmarks; + +377 +00:20:55,722 --> 00:20:59,059 +territories, such as cities and states; + +378 +00:20:59,092 --> 00:21:02,629 +and physical features, +such as mountain ranges and lakes. + +379 +00:21:03,897 --> 00:21:07,000 +To adopt Selectable Map Features +in your app, + +380 +00:21:07,034 --> 00:21:10,737 +all you need to do is go +through a few simple steps. + +381 +00:21:10,771 --> 00:21:14,441 +First, configure which feature types +should be selectable. + +382 +00:21:14,474 --> 00:21:17,277 +As we just saw, +there are three main feature types, + +383 +00:21:17,311 --> 00:21:19,279 +and it might not make sense +for all of them + +384 +00:21:19,313 --> 00:21:21,782 +to be interactive in the context +of your app. + +385 +00:21:22,616 --> 00:21:24,318 +For points-of-interest features, + +386 +00:21:24,351 --> 00:21:27,087 +you can also use our existing filter API + +387 +00:21:27,120 --> 00:21:30,490 +to further restrict which +points-of-interest categories can appear + +388 +00:21:30,524 --> 00:21:31,959 +and therefore be selectable. + +389 +00:21:33,627 --> 00:21:36,964 +Second, implement the MKMapView +delegate callbacks + +390 +00:21:36,997 --> 00:21:38,932 +to handle selection events. + +391 +00:21:38,966 --> 00:21:42,369 +You are free to handle these events +however you choose. + +392 +00:21:42,402 --> 00:21:45,372 +You might want to control how selected +features appear, + +393 +00:21:45,405 --> 00:21:47,674 +or you might want to show +some additional UI + +394 +00:21:47,708 --> 00:21:49,510 +in response to the selection events. + +395 +00:21:50,944 --> 00:21:53,480 +Third, you'll want to request and display + +396 +00:21:53,514 --> 00:21:54,982 +additional place information + +397 +00:21:55,015 --> 00:21:56,917 +in your application's user interface. + +398 +00:21:57,618 --> 00:21:59,586 +The information embedded +in the map feature + +399 +00:21:59,620 --> 00:22:01,522 +is limited to what you see on screen. + +400 +00:22:02,756 --> 00:22:06,059 +To give users more context about the place +they selected, + +401 +00:22:06,093 --> 00:22:08,428 +you'll need to request +additional information. + +402 +00:22:09,196 --> 00:22:11,632 +Let me walk you through the new APIs + +403 +00:22:11,665 --> 00:22:13,267 +for each of these steps. + +404 +00:22:14,768 --> 00:22:17,137 +We'll start with configuring +which map features + +405 +00:22:17,171 --> 00:22:18,839 +should be selectable + +406 +00:22:18,872 --> 00:22:22,376 +using the new selectableMapFeatures +property. + +407 +00:22:23,977 --> 00:22:27,714 +You can choose any combination +of points of interest, territories, + +408 +00:22:27,748 --> 00:22:29,850 +and physical features. + +409 +00:22:29,883 --> 00:22:32,619 +Once you have configured +the selectable map features + +410 +00:22:32,653 --> 00:22:35,389 +and the user taps one of those features, + +411 +00:22:35,422 --> 00:22:38,258 +you'll start receiving +some new delegate callbacks + +412 +00:22:38,292 --> 00:22:41,695 +allowing you to customize +the selection behavior. + +413 +00:22:41,728 --> 00:22:45,732 +The first callback you'll get is +the new didSelect annotation callback. + +414 +00:22:46,333 --> 00:22:50,404 +This callback is a great opportunity +for you to request additional data + +415 +00:22:50,437 --> 00:22:51,839 +about the map item + +416 +00:22:51,872 --> 00:22:54,842 +using the new request API we'll cover +in a moment. + +417 +00:22:56,210 --> 00:23:00,914 +The second callback you'll get is +the existing viewFor annotation callback. + +418 +00:23:00,948 --> 00:23:03,183 +This is where you can customize the view + +419 +00:23:03,217 --> 00:23:06,053 +which will be shown +for the selected state. + +420 +00:23:06,086 --> 00:23:07,955 +While this is existing API, + +421 +00:23:07,988 --> 00:23:10,991 +we've added a new type of annotation class + +422 +00:23:11,024 --> 00:23:13,760 +called MapFeatureAnnotation. + +423 +00:23:13,794 --> 00:23:16,463 +This class will be passed to view +for annotation + +424 +00:23:16,496 --> 00:23:18,699 +when the user selects a map feature. + +425 +00:23:19,800 --> 00:23:23,003 +MapFeatureAnnotation +has a number of properties. + +426 +00:23:23,036 --> 00:23:25,138 +You can inspect the FeatureType property + +427 +00:23:25,172 --> 00:23:26,640 +to determine whether the map feature + +428 +00:23:26,673 --> 00:23:28,041 +is a point of interest, + +429 +00:23:28,075 --> 00:23:30,344 +a territory, or a physical feature. + +430 +00:23:31,345 --> 00:23:33,814 +If the map feature is a point of interest, + +431 +00:23:33,847 --> 00:23:35,616 +the pointOfInterestCategory property + +432 +00:23:35,649 --> 00:23:37,818 +will let you know what its category is, + +433 +00:23:37,851 --> 00:23:40,287 +and the iconStyle property will let you + +434 +00:23:40,320 --> 00:23:42,756 +obtain additional information +about the icon, + +435 +00:23:42,789 --> 00:23:45,125 +such as its background color + +436 +00:23:45,158 --> 00:23:46,727 +and the icon image itself. + +437 +00:23:48,262 --> 00:23:51,598 +Let's look at an example of how +to customize your annotation view + +438 +00:23:51,632 --> 00:23:53,600 +using the viewFor annotation callback. + +439 +00:23:55,102 --> 00:23:57,738 +If you want to achieve +the same selection style + +440 +00:23:57,771 --> 00:24:00,007 +as the Maps app, all you have to do + +441 +00:24:00,040 --> 00:24:01,108 +is return nil. + +442 +00:24:02,309 --> 00:24:04,845 +If you want to customize +the selection style, + +443 +00:24:04,878 --> 00:24:06,947 +you can return an annotationView, + +444 +00:24:06,980 --> 00:24:09,783 +the same way you would +for your own annotations. + +445 +00:24:10,784 --> 00:24:13,554 +The markerAnnotationView +is a great option. + +446 +00:24:13,587 --> 00:24:15,689 +It will give you +the same balloon-style shape + +447 +00:24:15,722 --> 00:24:17,024 +as the Maps app, + +448 +00:24:17,057 --> 00:24:18,525 +a gradient treatment, + +449 +00:24:18,559 --> 00:24:21,428 +and it allows you to choose your own color +or icon. + +450 +00:24:22,429 --> 00:24:26,333 +In my example here, I've chosen to use +the same image obtained + +451 +00:24:26,366 --> 00:24:29,870 +from the feature's icon style +and only change the color + +452 +00:24:29,903 --> 00:24:31,872 +to match the application's tint color. + +453 +00:24:33,040 --> 00:24:35,008 +If you want to go fully custom, + +454 +00:24:35,042 --> 00:24:37,444 +you can provide +any annotation view subclass + +455 +00:24:37,477 --> 00:24:39,079 +that you create. + +456 +00:24:40,047 --> 00:24:42,950 +As you just saw, you can use +the feature annotation + +457 +00:24:42,983 --> 00:24:45,986 +to retrieve visual information +about the selected feature. + +458 +00:24:46,854 --> 00:24:51,925 +By passing the feature annotation on to +our new MKMapItemRequest API, + +459 +00:24:51,959 --> 00:24:55,028 +you can also retrieve a map item +for the selected feature. + +460 +00:24:57,231 --> 00:25:00,901 +This map item contains additional metadata +about the place, + +461 +00:25:00,934 --> 00:25:06,139 +such as an address, a name, +a phone number, and a URL. + +462 +00:25:07,508 --> 00:25:11,144 +The map item also provides a function +to punch out to the Maps app + +463 +00:25:11,178 --> 00:25:13,881 +if your users want to see +additional metadata + +464 +00:25:13,914 --> 00:25:15,983 +which isn't available through MapKit. + +465 +00:25:17,184 --> 00:25:20,220 +And now, I will again hand it over +to Yingxiu. + +466 +00:25:20,254 --> 00:25:21,688 +Yingxiu: Thank you, Eric. + +467 +00:25:22,956 --> 00:25:26,159 +Eric just walked you through +the Selectable Map Features. + +468 +00:25:26,193 --> 00:25:30,063 +I'm going to show you +how easy it is to leverage that API. + +469 +00:25:33,000 --> 00:25:36,470 +For that, we will implement +our Explore feature. + +470 +00:25:36,503 --> 00:25:40,107 +We want our users to be able to explore +some interesting places + +471 +00:25:40,140 --> 00:25:41,575 +near the waterfront. + +472 +00:25:41,608 --> 00:25:43,076 +If they tap on POIs, + +473 +00:25:43,110 --> 00:25:44,978 +annotations should show up. + +474 +00:25:45,012 --> 00:25:48,315 +We will perform a camera animation +to the tapped location, + +475 +00:25:48,348 --> 00:25:50,617 +and show an info card from the bottom. + +476 +00:25:52,452 --> 00:25:55,422 +First, let's filter points of interest +on the map + +477 +00:25:55,455 --> 00:25:59,059 +and remove the categories +which are irrelevant to our tour. + +478 +00:26:00,661 --> 00:26:03,230 +Aside from applying the filter in code, + +479 +00:26:03,263 --> 00:26:07,167 +we can also apply it +in Interface Builder inspector. + +480 +00:26:07,201 --> 00:26:09,069 +Let's select the map view, + +481 +00:26:09,102 --> 00:26:11,371 +and go to inspector +on the right-hand side. + +482 +00:26:13,307 --> 00:26:16,243 +Here, we will do an exclusion filter. + +483 +00:26:18,278 --> 00:26:21,048 +I'll select the categories +that we don't want, + +484 +00:26:21,081 --> 00:26:25,319 +for example: airport, + +485 +00:26:25,352 --> 00:26:31,925 +car rental, hospital, and laundry. + +486 +00:26:33,227 --> 00:26:36,163 +Now we should only have +desired point of interests + +487 +00:26:36,196 --> 00:26:37,664 +on the map view. + +488 +00:26:42,436 --> 00:26:45,639 +It's very simple to enable +Selectable Map Features. + +489 +00:26:45,672 --> 00:26:47,908 +All we need to do is to specify + +490 +00:26:47,941 --> 00:26:50,911 +an option set +of desired selectable features. + +491 +00:26:54,848 --> 00:26:59,152 +In the scope of this sample app, +we'll just use points of interest, + +492 +00:26:59,186 --> 00:27:00,921 +but keep in mind, we also support + +493 +00:27:00,954 --> 00:27:02,489 +selectable physical features + +494 +00:27:02,523 --> 00:27:03,991 +and territories. + +495 +00:27:10,397 --> 00:27:15,169 +We can use the existing delegate method +mapView viewForAnnotation + +496 +00:27:15,202 --> 00:27:17,404 +to create a view +for the feature annotation. + +497 +00:27:19,039 --> 00:27:22,242 +Here, we'll just return nil for now... + +498 +00:27:26,280 --> 00:27:29,883 +To use the default gradient annotation +offered by MapKit. + +499 +00:27:30,651 --> 00:27:32,986 +We'll come back to customize it later. + +500 +00:27:35,756 --> 00:27:38,158 +If an annotation is on Selected state, + +501 +00:27:38,192 --> 00:27:41,962 +we will be informed +through the new delegate method + +502 +00:27:41,995 --> 00:27:44,598 +mapView didSelectAnnotation. + +503 +00:27:46,200 --> 00:27:49,636 +Let's use this function +to perform a camera animation + +504 +00:27:49,670 --> 00:27:51,839 +and zoom in on the selected feature. + +505 +00:27:53,640 --> 00:27:58,111 +First, let's cast the annotation +to featureAnnotation, + +506 +00:27:58,145 --> 00:28:00,814 +then create a map item request with it. + +507 +00:28:04,751 --> 00:28:08,856 +This is a new API to fetch +additional place informations + +508 +00:28:08,889 --> 00:28:10,691 +with feature annotations. + +509 +00:28:11,992 --> 00:28:13,994 +Let's issue the request. + +510 +00:28:19,099 --> 00:28:21,802 +Once the fetch operation succeeds, + +511 +00:28:21,835 --> 00:28:24,204 +we will animate to the map item. + +512 +00:28:27,007 --> 00:28:29,543 +When the camera animation has completed, + +513 +00:28:29,576 --> 00:28:31,879 +we will get details from the feature item + +514 +00:28:31,912 --> 00:28:33,647 +and show them on an info card. + +515 +00:28:34,781 --> 00:28:36,984 +Compile it and see how it looks. + +516 +00:28:41,555 --> 00:28:44,892 +Let's check out some interesting places +at the waterfront. + +517 +00:28:47,261 --> 00:28:49,229 +The gradient annotations show up. + +518 +00:28:49,263 --> 00:28:51,932 +The camera animates to the tab location. + +519 +00:28:51,965 --> 00:28:53,667 +Then the info card shows up. + +520 +00:28:54,601 --> 00:28:55,802 +This is a museum. + +521 +00:28:55,836 --> 00:28:57,037 +Here is the URL. + +522 +00:28:57,070 --> 00:28:59,039 +We can check it out if we're interested. + +523 +00:28:59,773 --> 00:29:01,708 +And we get the full address. + +524 +00:29:03,443 --> 00:29:05,279 +If it's a landmark, + +525 +00:29:05,312 --> 00:29:07,748 +we will get this beautiful iconography. + +526 +00:29:14,655 --> 00:29:16,223 +Now let's go back to the code + +527 +00:29:16,256 --> 00:29:18,125 +and customize our annotation + +528 +00:29:18,158 --> 00:29:19,293 +for the selection state. + +529 +00:29:22,663 --> 00:29:25,899 +Instead of nil, let's create +a MarkerAnnotationView. + +530 +00:29:26,700 --> 00:29:29,703 +We'll cast the annotation +to featureAnnotation first, + +531 +00:29:29,736 --> 00:29:33,674 +so we can use specific data from it +to customize the view. + +532 +00:29:34,875 --> 00:29:37,978 +Let's tint the annotation +with a purple-ish color + +533 +00:29:38,011 --> 00:29:41,181 +so it's in line +with our corporate identity. + +534 +00:29:42,950 --> 00:29:45,552 +We can also customize +the annotation glyph. + +535 +00:29:47,054 --> 00:29:51,158 +SelectedGlyphImage +is for annotations on Selected state. + +536 +00:29:52,659 --> 00:29:54,895 +GlyphImage is smaller. + +537 +00:29:54,928 --> 00:29:58,332 +It is the glyph for annotations +on Unselected state. + +538 +00:29:59,333 --> 00:30:02,202 +We recommend assigning them the same glyph + +539 +00:30:02,236 --> 00:30:06,573 +for a smooth transition +from the Unselected to Selected state. + +540 +00:30:08,242 --> 00:30:12,346 +Let's use the icon style image we got +from the featureAnnotation. + +541 +00:30:13,881 --> 00:30:17,951 +MKIconStyle is a new class in iOS16. + +542 +00:30:17,985 --> 00:30:22,689 +It has the iconography and color info +of the selected POI. + +543 +00:30:22,723 --> 00:30:24,758 +Compile and see how it looks in the app. + +544 +00:30:29,696 --> 00:30:30,931 +Here you go! + +545 +00:30:30,964 --> 00:30:32,332 +Now we have an annotation + +546 +00:30:32,366 --> 00:30:34,334 +that matches our corporate colors, + +547 +00:30:34,368 --> 00:30:36,503 +but still uses Apple iconography. + +548 +00:30:40,374 --> 00:30:43,143 +That's how you can enable +selectable map features + +549 +00:30:43,177 --> 00:30:46,113 +and customize annotations +in your own apps! + +550 +00:30:46,146 --> 00:30:48,382 +With that, I hand it back to Eric. + +551 +00:30:49,183 --> 00:30:50,384 +Eric: Thanks, Yingxiu! + +552 +00:30:50,417 --> 00:30:53,554 +As you just saw, +the Selectable Map Features API + +553 +00:30:53,587 --> 00:30:55,689 +enables your users to interact +with the map + +554 +00:30:55,722 --> 00:30:57,424 +in a whole new way. + +555 +00:30:57,457 --> 00:30:59,359 +The MapFeatureAnnotation class, + +556 +00:30:59,393 --> 00:31:02,262 +in combination +with the MapView delegate callbacks, + +557 +00:31:02,296 --> 00:31:05,065 +allow you to customize +the selection look and feel, + +558 +00:31:05,098 --> 00:31:08,235 +while the MapItemRequest +allows you to resolve a feature + +559 +00:31:08,268 --> 00:31:10,437 +to a map item, giving you access + +560 +00:31:10,470 --> 00:31:11,705 +to additional information + +561 +00:31:11,738 --> 00:31:13,440 +about the selected map feature. + +562 +00:31:14,074 --> 00:31:16,977 +Next, let's talk about around Look Around! + +563 +00:31:18,245 --> 00:31:21,782 +The Maps app introduced Look Around +in iOS 13, + +564 +00:31:21,815 --> 00:31:24,117 +and people absolutely love it. + +565 +00:31:24,151 --> 00:31:25,319 +You can use Look Around + +566 +00:31:25,352 --> 00:31:27,621 +to get a real sense of a place. + +567 +00:31:27,654 --> 00:31:30,958 +Look Around imagery offers +an incredible level of detail, + +568 +00:31:30,991 --> 00:31:33,994 +leveraging 3D models to provide a level +of realism + +569 +00:31:34,027 --> 00:31:35,362 +like no other map. + +570 +00:31:37,331 --> 00:31:39,199 +Look Around is available in many places + +571 +00:31:39,233 --> 00:31:41,468 +around the world, including these cities, + +572 +00:31:41,502 --> 00:31:43,036 +and entire countries! + +573 +00:31:44,872 --> 00:31:47,908 +We're continuously adding support +for new regions, + +574 +00:31:47,941 --> 00:31:51,111 +so I again encourage you to check out +the Look Around section + +575 +00:31:51,144 --> 00:31:54,715 +on the feature availability website +linked in the session notes. + +576 +00:31:54,748 --> 00:31:58,919 +With iOS 16, we're bringing Look Around +to MapKit, + +577 +00:31:58,952 --> 00:32:01,889 +and adopting it only requires +three simple steps. + +578 +00:32:03,590 --> 00:32:05,692 +First, you'll need to check whether data + +579 +00:32:05,726 --> 00:32:07,694 +is available for the desired location. + +580 +00:32:08,362 --> 00:32:11,131 +Even if Look Around is available +in your target region, + +581 +00:32:11,164 --> 00:32:13,934 +not every location can be seen +from a street, + +582 +00:32:13,967 --> 00:32:15,736 +and therefore, Look Around imagery + +583 +00:32:15,769 --> 00:32:18,305 +might not always be available. + +584 +00:32:18,338 --> 00:32:21,475 +Once you've determined +whether Look Around data is available, + +585 +00:32:21,508 --> 00:32:22,910 +you'll need to pass that data on + +586 +00:32:22,943 --> 00:32:25,045 +to either the Look Around View Controller + +587 +00:32:25,078 --> 00:32:26,713 +or the Look Around Snapshotter. + +588 +00:32:27,848 --> 00:32:30,684 +And finally, if Look Around data +is available, + +589 +00:32:30,717 --> 00:32:32,386 +you'll want to update your app UI + +590 +00:32:32,419 --> 00:32:35,155 +to show the Look Around preview. + +591 +00:32:35,189 --> 00:32:37,791 +Let's take a look at the new APIs +you will be using + +592 +00:32:37,824 --> 00:32:39,826 +to accomplish those three simple tasks. + +593 +00:32:41,762 --> 00:32:44,631 +The first step in attempting to show +a Look Around preview + +594 +00:32:44,665 --> 00:32:46,800 +is to check for data availability. + +595 +00:32:46,834 --> 00:32:50,304 +For this, you'll need to create +a LookAroundSceneRequest, + +596 +00:32:50,337 --> 00:32:54,208 +which is a new class we're introducing +in iOS 16. + +597 +00:32:54,241 --> 00:32:57,845 +You can initialize a new instance +with either a coordinate + +598 +00:32:57,878 --> 00:32:59,313 +or a map item. + +599 +00:33:00,547 --> 00:33:03,984 +Next, you'll want to retrieve +its scene property. + +600 +00:33:04,017 --> 00:33:07,855 +This is an optional async property. + +601 +00:33:07,888 --> 00:33:11,525 +If data is available, you will get back +a scene instance. + +602 +00:33:11,558 --> 00:33:12,926 +If data is not available, + +603 +00:33:12,960 --> 00:33:15,128 +you will get back a nil instead. + +604 +00:33:15,162 --> 00:33:17,297 +And if there +was a problem with the request, + +605 +00:33:17,331 --> 00:33:18,765 +an error will be thrown. + +606 +00:33:20,200 --> 00:33:22,402 +The Look Around Scene is an opaque object + +607 +00:33:22,436 --> 00:33:23,871 +with no properties. + +608 +00:33:23,904 --> 00:33:25,205 +It acts as a token + +609 +00:33:25,239 --> 00:33:27,908 +that ensures the availability +of Look Around imagery + +610 +00:33:27,941 --> 00:33:29,510 +for a requested location. + +611 +00:33:30,978 --> 00:33:33,914 +To show an interactive preview +of the Look Around scene, + +612 +00:33:33,947 --> 00:33:35,883 +you simply pass the scene on + +613 +00:33:35,916 --> 00:33:38,652 +to a new +Look Around View Controller instance + +614 +00:33:38,685 --> 00:33:41,121 +as an init parameter + +615 +00:33:41,154 --> 00:33:43,857 +or assign it +to the read write scene property + +616 +00:33:43,891 --> 00:33:46,527 +of an existing instance. + +617 +00:33:46,560 --> 00:33:49,596 +Alternatively, if all you need +is a static image, + +618 +00:33:49,630 --> 00:33:51,331 +you can also pass the scene on + +619 +00:33:51,365 --> 00:33:53,934 +to a new Look Around View +Snapshotter instance + +620 +00:33:53,967 --> 00:33:55,702 +as an init parameter + +621 +00:33:55,736 --> 00:33:58,639 +and subsequently retrieve +its snapshot async property. + +622 +00:34:00,174 --> 00:34:01,942 +The Look Around view controller is + +623 +00:34:01,975 --> 00:34:04,311 +designed to make it as easy as possible + +624 +00:34:04,344 --> 00:34:07,814 +to embed a smaller static preview +of a Look Around image, + +625 +00:34:07,848 --> 00:34:09,683 +which the user can tap on to enter + +626 +00:34:09,716 --> 00:34:12,219 +a full-screen +Look Around interactive session. + +627 +00:34:13,687 --> 00:34:16,557 +And now, we'll hand it over to Yingxiu +once more + +628 +00:34:16,590 --> 00:34:20,160 +so that she can show us how easy +it really is to put it all together. + +629 +00:34:20,928 --> 00:34:22,930 +Yingxiu: Thank you, Eric. + +630 +00:34:22,963 --> 00:34:26,300 +Eric just showed us the immersive +Look Around experience support + +631 +00:34:26,333 --> 00:34:27,868 +coming to MapKit. + +632 +00:34:27,901 --> 00:34:30,370 +I'm going to show you +how straightforward it is + +633 +00:34:30,404 --> 00:34:32,472 +to integrate it in our sample app. + +634 +00:34:32,506 --> 00:34:36,743 +For that, we'll move on +to our last feature, Highlights. + +635 +00:34:36,777 --> 00:34:39,880 +Users can get a realistic view +of must-see places. + +636 +00:34:44,251 --> 00:34:47,254 +We already have a couple +of San Francisco landmark names + +637 +00:34:47,287 --> 00:34:49,723 +in the segment control bar +on top of the screen. + +638 +00:34:50,757 --> 00:34:52,359 +When users tap on one of them, + +639 +00:34:52,392 --> 00:34:54,728 +we want to perform a camera animation + +640 +00:34:54,761 --> 00:34:55,929 +to the tapped location. + +641 +00:34:57,030 --> 00:35:01,201 +We also want to show a Look around preview +at bottom left, + +642 +00:35:01,235 --> 00:35:03,637 +which our users can expand +to full screen. + +643 +00:35:04,404 --> 00:35:05,372 +Let's do it! + +644 +00:35:06,907 --> 00:35:08,909 +First we need to add a container view + +645 +00:35:08,942 --> 00:35:10,310 +for our Look Around preview. + +646 +00:35:22,723 --> 00:35:24,558 +Let's go to the size inspector. + +647 +00:35:27,661 --> 00:35:33,400 +Let's give it a position and size. + +648 +00:35:37,171 --> 00:35:39,373 +We want to hide this preview +at the beginning, + +649 +00:35:39,406 --> 00:35:45,512 +so let's open the attributes inspector +and check Hidden. + +650 +00:35:48,415 --> 00:35:51,518 +Next, we need to create +a Look Around view controller... + +651 +00:36:00,027 --> 00:36:02,362 +And embed it +to the container view. + +652 +00:36:09,303 --> 00:36:11,138 +Same as any other segue, + +653 +00:36:11,171 --> 00:36:13,207 +I need to give it an identifier. + +654 +00:36:15,008 --> 00:36:16,443 +Let's call it + +655 +00:36:16,476 --> 00:36:22,583 +"presentLookAroundEmbedded." + +656 +00:36:27,988 --> 00:36:30,057 +And import it to code so we can update +its visibility later. + +657 +00:36:44,071 --> 00:36:46,640 +Let's name it "preview." + +658 +00:36:53,547 --> 00:36:56,817 +Here, we already have +a LookAroundViewController declared. + +659 +00:36:56,850 --> 00:36:59,653 +We just need to grab +the instance in the prepare function. + +660 +00:37:04,391 --> 00:37:06,960 +Make sure the segue identifier is matched. + +661 +00:37:12,432 --> 00:37:16,170 +Then, in the segment control function, + +662 +00:37:16,203 --> 00:37:19,740 +we'll create a local search +with the landmark name. + +663 +00:37:24,511 --> 00:37:27,748 +If the request succeeds, +we will get a map item + +664 +00:37:27,781 --> 00:37:30,484 +which will be used +in the following camera animation + +665 +00:37:30,517 --> 00:37:32,452 +and Look Around scene retrieval. + +666 +00:37:35,589 --> 00:37:37,124 +For camera animation, + +667 +00:37:37,157 --> 00:37:40,861 +we first need to create a camera +with the new API. + +668 +00:37:44,665 --> 00:37:47,568 +MapCamera looking at map item. + +669 +00:37:47,601 --> 00:37:50,637 +Let's use map view frame size +for the view size, + +670 +00:37:50,671 --> 00:37:52,873 +and set allow pitch to true. + +671 +00:37:56,610 --> 00:37:59,179 +This will give us +a pitch view to landmarks + +672 +00:37:59,213 --> 00:38:01,248 +and a top-down view to other places. + +673 +00:38:02,482 --> 00:38:04,551 +Assign the new camera. +That's it. + +674 +00:38:07,120 --> 00:38:09,389 +Once the camera animation is completed, + +675 +00:38:09,423 --> 00:38:11,859 +we'll show its Look Around preview. + +676 +00:38:15,262 --> 00:38:18,432 +First, we need to determine +if the Look Around data + +677 +00:38:18,465 --> 00:38:20,801 +is available for this map item. + +678 +00:38:20,834 --> 00:38:22,536 +To do that, we need to use + +679 +00:38:22,569 --> 00:38:24,738 +the new LookAroundSceneRequest class. + +680 +00:38:25,339 --> 00:38:28,609 +Let's create our request +and pass in the map item. + +681 +00:38:29,610 --> 00:38:31,645 +Then perform the request. + +682 +00:38:36,650 --> 00:38:39,620 +If get scene request succeeds, + +683 +00:38:39,653 --> 00:38:40,787 +just assign the scene + +684 +00:38:40,821 --> 00:38:43,724 +to our LookAroundViewController. + +685 +00:38:43,757 --> 00:38:47,294 +If there is no error but we get nil +for the scene, + +686 +00:38:47,327 --> 00:38:51,331 +it means Look Around data +is not available at the request location. + +687 +00:38:53,567 --> 00:38:55,802 +Finally, don't forget to show the preview. + +688 +00:38:57,271 --> 00:38:59,106 +Let's see how it looks in the app! + +689 +00:39:04,144 --> 00:39:06,346 +Let's try with the Ferry Building. + +690 +00:39:08,582 --> 00:39:09,483 +There you go. + +691 +00:39:09,516 --> 00:39:11,919 +The Ferry Building is a landmark, +so we see + +692 +00:39:11,952 --> 00:39:14,254 +this fantastic curated camera framing + +693 +00:39:14,288 --> 00:39:16,056 +to a hero angle, + +694 +00:39:16,089 --> 00:39:17,891 +and the Look Around preview shows up. + +695 +00:39:18,825 --> 00:39:21,495 +Let's try with another place, Dragon Gate. + +696 +00:39:23,263 --> 00:39:25,098 +Dragon Gate is not a landmark, + +697 +00:39:25,132 --> 00:39:26,767 +so we get its top-down view. + +698 +00:39:29,469 --> 00:39:31,939 +Let's tap on the preview +to enter full screen. + +699 +00:39:34,174 --> 00:39:37,177 +It's an interactive view, +so I can navigate around. + +700 +00:39:42,015 --> 00:39:45,319 +I also get these beautiful icons +and labels for stores + +701 +00:39:45,352 --> 00:39:47,187 +in the Look Around full-screen view. + +702 +00:39:51,792 --> 00:39:54,795 +It's that easy to add +an immersive Look Around experience + +703 +00:39:54,828 --> 00:39:56,063 +in your own app. + +704 +00:39:56,096 --> 00:39:57,998 +With that, I hand it back to Eric. + +705 +00:39:58,699 --> 00:39:59,967 +Eric: Thank you, Yingxiu! + +706 +00:40:00,000 --> 00:40:01,768 +The Interface Builder support +for Look Around + +707 +00:40:01,802 --> 00:40:03,170 +makes it look so easy! + +708 +00:40:03,904 --> 00:40:06,573 +So we've covered a lot of topics today, + +709 +00:40:07,541 --> 00:40:09,810 +from the automatic adoption +of the all-new map + +710 +00:40:09,843 --> 00:40:12,112 +and the new Map Configuration API, + +711 +00:40:12,145 --> 00:40:14,448 +new behaviors and advances for overlays, + +712 +00:40:14,481 --> 00:40:17,417 +to all-new capabilities +like Selectable Map Features + +713 +00:40:17,451 --> 00:40:19,219 +and Look Around support. + +714 +00:40:19,253 --> 00:40:20,954 +We think you'll agree there's a lot here + +715 +00:40:20,988 --> 00:40:24,958 +you can use to take the map experience +in your app to the next level, + +716 +00:40:24,992 --> 00:40:27,160 +and we can't wait to see +what you'll accomplish with it. + +717 +00:40:28,028 --> 00:40:29,730 +Before I go, there's a few things + +718 +00:40:29,763 --> 00:40:30,898 +I'd like to leave you with. + +719 +00:40:32,332 --> 00:40:34,801 +As always, your feedback is instrumental + +720 +00:40:34,835 --> 00:40:37,838 +in helping us prioritize +what we focus on next, + +721 +00:40:37,871 --> 00:40:40,741 +so please be sure +to use the Feedback Assistant + +722 +00:40:40,774 --> 00:40:43,911 +to send us your bug reports +and the features you'd find + +723 +00:40:43,944 --> 00:40:45,279 +most helpful for your app. + +724 +00:40:46,980 --> 00:40:50,050 +In addition, we've updated +many of our existing samples + +725 +00:40:50,083 --> 00:40:52,319 +to incorporate what we just discussed. + +726 +00:40:52,352 --> 00:40:53,954 +We encourage you to check them out. + +727 +00:40:55,322 --> 00:40:58,792 +And finally, we've announced a new set +of REST APIs + +728 +00:40:58,825 --> 00:41:00,527 +you'll want to take a look at. + +729 +00:41:00,561 --> 00:41:02,796 +We think these APIs will be really useful + +730 +00:41:02,829 --> 00:41:05,332 +for those of you looking to move +common calls + +731 +00:41:05,365 --> 00:41:09,603 +for geocoding, ETA determination, +and more to your server. + +732 +00:41:10,904 --> 00:41:13,140 +For all the details, be sure to check out + +733 +00:41:13,173 --> 00:41:16,543 +the session titled +Meet Apple Maps Server APIs. + +734 +00:41:17,678 --> 00:41:20,814 +On behalf of the Maps team, +thanks for watching... + +735 +00:41:20,848 --> 00:41:22,850 +both: And have a great WWDC! + +736 +00:41:22,883 --> 00:41:25,853 +[spacey music] + diff --git a/eng/2022 Session 10037 Writing for interfaces en.srt b/eng/2022 Session 10037 Writing for interfaces en.srt new file mode 100644 index 0000000..4351822 --- /dev/null +++ b/eng/2022 Session 10037 Writing for interfaces en.srt @@ -0,0 +1,1964 @@ +1 +00:00:00,467 --> 00:00:06,473 +[spacey percussive music] + +2 +00:00:09,309 --> 00:00:12,379 +Kaely Coon: Hi, my name is Kaely Coon. + +3 +00:00:12,412 --> 00:00:13,747 +Jennifer Bush: +And I'm Jennifer Bush. + +4 +00:00:13,780 --> 00:00:16,350 +We're writers here at Apple, +working as part + +5 +00:00:16,383 --> 00:00:18,051 +of Human Interface design teams + +6 +00:00:18,085 --> 00:00:20,354 +across Apple products and services. + +7 +00:00:20,387 --> 00:00:24,024 +Today, we're going to share some tips +and best practices we've learned + +8 +00:00:24,057 --> 00:00:26,426 +that we hope will help you, +as developers, + +9 +00:00:26,460 --> 00:00:29,263 +approach writing for your app or game. + +10 +00:00:29,296 --> 00:00:33,100 +Kaely: Sometimes we're called +UX Writers or Content Designers, + +11 +00:00:33,133 --> 00:00:37,337 +and what we write can be +referred to as strings, copy, or text, + +12 +00:00:37,371 --> 00:00:41,742 +but essentially, it's our job +to design through the lens of language. + +13 +00:00:41,775 --> 00:00:46,280 +From the earliest days at Apple, +we wanted to design devices for everyone. + +14 +00:00:46,313 --> 00:00:49,416 +This meant they needed to be able +to communicate with anyone. + +15 +00:00:49,449 --> 00:00:51,852 +The first Macs were built +on the design principle + +16 +00:00:51,885 --> 00:00:55,422 +"what you see is what you get," +which includes the language. + +17 +00:00:55,455 --> 00:00:59,993 +Mac was built using simple language +that was easy to understand. + +18 +00:01:00,027 --> 00:01:03,864 +It focused on the person using it, +welcoming you in, + +19 +00:01:03,897 --> 00:01:07,000 +and now, across our products, +as soon as you turn them on, + +20 +00:01:07,034 --> 00:01:09,703 +we say "Hello." + +21 +00:01:09,736 --> 00:01:14,241 +Since those early days, we've continued +to be conversational in our interfaces. + +22 +00:01:14,274 --> 00:01:17,978 +When words work seamlessly with design, +you may not even notice them, + +23 +00:01:18,011 --> 00:01:21,782 +but they're pivotal to every part +of a user experience. + +24 +00:01:21,815 --> 00:01:24,651 +The words are there to help people +do what they want to do, + +25 +00:01:24,685 --> 00:01:29,423 +whether it's create a Memoji, +watch a movie, or find focus. + +26 +00:01:29,456 --> 00:01:32,593 +Let's take a look at an app +with the words removed. + +27 +00:01:32,626 --> 00:01:35,529 +Feels a bit empty, doesn't it? + +28 +00:01:35,562 --> 00:01:39,766 +In the Apple Music app, the visuals +give you a hint at what each section is, + +29 +00:01:39,800 --> 00:01:44,037 +but would you know the difference +between an album and a playlist? + +30 +00:01:44,071 --> 00:01:47,407 +How about how +to browse or listen to the radio? + +31 +00:01:47,441 --> 00:01:49,676 +Even if there aren't +a lot of words on a screen, + +32 +00:01:49,710 --> 00:01:54,047 +they help you understand what's expected, +and what to do next. + +33 +00:01:54,081 --> 00:01:57,985 +And if you were to come across an error, +they'd help you find your way. + +34 +00:01:58,018 --> 00:02:01,722 +Writing is about communicating, +which, of course, includes the words, + +35 +00:02:01,755 --> 00:02:05,225 +but it also includes the structure +of those words, + +36 +00:02:05,259 --> 00:02:09,997 +the timing in which they appear, +and the feeling people have reading them. + +37 +00:02:10,030 --> 00:02:13,967 +I mentioned how UX writers +designed through the lens of language. + +38 +00:02:14,001 --> 00:02:17,704 +Now, you may hear that and wonder, +"What does that even mean?" + +39 +00:02:17,738 --> 00:02:20,674 +It means the earlier you make writing +a part of the process + +40 +00:02:20,707 --> 00:02:22,176 +of designing your app, + +41 +00:02:22,209 --> 00:02:25,946 +the better the experience +will be for the people who use it. + +42 +00:02:25,979 --> 00:02:28,415 +In this session, +we'll walk you through the types of things + +43 +00:02:28,448 --> 00:02:31,552 +we consider as we develop +the words in our products. + +44 +00:02:31,585 --> 00:02:34,354 +They're not the only things +we think about when we write, + +45 +00:02:34,388 --> 00:02:36,757 +as there are often many considerations, + +46 +00:02:36,790 --> 00:02:38,825 +but we hope this provides +a useful framework + +47 +00:02:38,859 --> 00:02:41,361 +to keep in mind when writing for your app. + +48 +00:02:41,395 --> 00:02:46,500 +These are: Purpose, Anticipation, + +49 +00:02:46,533 --> 00:02:48,936 +Context, and Empathy. + +50 +00:02:50,170 --> 00:02:52,639 +You'll notice we've given +you an acronym of "PACE," + +51 +00:02:52,673 --> 00:02:55,142 +which is an important thing +to consider while writing. + +52 +00:02:56,143 --> 00:02:59,746 +Pace is about creating a natural flow +so you keep people engaged + +53 +00:02:59,780 --> 00:03:02,583 +and interested +in what you're telling them. + +54 +00:03:02,616 --> 00:03:08,088 +This means knowing what to say, +how to say it, and when. + +55 +00:03:08,121 --> 00:03:10,891 +Now, I'll hand it to Jen +to talk about Purpose. + +56 +00:03:12,626 --> 00:03:14,862 +Jen: As you develop the screens +in your app, + +57 +00:03:14,895 --> 00:03:18,799 +think about the most important thing +someone needs to know at that moment. + +58 +00:03:18,832 --> 00:03:22,402 +That's the purpose of your screen. +So how do you convey that? + +59 +00:03:24,771 --> 00:03:27,641 +First, consider information hierarchy, + +60 +00:03:27,674 --> 00:03:30,210 +or how you order the elements +on the screen. + +61 +00:03:30,244 --> 00:03:31,979 +Here's an example. + +62 +00:03:32,012 --> 00:03:34,982 +This is an intro screen +for a Messages flow. + +63 +00:03:35,015 --> 00:03:38,318 +You know the purpose because +it's right at the top: + +64 +00:03:38,352 --> 00:03:40,954 +"Share Your Name and Photo with Friends." + +65 +00:03:40,988 --> 00:03:42,856 +You probably noticed that first. + +66 +00:03:43,824 --> 00:03:48,695 +Next, you might have read the button +that says "Choose Name and Photo." + +67 +00:03:48,729 --> 00:03:52,332 +We know people don't always read +the text on screen in order, + +68 +00:03:52,366 --> 00:03:55,569 +so make sure your headers +and buttons are clear. + +69 +00:03:56,837 --> 00:04:00,707 +The rest of the text on the screen +still has an important purpose. + +70 +00:04:00,741 --> 00:04:04,044 +It tells you what you can customize, +and that you can choose + +71 +00:04:04,077 --> 00:04:08,582 +who you want to share this with, +but the text is a little smaller. + +72 +00:04:08,615 --> 00:04:13,387 +The information hierarchy helps +convey this screen's purpose. + +73 +00:04:13,420 --> 00:04:15,689 +Know what to leave out. + +74 +00:04:15,722 --> 00:04:20,027 +When you know the purpose of a screen, +you can make choices about which ideas + +75 +00:04:20,060 --> 00:04:22,930 +you want to keep +in, which you can take out, + +76 +00:04:22,963 --> 00:04:25,599 +or which ones you can move someplace else. + +77 +00:04:26,366 --> 00:04:28,635 +Here's a wireframe of a made-up version + +78 +00:04:28,669 --> 00:04:31,705 +of a temperature warning screen +for iPhone. + +79 +00:04:31,738 --> 00:04:36,577 +The title says "iPhone Needs +to Cool Down." That sounds clear. + +80 +00:04:36,610 --> 00:04:40,414 +But there's a lot more text, +and this screen is trying to do too much. + +81 +00:04:41,548 --> 00:04:43,951 +Think about the purpose of the screen. + +82 +00:04:43,984 --> 00:04:47,521 +Is it to tell you +why iPhone needs to cool down? + +83 +00:04:47,554 --> 00:04:52,793 +If so, it tells you that you might +be in the sun or using too many apps. + +84 +00:04:52,826 --> 00:04:56,396 +Maybe it's to tell you that +you can still make an emergency call. + +85 +00:04:56,430 --> 00:04:58,131 +That button is pretty large. + +86 +00:04:58,165 --> 00:05:02,503 +It almost looks like the screen +is suggesting it's necessary. + +87 +00:05:02,536 --> 00:05:05,606 +Let's look at what we went with. + +88 +00:05:05,639 --> 00:05:07,941 +This screen is much simpler. + +89 +00:05:09,643 --> 00:05:12,479 +The headline is just "Temperature." + +90 +00:05:12,513 --> 00:05:15,983 +Because the thermometer image +conveys that the temperature is high, + +91 +00:05:16,016 --> 00:05:18,485 +we don't need to say that explicitly. + +92 +00:05:18,519 --> 00:05:21,788 +And the single sentence tells you +that iPhone needs to cool down + +93 +00:05:21,822 --> 00:05:23,824 +before you can use it. + +94 +00:05:23,857 --> 00:05:26,293 +The button just says "Emergency," + +95 +00:05:26,326 --> 00:05:28,562 +so if you needed to make +an emergency call, + +96 +00:05:28,595 --> 00:05:29,963 +it would be clear what to do. + +97 +00:05:30,964 --> 00:05:34,368 +Rather than give too many details, +aim for simplicity. + +98 +00:05:34,401 --> 00:05:39,072 +You can tell people the purpose +of a screen; it's not a secret! + +99 +00:05:39,106 --> 00:05:42,743 +When introducing a new feature to someone, +for example, + +100 +00:05:42,776 --> 00:05:45,279 +tell them why it's there, +and why it's important. + +101 +00:05:45,312 --> 00:05:50,617 +In this intro screen with the header +"Wind Down Shortcuts," it tells you, + +102 +00:05:50,651 --> 00:05:51,852 +"Reducing screen time + +103 +00:05:51,885 --> 00:05:54,254 +is one of the best things you can do +before bed." + +104 +00:05:54,955 --> 00:05:57,591 +That's the purpose of this feature, +and now you know + +105 +00:05:57,624 --> 00:05:59,660 +why you might want to set it up. + +106 +00:05:59,693 --> 00:06:02,329 +Have a purpose for every screen. + +107 +00:06:02,362 --> 00:06:07,100 +When there are multiple steps in a flow, +define the purpose of the entire flow, + +108 +00:06:07,134 --> 00:06:09,570 +as well as each screen within it. + +109 +00:06:09,603 --> 00:06:14,141 +This can help keep screens brief +and reduce unnecessary steps. + +110 +00:06:14,174 --> 00:06:17,344 +At the start, +think about how to welcome people in + +111 +00:06:17,377 --> 00:06:19,146 +and teach them what they need to know. + +112 +00:06:20,147 --> 00:06:23,617 +Here are a few of the screens +in a flow designed to help you set up + +113 +00:06:23,650 --> 00:06:25,485 +Apple Cash for family sharing. + +114 +00:06:26,787 --> 00:06:29,656 +The text tells you who Apple Cash is for, + +115 +00:06:29,690 --> 00:06:33,260 +"family members who are under 18," +and what they can do with it, + +116 +00:06:33,293 --> 00:06:36,763 +"send and receive money and use +Apple Pay for purchases." + +117 +00:06:38,565 --> 00:06:41,602 +In this next step, +the purpose is to let you know + +118 +00:06:41,635 --> 00:06:46,740 +what you're going to do next, +verify your identity, + +119 +00:06:46,773 --> 00:06:50,811 +and give you some important information +about data and privacy. + +120 +00:06:50,844 --> 00:06:54,982 +This privacy information takes up +quite a bit of space on the screen, + +121 +00:06:55,015 --> 00:06:56,750 +but it's important. + +122 +00:06:56,783 --> 00:06:59,520 +Privacy is one of Apple's core values. + +123 +00:06:59,553 --> 00:07:01,088 +Think about your app's values + +124 +00:07:01,121 --> 00:07:03,190 +and make sure +they're represented throughout. + +125 +00:07:04,324 --> 00:07:07,995 +After you verify your identity, +and you're at the end of this flow, + +126 +00:07:08,028 --> 00:07:11,331 +you're still not quite ready +to start using the feature. + +127 +00:07:11,365 --> 00:07:14,201 +This "Almost Ready" screen tells you + +128 +00:07:14,234 --> 00:07:18,405 +that the "card activation may take a few +minutes," and to expect a notification. + +129 +00:07:19,506 --> 00:07:23,744 +And here it is! +Now you can send money with Apple Cash. + +130 +00:07:23,777 --> 00:07:27,581 +If you're having a hard time +deciding what to write on a screen, + +131 +00:07:27,614 --> 00:07:29,383 +go back to its purpose. + +132 +00:07:29,416 --> 00:07:34,488 +You can convey purpose +by considering information hierarchy, + +133 +00:07:34,521 --> 00:07:40,160 +knowing what to leave out, +and having a purpose for every screen. + +134 +00:07:40,194 --> 00:07:43,030 +Kaely: +Next, let's talk about Anticipation. + +135 +00:07:44,798 --> 00:07:48,869 +It helps to think of the words +in your app as part of a conversation. + +136 +00:07:48,902 --> 00:07:53,006 +A dialogue happens +between your app and whoever's using it. + +137 +00:07:53,040 --> 00:07:56,310 +In any good conversation, +there's a back and forth. + +138 +00:07:56,343 --> 00:08:00,981 +Sometimes you're listening, sometimes +you're talking, or asking questions. + +139 +00:08:01,014 --> 00:08:03,183 +Similarly, in your app, +you're anticipating + +140 +00:08:03,217 --> 00:08:05,419 +how to best communicate for each moment. + +141 +00:08:06,920 --> 00:08:10,224 +Let's say it's a weekend +and you've decided to sleep in, + +142 +00:08:10,257 --> 00:08:13,026 +so you go to the Clock app +to change your alarm. + +143 +00:08:13,060 --> 00:08:15,829 +You want to make sure you're changing +the alarm just for tomorrow, + +144 +00:08:15,863 --> 00:08:17,097 +not your whole schedule. + +145 +00:08:17,798 --> 00:08:20,934 +The action sheet asks, +"Would you like to apply this change + +146 +00:08:20,968 --> 00:08:22,669 +to all weekends in this schedule?" + +147 +00:08:23,504 --> 00:08:26,640 +The first selection is +to "Change Next Alarm Only," + +148 +00:08:26,673 --> 00:08:28,475 +anticipating that most of the time, + +149 +00:08:28,509 --> 00:08:32,379 +you're likely to just be thinking +about tomorrow's wake-up. + +150 +00:08:32,412 --> 00:08:35,682 +When you head to bed, +the lock screen says "Sleep Well" + +151 +00:08:35,716 --> 00:08:38,418 +and tells you that Sleep Focus is on. + +152 +00:08:38,452 --> 00:08:42,489 +The small button shows you +that your alarm is set for 8:30 AM. + +153 +00:08:42,523 --> 00:08:45,192 +"Sleep Well" has +a friendly and familiar tone, + +154 +00:08:45,225 --> 00:08:48,428 +and acknowledges +that you're going to sleep. + +155 +00:08:48,462 --> 00:08:50,731 +The next morning, if you get up +before the alarm + +156 +00:08:50,764 --> 00:08:52,499 +and start using your iPhone, + +157 +00:08:52,533 --> 00:08:55,903 +it anticipates that you might not want +that alarm to sound. + +158 +00:08:55,936 --> 00:08:58,805 +So it asks, "It looks like you're awake. + +159 +00:08:58,839 --> 00:09:01,808 +Would you like to turn off +your alarm and sleep mode?" + +160 +00:09:01,842 --> 00:09:06,180 +The conversation feels intuitive, like +your device is speaking to your needs. + +161 +00:09:06,213 --> 00:09:08,415 +When it comes to being conversational, + +162 +00:09:08,448 --> 00:09:11,985 +writers talk a lot about voice and tone. + +163 +00:09:12,019 --> 00:09:16,323 +Develop your app's voice first, +and then you can vary its tone. + +164 +00:09:16,356 --> 00:09:20,661 +Start by asking yourself: +what would it say and not say? + +165 +00:09:20,694 --> 00:09:24,031 +Are you developing a game +that's exciting and fun? + +166 +00:09:24,064 --> 00:09:27,968 +Is it a banking app that needs +to be secure and trustworthy? + +167 +00:09:28,001 --> 00:09:30,237 +Is it an app that's kid-friendly? + +168 +00:09:30,270 --> 00:09:33,073 +Think about who you're talking to +to help figure out the type + +169 +00:09:33,106 --> 00:09:35,209 +of vocabulary you'll use. + +170 +00:09:35,242 --> 00:09:37,945 +Make a list of commonly used terms. + +171 +00:09:37,978 --> 00:09:41,114 +This can help shape the voice +that you can also use for your website, + +172 +00:09:41,148 --> 00:09:43,283 +emails, and other communication. + +173 +00:09:43,317 --> 00:09:47,588 +Anticipating what people are going through +helps you consider your tone. + +174 +00:09:47,621 --> 00:09:51,491 +Here, Apple Watch says, +"It looks like you've taken a hard fall" + +175 +00:09:51,525 --> 00:09:54,494 +and if you're okay, +you can answer, "I'm okay." + +176 +00:09:54,528 --> 00:09:58,065 +The tone is calm and clear +in what could be a stressful moment. + +177 +00:09:58,098 --> 00:10:01,502 +Apple has one consistent voice +that you'll recognize + +178 +00:10:01,535 --> 00:10:03,737 +no matter which +of our devices you're using, + +179 +00:10:03,770 --> 00:10:07,307 +but our tone changes +depending on the situation. + +180 +00:10:07,341 --> 00:10:10,177 +Think of how your tone of voice +changes when you pick up the phone + +181 +00:10:10,210 --> 00:10:13,347 +and talk to your friend versus, +say, your bank. + +182 +00:10:14,047 --> 00:10:17,684 +Here, in the Activity app on Watch, +when you've set a Move streak, + +183 +00:10:17,718 --> 00:10:19,653 +it says, "You set a personal record + +184 +00:10:19,686 --> 00:10:22,923 +for your longest daily Move streak: +35 days!" + +185 +00:10:22,956 --> 00:10:27,060 +The tone is quite celebratory, +so it has an exclamation point. + +186 +00:10:27,094 --> 00:10:29,029 +Be careful how often +you use those, though. + +187 +00:10:29,062 --> 00:10:30,731 +They can look silly when they're frequent. + +188 +00:10:31,798 --> 00:10:35,969 +When writing for apps, +you always have to ask, "What comes next?" + +189 +00:10:36,003 --> 00:10:39,473 +Anticipating the next action +or question someone's going to have + +190 +00:10:39,506 --> 00:10:41,208 +will help you know what to say. + +191 +00:10:41,241 --> 00:10:44,578 +In the Breathe app on Apple Watch, +the instructions tell you + +192 +00:10:44,611 --> 00:10:47,347 +to "Be still, +and bring your attention to your breath." + +193 +00:10:48,348 --> 00:10:53,921 +You might not be sure how to start +doing that, so then it says, "Now inhale…" + +194 +00:10:53,954 --> 00:10:55,389 +"And exhale." + +195 +00:10:55,422 --> 00:11:00,460 +It answers your question of how to use it, +as the visuals reinforce the message, + +196 +00:11:00,494 --> 00:11:03,830 +and the watch vibrates +to help with timing. + +197 +00:11:03,864 --> 00:11:07,568 +And in this example from Maps, +when you're likely to be heading home, + +198 +00:11:07,601 --> 00:11:11,672 +you see a notification that tells you +about your commute: "8 minutes to Home" + +199 +00:11:11,705 --> 00:11:15,108 +and to "Take Audubon Ave, +traffic is light." + +200 +00:11:15,142 --> 00:11:19,446 +It helps you make a decision +by anticipating what you'll do next. + +201 +00:11:19,479 --> 00:11:22,049 +When design +and text work closely together, + +202 +00:11:22,082 --> 00:11:24,785 +an app feels seamless as you use it. + +203 +00:11:24,818 --> 00:11:27,387 +Anticipation is about thinking of your app + +204 +00:11:27,421 --> 00:11:29,523 +as being part of a conversation. + +205 +00:11:31,091 --> 00:11:35,329 +Develop your voice and vary your tone +according to the situation. + +206 +00:11:36,463 --> 00:11:40,133 +Then answer +the question of what comes next. + +207 +00:11:40,167 --> 00:11:43,670 +Speaking directly to people +in the right way is one way to take + +208 +00:11:43,704 --> 00:11:47,474 +your app experience from functional +to magical. + +209 +00:11:47,508 --> 00:11:51,378 +Jen: Next, we'll cover context. + +210 +00:11:51,411 --> 00:11:54,214 +Start by thinking outside the app. + +211 +00:11:54,248 --> 00:11:57,417 +When someone uses your app, +are they likely to be at home + +212 +00:11:57,451 --> 00:12:01,188 +in a quiet space, +or traveling at busy airport? + +213 +00:12:01,221 --> 00:12:04,892 +Are they occupied with something else, +like driving or cooking, + +214 +00:12:04,925 --> 00:12:07,561 +or can they give +your app their full attention? + +215 +00:12:07,594 --> 00:12:10,297 +This reminder shows up on your Apple Watch + +216 +00:12:10,330 --> 00:12:14,868 +if it notices that you're exercising, +but haven't started recording the workout. + +217 +00:12:14,902 --> 00:12:17,471 +The context is, you're on an outdoor walk, + +218 +00:12:17,504 --> 00:12:20,274 +so you don't want +to have to stop to read a lot. + +219 +00:12:20,307 --> 00:12:25,612 +There's one large button +clearly labeled "Record Outdoor Walk." + +220 +00:12:25,646 --> 00:12:30,217 +On the other hand, once you stop +exercising, your context has changed. + +221 +00:12:30,250 --> 00:12:32,519 +You can take in +a little bit more information, + +222 +00:12:32,553 --> 00:12:36,156 +like your total distance, +average pace, and active calories. + +223 +00:12:37,090 --> 00:12:40,861 +Here, when taking a panorama photo, +your focus is split + +224 +00:12:40,894 --> 00:12:44,298 +between the camera app and whatever +you're trying to photograph. + +225 +00:12:44,331 --> 00:12:47,668 +An on-screen arrow guides you +with instructions, + +226 +00:12:47,701 --> 00:12:50,771 +to "Move continuously +when taking a Panorama." + +227 +00:12:50,804 --> 00:12:55,242 +The words appear right below the arrow +because that's where you're looking. + +228 +00:12:56,143 --> 00:12:59,479 +Now let's talk +about how to write helpful alerts. + +229 +00:12:59,513 --> 00:13:03,217 +Chances are, your app has some alerts, +which are best used + +230 +00:13:03,250 --> 00:13:07,154 +when you want someone to confirm +an action or make a choice. + +231 +00:13:07,187 --> 00:13:12,292 +They are, by their context, interruptions, +so you want your alerts to be helpful + +232 +00:13:12,326 --> 00:13:13,560 +and clear. + +233 +00:13:14,261 --> 00:13:16,530 +In this alert, +you've opened the Weather app, + +234 +00:13:16,563 --> 00:13:19,867 +and it needs permission +to use your location. + +235 +00:13:19,900 --> 00:13:24,505 +Because it appears at the time +you open the app, it feels contextual. + +236 +00:13:24,538 --> 00:13:28,775 +The alert also gives you the context +in which the permission will be used: + +237 +00:13:28,809 --> 00:13:32,012 +to show local weather +and send relevant notifications. + +238 +00:13:34,581 --> 00:13:37,351 +Some alerts +might have a destructive action, + +239 +00:13:37,384 --> 00:13:41,788 +or something that can't be undone, +like removing a device from your account. + +240 +00:13:41,822 --> 00:13:44,024 +Let's take a closer look. + +241 +00:13:44,057 --> 00:13:46,960 +This alert asks the question +"Remove iPhone?" + +242 +00:13:47,895 --> 00:13:51,298 +And the buttons answer +with Remove or Cancel. + +243 +00:13:51,331 --> 00:13:55,135 +The context here is that someone +needs to make an important choice, + +244 +00:13:55,169 --> 00:13:58,005 +one in which they may lose information. + +245 +00:13:58,038 --> 00:14:03,110 +"Remove" is the destructive action, +so its button is red and on the left. + +246 +00:14:03,143 --> 00:14:06,246 +The "Cancel" button is on the right +and dismisses the alert + +247 +00:14:06,280 --> 00:14:07,681 +without taking any action. + +248 +00:14:08,515 --> 00:14:12,085 +But be careful how you use Cancel. + +249 +00:14:12,119 --> 00:14:15,756 +As you can see in this fake alert +for a made-up subscription, + +250 +00:14:15,789 --> 00:14:18,659 +the title of "Confirm Cancellation" + +251 +00:14:18,692 --> 00:14:21,228 +makes it hard to know which button +to choose, + +252 +00:14:21,261 --> 00:14:23,230 +the one on the left that says "Cancel" + +253 +00:14:23,263 --> 00:14:26,133 +or the one on the right +that says "Confirm." + +254 +00:14:26,166 --> 00:14:30,571 +The rest of the text reads, +"If you confirm and end this plan now, + +255 +00:14:30,604 --> 00:14:34,842 +you'll lose access on June 21, 2022." + +256 +00:14:34,875 --> 00:14:36,510 +This isn't giving much more help. + +257 +00:14:37,911 --> 00:14:40,914 +It's unclear whether you're ending +this plan now + +258 +00:14:40,948 --> 00:14:43,050 +or if you can still access it. + +259 +00:14:43,083 --> 00:14:45,619 +The phrase "this plan" is also vague, + +260 +00:14:45,652 --> 00:14:48,088 +especially if you have +more than one subscription. + +261 +00:14:49,890 --> 00:14:52,860 +With a few small changes, we can fix it. + +262 +00:14:52,893 --> 00:14:56,063 +This new title +of "Cancel Platinum Subscription?" + +263 +00:14:56,096 --> 00:14:59,466 +tells you which subscription +you're canceling and matches well + +264 +00:14:59,499 --> 00:15:03,570 +with the buttons labeled +Cancel Subscription or Keep Subscription. + +265 +00:15:04,471 --> 00:15:07,007 +The message body is brief, +telling you + +266 +00:15:07,040 --> 00:15:11,411 +that "You'll continue +to have access until June 21, 2022." + +267 +00:15:12,880 --> 00:15:14,414 +You'll notice that the buttons + +268 +00:15:14,448 --> 00:15:16,650 +aren't labeled Yes and No. + +269 +00:15:16,683 --> 00:15:19,953 +When writing for alerts, +it's always best to be specific + +270 +00:15:19,987 --> 00:15:22,689 +about the action +the buttons are going to take. + +271 +00:15:22,723 --> 00:15:25,859 +On this alert, if you only read +the button labels, + +272 +00:15:25,893 --> 00:15:28,061 +you would still understand +what you were choosing. + +273 +00:15:29,029 --> 00:15:33,166 +Another context for seeing an alert +is when something goes wrong. + +274 +00:15:33,200 --> 00:15:35,869 +Errors will often show up as alerts, +as shown + +275 +00:15:35,903 --> 00:15:38,138 +in this pretty unhelpful made-up example. + +276 +00:15:38,172 --> 00:15:43,777 +The title of "Oops! you can't do that" +doesn't say what it is you can't do. + +277 +00:15:43,810 --> 00:15:48,715 +You're unlikely to know +what the error code 1234567 means, + +278 +00:15:48,749 --> 00:15:51,718 +and the rest of the message, +"Sorry, bad input. + +279 +00:15:51,752 --> 00:15:54,888 +Please try again," +doesn't add any information. + +280 +00:15:54,922 --> 00:15:58,859 +And it's unclear why there +are two buttons, Okay and Cancel. + +281 +00:15:58,892 --> 00:16:01,528 +It seems like they'd both do +the same thing. + +282 +00:16:01,562 --> 00:16:05,899 +As for the tone of the message, +interjections like "oops!" or "uh-oh" + +283 +00:16:05,933 --> 00:16:07,534 +can sound patronizing, + +284 +00:16:07,568 --> 00:16:10,804 +and "please" and "sorry" +can sound insincere. + +285 +00:16:10,838 --> 00:16:12,606 +Use them sparingly. + +286 +00:16:14,241 --> 00:16:16,610 +Here's an example of a better message. + +287 +00:16:16,643 --> 00:16:19,580 +The title clearly tells you +there's a billing problem, + +288 +00:16:19,613 --> 00:16:22,783 +and the text explains +exactly what to do about it. + +289 +00:16:23,617 --> 00:16:26,053 +To continue +accessing your subscription, + +290 +00:16:26,086 --> 00:16:27,955 +add a new payment method. + +291 +00:16:27,988 --> 00:16:31,592 +The top button, Add Payment Method, +takes you right to where + +292 +00:16:31,625 --> 00:16:33,060 +you can fix the problem, + +293 +00:16:33,093 --> 00:16:35,262 +and the second button, Not Now, + +294 +00:16:35,295 --> 00:16:38,699 +lets you continue what you were doing +so you can come back later. + +295 +00:16:38,732 --> 00:16:42,703 +Now let's talk +about how to create useful empty states, + +296 +00:16:42,736 --> 00:16:46,073 +or places in your app +where there isn't any content. + +297 +00:16:46,106 --> 00:16:49,943 +Empty states can be times for education +or even celebration, + +298 +00:16:49,977 --> 00:16:52,913 +like when you complete the last item +on your to-do list, + +299 +00:16:52,946 --> 00:16:55,215 +but it depends on context. + +300 +00:16:55,249 --> 00:16:57,117 +Here's another example. + +301 +00:16:57,150 --> 00:16:58,952 +In this made up restaurant app, + +302 +00:16:58,986 --> 00:17:01,855 +you haven't saved anything +to your favorites. + +303 +00:17:01,889 --> 00:17:04,925 +It uses the title +"Nothing strike your fancy?" + +304 +00:17:04,958 --> 00:17:07,027 +which has a whimsical feeling. + +305 +00:17:07,060 --> 00:17:09,830 +That can work +if it matches the voice of your app, + +306 +00:17:09,863 --> 00:17:14,134 +but the idiom might not be +universally understood or translate well, + +307 +00:17:15,369 --> 00:17:16,603 +and the text, + +308 +00:17:16,637 --> 00:17:19,306 +"Please come back if you do find +something you want to eat," + +309 +00:17:19,339 --> 00:17:22,409 +doesn't give any useful guidance +or indication of how content + +310 +00:17:22,442 --> 00:17:23,510 +might show up here. + +311 +00:17:25,012 --> 00:17:28,081 +On the other hand, +the Apple Podcasts library + +312 +00:17:28,115 --> 00:17:31,618 +clearly tells you that you have +"No Saved Episodes." + +313 +00:17:32,586 --> 00:17:34,121 +It then explains that you can + +314 +00:17:34,154 --> 00:17:36,723 +"Save episodes +you want to listen to later, + +315 +00:17:36,757 --> 00:17:38,592 +and they'll show up here." + +316 +00:17:38,625 --> 00:17:40,194 +Much better. + +317 +00:17:40,227 --> 00:17:43,830 +So to recap, when thinking about context, + +318 +00:17:43,864 --> 00:17:45,566 +think outside of the app, + +319 +00:17:45,599 --> 00:17:48,268 +about what else people +might be doing while using it. + +320 +00:17:49,970 --> 00:17:54,374 +Write helpful alerts that are contextual +and offer clear choices. + +321 +00:17:56,043 --> 00:18:00,047 +And create useful empty states +by using an appropriate tone + +322 +00:18:00,080 --> 00:18:01,782 +and giving helpful guidance. + +323 +00:18:02,850 --> 00:18:05,419 +Kealy: Finally, let's talk about empathy. + +324 +00:18:07,487 --> 00:18:10,057 +In user experience writing, having empathy + +325 +00:18:10,090 --> 00:18:12,893 +means you should aim +to write for everyone. + +326 +00:18:12,926 --> 00:18:16,463 +Your app might have a specific audience, +like musicians, + +327 +00:18:16,496 --> 00:18:20,701 +gamers, or other developers, +and you should speak to them, + +328 +00:18:20,734 --> 00:18:23,403 +but you don't want leave anyone out. + +329 +00:18:23,437 --> 00:18:29,109 +It's best to use simple, plain language, +as idioms and humor can be misunderstood + +330 +00:18:29,142 --> 00:18:33,347 +or not translate, and some phrases +have meanings that exclude people. + +331 +00:18:35,015 --> 00:18:39,319 +A key aspect of empathy is +being responsive to localization needs. + +332 +00:18:39,353 --> 00:18:41,855 +Your app could be used +by people all over the world. + +333 +00:18:41,889 --> 00:18:44,057 +That's an exciting opportunity! + +334 +00:18:44,091 --> 00:18:48,362 +It also means your app needs to adapt +to a lot of languages and cultures. + +335 +00:18:49,730 --> 00:18:52,366 +When you translate +from one language to another, + +336 +00:18:52,399 --> 00:18:55,102 +words can get longer or shorter. + +337 +00:18:55,135 --> 00:18:58,038 +Some languages +require more vertical space. + +338 +00:18:58,071 --> 00:18:59,439 +Someone could be using your app + +339 +00:18:59,473 --> 00:19:01,742 +with a language that reads +from left to right, + +340 +00:19:01,775 --> 00:19:03,343 +or right to left. + +341 +00:19:03,377 --> 00:19:04,778 +Here's an example. + +342 +00:19:04,811 --> 00:19:08,815 +This is the confirmation you see +when changing languages on iPhone, + +343 +00:19:08,849 --> 00:19:10,884 +in this case, from English to Thai. + +344 +00:19:12,119 --> 00:19:14,121 +In English, the notification reads, + +345 +00:19:14,154 --> 00:19:17,224 +"Applying this setting will restart +your iPhone," + +346 +00:19:17,257 --> 00:19:19,993 +with the buttons +Change to Thai and Cancel. + +347 +00:19:21,128 --> 00:19:23,997 +In Thai, the text +at the top is a little longer, + +348 +00:19:24,031 --> 00:19:27,367 +and the space +accommodates the taller characters, + +349 +00:19:27,401 --> 00:19:30,270 +while in Dutch, the text is much longer. + +350 +00:19:30,304 --> 00:19:35,242 +The space at the top grows +so the text can continue to a second line. + +351 +00:19:35,275 --> 00:19:38,846 +And here's Hebrew, +a language that reads right to left. + +352 +00:19:40,414 --> 00:19:43,483 +There are also considerations +beyond just word length. + +353 +00:19:43,517 --> 00:19:44,718 +In U.S. English, + +354 +00:19:44,751 --> 00:19:47,054 +the calendar abbreviations +for the days of the week + +355 +00:19:47,087 --> 00:19:52,893 +are a single letter, M for Monday, +T for Tuesday, and so on. + +356 +00:19:54,361 --> 00:19:56,763 +But in Catalan, +the days of the week are abbreviated + +357 +00:19:56,797 --> 00:19:57,865 +to two letters-- + +358 +00:19:57,898 --> 00:20:01,101 +there isn't a single-letter variation-- + +359 +00:20:01,134 --> 00:20:04,605 +while in Arabic, the days of the week +are not abbreviated at all. + +360 +00:20:05,339 --> 00:20:08,842 +Your UI needs to be able to adapt +to those language changes. + +361 +00:20:10,010 --> 00:20:11,979 +Now let's talk about accessibility. + +362 +00:20:12,846 --> 00:20:15,916 +It's important to welcome +everyone into your app. + +363 +00:20:15,949 --> 00:20:18,685 +For your app to be used +by as many people as possible, + +364 +00:20:18,719 --> 00:20:22,055 +it needs to speak +to as many people as possible. + +365 +00:20:22,089 --> 00:20:25,759 +Someone who is blind or has low vision, +for example, may set their device + +366 +00:20:25,792 --> 00:20:30,030 +to display bold or larger text, +or use VoiceOver to navigate. + +367 +00:20:31,031 --> 00:20:35,035 +Pay attention to the language you use +for labeling the elements in your app. + +368 +00:20:35,068 --> 00:20:38,605 +Those words will be some people's +entire experience using it, + +369 +00:20:38,639 --> 00:20:40,974 +and you want +that experience to be well designed. + +370 +00:20:42,075 --> 00:20:45,679 +Every element needs to have thoughtful, +useful, descriptive text. + +371 +00:20:45,712 --> 00:20:48,849 +This includes not just +the Voice Over text for navigation items, + +372 +00:20:48,882 --> 00:20:54,922 +like links and buttons, but any symbols, +graphs, or images as well. + +373 +00:20:54,955 --> 00:20:58,559 +Let's look at how Voice Over +describes a few Memoji Stickers. + +374 +00:20:58,592 --> 00:21:02,329 +This first one is described +as "Person tilting head to the side + +375 +00:21:02,362 --> 00:21:05,532 +with hand beside mouth +as if sharing a secret." + +376 +00:21:05,566 --> 00:21:08,802 +Notice that it isn't just +the physical details that are described, + +377 +00:21:08,836 --> 00:21:11,338 +but the intention, +"as if sharing a secret." + +378 +00:21:12,840 --> 00:21:15,475 +The second is described +as "person meditating + +379 +00:21:15,509 --> 00:21:18,312 +with relaxed arms +and forefingers touching." + +380 +00:21:18,345 --> 00:21:23,350 +Again, both the physical details-- +relaxed arms and forefingers touching-- + +381 +00:21:23,383 --> 00:21:26,086 +and the context +of meditation are described. + +382 +00:21:27,588 --> 00:21:30,490 +And the third example +again describes both context + +383 +00:21:30,524 --> 00:21:34,261 +and position: +nervous person biting fingernails. + +384 +00:21:34,294 --> 00:21:38,098 +Notice also that each of these examples +is described as a "person," + +385 +00:21:38,131 --> 00:21:40,667 +not, say, a "man" or a "woman." + +386 +00:21:40,701 --> 00:21:43,270 +You can also help everyone +feel welcome in your app + +387 +00:21:43,303 --> 00:21:47,374 +by avoiding unnecessary references +to specific genders. + +388 +00:21:47,407 --> 00:21:50,844 +Be equally thoughtful +about any descriptive language you use. + +389 +00:21:51,612 --> 00:21:55,415 +It's critical to use empathy +when writing for your app. + +390 +00:21:55,449 --> 00:21:58,051 +Write for everyone, using simple language. + +391 +00:21:59,219 --> 00:22:03,123 +Be responsive to localization needs, +including text size changes. + +392 +00:22:04,124 --> 00:22:07,194 +And design for accessibility, +using thoughtful text + +393 +00:22:07,227 --> 00:22:10,130 +for people who use VoiceOver to navigate. + +394 +00:22:10,163 --> 00:22:12,900 +Don't be afraid to review +your app's language often, + +395 +00:22:12,933 --> 00:22:14,401 +and change things. + +396 +00:22:14,434 --> 00:22:18,872 +Just as language evolves more broadly, +so should the words your app uses. + +397 +00:22:18,906 --> 00:22:21,775 +There is so much more +to know about these topics. + +398 +00:22:21,808 --> 00:22:24,244 +Check out the Human Interface Guidelines +to learn more + +399 +00:22:24,278 --> 00:22:26,079 +about designing inclusive apps. + +400 +00:22:26,813 --> 00:22:31,351 +Today, we talked about Purpose, +Anticipation, + +401 +00:22:31,385 --> 00:22:33,887 +Context, and Empathy, + +402 +00:22:33,921 --> 00:22:37,591 +four concepts you can use to make sure +the text in your app or game + +403 +00:22:37,624 --> 00:22:39,760 +is well designed. + +404 +00:22:39,793 --> 00:22:42,896 +We hope you saw the value +of considering writing early, + +405 +00:22:42,930 --> 00:22:47,367 +as part of your design process, +not as something you fill in later. + +406 +00:22:47,401 --> 00:22:50,838 +Jen: And if you do find yourself +struggling to find the right words, + +407 +00:22:50,871 --> 00:22:54,041 +we have one last tip for you, +and it's the simplest one: + +408 +00:22:54,074 --> 00:22:56,577 +read your writing out loud. + +409 +00:22:56,610 --> 00:23:00,180 +It can really help make sure +your writing sounds conversational, + +410 +00:23:00,214 --> 00:23:01,748 +like how you'd talk to a friend. + +411 +00:23:01,782 --> 00:23:07,087 +Reading out loud can also help you +find unnecessary or repetitive words, + +412 +00:23:07,120 --> 00:23:09,356 +grammatical mistakes, or typos. + +413 +00:23:09,389 --> 00:23:10,924 +Those details matter. + +414 +00:23:10,958 --> 00:23:14,127 +Kaely: Writing for interfaces +begins with curiosity + +415 +00:23:14,161 --> 00:23:16,597 +for who's on the other side of the screen. + +416 +00:23:16,630 --> 00:23:19,499 +Speak to them +with respect and understanding, + +417 +00:23:19,533 --> 00:23:21,768 +and we know you'll find the right words. + +418 +00:23:22,903 --> 00:23:26,840 +[spacey music] + diff --git a/eng/2022 Session 10038 What's new with SKAdNetwork en.srt b/eng/2022 Session 10038 What's new with SKAdNetwork en.srt new file mode 100644 index 0000000..5b3a27f --- /dev/null +++ b/eng/2022 Session 10038 What's new with SKAdNetwork en.srt @@ -0,0 +1,1398 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,743 --> 00:00:13,146 +Hello and welcome to WWDC. + +3 +00:00:13,180 --> 00:00:17,751 +I’m Nikhil and today, I’ll share with you +the changes coming to SKAdNetwork. + +4 +00:00:17,784 --> 00:00:19,653 +Before diving into the future, + +5 +00:00:19,686 --> 00:00:23,590 +let’s remember where we are +and how we got here. + +6 +00:00:23,624 --> 00:00:28,061 +At Apple, we believe privacy is +a fundamental human right + +7 +00:00:28,095 --> 00:00:32,733 +and protecting it is woven +into everything we do here. + +8 +00:00:32,766 --> 00:00:36,236 +Which is why we created SKAdNetwork. + +9 +00:00:36,270 --> 00:00:41,642 +SKAdNetwork is Apple’s privacy-preserving +install attribution system. + +10 +00:00:41,675 --> 00:00:44,478 +Attribution data is sent back +to the advertiser + +11 +00:00:44,511 --> 00:00:47,948 +while preserving the user’s privacy. + +12 +00:00:47,981 --> 00:00:51,118 +SKAdNetwork involves three actors: + +13 +00:00:51,151 --> 00:00:56,056 +ad networks, publisher apps, +and advertiser apps. + +14 +00:00:56,089 --> 00:01:00,928 +Let's also define some terms +used commonly within SKAdNetwork. + +15 +00:01:00,961 --> 00:01:02,429 +Impression: + +16 +00:01:02,462 --> 00:01:06,099 +an impression in the context +of this discussion is input + +17 +00:01:06,133 --> 00:01:09,603 +from the publisher app to SKAdNetwork. + +18 +00:01:09,636 --> 00:01:10,804 +Engagement: + +19 +00:01:10,838 --> 00:01:15,375 +an engagement happens when the user +interacts with the advertiser app. + +20 +00:01:15,409 --> 00:01:18,145 +The advertiser app captures +these engagements + +21 +00:01:18,178 --> 00:01:21,849 +in the form of conversion value updates. + +22 +00:01:21,882 --> 00:01:26,720 +And finally, conversions, +which are also called postbacks. + +23 +00:01:26,753 --> 00:01:31,792 +The postback contains attribution data +which is sent to the ad network. + +24 +00:01:31,825 --> 00:01:35,229 +The attribution data provides a signal +to the ad network + +25 +00:01:35,262 --> 00:01:39,766 +that this was a successful conversion +of the original ad. + +26 +00:01:39,800 --> 00:01:45,005 +The ad network generates a signed +impression for the advertiser app. + +27 +00:01:45,038 --> 00:01:50,110 +The publisher app uses this impression +and displays this ad. + +28 +00:01:50,143 --> 00:01:54,815 +The user then taps the ad +and installs the app. + +29 +00:01:54,848 --> 00:01:56,517 +When launched for the first time, + +30 +00:01:56,550 --> 00:02:00,654 +the app calls an API +to signal the conversion. + +31 +00:02:00,687 --> 00:02:05,025 +The app can then repeatedly call this API +to update the conversion value + +32 +00:02:05,058 --> 00:02:10,330 +and capture various levels of engagement +and return on ad spent. + +33 +00:02:10,364 --> 00:02:13,166 +Once the timer expires, +we send the postback + +34 +00:02:13,200 --> 00:02:16,703 +containing the attribution data +to the ad network. + +35 +00:02:17,704 --> 00:02:22,342 +Taking a quick look at the version history +for SKAdNetwork. + +36 +00:02:22,376 --> 00:02:28,682 +SKAdNetwork 2.0 introduced +privacy-preserving ad attribution. + +37 +00:02:28,715 --> 00:02:34,421 +2.2 enabled publisher apps +to show custom ads. + +38 +00:02:34,454 --> 00:02:39,726 +3.0 added postbacks +for non-winning impressions. + +39 +00:02:39,760 --> 00:02:45,199 +And in iOS 15.0, we introduced +postback copies for developers + +40 +00:02:45,232 --> 00:02:48,802 +for all versions of SKAdNetwork. + +41 +00:02:49,269 --> 00:02:53,407 +For more on the history of SKAdNetwork, +refer to the session + +42 +00:02:53,440 --> 00:02:58,412 +"Meet privacy-preserving ad attribution" +from WWDC 2021. + +43 +00:02:59,413 --> 00:03:05,519 +Now let’s talk about the future, +SKAdNetwork 4.0. + +44 +00:03:05,552 --> 00:03:08,522 +Let’s have a look at the new features. + +45 +00:03:08,555 --> 00:03:12,326 +We are going to start by looking at +a few changes to the API + +46 +00:03:12,359 --> 00:03:16,230 +designed to provide more data +to advertisers. + +47 +00:03:16,263 --> 00:03:20,601 +Following this, we will look at +the conversion side of things. + +48 +00:03:20,634 --> 00:03:25,205 +We will then move on to attribution +for ads on the web, + +49 +00:03:25,239 --> 00:03:30,511 +and wrap up by talking about +SKAdNetwork testability. + +50 +00:03:30,544 --> 00:03:34,615 +Hierarchical IDs and conversion values +are the first new feature + +51 +00:03:34,648 --> 00:03:37,951 +coming to SKAdNetwork 4.0. + +52 +00:03:38,785 --> 00:03:44,024 +Before diving into the new feature, +I would like to define crowd anonymity. + +53 +00:03:44,057 --> 00:03:49,229 +Crowd anonymity is the term we use +to refer to the privacy-preserving way + +54 +00:03:49,263 --> 00:03:53,433 +in which SKAdNetwork delivers +attribution data. + +55 +00:03:53,467 --> 00:03:56,470 +Counts of installs determine +the level of privacy + +56 +00:03:56,503 --> 00:04:00,140 +assured to the person using your app. + +57 +00:04:00,174 --> 00:04:03,510 +At the lower end, +we send less attribution data + +58 +00:04:03,544 --> 00:04:06,113 +to the advertiser in the postback. + +59 +00:04:06,146 --> 00:04:11,018 +When the install count is low, +we take extra steps to protect privacy + +60 +00:04:11,051 --> 00:04:15,122 +by limiting the trackable information +sent back. + +61 +00:04:15,155 --> 00:04:16,924 +As the count scales up + +62 +00:04:16,957 --> 00:04:20,561 +and the user’s uniqueness starts +to blend into the crowd, + +63 +00:04:20,594 --> 00:04:23,330 +we send more data back. + +64 +00:04:23,363 --> 00:04:26,366 +Finally, as the count reaches +the highest tier, + +65 +00:04:26,400 --> 00:04:32,005 +we are able to send the most data back +while still preserving privacy. + +66 +00:04:32,039 --> 00:04:37,411 +In SKAdNetwork 4.0, +we have a way to send more data + +67 +00:04:37,444 --> 00:04:41,548 +while retaining our privacy protections. + +68 +00:04:41,582 --> 00:04:45,919 +For this, we are changing +the campaign identifier field. + +69 +00:04:45,953 --> 00:04:49,356 +At present, this is a 2 digit field. + +70 +00:04:49,389 --> 00:04:52,893 +We will increase the range +of this field to 4 digits + +71 +00:04:52,926 --> 00:04:56,463 +and rename it to source identifier. + +72 +00:04:56,496 --> 00:05:00,133 +The new name reflects our way +of thinking about this field + +73 +00:05:00,167 --> 00:05:04,471 +as being capable of representing +anything you choose to use it for + +74 +00:05:04,505 --> 00:05:08,208 +and not solely for identifying campaigns. + +75 +00:05:09,209 --> 00:05:11,411 +Though this is a single number, + +76 +00:05:11,445 --> 00:05:15,249 +we encourage thinking about it +as three hierarchical numbers, + +77 +00:05:15,282 --> 00:05:19,319 +a 2, 3, and 4 digit number. + +78 +00:05:19,353 --> 00:05:21,788 +Thinking about the source identifier field + +79 +00:05:21,822 --> 00:05:24,691 +as a hierarchical set of numbers +is helpful + +80 +00:05:24,725 --> 00:05:28,428 +in ascribing meaning to +the different numbers. + +81 +00:05:28,462 --> 00:05:33,433 +As an example, the 2 digits +could represent the ad campaign, + +82 +00:05:33,467 --> 00:05:38,138 +the 3rd could represent +the bucketed location of the user, + +83 +00:05:38,172 --> 00:05:42,776 +and the 4th could stand for +ad placement on screen. + +84 +00:05:42,809 --> 00:05:47,714 +Alternatively, the 2 digits +could stand for treatments, + +85 +00:05:47,748 --> 00:05:51,785 +the 3rd digit could represent +bucketed time of day, + +86 +00:05:51,818 --> 00:05:56,123 +and the 4th digit could stands +for the size of the ad shown. + +87 +00:05:56,156 --> 00:05:58,492 +These are simply examples, + +88 +00:05:58,525 --> 00:06:02,796 +and ultimately, what we wanted here +was to open this field up to you + +89 +00:06:02,829 --> 00:06:05,666 +to use as best fits your need. + +90 +00:06:05,699 --> 00:06:08,802 +Continuing the theme +of providing more data, + +91 +00:06:08,836 --> 00:06:12,105 +the conversion value is also changing. + +92 +00:06:12,139 --> 00:06:15,709 +Currently this is a 6 bit value. + +93 +00:06:15,742 --> 00:06:21,915 +With SKAdNetwork 4.0, +we are introducing two conversion values: + +94 +00:06:21,949 --> 00:06:25,853 +a fine-grained value +and a coarse-grained value. + +95 +00:06:25,886 --> 00:06:29,690 +The fine-grained value is the same +as the conversion value today. + +96 +00:06:29,723 --> 00:06:34,127 +The coarse-grained value can assume +one of three values. + +97 +00:06:34,161 --> 00:06:37,264 +Considering a conversion value of high 42, + +98 +00:06:37,297 --> 00:06:41,034 +where high is the coarse value +and 42 is the fine value, + +99 +00:06:41,068 --> 00:06:44,304 +the install count required +to receive the coarse value + +100 +00:06:44,338 --> 00:06:48,242 +is lower than that needed +for the fine value. + +101 +00:06:48,275 --> 00:06:53,547 +Consequently, apps will receive the former +much faster than the latter. + +102 +00:06:53,580 --> 00:06:58,952 +Note however, that only one of these +will be sent back to the advertiser. + +103 +00:06:58,986 --> 00:07:02,890 +Let us take a look at how crowd anonymity +affects these new fields. + +104 +00:07:04,224 --> 00:07:09,663 +Using our earlier example of 5739 +as the source identifier, + +105 +00:07:09,696 --> 00:07:11,865 +at the low end of crowd anonymity, + +106 +00:07:11,899 --> 00:07:16,737 +you can expect to receive the 2 digit +component of the source identifier, + +107 +00:07:16,770 --> 00:07:19,806 +which in this case is 39. + +108 +00:07:19,840 --> 00:07:24,678 +At the medium level, you can expect +to receive the 3 digit component. + +109 +00:07:24,711 --> 00:07:27,548 +And at the highest level, +you can expect to receive + +110 +00:07:27,581 --> 00:07:31,752 +the full 4 digit source identifier back. + +111 +00:07:31,785 --> 00:07:34,154 +As the crowd anonymity level increases, + +112 +00:07:34,188 --> 00:07:38,926 +more digits of the source identifier +are sent back. + +113 +00:07:38,959 --> 00:07:44,064 +And taking our earlier example of high 42 +for the conversion value, + +114 +00:07:44,097 --> 00:07:46,233 +at the low end of crowd anonymity, + +115 +00:07:46,266 --> 00:07:50,938 +you will not receive a conversion value +in your postback. + +116 +00:07:50,971 --> 00:07:54,975 +At the medium level, you will receive +the coarse conversion value. + +117 +00:07:55,008 --> 00:07:56,276 +And at the highest level, + +118 +00:07:56,310 --> 00:08:01,148 +you will receive the fine-grained +conversion value in your postback. + +119 +00:08:01,181 --> 00:08:07,421 +As the crowd anonymity increases, +the conversion value returned changes. + +120 +00:08:07,454 --> 00:08:10,290 +Let’s take a look at the API changes. + +121 +00:08:10,324 --> 00:08:15,562 +If you are using instances +of SKAdImpression to show your ads, + +122 +00:08:15,596 --> 00:08:22,035 +you can set the new sourceIdentifier +property on the SKAdImpression instance. + +123 +00:08:23,036 --> 00:08:26,240 +If you are setting your impression +via a dictionary, + +124 +00:08:26,273 --> 00:08:32,846 +you can set the sourceIdentifier value +using the new source identifier key. + +125 +00:08:33,847 --> 00:08:39,319 +For advertised apps, you will call the new +updatePostbackConversionValue method + +126 +00:08:39,353 --> 00:08:43,190 +which has been changed to now take +a coarse-grained conversion value + +127 +00:08:43,223 --> 00:08:47,494 +in addition to the fine-grained +conversion value as shown here. + +128 +00:08:47,528 --> 00:08:51,965 +We recommend that you use the completion +handler to do any follow up tasks + +129 +00:08:51,999 --> 00:08:56,069 +after updating your postback’s +conversion values. + +130 +00:08:56,103 --> 00:08:59,673 +Here are some recommendations on adopting +the new hierarchical IDs + +131 +00:08:59,706 --> 00:09:02,476 +and conversion values API. + +132 +00:09:02,509 --> 00:09:05,846 +Different portions of +the source identifier will be returned + +133 +00:09:05,879 --> 00:09:08,949 +depending on +the privacy level of the install. + +134 +00:09:08,982 --> 00:09:14,288 +Consider this when composing +source identifiers for your impressions. + +135 +00:09:14,321 --> 00:09:18,659 +Coarse conversion values are at +a much lower granularity + +136 +00:09:18,692 --> 00:09:20,527 +than fine conversion values. + +137 +00:09:20,561 --> 00:09:25,199 +Consider this when ascribing meaning +to their values. + +138 +00:09:25,232 --> 00:09:29,136 +Conversion values can increase +or decrease. + +139 +00:09:29,169 --> 00:09:32,139 +And finally, +when making changes to your server + +140 +00:09:32,172 --> 00:09:35,876 +to process SKAdNetwork 4.0 postbacks, + +141 +00:09:35,909 --> 00:09:39,613 +make them capable of parsing +the new source identifiers + +142 +00:09:39,646 --> 00:09:42,449 +and coarse conversion values. + +143 +00:09:42,482 --> 00:09:46,220 +That wraps up our introduction to +hierarchical IDs and conversion values, + +144 +00:09:46,253 --> 00:09:51,258 +providing more data to advertisers +while retaining our privacy protections. + +145 +00:09:51,291 --> 00:09:57,898 +Let’s focus on another critical area +of the attribution flow, conversions. + +146 +00:09:57,931 --> 00:10:01,835 +SKAdNetwork today uses +a single conversion model. + +147 +00:10:03,103 --> 00:10:06,673 +From the time of install, +we update the conversion value + +148 +00:10:06,707 --> 00:10:09,610 +to capture various user engagements. + +149 +00:10:09,643 --> 00:10:12,112 +And after a certain amount of time +elapses, + +150 +00:10:12,145 --> 00:10:15,549 +we send the postback to the ad network. + +151 +00:10:15,582 --> 00:10:18,018 +Let’s have a look at an example. + +152 +00:10:18,051 --> 00:10:19,720 +Let’s use Food Truck. + +153 +00:10:19,753 --> 00:10:22,623 +Food Truck puts you in control +of your food truck. + +154 +00:10:22,656 --> 00:10:27,761 +Customize, control, and create +your own unique donut delivery experience. + +155 +00:10:27,794 --> 00:10:29,663 +As Food Truck’s developer, + +156 +00:10:29,696 --> 00:10:33,467 +I want to know the value +of my advertising spend. + +157 +00:10:33,500 --> 00:10:37,938 +As an example of this, +when the user starts Food Truck, + +158 +00:10:37,971 --> 00:10:41,508 +I will update the conversion value +to mark this. + +159 +00:10:41,542 --> 00:10:44,411 +Once they pick up their first batch +for delivery, + +160 +00:10:44,444 --> 00:10:47,614 +I update the conversion value again. + +161 +00:10:47,648 --> 00:10:52,853 +Due to this batch of donuts requiring +a certain amount of time to age, + +162 +00:10:52,886 --> 00:10:55,789 +the user waits. + +163 +00:10:55,822 --> 00:10:58,926 +Somewhere in the aging time, +the conversion is sent + +164 +00:10:58,959 --> 00:11:03,997 +in the form a postback to both +the ad network and optionally to me. + +165 +00:11:04,031 --> 00:11:07,868 +However, when the user finally delivers +the donut, + +166 +00:11:07,901 --> 00:11:12,773 +I will have no way of measuring this +or any further user actions + +167 +00:11:12,806 --> 00:11:16,844 +since the postback has already been sent. + +168 +00:11:16,877 --> 00:11:21,181 +In other words, re-engagement is lost. + +169 +00:11:21,215 --> 00:11:24,451 +This is what we are changing. + +170 +00:11:24,484 --> 00:11:30,290 +We are moving from a single postback +to three postbacks. + +171 +00:11:30,324 --> 00:11:33,227 +The postbacks are tied to +definite time windows + +172 +00:11:33,260 --> 00:11:36,463 +and are sent at the end of those windows. + +173 +00:11:36,496 --> 00:11:39,466 +Let’s revisit the earlier example. + +174 +00:11:39,499 --> 00:11:42,503 +As before, the user starts Food Truck + +175 +00:11:42,536 --> 00:11:47,441 +and proceeds to pick the first batch up +for delivery. + +176 +00:11:47,474 --> 00:11:52,513 +The first postback’s time window elapses +and we send it out. + +177 +00:11:52,546 --> 00:11:54,748 +The user delivers the donuts. + +178 +00:11:54,781 --> 00:11:57,784 +Whereas earlier, +this would not have been captured, + +179 +00:11:57,818 --> 00:12:01,588 +now we find ourselves in +the second postback’s time window. + +180 +00:12:01,622 --> 00:12:05,259 +We update the conversion value +to capture this. + +181 +00:12:05,292 --> 00:12:10,664 +The second postback’s time window +passes and we send it out. + +182 +00:12:10,697 --> 00:12:15,769 +Using the tools in the app, +the user creates a brand new donut recipe + +183 +00:12:15,802 --> 00:12:19,339 +and then heads out again +to deliver a fresh batch. + +184 +00:12:19,373 --> 00:12:24,011 +The third window elapses +and the third postback is sent out. + +185 +00:12:24,044 --> 00:12:27,247 +There are more opportunities to measure +the value of the conversion + +186 +00:12:27,281 --> 00:12:30,484 +and to receive these measurements +more often. + +187 +00:12:30,517 --> 00:12:33,820 +A few points to note about +multiple conversions: + +188 +00:12:33,854 --> 00:12:37,691 +Only the first postback will get +the fine conversion value. + +189 +00:12:37,724 --> 00:12:42,462 +The additional postbacks can carry +the coarse conversion value. + +190 +00:12:42,496 --> 00:12:47,034 +Only the winner and developer +will receive the additional postbacks. + +191 +00:12:47,067 --> 00:12:53,307 +Next, let’s review SKAdNetwork +attributions for ads on the web. + +192 +00:12:53,340 --> 00:13:00,047 +We know that publisher apps show ads +which open product pages in the App Store. + +193 +00:13:00,080 --> 00:13:04,017 +The App Store, in turn, works with +SKAdNetwork to attribute installs + +194 +00:13:05,519 --> 00:13:10,324 +while providing the privacy protections +that our users rely on. + +195 +00:13:10,357 --> 00:13:15,329 +We want to extend the same privacy +to ads shown on web pages. + +196 +00:13:15,362 --> 00:13:18,265 +Let’s take a look at how this will work. + +197 +00:13:18,298 --> 00:13:22,503 +The user taps on a link for an ad +in Safari. + +198 +00:13:22,536 --> 00:13:28,842 +Safari launches the App Store and lands on +the product page for the advertised app. + +199 +00:13:28,876 --> 00:13:30,744 +Two things happen here. + +200 +00:13:30,777 --> 00:13:35,015 +The App Store fetches the ad impression +from the ad network server, + +201 +00:13:35,048 --> 00:13:39,019 +and the user installs the app. + +202 +00:13:39,052 --> 00:13:42,289 +Following this, everything flows +as it does today, + +203 +00:13:42,322 --> 00:13:47,561 +and eventually, +SKAdNetwork sends the postback out. + +204 +00:13:47,594 --> 00:13:50,964 +Let’s have a look at +how the link is composed. + +205 +00:13:50,998 --> 00:13:56,803 +The href portion contains the App Store +link for the app being advertised. + +206 +00:13:56,837 --> 00:14:00,541 +The attributionDestination tells Apple +where to fetch + +207 +00:14:00,574 --> 00:14:03,510 +the signed ad impression from. + +208 +00:14:03,544 --> 00:14:07,114 +And the attributionSourceNonce +helps the ad network find + +209 +00:14:07,147 --> 00:14:09,917 +the exact ad impression to send back. + +210 +00:14:11,318 --> 00:14:15,022 +Note that this link can be served +on first party sites + +211 +00:14:15,055 --> 00:14:18,792 +and in embedded cross site iframes. + +212 +00:14:18,825 --> 00:14:22,763 +Let’s take a closer look at +the URL construction. + +213 +00:14:22,796 --> 00:14:28,936 +We extract the eTLD+1 component +from the attribution destination. + +214 +00:14:28,969 --> 00:14:32,039 +To this, we then add +the well-known qualifier + +215 +00:14:32,072 --> 00:14:34,608 +and two more path components. + +216 +00:14:34,641 --> 00:14:39,580 +This gives us the URL to which +we will make the HTTP POST request + +217 +00:14:39,613 --> 00:14:42,749 +to get the signed impression. + +218 +00:14:42,783 --> 00:14:47,521 +Note that the protocol and +highlighted path components are fixed. + +219 +00:14:47,554 --> 00:14:53,060 +You, the ad network, decide the value +of the host component. + +220 +00:14:54,061 --> 00:14:58,232 +SKAdNetwork will make an HTTP POST request + +221 +00:14:58,265 --> 00:15:01,635 +to the URL constructed as just discussed. + +222 +00:15:01,668 --> 00:15:05,138 +The POST body will be JSON. + +223 +00:15:05,172 --> 00:15:10,277 +You will recognize the source_nonce as +being the same as in the original ad link. + +224 +00:15:10,310 --> 00:15:13,580 +This value is what the ad network +will use to identify + +225 +00:15:13,614 --> 00:15:16,750 +the signed impression it wants to serve. + +226 +00:15:17,751 --> 00:15:22,422 +Let’s look at what we expect to receive +from this POST request. + +227 +00:15:22,456 --> 00:15:28,061 +This is a signed impression, +ready for consumption by SKAdNetwork. + +228 +00:15:28,095 --> 00:15:31,231 +Note the source domain field +in the impression. + +229 +00:15:31,265 --> 00:15:34,468 +This is the link analog +of the source app ID + +230 +00:15:34,501 --> 00:15:38,772 +in the app driven SKAdNetwork flow. + +231 +00:15:38,805 --> 00:15:43,810 +Looking at how to get started using this +new entry into the SKAdNetwork system, + +232 +00:15:43,844 --> 00:15:46,513 +ad networks have to do the following: + +233 +00:15:46,547 --> 00:15:52,486 +Create the uniquely identifiable parts +of the link and the link itself. + +234 +00:15:52,519 --> 00:15:56,123 +Expose an endpoint capable of serving +signed impressions + +235 +00:15:56,156 --> 00:15:59,660 +at the location constructed +as we discussed before. + +236 +00:15:59,693 --> 00:16:02,863 +Update your postback servers +to parse and understand + +237 +00:16:02,896 --> 00:16:05,532 +the new optional source domain field. + +238 +00:16:05,566 --> 00:16:09,036 +For webpages that want to use the links +to SKAdNetwork, + +239 +00:16:09,069 --> 00:16:14,341 +simply embed the ad links provided to you +by your ad network. + +240 +00:16:14,374 --> 00:16:19,913 +That was an introduction to SKAdNetwork +attribution for ads on the web. + +241 +00:16:19,947 --> 00:16:23,817 +We are incredibly excited to see +this feature being adopted + +242 +00:16:23,851 --> 00:16:28,021 +across the wider advertising ecosystem. + +243 +00:16:28,055 --> 00:16:34,595 +Next, we will go over how you can test +your SKAdNetwork implementations. + +244 +00:16:34,628 --> 00:16:40,133 +At a high level, SKAdNetwork deals +with impressions and postbacks. + +245 +00:16:40,167 --> 00:16:45,072 +From working with our developer community +we know that on the side of impressions, + +246 +00:16:45,105 --> 00:16:49,643 +signing and configuring +have been points of friction. + +247 +00:16:49,676 --> 00:16:54,848 +And with postbacks, receiving and +validating them in a test-friendly manner + +248 +00:16:54,882 --> 00:16:57,451 +has been an area for improvement. + +249 +00:16:57,484 --> 00:17:01,355 +To help improve the developer experience +with SKAdNetwork, + +250 +00:17:01,388 --> 00:17:08,095 +we released SKAdNetwork +testability changes in Xcode 13.3. + +251 +00:17:08,128 --> 00:17:12,132 +This is available as a unit testing +framework within StoreKitTest + +252 +00:17:12,165 --> 00:17:15,035 +that a lot of you have already been using + +253 +00:17:15,068 --> 00:17:19,540 +for testing various parts +of your StoreKit implementations. + +254 +00:17:19,573 --> 00:17:23,076 +Let’s take a look at +validating an impression. + +255 +00:17:23,110 --> 00:17:28,515 +You create and configure an instance +of SKAdImpression. + +256 +00:17:28,549 --> 00:17:32,386 +You supply the public key counterpart +of the private key + +257 +00:17:32,419 --> 00:17:34,922 +used to generate the signature, + +258 +00:17:34,955 --> 00:17:40,427 +and then you call the validate method +on the SKAdTestSession instance. + +259 +00:17:40,460 --> 00:17:43,397 +This will validate your impression and +throw an error + +260 +00:17:43,430 --> 00:17:49,369 +if either the impression is misconfigured +or the signature is invalid. + +261 +00:17:49,403 --> 00:17:53,006 +The other main area to test is +the receipt of postbacks. + +262 +00:17:53,040 --> 00:17:54,808 +There are 2 steps here. + +263 +00:17:54,842 --> 00:17:59,746 +Step 1: adding the test postback +to your test session. + +264 +00:17:59,780 --> 00:18:03,150 +You will create an instance +of SKAdTestPostback + +265 +00:18:03,183 --> 00:18:07,621 +and configure it with the values you want +in your postback. + +266 +00:18:07,654 --> 00:18:10,390 +Pay special note to the postback URL, + +267 +00:18:10,424 --> 00:18:13,961 +since this is where your postback +will be sent to. + +268 +00:18:13,994 --> 00:18:18,465 +This can point to +a remote or a local server. + +269 +00:18:18,498 --> 00:18:20,801 +You will then add this to +your test session + +270 +00:18:20,834 --> 00:18:25,739 +using the setPostbacks method +on SKAdTestSession. + +271 +00:18:25,772 --> 00:18:29,776 +Step 2 is to actually send +the postback out. + +272 +00:18:29,810 --> 00:18:33,413 +All you need to do here is to call +the flushPostbacks method + +273 +00:18:33,447 --> 00:18:37,718 +on SKAdTestSession +and your postback will be off on it’s way. + +274 +00:18:38,919 --> 00:18:43,357 +Note that SKAdTestSession will send +your postback over the network + +275 +00:18:43,390 --> 00:18:47,628 +to the server you specified +when composing your postback. + +276 +00:18:47,661 --> 00:18:52,866 +These are a few things coming to +SKAdNetwork testability later this year: + +277 +00:18:52,900 --> 00:18:56,170 +Support for the new +source identifier field. + +278 +00:18:56,203 --> 00:18:59,873 +Support for fine +and coarse conversion values. + +279 +00:18:59,907 --> 00:19:03,043 +Support for testing multiple conversions. + +280 +00:19:03,076 --> 00:19:08,015 +And that was a quick look at +the SKAdNetwork testability changes + +281 +00:19:08,048 --> 00:19:11,919 +that were first available in Xcode 13.3. + +282 +00:19:11,952 --> 00:19:15,656 +Today, we talked about hierarchical IDs +and conversion values, + +283 +00:19:15,689 --> 00:19:19,359 +aiming to get more data to you sooner. + +284 +00:19:19,393 --> 00:19:22,029 +And then we talked about +multiple conversions, + +285 +00:19:22,062 --> 00:19:26,366 +allowing you to measure re-engagement +over a longer period. + +286 +00:19:26,400 --> 00:19:29,703 +Then we talked about +link-driven attribution + +287 +00:19:29,736 --> 00:19:32,873 +and how we are adding +a bridge to SKAdNetwork, + +288 +00:19:32,906 --> 00:19:36,476 +bringing with it +the same privacy protections. + +289 +00:19:36,510 --> 00:19:42,115 +And we finished by talking about +SKAdNetwork testability in Xcode. + +290 +00:19:42,149 --> 00:19:47,754 +And that wraps up our journey +through SKAdNetwork 4.0. + +291 +00:19:47,788 --> 00:19:51,592 +In closing, everything we build is driven +by feedback + +292 +00:19:51,625 --> 00:19:54,428 +from our amazing community of developers. + +293 +00:19:54,461 --> 00:19:58,332 +It has been incredibly fulfilling +and humbling to hear about + +294 +00:19:58,365 --> 00:20:04,338 +your experiences building a privacy first +ad experience for our users. + +295 +00:20:04,371 --> 00:20:06,373 +Thank you. + +296 +00:20:06,406 --> 00:20:10,410 +♪ ♪ + diff --git a/eng/2022 Session 10039 What's new in StoreKit testing en.srt b/eng/2022 Session 10039 What's new in StoreKit testing en.srt new file mode 100644 index 0000000..7fdf229 --- /dev/null +++ b/eng/2022 Session 10039 What's new in StoreKit testing en.srt @@ -0,0 +1,2671 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,411 +Greg: Hi, I'm Greg. + +3 +00:00:11,445 --> 00:00:13,881 +Welcome to what's new in StoreKit Testing. + +4 +00:00:13,914 --> 00:00:17,417 +In this session, Peter and I will +highlight some great new features + +5 +00:00:17,451 --> 00:00:20,921 +available for testing +in-app purchases in StoreKit. + +6 +00:00:20,954 --> 00:00:25,092 +We'll begin by taking a look at +some of the ways you can use Xcode 14 + +7 +00:00:25,125 --> 00:00:27,861 +to streamline +your in-app purchase testing. + +8 +00:00:27,895 --> 00:00:32,099 +Next, we'll take a look at some brand-new +capabilities you can take advantage of + +9 +00:00:32,132 --> 00:00:37,004 +to cover even more corner cases in +your in-app subscription implementations. + +10 +00:00:37,037 --> 00:00:39,940 +And finally, +Peter will show new enhancements + +11 +00:00:39,973 --> 00:00:42,643 +to the sandbox testing environment. + +12 +00:00:42,676 --> 00:00:44,511 +We'll be working with Food Truck, + +13 +00:00:44,545 --> 00:00:49,116 +an app to provide powerful capabilities +to food truck operators who sell donuts. + +14 +00:00:49,149 --> 00:00:52,352 +I already integrated with StoreKit +to offer the full version + +15 +00:00:52,386 --> 00:00:54,555 +of the Food Truck sales history feature, + +16 +00:00:54,588 --> 00:00:58,392 +and a subscription for an enhanced version +of the Social Feed service. + +17 +00:00:58,425 --> 00:01:01,428 +Throughout the session +we'll use StoreKit Testing in Xcode + +18 +00:01:01,461 --> 00:01:04,398 +to test the in-app purchase +functionality of our app. + +19 +00:01:04,431 --> 00:01:08,836 +Back at WWDC 2020, +we introduced StoreKit Testing in Xcode, + +20 +00:01:08,869 --> 00:01:12,840 +allowing you to start testing +your in-app purchases directly in Xcode. + +21 +00:01:12,873 --> 00:01:16,743 +This year, with Xcode 14, +we're excited to share some updates + +22 +00:01:16,777 --> 00:01:20,047 +to the testing life cycle +of a StoreKit app. + +23 +00:01:20,080 --> 00:01:24,284 +Just like before, you can create +a StoreKit Configuration File in Xcode, + +24 +00:01:24,318 --> 00:01:26,920 +and start testing +your in-app purchase implementation + +25 +00:01:26,954 --> 00:01:30,424 +without setting up an app +in App Store Connect. + +26 +00:01:30,457 --> 00:01:33,026 +When you're ready to configure +your app in App Store Connect, + +27 +00:01:33,060 --> 00:01:35,996 +we're introducing +a brand-new feature in Xcode 14 + +28 +00:01:36,029 --> 00:01:38,332 +to allow you to use +the same in-app purchase products + +29 +00:01:38,365 --> 00:01:42,302 +that you've entered in App Store Connect +with StoreKit Testing in Xcode. + +30 +00:01:42,336 --> 00:01:44,171 +If you already have an app on the store, + +31 +00:01:44,204 --> 00:01:47,407 +you can start using StoreKit Testing +in Xcode right now, + +32 +00:01:47,441 --> 00:01:50,744 +without needing to set up a StoreKit +Configuration File from scratch. + +33 +00:01:50,777 --> 00:01:54,581 +This convenient feature enables you +to configure your in-app purchases once, + +34 +00:01:54,615 --> 00:01:58,051 +and use the same configuration +locally in Xcode, + +35 +00:01:58,085 --> 00:02:01,355 +inside your unit tests, +in the Sandbox environment, + +36 +00:02:01,388 --> 00:02:05,025 +and when you're ready for release, +on the App Store. + +37 +00:02:05,058 --> 00:02:08,161 +It's easy to sync your products +in App Store Connect with Xcode. + +38 +00:02:08,195 --> 00:02:11,164 +First, you configure your products +in App Store Connect, + +39 +00:02:11,198 --> 00:02:14,668 +like this Social Feed+ subscription, +for example. + +40 +00:02:14,701 --> 00:02:18,205 +Then, you create a synced +configuration file in Xcode + +41 +00:02:18,238 --> 00:02:21,008 +which will load the product data +into Xcode. + +42 +00:02:21,041 --> 00:02:25,345 +If you want to make a change, +for example updating the US English title, + +43 +00:02:25,379 --> 00:02:27,314 +you can make the change +in App Store Connect + +44 +00:02:27,347 --> 00:02:30,651 +and sync your Configuration +in Xcode again. + +45 +00:02:30,684 --> 00:02:33,086 +You can also convert a configuration +you've synced + +46 +00:02:33,120 --> 00:02:36,890 +into a local, editable file +to make on the fly changes. + +47 +00:02:36,924 --> 00:02:39,960 +Converting a synced configuration +to a local configuration + +48 +00:02:39,993 --> 00:02:41,562 +is a one-way operation, + +49 +00:02:41,595 --> 00:02:45,365 +to sync again you'll need to create +a new configuration file. + +50 +00:02:45,399 --> 00:02:49,570 +I've already gotten started by setting up +a subscription group for Social Feed+, + +51 +00:02:49,603 --> 00:02:53,674 +an enhanced version of the Social Feed +service the Food Truck app offers. + +52 +00:02:53,707 --> 00:02:56,743 +Let's jump into Xcode and take a look +at how to use these products + +53 +00:02:56,777 --> 00:02:58,979 +with StoreKit Testing in Xcode. + +54 +00:02:59,012 --> 00:03:01,181 +I have the Food Truck project +open on my Mac. + +55 +00:03:01,215 --> 00:03:04,852 +To get started we'll create a new +StoreKit Configuration File + +56 +00:03:04,885 --> 00:03:08,322 +by going to the File Menu, + +57 +00:03:08,355 --> 00:03:10,757 +making a new file, + +58 +00:03:10,791 --> 00:03:12,926 +filtering by StoreKit, + +59 +00:03:12,960 --> 00:03:14,728 +and clicking next. + +60 +00:03:17,231 --> 00:03:21,101 +In Xcode 14, when we create +a new configuration file, + +61 +00:03:21,134 --> 00:03:24,538 +we get this checkbox to enable keeping +the file in sync + +62 +00:03:24,571 --> 00:03:26,807 +with an app in App Store Connect. + +63 +00:03:26,840 --> 00:03:32,446 +To create a local file, fill in a name +and leave the box unchecked. + +64 +00:03:32,479 --> 00:03:36,283 +To set up syncing, +we just have to check the box, + +65 +00:03:36,316 --> 00:03:40,020 +and confirm that the correct team +and app are selected. + +66 +00:03:40,053 --> 00:03:45,359 +If desired, we could choose a different +team and app using the picker menus. + +67 +00:03:45,392 --> 00:03:47,561 +We click next, + +68 +00:03:47,594 --> 00:03:51,331 +and choose somewhere to save our file. + +69 +00:03:51,365 --> 00:03:52,966 +As soon as we save the file, + +70 +00:03:53,000 --> 00:03:56,670 +the in-app purchase metadata +begins syncing from App Store Connect. + +71 +00:03:56,703 --> 00:03:59,640 +While the data is downloading, +we can keep working on our app + +72 +00:03:59,673 --> 00:04:02,943 +and keep track of its progress +in the Activity Bar. + +73 +00:04:02,976 --> 00:04:05,879 +When the sync is finished, +you'll notice this file looks different + +74 +00:04:05,913 --> 00:04:09,616 +than a typical +StoreKit Configuration File. + +75 +00:04:09,650 --> 00:04:13,153 +That's because the synced file +is in a read-only state. + +76 +00:04:13,187 --> 00:04:16,223 +We can see all the data +in Xcode at a glance, + +77 +00:04:16,256 --> 00:04:20,360 +but to make changes +we have to open App Store Connect. + +78 +00:04:21,728 --> 00:04:25,499 +I have the Social Feed+ monthly product +up in Safari. + +79 +00:04:25,532 --> 00:04:29,203 +Let's update the English title +for this product by adding a suffix + +80 +00:04:29,236 --> 00:04:32,439 +to help differentiate the product +from the yearly plan. + +81 +00:04:37,010 --> 00:04:41,048 +Now that this is updated, +let's save + +82 +00:04:41,081 --> 00:04:43,116 +and go back to Xcode. + +83 +00:04:45,519 --> 00:04:48,355 +To have this change +reflect in our configuration file, + +84 +00:04:48,388 --> 00:04:52,392 +we just have to press this sync button +in the bottom left corner. + +85 +00:04:56,897 --> 00:04:58,665 +Once the sync is complete, + +86 +00:04:58,699 --> 00:05:01,902 +we can see the change reflected in Xcode. + +87 +00:05:03,804 --> 00:05:06,039 +Even though the synced file is read-only, + +88 +00:05:06,073 --> 00:05:09,109 +we can still copy over data +to a local file + +89 +00:05:09,142 --> 00:05:12,246 +to make quick changes inside of Xcode. + +90 +00:05:21,021 --> 00:05:23,991 +In addition to copying items +from the configuration file, + +91 +00:05:24,024 --> 00:05:29,029 +we can also convert an entire synced file +to a local, editable file. + +92 +00:05:29,062 --> 00:05:34,101 +All we need to do is open our synced file, + +93 +00:05:34,134 --> 00:05:37,037 +go to the editor menu, + +94 +00:05:37,070 --> 00:05:41,275 +and click on +"Convert to Local StoreKit Configuration". + +95 +00:05:43,243 --> 00:05:47,381 +Keep in mind, you can't undo +this operation after converting the file. + +96 +00:05:47,414 --> 00:05:52,286 +To sync with the app again, you'll need to +create a new StoreKit Configuration File. + +97 +00:05:52,319 --> 00:05:55,189 +I want to keep this file in sync +with App Store Connect, + +98 +00:05:55,222 --> 00:05:59,426 +so let's cancel out of this alert. + +99 +00:05:59,459 --> 00:06:01,328 +Now that we have our file synced, + +100 +00:06:01,361 --> 00:06:04,331 +let's configure our testing environment. + +101 +00:06:04,364 --> 00:06:07,734 +To get started, +we'll open the scheme editor. + +102 +00:06:11,271 --> 00:06:13,807 +Select the run action, + +103 +00:06:13,841 --> 00:06:15,642 +and select options. + +104 +00:06:18,078 --> 00:06:21,582 +In the options, we can switch between +different StoreKit environments + +105 +00:06:21,615 --> 00:06:23,283 +from the picker menu. + +106 +00:06:23,317 --> 00:06:26,386 +If we choose "None" +we'll connect to Sandbox, + +107 +00:06:26,420 --> 00:06:30,457 +and if we choose "Food Truck" +we'll connect to the Xcode environment. + +108 +00:06:30,490 --> 00:06:32,960 +It's that easy to switch +between environments + +109 +00:06:32,993 --> 00:06:35,262 +depending on our current testing needs, + +110 +00:06:35,295 --> 00:06:38,765 +and both environments will now be using +the exact same product + +111 +00:06:38,799 --> 00:06:41,502 +and subscription metadata. + +112 +00:06:41,535 --> 00:06:44,671 +Let's pick our synced configuration +file for now. + +113 +00:06:47,074 --> 00:06:51,111 +We've now set up StoreKit in Xcode, +so let's get to testing. + +114 +00:06:51,144 --> 00:06:52,946 +Since we're using a SwiftUI app, + +115 +00:06:52,980 --> 00:06:56,583 +we can preview our subscription store +right in Xcode. + +116 +00:07:00,454 --> 00:07:04,558 +Starting in Xcode 14, +products from StoreKit configuration files + +117 +00:07:04,591 --> 00:07:07,094 +will load right into SwiftUI previews. + +118 +00:07:07,127 --> 00:07:11,431 +This makes it super easy to build and test +great looking store user interfaces, + +119 +00:07:11,465 --> 00:07:14,168 +using real in-app purchase data. + +120 +00:07:14,201 --> 00:07:17,037 +Let's try to add some detail +to the product options + +121 +00:07:17,070 --> 00:07:20,974 +by including a subtitle for our products. + +122 +00:07:21,008 --> 00:07:25,045 +We'll just add a Text view containing +the product's localized description. + +123 +00:07:29,449 --> 00:07:31,752 +And watch the preview update immediately + +124 +00:07:31,785 --> 00:07:34,621 +with the description we set up +in App Store Connect. + +125 +00:07:34,655 --> 00:07:37,324 +I think this is looking a lot better now. + +126 +00:07:38,825 --> 00:07:40,694 +Now that our UI is in good shape, + +127 +00:07:40,727 --> 00:07:42,729 +let's run the app on an iPhone + +128 +00:07:42,763 --> 00:07:45,299 +and start some functional testing. + +129 +00:07:56,176 --> 00:07:59,379 +In Xcode 14, +there are some powerful new tools + +130 +00:07:59,413 --> 00:08:01,515 +in the StoreKit Transaction Manager. + +131 +00:08:01,548 --> 00:08:04,952 +With our app running, +we can open the transaction manager + +132 +00:08:04,985 --> 00:08:08,589 +by pressing the purchases +icon in the debug bar. + +133 +00:08:10,691 --> 00:08:13,627 +On the right +there's a new transaction inspector + +134 +00:08:13,660 --> 00:08:15,262 +that allows us to visualize + +135 +00:08:15,295 --> 00:08:18,832 +all of the under the hood details +about a transaction. + +136 +00:08:18,866 --> 00:08:22,836 +This tool can be useful to understand +the state of an in-app transaction. + +137 +00:08:22,870 --> 00:08:27,941 +For example, we can see the date +this subscription to Social Feed+ expired, + +138 +00:08:27,975 --> 00:08:32,112 +and information +about its upcoming renewals. + +139 +00:08:32,145 --> 00:08:35,716 +We can also jump to the configuration file +for a product, + +140 +00:08:35,749 --> 00:08:38,719 +subscription group, or subscription offer. + +141 +00:08:38,752 --> 00:08:43,257 +We just have to click the jump button +next to this subscription group. + +142 +00:08:45,692 --> 00:08:49,796 +And we're brought straight to Social Feed+ +in our configuration file. + +143 +00:08:53,467 --> 00:08:56,136 +This inspector will help us out later +in the session + +144 +00:08:56,170 --> 00:08:58,572 +as we look at more advanced test cases. + +145 +00:09:00,974 --> 00:09:03,477 +We can also filter our transactions now, + +146 +00:09:03,510 --> 00:09:06,847 +which is really useful +for navigating the list of transactions + +147 +00:09:06,880 --> 00:09:10,784 +with all of these Social Feed+ renewals. + +148 +00:09:10,817 --> 00:09:15,923 +In our app you'll notice we have access +to the annual sales history feature. + +149 +00:09:17,858 --> 00:09:19,927 +We have all of +these subscription renewals, + +150 +00:09:19,960 --> 00:09:25,199 +which makes it difficult to tell which +transaction entitled us to the feature. + +151 +00:09:25,232 --> 00:09:27,935 +We can easily find the transaction +for the product + +152 +00:09:27,968 --> 00:09:30,971 +by beginning to type out its ID... + +153 +00:09:32,940 --> 00:09:37,244 +And selecting the product ID filter +from the auto-complete menu. + +154 +00:09:40,681 --> 00:09:43,050 +We can also filter by purchase date, + +155 +00:09:43,083 --> 00:09:46,720 +so we can focus on just the purchases +we're making now. + +156 +00:09:52,926 --> 00:09:55,796 +Since our subscription +to Social Feed+ is expired, + +157 +00:09:55,829 --> 00:09:59,032 +let's go into the app and subscribe again. + +158 +00:10:06,840 --> 00:10:08,909 +Now that we've confirmed the subscription, + +159 +00:10:08,942 --> 00:10:11,845 +we can see +just the new transaction appear. + +160 +00:10:13,247 --> 00:10:17,384 +We just looked at some ways to enhance +your in-app purchase testing in Xcode, + +161 +00:10:17,417 --> 00:10:20,821 +by syncing products and subscriptions +from App Store Connect, + +162 +00:10:20,854 --> 00:10:24,024 +using your StoreKit Configurations +with SwiftUI Previews, + +163 +00:10:24,057 --> 00:10:28,395 +and taking advantage of the new tools +in the Transaction Manager. + +164 +00:10:28,428 --> 00:10:32,933 +Now, we're going to continue testing +Food Truck's in-app purchase functionality + +165 +00:10:32,966 --> 00:10:35,402 +by using some new capabilities in Xcode + +166 +00:10:35,435 --> 00:10:38,472 +to cover advanced subscription cases. + +167 +00:10:38,505 --> 00:10:41,608 +First, we'll look at testing +refund requests, + +168 +00:10:41,642 --> 00:10:46,013 +allowing people to request refunds +for their purchases in Food Truck. + +169 +00:10:46,046 --> 00:10:48,182 +Next, we'll test offer codes, + +170 +00:10:48,215 --> 00:10:51,919 +to offer promotions +to Social Feed+ subscribers, + +171 +00:10:51,952 --> 00:10:54,521 +then we'll look at +handling price increases + +172 +00:10:54,555 --> 00:10:56,523 +in Food Truck's user interface, + +173 +00:10:56,557 --> 00:11:00,794 +and last, +reducing Social Feed+ involuntary churn + +174 +00:11:00,827 --> 00:11:04,565 +by supporting billing retry +and grace period. + +175 +00:11:04,598 --> 00:11:06,567 +To begin testing refund requests, + +176 +00:11:06,600 --> 00:11:09,837 +we'll navigate to this support view +in our app, + +177 +00:11:09,870 --> 00:11:14,041 +which allows us to choose +a recent transaction to refund. + +178 +00:11:14,074 --> 00:11:16,343 +The code for this is simple. + +179 +00:11:16,376 --> 00:11:20,380 +I just added a refundRequestSheet +view modifier to our view, + +180 +00:11:20,414 --> 00:11:22,850 +and when we press the refund button, + +181 +00:11:22,883 --> 00:11:25,552 +we'll flip the isPresented Binding +to true. + +182 +00:11:25,586 --> 00:11:27,855 +Now, let's see this in action. + +183 +00:11:29,756 --> 00:11:34,528 +When the Binding is true, the refund +request sheet appears above our view. + +184 +00:11:34,561 --> 00:11:39,633 +When testing in the Xcode environment, +the issue we select corresponds 1:1 + +185 +00:11:39,666 --> 00:11:43,871 +with a RevocationReason +in the StoreKit API. + +186 +00:11:43,904 --> 00:11:48,108 +Let's pick "Developer Issue" +and press "Request Refund". + +187 +00:11:49,476 --> 00:11:53,313 +In the App Store, refund requests +will take some time to process, + +188 +00:11:53,347 --> 00:11:55,516 +but when testing with Xcode or Sandbox, + +189 +00:11:55,549 --> 00:12:00,120 +refund requests +will immediately refund the transaction. + +190 +00:12:00,153 --> 00:12:03,423 +In the transaction manager, + +191 +00:12:03,457 --> 00:12:06,894 +we can look at the inspector +for this updated transaction + +192 +00:12:06,927 --> 00:12:09,730 +to see the revocation reason +we just selected, + +193 +00:12:09,763 --> 00:12:11,899 +and the revocation date. + +194 +00:12:13,667 --> 00:12:17,037 +You can also test refunds +by just clicking the refund button + +195 +00:12:17,070 --> 00:12:19,606 +in the transaction manager. + +196 +00:12:19,640 --> 00:12:23,410 +The refund request API helps us provide +great customer support + +197 +00:12:23,443 --> 00:12:25,579 +for people who use Food Truck. + +198 +00:12:25,612 --> 00:12:28,749 +Now that we looked at how to test +refund requests in Xcode, + +199 +00:12:28,782 --> 00:12:32,853 +let's look at some ways you can use +StoreKit to handle refunded transactions. + +200 +00:12:33,954 --> 00:12:35,889 +After refunding a transaction, + +201 +00:12:35,923 --> 00:12:40,994 +an updated Transaction value will emit +from the Transaction.updates sequence. + +202 +00:12:41,028 --> 00:12:44,598 +We can use the revocationDate +and revocationReason properties + +203 +00:12:44,631 --> 00:12:47,467 +to detect these refunded transactions. + +204 +00:12:47,501 --> 00:12:50,437 +It's easy to test +the two revocation reason cases + +205 +00:12:50,470 --> 00:12:55,409 +by selecting the corresponding option +in the refund request sheet in Xcode. + +206 +00:12:55,442 --> 00:12:58,145 +That's how you test +the refund request sheet in Xcode. + +207 +00:12:58,178 --> 00:13:00,681 +This works on iOS and macOS + +208 +00:13:00,714 --> 00:13:03,984 +when using either the Xcode environment +or Sandbox. + +209 +00:13:04,017 --> 00:13:07,187 +For testing with Xcode, +you'll need your iPhone or iPad + +210 +00:13:07,221 --> 00:13:11,225 +to run iOS or iPadOS 15.2 or later. + +211 +00:13:11,258 --> 00:13:15,963 +To test with Xcode on your Mac, +you'll need macOS 12.1 or later. + +212 +00:13:15,996 --> 00:13:19,867 +Now, let's take a look +at testing subscription offer codes. + +213 +00:13:19,900 --> 00:13:24,371 +For this, we'll be using +our local StoreKit Configuration file. + +214 +00:13:24,404 --> 00:13:27,908 +To make a new offer for codes, +we select a subscription, + +215 +00:13:27,941 --> 00:13:32,012 +and press the "+" +under the offer codes table. + +216 +00:13:32,045 --> 00:13:34,248 +We then can configure our offer. + +217 +00:13:34,281 --> 00:13:38,452 +We'll name this "Free month" + +218 +00:13:38,485 --> 00:13:41,288 +and make it a free offer for one month. + +219 +00:13:42,923 --> 00:13:44,791 +Just like in App Store Connect, + +220 +00:13:44,825 --> 00:13:47,060 +we select which customers are eligible, + +221 +00:13:47,094 --> 00:13:51,632 +and whether the introductory offer +can be redeemed with this offer. + +222 +00:13:51,665 --> 00:13:54,468 +Let's leave the default settings for now. + +223 +00:13:54,501 --> 00:13:58,805 +Now that our code is configured, +we'll press "Done". + +224 +00:13:58,839 --> 00:14:01,742 +Of course, if you're syncing +with App Store Connect + +225 +00:14:01,775 --> 00:14:05,946 +your configured offers will show up +in this table automatically. + +226 +00:14:06,813 --> 00:14:08,749 +Now that our offer is configured, + +227 +00:14:08,782 --> 00:14:12,419 +let's navigate to the store view +in the app. + +228 +00:14:12,452 --> 00:14:14,888 +I've added this button +near the bottom of the view + +229 +00:14:14,922 --> 00:14:18,158 +for redeeming a subscription offer. + +230 +00:14:18,192 --> 00:14:21,795 +If we open the store view's +implementation in Xcode, + +231 +00:14:21,828 --> 00:14:26,767 +implementing offer codes is as effortless +as adding an offerCodeRedemption modifier + +232 +00:14:26,800 --> 00:14:30,437 +to our view, +and flipping the isPresented Binding + +233 +00:14:30,470 --> 00:14:33,540 +to true when someone taps the button. + +234 +00:14:33,574 --> 00:14:35,309 +Let's see how this works. + +235 +00:14:37,711 --> 00:14:39,546 +When we press the button, + +236 +00:14:39,580 --> 00:14:42,115 +the redeem sheet appears above our app. + +237 +00:14:42,149 --> 00:14:44,918 +In the App Store, +people can type in offer codes + +238 +00:14:44,952 --> 00:14:46,687 +that you generate in App Store Connect, + +239 +00:14:46,720 --> 00:14:51,024 +but in Xcode the testing experience +is much more streamlined. + +240 +00:14:51,058 --> 00:14:54,595 +We have a list of all the offers for codes +in our configuration file, + +241 +00:14:54,628 --> 00:14:57,531 +grouped by the subscription they unlock. + +242 +00:14:57,564 --> 00:15:01,268 +To redeem, +let's tap the offer we just created, + +243 +00:15:01,301 --> 00:15:03,704 +and press the redeem button. + +244 +00:15:03,737 --> 00:15:06,874 +The payment sheet appears, +and we can see the offer for codes + +245 +00:15:06,907 --> 00:15:11,478 +will start right after +the pay as you go introductory offer. + +246 +00:15:17,384 --> 00:15:20,854 +After subscribing, +we'll get a confirmation screen, + +247 +00:15:20,888 --> 00:15:22,956 +and we can now close the sheet + +248 +00:15:22,990 --> 00:15:27,761 +and verify our app unlocks access +to Social Feed+. + +249 +00:15:30,564 --> 00:15:33,333 +If we look at the inspector +for this new transaction, + +250 +00:15:33,367 --> 00:15:37,037 +we can see the introductory offer +is currently applied. + +251 +00:15:37,070 --> 00:15:38,939 +Since the offer is pay as you go, + +252 +00:15:38,972 --> 00:15:40,674 +the renewals section shows + +253 +00:15:40,707 --> 00:15:43,844 +we'll get two more renewals +of the introductory offer. + +254 +00:15:43,877 --> 00:15:47,381 +After that, +the free month code we just redeemed. + +255 +00:15:47,414 --> 00:15:51,919 +Then, the standard subscription +will renew indefinitely. + +256 +00:15:51,952 --> 00:15:54,421 +The inspector makes it very clear +what's happening + +257 +00:15:54,454 --> 00:15:56,089 +to the state of our subscription, + +258 +00:15:56,123 --> 00:16:00,694 +even with complicated scenarios +like multiple offers. + +259 +00:16:00,727 --> 00:16:05,399 +We just looked at how to configure offer +codes in our local StoreKit configuration, + +260 +00:16:05,432 --> 00:16:08,235 +and how to test redeeming them on iPhone. + +261 +00:16:08,268 --> 00:16:11,371 +Offer codes are a great way to offer +flexible promotions + +262 +00:16:11,405 --> 00:16:13,907 +to our future and existing subscribers, + +263 +00:16:13,941 --> 00:16:16,043 +and now it's easier than ever +to get started + +264 +00:16:16,076 --> 00:16:18,178 +using offer codes in Food Truck. + +265 +00:16:18,212 --> 00:16:22,916 +Now, let's take a look at how to handle +these offers using StoreKit. + +266 +00:16:22,950 --> 00:16:26,220 +After redeeming the code, +both the Transaction.updates + +267 +00:16:26,253 --> 00:16:30,891 +and Status.updates sequences +will emit new values. + +268 +00:16:30,924 --> 00:16:34,528 +We can check the offerType property +on the transaction value + +269 +00:16:34,561 --> 00:16:38,799 +to see if there is an offer applied +to the current transaction. + +270 +00:16:38,832 --> 00:16:43,103 +In the case we just looked at the value +of offerType will be introductory, + +271 +00:16:43,136 --> 00:16:46,874 +because we allowed the subscriber +to redeem the introductory offer + +272 +00:16:46,907 --> 00:16:49,109 +with the offer for codes. + +273 +00:16:49,142 --> 00:16:51,011 +On the renewalInfo value, + +274 +00:16:51,044 --> 00:16:52,880 +we can check the offerType property + +275 +00:16:52,913 --> 00:16:57,017 +to see what kind of offer +will be present in the next renewal. + +276 +00:16:57,050 --> 00:16:58,952 +In the case we just looked at, + +277 +00:16:58,986 --> 00:17:01,889 +we can expect the initial value +to be introductory + +278 +00:17:01,922 --> 00:17:04,925 +since we used a pay as you go offer. + +279 +00:17:04,958 --> 00:17:08,929 +After two subscription periods +we'll see the value switch to code, + +280 +00:17:08,962 --> 00:17:11,932 +as we have a code offer stacked. + +281 +00:17:11,965 --> 00:17:15,736 +When offerType is code, +we can use the offerID property + +282 +00:17:15,769 --> 00:17:19,873 +to get the reference name +of the applied offer for codes. + +283 +00:17:19,907 --> 00:17:23,010 +That's how you test offers for codes in Xcode. + +284 +00:17:23,043 --> 00:17:27,147 +You can configure offers for codes +starting in Xcode 13.3, + +285 +00:17:27,181 --> 00:17:32,219 +and test them on iPhones and iPads +running iOS 15.4 or later. + +286 +00:17:32,252 --> 00:17:35,455 +Now that we've verified +offers for codes work in Food Truck, + +287 +00:17:35,489 --> 00:17:40,561 +let's test how our app handles +a price increase for Social Feed+. + +288 +00:17:40,594 --> 00:17:43,830 +Testing a price increase +is really simple in Xcode. + +289 +00:17:43,864 --> 00:17:48,669 +To get started, we'll increase the price +for the monthly social feed subscription. + +290 +00:17:50,938 --> 00:17:52,506 +This step is optional. + +291 +00:17:52,539 --> 00:17:54,141 +You can leave the price the same + +292 +00:17:54,174 --> 00:17:56,677 +and still simulate a price increase. + +293 +00:17:56,710 --> 00:18:00,480 +Back to the transaction manager, + +294 +00:18:00,514 --> 00:18:04,818 +all we need to do is select the latest +transaction for a subscription + +295 +00:18:04,852 --> 00:18:09,089 +and press "Request Price Increase Consent" +in the toolbar. + +296 +00:18:10,791 --> 00:18:12,960 +We can see in the transaction manager + +297 +00:18:12,993 --> 00:18:16,263 +our transaction is now in +a "Price Increase Pending" state, + +298 +00:18:16,296 --> 00:18:20,901 +and if we look at the device we'll notice +a sheet appeared above our app, + +299 +00:18:20,934 --> 00:18:24,071 +asking to consent to the price increase. + +300 +00:18:24,104 --> 00:18:27,307 +This sheet will appear on its own +without adding any code, + +301 +00:18:27,341 --> 00:18:32,646 +but we took advantage of the new +Messages API to customize its behavior. + +302 +00:18:33,814 --> 00:18:38,719 +Let's take a look at how we integrated +with the Messages API in the code. + +303 +00:18:48,996 --> 00:18:52,366 +We have a for loop iterating +the messages sequence here, + +304 +00:18:52,399 --> 00:18:55,769 +and if we get a message +like price increase, + +305 +00:18:55,802 --> 00:18:58,572 +make sure we don't have +a sensitive view presented, + +306 +00:18:58,605 --> 00:19:00,307 +like the donut editor. + +307 +00:19:00,340 --> 00:19:03,610 +Otherwise, +we'll use the DisplayMessageAction + +308 +00:19:03,644 --> 00:19:06,013 +to display the message. + +309 +00:19:06,046 --> 00:19:08,448 +If the donut editor is presented, + +310 +00:19:08,482 --> 00:19:10,551 +we'll hold on to the Message value + +311 +00:19:10,584 --> 00:19:13,587 +and display it after +the donut editing is finished. + +312 +00:19:14,955 --> 00:19:16,990 +Let's get back to testing. + +313 +00:19:19,626 --> 00:19:24,398 +In the App Store, existing subscribers +may get multiple price increase messages + +314 +00:19:24,431 --> 00:19:27,000 +at different times +until they make a decision + +315 +00:19:27,034 --> 00:19:29,736 +to cancel or consent +to the price increase. + +316 +00:19:29,770 --> 00:19:33,674 +In Xcode, we have full control +of when these messages come. + +317 +00:19:33,707 --> 00:19:36,844 +Each time we press the button +in the transaction manager, + +318 +00:19:36,877 --> 00:19:38,545 +we'll get a message again, + +319 +00:19:38,579 --> 00:19:43,383 +even if the transaction is already +in a price increase state. + +320 +00:19:43,417 --> 00:19:48,555 +Now we can test +if our deferral logic actually works. + +321 +00:19:48,589 --> 00:19:50,958 +So I'll open the donut editor... + +322 +00:19:54,494 --> 00:19:57,164 +And send a message +to open the sheet again. + +323 +00:20:02,302 --> 00:20:04,671 +The sheet doesn't appear yet, + +324 +00:20:04,705 --> 00:20:08,308 +but if we leave the donut editor, + +325 +00:20:08,342 --> 00:20:10,878 +the sheet appears as expected. + +326 +00:20:10,911 --> 00:20:15,582 +While we could accept the price increase, +or cancel the subscription in the sheet, + +327 +00:20:15,616 --> 00:20:18,719 +in reality, users might respond +to the price increase + +328 +00:20:18,752 --> 00:20:21,421 +via an external source, like an email. + +329 +00:20:21,455 --> 00:20:26,593 +To simulate this, we can use +the approve and decline buttons + +330 +00:20:26,627 --> 00:20:29,162 +in the transaction manager. + +331 +00:20:29,196 --> 00:20:31,832 +Since the donut editing experience +was so great, + +332 +00:20:31,865 --> 00:20:38,505 +I'll consent to the new price by pressing +Approve in the transaction manager. + +333 +00:20:38,539 --> 00:20:42,242 +Using StoreKit in Xcode makes testing +a complicated corner case + +334 +00:20:42,276 --> 00:20:45,112 +like a price increase very smooth. + +335 +00:20:45,145 --> 00:20:47,748 +Now that we looked at how +to simulate a price increase, + +336 +00:20:47,781 --> 00:20:52,152 +let's look at how we can use StoreKit +to handle price increases in our app. + +337 +00:20:53,453 --> 00:20:55,789 +When testing the price increase status, + +338 +00:20:55,822 --> 00:21:00,661 +the status updates sequence will emit +new values with every state change. + +339 +00:21:00,694 --> 00:21:02,896 +We can detect these updates +in our app + +340 +00:21:02,930 --> 00:21:07,901 +by checking the priceIncreaseStatus +property on the RenewalInfo value. + +341 +00:21:07,935 --> 00:21:11,638 +If a customer cancels their subscription +due to a price increase, + +342 +00:21:11,672 --> 00:21:13,240 +we'll be able to detect this + +343 +00:21:13,273 --> 00:21:16,476 +by checking for +didNotConsentToPriceIncrease + +344 +00:21:16,510 --> 00:21:19,813 +in the expirationReason property. + +345 +00:21:19,847 --> 00:21:23,483 +We can also write unit tests +around testing price increase. + +346 +00:21:23,517 --> 00:21:27,187 +To start, disabling dialogs +will allow us to test + +347 +00:21:27,221 --> 00:21:31,658 +without actually showing +the price increase UI above our app. + +348 +00:21:31,692 --> 00:21:33,827 +After purchasing the subscription, + +349 +00:21:33,861 --> 00:21:37,297 +we can use the +requestPriceIncreaseConsentForTransaction + +350 +00:21:37,331 --> 00:21:39,666 +API to start the process, + +351 +00:21:39,700 --> 00:21:44,705 +passing in the ID of the latest +transaction for the subscription. + +352 +00:21:44,738 --> 00:21:47,774 +To verify a test transaction +is pending a price increase, + +353 +00:21:47,808 --> 00:21:52,279 +we'll check the +isPendingPriceIncreaseConsent property. + +354 +00:21:52,312 --> 00:21:55,115 +Finally, +depending on what we're testing, + +355 +00:21:55,148 --> 00:21:58,785 +we can call +consentToPriceIncreaseForTransaction + +356 +00:21:58,819 --> 00:22:02,155 +or declinePriceIncreaseForTransaction + +357 +00:22:02,189 --> 00:22:06,827 +to see how our app responds +to finished price increase cases. + +358 +00:22:06,860 --> 00:22:09,029 +That's all for testing price increase. + +359 +00:22:09,062 --> 00:22:14,334 +Price increase is testable with Xcode 13.3 +on all platforms. + +360 +00:22:14,368 --> 00:22:20,240 +Note that the price increase message +is only testable on iOS 15.4 or later. + +361 +00:22:20,274 --> 00:22:26,046 +Finally, let's take a look at subscription +billing retry and grace period. + +362 +00:22:26,079 --> 00:22:29,183 +Billing retry is a state +where an error occurred + +363 +00:22:29,216 --> 00:22:30,951 +when trying to renew a subscription, + +364 +00:22:30,984 --> 00:22:33,387 +like an expired credit card. + +365 +00:22:33,420 --> 00:22:35,822 +In the App Store, during billing retry + +366 +00:22:35,856 --> 00:22:41,195 +the App Store will attempt to fix +the issue and recover the subscription. + +367 +00:22:41,228 --> 00:22:43,730 +You can optionally enable a grace period, + +368 +00:22:43,764 --> 00:22:47,634 +which allows people to keep using +their subscription for a limited time, + +369 +00:22:47,668 --> 00:22:50,304 +at the beginning +of the billing retry state. + +370 +00:22:50,337 --> 00:22:54,508 +Let me demonstrate how to simulate this +when testing in Xcode. + +371 +00:22:54,541 --> 00:22:57,845 +To simulate billing issues +on a subscription renewal, + +372 +00:22:57,878 --> 00:23:01,648 +we open the "Editor" menu on the StoreKit +Configuration we're testing with + +373 +00:23:01,682 --> 00:23:05,919 +and enable "Billing Retry on Renewal". + +374 +00:23:08,956 --> 00:23:11,925 +I want Food Truck to support +a billing grace period, + +375 +00:23:11,959 --> 00:23:15,896 +so let's enable "Billing Grace Period" +in the menu as well. + +376 +00:23:17,931 --> 00:23:20,167 +We'll speed up the subscription rate too, + +377 +00:23:20,200 --> 00:23:22,703 +so we can watch how the state changes. + +378 +00:23:28,909 --> 00:23:33,247 +Let's first subscribe to Social Feed+. + +379 +00:23:44,658 --> 00:23:48,428 +Now, let's wait for it to be time +for a renewal. + +380 +00:23:51,265 --> 00:23:53,233 +When the transaction expires, + +381 +00:23:53,267 --> 00:23:57,237 +notice we first enter +the billing grace period state. + +382 +00:23:57,271 --> 00:24:02,176 +We can look at the transaction inspector +and see the time each state will end. + +383 +00:24:04,011 --> 00:24:06,346 +The billing grace period just expired, + +384 +00:24:06,380 --> 00:24:09,750 +and now we're in +the standard billing retry state. + +385 +00:24:09,783 --> 00:24:14,855 +At any time we can use +the "Resolve Issue For Transaction" button + +386 +00:24:14,888 --> 00:24:17,124 +to simulate fixing the billing error. + +387 +00:24:17,157 --> 00:24:19,860 +Let's test resolving the issue. + +388 +00:24:22,963 --> 00:24:24,932 +Now that the issue is resolved, + +389 +00:24:24,965 --> 00:24:27,234 +we get a new transaction. + +390 +00:24:30,437 --> 00:24:33,440 +So long as we have +"Billing retry on renewal" enabled, + +391 +00:24:33,473 --> 00:24:37,077 +each new transaction +will continue to enter billing retry, + +392 +00:24:37,110 --> 00:24:41,648 +so we can repeat this test +as many times as we'd like. + +393 +00:24:41,682 --> 00:24:44,351 +Handling Billing Retry +and Grace Period properly + +394 +00:24:44,384 --> 00:24:48,889 +is key to retaining subscribers +by reducing involuntary churn. + +395 +00:24:48,922 --> 00:24:52,759 +We just looked at how straightforward +it is to simulate these states with Xcode, + +396 +00:24:52,793 --> 00:24:56,230 +so now let's go over +how to handle them using StoreKit. + +397 +00:24:56,964 --> 00:25:00,167 +As the billing retry +and grace period states change, + +398 +00:25:00,200 --> 00:25:04,137 +the status updates sequence +will emit a new value. + +399 +00:25:04,171 --> 00:25:07,341 +Since we offer +a billing grace period in Food Truck, + +400 +00:25:07,374 --> 00:25:11,278 +we need to make sure +to give subscribers access to Social Feed+ + +401 +00:25:11,311 --> 00:25:13,680 +while they're in the grace period. + +402 +00:25:13,714 --> 00:25:16,783 +We can see how long the subscriber's +grace period should be + +403 +00:25:16,817 --> 00:25:21,688 +using the gracePeriodExpirationDate +property on the renewal info. + +404 +00:25:21,722 --> 00:25:26,026 +To check for billing retry, +we just have to check isInBillingRetry. + +405 +00:25:27,461 --> 00:25:33,500 +We can also detect either of these states +easily with the state property of Status. + +406 +00:25:33,534 --> 00:25:36,370 +If we see a customer +is in either of these states, + +407 +00:25:36,403 --> 00:25:41,542 +we can direct them to a deep link +to the App Store to fix the billing issue. + +408 +00:25:41,575 --> 00:25:44,344 +If you're using +any current entitlement API, + +409 +00:25:44,378 --> 00:25:47,281 +you'll receive transactions +for expired subscriptions + +410 +00:25:47,314 --> 00:25:50,117 +while they're in the grace period. + +411 +00:25:50,150 --> 00:25:54,321 +We can also control billing retry +and grace period in our unit tests + +412 +00:25:54,354 --> 00:25:57,558 +by setting +billingGracePeriodIsEnabled + +413 +00:25:57,591 --> 00:26:03,463 +and shouldEnterBillingRetryOnRenewal +on our StoreKit test session. + +414 +00:26:03,497 --> 00:26:07,134 +After our app notices +a subscription enter billing retry, + +415 +00:26:07,167 --> 00:26:12,172 +the test transaction's +hasPurchaseIssue property will be true. + +416 +00:26:12,206 --> 00:26:14,842 +After waiting for various status updates + +417 +00:26:14,875 --> 00:26:17,377 +and asserting our app updates as expected, + +418 +00:26:17,411 --> 00:26:20,714 +we can use the resolve issue +for transaction method + +419 +00:26:20,747 --> 00:26:24,051 +to simulate the App Store +recovering the subscription. + +420 +00:26:24,084 --> 00:26:28,422 +Billing retry and grace period +are testable in Xcode 13.3 or later + +421 +00:26:28,455 --> 00:26:30,357 +on all platforms. + +422 +00:26:30,390 --> 00:26:33,193 +Later in the session, +Peter will go more into detail + +423 +00:26:33,227 --> 00:26:37,664 +on how to test these states in Sandbox +on iOS and iPadOS 16. + +424 +00:26:39,099 --> 00:26:42,669 +We covered advanced test cases +from requesting refunds + +425 +00:26:42,703 --> 00:26:45,405 +to handling billing retry +and grace period. + +426 +00:26:45,439 --> 00:26:48,242 +For more details on how to use +the new StoreKit APIs + +427 +00:26:48,275 --> 00:26:50,043 +to support some of these cases, + +428 +00:26:50,077 --> 00:26:53,380 +check out +"What's new with in-app purchase." + +429 +00:26:55,282 --> 00:26:57,818 +This was just a quick overview +of what's new for StoreKit Testing + +430 +00:26:57,851 --> 00:27:01,455 +in Xcode this year, +but we didn't cover everything. + +431 +00:27:01,488 --> 00:27:04,124 +There are new subscription renewal rates, + +432 +00:27:04,157 --> 00:27:09,363 +you can test the StoreKit 2 in-app manage +subscriptions sheet in Xcode, + +433 +00:27:09,396 --> 00:27:13,534 +and you can write unit tests +for your SKAdNetwork implementations + +434 +00:27:13,567 --> 00:27:15,669 +using StoreKitTest. + +435 +00:27:15,702 --> 00:27:19,907 +Check out "What's new in +SKAdNetwork" to learn more. + +436 +00:27:19,940 --> 00:27:22,342 +Now Peter will walk you through what's new + +437 +00:27:22,376 --> 00:27:25,646 +in the Sandbox testing environment +this year. + +438 +00:27:25,679 --> 00:27:26,914 +Peter: Thanks, Greg. + +439 +00:27:26,947 --> 00:27:29,850 +Hi, I'm Peter, +an App Store server engineer. + +440 +00:27:29,883 --> 00:27:33,153 +We saw how new features +with StoreKit Testing in Xcode + +441 +00:27:33,187 --> 00:27:36,857 +can help you test more complex +in-app purchase implementations. + +442 +00:27:37,724 --> 00:27:39,826 +We're constantly listening +to your feedback, + +443 +00:27:39,860 --> 00:27:43,330 +and we know many of you rely on +the App Store Sandbox environment + +444 +00:27:43,363 --> 00:27:46,800 +to test your in-app purchases +and server implementations. + +445 +00:27:46,834 --> 00:27:50,137 +I'm excited to share some new +enhancements we're making in Sandbox + +446 +00:27:50,170 --> 00:27:55,275 +so you can more easily test your app +and server in an online test environment. + +447 +00:27:55,309 --> 00:27:59,513 +We'll be introducing enhancements +to Sandbox Apple ID creation, + +448 +00:27:59,546 --> 00:28:01,748 +the App Store Connect API, + +449 +00:28:01,782 --> 00:28:04,518 +and billing failure simulation. + +450 +00:28:04,551 --> 00:28:06,353 +To use the sandbox environment, + +451 +00:28:06,386 --> 00:28:10,190 +we first need to set up a Sandbox Apple ID +in App Store Connect. + +452 +00:28:11,158 --> 00:28:13,794 +You'll notice we moved +the Sandbox tester list + +453 +00:28:13,827 --> 00:28:17,998 +to the navigation bar +on the Users and Access page. + +454 +00:28:18,031 --> 00:28:22,336 +Here, we can create a new tester +with the Plus button. + +455 +00:28:22,369 --> 00:28:25,706 +We streamlined the creation process +by removing several fields + +456 +00:28:25,739 --> 00:28:27,274 +from the new tester window. + +457 +00:28:27,307 --> 00:28:30,244 +We're now only asking +for the minimum amount of info, + +458 +00:28:30,277 --> 00:28:32,312 +so you can move forward +with creating your account + +459 +00:28:32,346 --> 00:28:35,549 +without unnecessary information. + +460 +00:28:35,582 --> 00:28:38,485 +You can also use a "plus symbol" +in your email address, + +461 +00:28:38,519 --> 00:28:43,190 +so you don't need to create +a brand-new email address for each tester. + +462 +00:28:43,223 --> 00:28:47,461 +We know creating strong passwords can +be tedious, and we made this easier too. + +463 +00:28:47,494 --> 00:28:51,899 +We've also added in-line suggestions for +helping make your password more secure. + +464 +00:28:53,400 --> 00:28:55,936 +We hope streamlined +Apple ID creation form, + +465 +00:28:55,969 --> 00:28:57,838 +and better password complexity hints, + +466 +00:28:57,871 --> 00:29:00,674 +will help you spend less time +setting up accounts + +467 +00:29:00,707 --> 00:29:03,243 +and more time developing your app. + +468 +00:29:03,277 --> 00:29:05,546 +App Store Connect is the central location + +469 +00:29:05,579 --> 00:29:08,248 +where you create and manage +Sandbox Apple IDs, + +470 +00:29:08,282 --> 00:29:11,618 +as well as manage your app content +and organization. + +471 +00:29:11,652 --> 00:29:14,988 +Over the last few years, +we've been adding features to Sandbox + +472 +00:29:15,022 --> 00:29:16,390 +that you've been asking for, + +473 +00:29:16,423 --> 00:29:20,527 +like changing Sandbox account region +and clearing purchase history. + +474 +00:29:20,561 --> 00:29:23,463 +Many of these features are accessible +in App Store Connect + +475 +00:29:23,497 --> 00:29:27,668 +or on-device in the Sandbox +Manage Subscriptions page. + +476 +00:29:27,701 --> 00:29:31,171 +Later this year, we will be bringing +several of these Sandbox features + +477 +00:29:31,205 --> 00:29:32,906 +to the App Store Connect API, + +478 +00:29:32,940 --> 00:29:36,143 +including querying +for a list of Sandbox Apple IDs, + +479 +00:29:36,176 --> 00:29:40,080 +clearing purchase history, +and setting interrupted purchase state. + +480 +00:29:40,113 --> 00:29:43,317 +This will enable faster testing +with your sandbox accounts + +481 +00:29:43,350 --> 00:29:47,855 +and help you setup automation clients +for commonly-used testing tools. + +482 +00:29:47,888 --> 00:29:53,060 +Finally, I'm happy to announce support +for billing failure simulation in Sandbox. + +483 +00:29:53,093 --> 00:29:56,830 +In 2018, we announced Billing Retry +and Grace Period + +484 +00:29:56,864 --> 00:30:01,401 +for auto-renewing subscriptions, +to help you reduce involuntary churn. + +485 +00:30:01,435 --> 00:30:05,272 +Since launching in 2019, +Billing Grace Period has allowed you + +486 +00:30:05,305 --> 00:30:09,810 +to recover 300 million days +of paid service to your customers. + +487 +00:30:09,843 --> 00:30:12,613 +This results in incremental revenue +for your business + +488 +00:30:12,646 --> 00:30:16,416 +while your customers experience +no interruption in service. + +489 +00:30:16,450 --> 00:30:20,420 +While many of you are already handling +billing failure cases in production, + +490 +00:30:20,454 --> 00:30:23,524 +we want to provide +more testing scenarios in Sandbox, + +491 +00:30:23,557 --> 00:30:25,659 +so you can test and handle billing +failures + +492 +00:30:25,692 --> 00:30:28,962 +before your app is published +on the App Store. + +493 +00:30:28,996 --> 00:30:32,299 +You'll be able to use a new +Sandbox Account Settings page + +494 +00:30:32,332 --> 00:30:35,736 +to enable billing failure simulation +for your account, + +495 +00:30:35,769 --> 00:30:38,472 +test foreground and background +subscription failures + +496 +00:30:38,505 --> 00:30:40,174 +in the context of your app, + +497 +00:30:40,207 --> 00:30:43,243 +and verify subscription status +with verifyReceipt, + +498 +00:30:43,277 --> 00:30:44,878 +App Store Server API, + +499 +00:30:44,912 --> 00:30:48,949 +and App Store Server Notifications V2 +in Sandbox. + +500 +00:30:48,982 --> 00:30:52,686 +For more info on Billing Retry +and reducing involuntary churn, + +501 +00:30:52,719 --> 00:30:57,691 +I recommend the 2018 WWDC session, +"Engineering Subscriptions". + +502 +00:30:57,724 --> 00:31:01,628 +This year, we're introducing a switch +in the new Sandbox Account Settings + +503 +00:31:01,662 --> 00:31:04,398 +to simulate +a failed in-app purchase attempt. + +504 +00:31:04,431 --> 00:31:08,168 +This is also the new home for +the Sandbox subscriptions page. + +505 +00:31:08,202 --> 00:31:10,437 +With billing failure simulation enabled, + +506 +00:31:10,470 --> 00:31:12,906 +foreground in-app purchases will fail. + +507 +00:31:12,940 --> 00:31:14,875 +This behavior matches the behavior + +508 +00:31:14,908 --> 00:31:17,544 +when your customer's payment method +is declined. + +509 +00:31:17,578 --> 00:31:22,015 +Billing failure simulation also ensures +that auto-renewing subscription states + +510 +00:31:22,049 --> 00:31:24,985 +match those +for billing failures in production. + +511 +00:31:25,018 --> 00:31:28,121 +This means you can test in-app messaging +for your customers + +512 +00:31:28,155 --> 00:31:30,157 +who are having billing problems. + +513 +00:31:30,190 --> 00:31:32,159 +These subscription states +will be reflected + +514 +00:31:32,192 --> 00:31:37,064 +in your in-app purchase receipts, +verified with V2 Notifications. + +515 +00:31:37,097 --> 00:31:39,900 +Let's review the subscription lifecycle. + +516 +00:31:39,933 --> 00:31:43,036 +When you purchase an auto-renewing +subscription in Sandbox, + +517 +00:31:43,070 --> 00:31:45,506 +you already receive V2 Notifications, + +518 +00:31:45,539 --> 00:31:48,242 +like SUBSCRIBED, and DID_RENEW. + +519 +00:31:48,275 --> 00:31:51,044 +When you test failed in-app purchase +attempts for an account + +520 +00:31:51,078 --> 00:31:52,679 +with an active subscription, + +521 +00:31:52,713 --> 00:31:55,916 +the next renewal +will fall into billing retry state. + +522 +00:31:55,949 --> 00:31:59,219 +You'll now receive billing retry +notifications in Sandbox, + +523 +00:31:59,253 --> 00:32:01,221 +like DID_FAIL_TO_RENEW. + +524 +00:32:01,255 --> 00:32:05,225 +If you disable billing failure simulation +before we stop trying to recover + +525 +00:32:05,259 --> 00:32:07,494 +the renewal of your subscription, + +526 +00:32:07,528 --> 00:32:10,130 +the next renewal attempt +will be successful, + +527 +00:32:10,163 --> 00:32:12,633 +and you will receive +a DID_RENEW notification, + +528 +00:32:12,666 --> 00:32:15,135 +with subtype BILLING_RECOVERY. + +529 +00:32:15,169 --> 00:32:20,174 +If we reach the limit for retry attempts, +and billing failure simulation is enabled, + +530 +00:32:20,207 --> 00:32:23,310 +the subscription will expire +and you will receive EXPIRED, + +531 +00:32:23,343 --> 00:32:26,079 +with subtype BILLING_RETRY. + +532 +00:32:26,113 --> 00:32:28,949 +If you're already using +Grace Period in production, + +533 +00:32:28,982 --> 00:32:31,652 +and V2 Notifications in Sandbox, + +534 +00:32:31,685 --> 00:32:35,122 +you can expect to receive +the DID_FAIL_TO_RENEW notification + +535 +00:32:35,155 --> 00:32:37,624 +with subtype GRACE_PERIOD. + +536 +00:32:37,658 --> 00:32:41,895 +Here's an example subscription +in billing retry state, with Grace Period. + +537 +00:32:41,929 --> 00:32:44,598 +You will receive +DID_FAIL_TO_RENEW notifications + +538 +00:32:44,631 --> 00:32:46,867 +with subtype GRACE_PERIOD, + +539 +00:32:46,900 --> 00:32:48,902 +as well as GRACE_PERIOD_EXPIRED, + +540 +00:32:48,936 --> 00:32:53,907 +if billing failure simulation is still +enabled at the end of the grace period. + +541 +00:32:53,941 --> 00:32:57,845 +When verifying subscription information +with App Store Server API, + +542 +00:32:57,878 --> 00:33:00,914 +you can verify subscription state +by decoding the payload + +543 +00:33:00,948 --> 00:33:03,784 +of signedRenewalInfo. + +544 +00:33:03,817 --> 00:33:09,289 +Here, we see the expirationIntent +and billing retry fields are populated. + +545 +00:33:09,323 --> 00:33:14,161 +When calling /verifyReceipt with a receipt +for subscriptions in billing retry state, + +546 +00:33:14,194 --> 00:33:17,431 +you will see that the +is_in_billing_retry_period flag + +547 +00:33:17,464 --> 00:33:19,299 +is set to 1. + +548 +00:33:19,333 --> 00:33:21,368 +Also, when using grace period, + +549 +00:33:21,401 --> 00:33:25,739 +you can now expect grace period +expiration date fields to be populated. + +550 +00:33:26,573 --> 00:33:29,543 +Once you've completed testing +a billing failure in Sandbox, + +551 +00:33:29,576 --> 00:33:32,913 +you can disable the switch +in Sandbox Account Settings. + +552 +00:33:32,946 --> 00:33:35,082 +We hope this new testability +helps you build + +553 +00:33:35,115 --> 00:33:37,985 +the best possible experience +for your customers. + +554 +00:33:38,018 --> 00:33:41,555 +Today we discussed several +new testing capabilities you can use + +555 +00:33:41,588 --> 00:33:46,126 +to streamline testing +your app's in-app purchase functionality. + +556 +00:33:46,159 --> 00:33:49,796 +By syncing your configuration +in App Store Connect with Xcode, + +557 +00:33:49,830 --> 00:33:53,967 +you can use the same in-app purchase +configuration when testing locally + +558 +00:33:54,001 --> 00:33:56,203 +or with the Sandbox environment. + +559 +00:33:56,236 --> 00:34:00,040 +New capabilities like offer code +and refund testing in Xcode + +560 +00:34:00,073 --> 00:34:03,777 +will help you verify complex +StoreKit implementations. + +561 +00:34:03,810 --> 00:34:08,215 +And, subscription management testability +will enable you to evolve your app + +562 +00:34:08,248 --> 00:34:10,317 +to ensure a great customer experience, + +563 +00:34:10,350 --> 00:34:12,786 +even if their service is interrupted. + +564 +00:34:12,819 --> 00:34:16,557 +For details on how a billing failure +impacts subscription receipts, + +565 +00:34:16,590 --> 00:34:20,527 +plus, App Store Server Notifications V2 +in Sandbox, + +566 +00:34:20,561 --> 00:34:23,497 +I recommend the WWDC 21 session, + +567 +00:34:23,530 --> 00:34:26,233 +"Manage in-app purchases on your server." + +568 +00:34:26,266 --> 00:34:29,770 +Also, to hear about what's new +in App Store Server API + +569 +00:34:29,803 --> 00:34:34,141 +and V2 Notifications, check out +"What's new with in-app purchase." + +570 +00:34:34,174 --> 00:34:37,411 +We look forward to hearing your feedback +about these new features. + +571 +00:34:37,444 --> 00:34:39,012 +Thank you for joining. + diff --git a/eng/2022 Session 10040 Explore in-app purchase integration and migration en.srt b/eng/2022 Session 10040 Explore in-app purchase integration and migration en.srt new file mode 100644 index 0000000..5aa0881 --- /dev/null +++ b/eng/2022 Session 10040 Explore in-app purchase integration and migration en.srt @@ -0,0 +1,3616 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,011 +Gabriel Ting: +Hello, and welcome to our session, + +3 +00:00:11,044 --> 00:00:14,214 +"Explore in-app purchase +integration and migration." + +4 +00:00:14,248 --> 00:00:17,584 +This session is divided into two parts: +one dedicated to migrating + +5 +00:00:17,618 --> 00:00:20,287 +to the App Store Server API, +and one dedicated to migrating + +6 +00:00:20,320 --> 00:00:22,956 +to App Store Server Notifications +Version 2. + +7 +00:00:22,990 --> 00:00:24,458 +My name is Gabriel, and I am discussing + +8 +00:00:24,491 --> 00:00:27,628 +how to migrate +to the App Store Server API. + +9 +00:00:27,661 --> 00:00:29,830 +Alex Baker: My name is Alex, +and I will walk you through + +10 +00:00:29,863 --> 00:00:33,200 +migrating to App Store +Server Notifications Version 2. + +11 +00:00:33,233 --> 00:00:36,670 +Gabriel: Let's first begin with a brief +introduction of the App Store Server API + +12 +00:00:36,703 --> 00:00:38,639 +and App Store Server Notifications. + +13 +00:00:38,672 --> 00:00:42,342 +We introduced the App Store Server API +last year as a powerful, secure, + +14 +00:00:42,376 --> 00:00:45,946 +and efficient way to obtain data +and perform operations from your server. + +15 +00:00:45,979 --> 00:00:48,482 +We aim to give you +just the data that you need, + +16 +00:00:48,515 --> 00:00:51,885 +signed in the JSON Web Signature +or JWS format, + +17 +00:00:51,919 --> 00:00:55,022 +so that you can verify that the data +you receive is untampered with, + +18 +00:00:55,055 --> 00:00:58,125 +intended for you, +and signed by the App Store. + +19 +00:00:58,158 --> 00:01:00,894 +For example, +one of our App Store Server API endpoints, + +20 +00:01:00,928 --> 00:01:03,864 +the Get Transaction History endpoint, +in combination with the new + +21 +00:01:03,897 --> 00:01:07,134 +filter and sort features, +allow you to fetch any set of transactions + +22 +00:01:07,167 --> 00:01:10,337 +that you specify, +with just an originalTransactionId. + +23 +00:01:10,370 --> 00:01:13,473 +On the pane of +App Store Server Notifications Version 2, + +24 +00:01:13,507 --> 00:01:16,944 +with the increased number of states +of a subscription that can be represented, + +25 +00:01:16,977 --> 00:01:19,012 +Notifications Version 2 will update you + +26 +00:01:19,046 --> 00:01:22,416 +of all possible states of a subscription, +in real time. + +27 +00:01:22,449 --> 00:01:25,152 +We aim to proactively provide +all the information that you need + +28 +00:01:25,185 --> 00:01:27,454 +to know about what's going on +with your subscribers, + +29 +00:01:27,487 --> 00:01:29,990 +without having to ask us +for the information. + +30 +00:01:30,023 --> 00:01:33,427 +Alex will walk you through this +more in his portion of this session. + +31 +00:01:33,460 --> 00:01:36,363 +If using these features with ease +and efficacy interests you, + +32 +00:01:36,396 --> 00:01:37,731 +this session is for you. + +33 +00:01:37,764 --> 00:01:41,068 +We will walk you through how to get +started using the App Store Server API + +34 +00:01:41,101 --> 00:01:43,570 +and App Store Server Notifications +Version 2, + +35 +00:01:43,604 --> 00:01:46,840 +as well as some migration tips +and best practices. + +36 +00:01:46,874 --> 00:01:48,642 +For additional information +on each of these, + +37 +00:01:48,675 --> 00:01:51,612 +please refer to these +additional sessions listed below. + +38 +00:01:53,080 --> 00:01:57,551 +So let's get started discussing +migrating to the App Store Server API. + +39 +00:01:57,584 --> 00:02:02,422 +First off, we will be discussing how +to start using the App Store Server API. + +40 +00:02:02,456 --> 00:02:06,960 +Second, we will dive into some +of the details of signing JSON Web Tokens. + +41 +00:02:06,994 --> 00:02:09,696 +Third, we will demonstrate +how to verify signed transactions + +42 +00:02:09,730 --> 00:02:13,166 +you receive from the App Store +are genuine. + +43 +00:02:13,200 --> 00:02:16,103 +Finally, we will discuss how to migrate +from verifyReceipt + +44 +00:02:16,136 --> 00:02:19,106 +to the App Store Server API. + +45 +00:02:19,139 --> 00:02:20,474 +Let's get started. + +46 +00:02:20,507 --> 00:02:23,143 +First, let's get into talking about +how to use the App Store Server API + +47 +00:02:23,177 --> 00:02:27,247 +with different versions of StoreKit, +first with just original StoreKit, + +48 +00:02:27,281 --> 00:02:30,651 +then with just StoreKit 2, +then discussing methods to support both + +49 +00:02:30,684 --> 00:02:34,821 +simultaneously for both clients using +iOS versions that support StoreKit 2, + +50 +00:02:34,855 --> 00:02:38,358 +namely those on iOS 15 or greater, +and those that do not. + +51 +00:02:39,092 --> 00:02:41,128 +First off, +let's take a look at what requests + +52 +00:02:41,161 --> 00:02:43,630 +to the App Store Server API look like. + +53 +00:02:43,664 --> 00:02:47,701 +We see that the five APIs listed here +utilize originalTransactionId + +54 +00:02:47,734 --> 00:02:49,102 +as a path parameter. + +55 +00:02:49,136 --> 00:02:51,471 +This allows you to easily call these APIs + +56 +00:02:51,505 --> 00:02:54,474 +using the originalTransactionIds +that you receive from receipts, + +57 +00:02:54,508 --> 00:02:58,345 +signed transactions, +signed renewals, and notifications. + +58 +00:02:58,378 --> 00:03:00,881 +Next is the Look Up Order ID endpoint. + +59 +00:03:00,914 --> 00:03:04,017 +This endpoint instead uses +the orderId provided by a customer + +60 +00:03:04,051 --> 00:03:05,819 +for support queries. + +61 +00:03:05,853 --> 00:03:09,089 +This is so that you can better assist +with questions directly from the customer, + +62 +00:03:09,122 --> 00:03:13,026 +as the customer is provided an orderId +in customer receipts for each transaction, + +63 +00:03:13,060 --> 00:03:16,029 +but is not provided +an originalTransactionId. + +64 +00:03:16,063 --> 00:03:18,765 +This ensures that you can +directly respond to customer inquiries + +65 +00:03:18,799 --> 00:03:21,635 +with data customers have on hand. + +66 +00:03:21,668 --> 00:03:24,271 +The last endpoints listed here +are notification related, + +67 +00:03:24,304 --> 00:03:27,307 +which Alex will touch on +in his portion of this session. + +68 +00:03:28,442 --> 00:03:31,778 +Next, let's look at where you can get +the originalTransactionIds from + +69 +00:03:31,812 --> 00:03:33,413 +in Original StoreKit. + +70 +00:03:33,447 --> 00:03:36,116 +When you call verifyReceipt +with the unified app receipt, + +71 +00:03:36,149 --> 00:03:39,853 +the originalTransactionId you'll use +when calling the App Store Server API + +72 +00:03:39,887 --> 00:03:41,455 +comes back in the in_app field + +73 +00:03:41,488 --> 00:03:44,358 +in the receipts for each of +the transactions this user purchased, + +74 +00:03:44,391 --> 00:03:48,262 +as well as in the latest_receipt_info +and pending_renewal_info. + +75 +00:03:48,295 --> 00:03:51,498 +Now that we know how to get +the originalTransactionId + +76 +00:03:51,532 --> 00:03:54,668 +from Original StoreKit transactions, +let's look at the whole flow + +77 +00:03:54,701 --> 00:03:57,638 +between a customer, +the App Store Server, and your server. + +78 +00:03:59,173 --> 00:04:02,609 +First, obtain the app receipt +on your server. + +79 +00:04:02,643 --> 00:04:06,580 +Next, take the app receipt and call +verifyReceipt with it from your server. + +80 +00:04:08,549 --> 00:04:11,018 +This will return the decoded receipt. + +81 +00:04:11,051 --> 00:04:14,555 +From the decoded receipt, +gather all of the originalTransactionIds + +82 +00:04:14,588 --> 00:04:17,624 +in precisely the same ways +I previously showed. + +83 +00:04:17,658 --> 00:04:20,661 +Next, you can call +the Get Transaction History endpoint + +84 +00:04:20,694 --> 00:04:24,331 +with any one of the gathered +originalTransactionIds, which will return + +85 +00:04:24,364 --> 00:04:27,835 +the history of transactions +for this user as signed transactions. + +86 +00:04:27,868 --> 00:04:31,171 +These transactions include +non-consumables, refunded consumables, + +87 +00:04:31,205 --> 00:04:34,575 +non-renewing subscriptions, +and auto-renewing subscriptions. + +88 +00:04:34,608 --> 00:04:38,712 +Then, if you want to get the latest signed +transaction and signed renewal information + +89 +00:04:38,745 --> 00:04:43,083 +for a specific subscription, call +the Get All Subscription Statuses endpoint + +90 +00:04:43,116 --> 00:04:45,953 +with the corresponding +originalTransactionId. + +91 +00:04:45,986 --> 00:04:48,388 +This will return all of the signed +transactions and renewals + +92 +00:04:48,422 --> 00:04:52,526 +for the subscription that corresponds +to the given originalTransactionId. + +93 +00:04:52,559 --> 00:04:55,796 +Next, let's look at where +the originalTransactionId is + +94 +00:04:55,829 --> 00:04:58,031 +in the case of StoreKit 2 transactions. + +95 +00:04:58,065 --> 00:05:01,435 +This is the code on the client +to obtain an originalTransactionId + +96 +00:05:01,468 --> 00:05:03,003 +from a transaction. + +97 +00:05:03,036 --> 00:05:05,038 +On devices that support StoreKit 2– + +98 +00:05:05,072 --> 00:05:08,242 +namely devices that are on +iOS 15 or later– + +99 +00:05:08,275 --> 00:05:11,278 +you can get +the originalID property on a verified, + +100 +00:05:11,311 --> 00:05:15,082 +decoded transaction +to get the originalTransactionId. + +101 +00:05:15,115 --> 00:05:19,920 +Now, looking at the server side, here is +an example of a signed JWS transaction, + +102 +00:05:19,953 --> 00:05:24,057 +which is the data type that you receive +in signed transactions and signed renewals + +103 +00:05:24,091 --> 00:05:28,195 +from the App Store Server API +and App Store Server Notifications. + +104 +00:05:28,228 --> 00:05:32,132 +Here, we see that originalTransactionId +is a top level field. + +105 +00:05:34,434 --> 00:05:37,204 +Next, let's look at the whole flow +between a customer, + +106 +00:05:37,237 --> 00:05:41,275 +the App Store Server, and your server +for StoreKit 2 transactions. + +107 +00:05:41,308 --> 00:05:44,478 +First, take the signed transaction +on the device. + +108 +00:05:44,511 --> 00:05:48,081 +With StoreKit 2, +you can verify this transaction on-device. + +109 +00:05:48,115 --> 00:05:51,185 +Using the on-device status listener, +transaction listener, + +110 +00:05:51,218 --> 00:05:54,488 +or last transaction will keep you +updated on latest transactions, + +111 +00:05:54,521 --> 00:05:58,492 +cancellations, and refunds, which can then +be sent to your server for record-keeping. + +112 +00:05:58,525 --> 00:06:01,061 +For example, these are updated +on subscription renewals, + +113 +00:06:01,094 --> 00:06:04,231 +subscription offer redemptions, +expirations, and more. + +114 +00:06:05,532 --> 00:06:07,367 +Send the transaction to your server. + +115 +00:06:07,401 --> 00:06:09,970 +In combination +with App Store Server Notifications, + +116 +00:06:10,003 --> 00:06:12,439 +which Alex will dive more into +in the following section, + +117 +00:06:12,472 --> 00:06:15,108 +you can keep up-to-date +with the latest status and state + +118 +00:06:15,142 --> 00:06:19,479 +of a subscription without having +to call the App Store Server API. + +119 +00:06:19,513 --> 00:06:22,516 +When you do need to perform +an operation on a subscription, + +120 +00:06:22,549 --> 00:06:25,452 +such as extending +the renewal date of a subscription, + +121 +00:06:25,485 --> 00:06:29,423 +you can use the originalTransactionId +from the signed transaction to call + +122 +00:06:29,456 --> 00:06:33,293 +corresponding endpoints +and get back the data that you need. + +123 +00:06:33,327 --> 00:06:36,196 +Now that we've seen how to use +the App Store Server API + +124 +00:06:36,230 --> 00:06:39,433 +with original StoreKit and StoreKit 2, +let's talk about how to support + +125 +00:06:39,466 --> 00:06:42,336 +both StoreKit and StoreKit 2. + +126 +00:06:42,369 --> 00:06:44,605 +You can take advantage +of the App Store Server API + +127 +00:06:44,638 --> 00:06:47,241 +without fully adopting StoreKit 2. + +128 +00:06:47,274 --> 00:06:50,077 +As shown previously, +you can get the originalTransactionId + +129 +00:06:50,110 --> 00:06:52,779 +from a receipt in Original StoreKit. + +130 +00:06:52,813 --> 00:06:57,651 +You can also get the originalTransactionID +in StoreKit 2 from a JWS transaction. + +131 +00:06:59,386 --> 00:07:04,258 +You can also use the App Store Server API +independently of any other APIs. + +132 +00:07:04,291 --> 00:07:07,928 +It is not tied to using a specific version +of other APIs. + +133 +00:07:07,961 --> 00:07:09,963 +In terms of App Store +Server Notifications, + +134 +00:07:09,997 --> 00:07:13,567 +it can be used with either version 1 +or version 2 notifications. + +135 +00:07:13,600 --> 00:07:16,670 +We do recommend using version 2 +because it notifies you of changes + +136 +00:07:16,703 --> 00:07:21,241 +to subscriptions as they occur, +uses the secure JWS format, and more, + +137 +00:07:21,275 --> 00:07:24,945 +which Alex will dive into more +in his part of this session. + +138 +00:07:24,978 --> 00:07:27,648 +However, you can use +the App Store Server API separately, + +139 +00:07:27,681 --> 00:07:31,218 +with version 1 notifications +or without notifications at all. + +140 +00:07:31,251 --> 00:07:34,688 +Next, let's discuss how you can +process new purchases + +141 +00:07:34,721 --> 00:07:38,292 +after you completed the migration steps +I previously walked through. + +142 +00:07:38,325 --> 00:07:41,728 +To support new purchases +on devices using original StoreKit, + +143 +00:07:41,762 --> 00:07:45,299 +you can take new receipts as they come, +sending them to your server, + +144 +00:07:45,332 --> 00:07:48,202 +and do precisely the same steps +as I previously showed, + +145 +00:07:48,235 --> 00:07:50,637 +while collecting new data along the way– + +146 +00:07:50,671 --> 00:07:52,806 +call verifyReceipt with the new receipts, + +147 +00:07:52,840 --> 00:07:57,277 +and get the decoded receipt with the new +originalTransactionId in latest_receipt, + +148 +00:07:57,311 --> 00:08:01,148 +correlate those originalTransactionIds +with other originalTransactionIds + +149 +00:08:01,181 --> 00:08:03,050 +in the in_app section of the receipt + +150 +00:08:03,083 --> 00:08:06,019 +to group together +your transaction information. + +151 +00:08:06,053 --> 00:08:08,722 +Then, you can take +the new originalTransactionId, + +152 +00:08:08,755 --> 00:08:11,325 +and call the App Store Server API +as needed, + +153 +00:08:11,358 --> 00:08:14,561 +such as if you need to call +the Get All Subscription Statuses endpoint + +154 +00:08:14,595 --> 00:08:17,998 +to get you the latest status +of the corresponding subscription. + +155 +00:08:18,031 --> 00:08:20,968 +Now that we've covered +how to use the App Store Server API + +156 +00:08:21,001 --> 00:08:23,437 +with both Original StoreKit +and StoreKit 2, + +157 +00:08:23,470 --> 00:08:26,673 +let's dive into some of the details +of signing JSON Web Tokens, + +158 +00:08:26,707 --> 00:08:29,776 +a requirement +to call the App Store Server API. + +159 +00:08:29,810 --> 00:08:33,514 +In order to authenticate +that your developer account is the caller + +160 +00:08:33,547 --> 00:08:36,583 +of the App Store Server API, +we use JSON Web Tokens, + +161 +00:08:36,617 --> 00:08:39,887 +also known as JWTs, +to authenticate requests. + +162 +00:08:39,920 --> 00:08:42,256 +This token must be included +in every request + +163 +00:08:42,289 --> 00:08:45,559 +as an authorization header +in calls from your server. + +164 +00:08:45,592 --> 00:08:49,429 +A JWT consists of a header, +a payload, and signature. + +165 +00:08:49,463 --> 00:08:54,001 +Next, we'll go into how to construct +a JWT specific to your application. + +166 +00:08:55,068 --> 00:08:57,871 +Here, we can see +how a JSON Web Token is composed, + +167 +00:08:57,905 --> 00:09:00,541 +as well as the structure +of the header and payload. + +168 +00:09:00,574 --> 00:09:04,778 +The token itself can be broken +into three parts, separated by periods: + +169 +00:09:04,811 --> 00:09:09,016 +the base 64 encoded header, +the base 64 encoded payload, + +170 +00:09:09,049 --> 00:09:11,952 +and then the signature, +which is composed of the base 64 + +171 +00:09:11,985 --> 00:09:16,690 +encoded header and payload, +signed using your signing secret. + +172 +00:09:16,723 --> 00:09:19,593 +The header is composed of these fields +that contain metadata + +173 +00:09:19,626 --> 00:09:21,929 +about how to sign your data. + +174 +00:09:21,962 --> 00:09:24,598 +One of the important fields here +is the key ID, + +175 +00:09:24,631 --> 00:09:27,501 +which is your private key ID +in App Store Connect. + +176 +00:09:27,534 --> 00:09:31,038 +This needs to match the key +you use to sign the JWT. + +177 +00:09:32,706 --> 00:09:34,741 +The payload contains +additional information + +178 +00:09:34,775 --> 00:09:37,411 +about your specific application. + +179 +00:09:37,444 --> 00:09:40,314 +Please refer to the article +"Creating API Keys to Use + +180 +00:09:40,347 --> 00:09:43,684 +With the App Store Server API" +for additional information and guidance + +181 +00:09:43,717 --> 00:09:46,553 +on how to obtain your API Key. + +182 +00:09:46,587 --> 00:09:48,388 +For details on each of these fields, + +183 +00:09:48,422 --> 00:09:52,025 +please refer to the article +"Generating Tokens for API Requests." + +184 +00:09:53,260 --> 00:09:57,030 +Once you have the header and payload +with all the appropriate information, + +185 +00:09:57,064 --> 00:09:58,699 +next you will sign the JWT + +186 +00:09:58,732 --> 00:10:01,969 +using the certificate +that corresponds to the keyId. + +187 +00:10:02,002 --> 00:10:06,340 +Here is the core pseudocode +that you can use, regardless of language. + +188 +00:10:06,373 --> 00:10:09,209 +First, make sure that you have +the private key that corresponds + +189 +00:10:09,243 --> 00:10:12,679 +to the key id provided +in the header that we just looked at. + +190 +00:10:12,713 --> 00:10:16,383 +Then, call the signing function +that your JWT library exposes + +191 +00:10:16,416 --> 00:10:19,119 +with your private key, header, +and payload. + +192 +00:10:19,152 --> 00:10:21,522 +Since the header contains +the signing algorithm, + +193 +00:10:21,555 --> 00:10:25,158 +the JWT library signs it +according to the provided algorithm. + +194 +00:10:27,127 --> 00:10:31,298 +Finally, here is an example usage of this +token when authenticating a cURL call + +195 +00:10:31,331 --> 00:10:34,434 +to the Get All Subscription Statuses +endpoint. + +196 +00:10:34,468 --> 00:10:36,870 +Replace $\{token\} +and $\{originalTransactionId\} + +197 +00:10:36,904 --> 00:10:38,906 +with the values of the token you generated + +198 +00:10:38,939 --> 00:10:41,909 +and your desired originalTransactionId, +respectively. + +199 +00:10:41,942 --> 00:10:45,479 +Next, let's talk about how to verify +that the signed transactions + +200 +00:10:45,512 --> 00:10:49,316 +you receive are for you +and signed by the App Store. + +201 +00:10:49,349 --> 00:10:51,552 +Signed transactions are, in essence, + +202 +00:10:51,585 --> 00:10:54,454 +JavaScript Object Notation, +or JSON objects + +203 +00:10:54,488 --> 00:10:57,157 +that are cryptographically signed +such that if they are tampered with + +204 +00:10:57,191 --> 00:11:00,494 +between the App Store and your server, +you can detect it. + +205 +00:11:00,527 --> 00:11:04,831 +Signed transactions are signed in +the JSON Web Signature, or JWS format. + +206 +00:11:04,865 --> 00:11:09,303 +The signed transactions that the App Store +sends you will arrive in the JWS format. + +207 +00:11:09,336 --> 00:11:12,906 +By verifying the JWSs you receive, +you will verify that the data + +208 +00:11:12,940 --> 00:11:16,810 +came from the App Store, +and the contents are untampered with. + +209 +00:11:16,844 --> 00:11:20,514 +Now, let's look at how to verify +a signed transaction. + +210 +00:11:20,547 --> 00:11:23,183 +First, base 64 decode the header. + +211 +00:11:23,217 --> 00:11:27,721 +Then, one can determine what signing +algorithm to use via the alg claim. + +212 +00:11:27,754 --> 00:11:31,425 +This will be used +as part of verifying the JWS. + +213 +00:11:31,458 --> 00:11:34,928 +The certificate chain in the x5c claim +is issued by Apple, + +214 +00:11:34,962 --> 00:11:37,464 +and validation of the claim +indicates that the data is + +215 +00:11:37,497 --> 00:11:39,900 +properly signed and untampered with. + +216 +00:11:39,933 --> 00:11:42,302 +Please refer to the App Store +developer documentation + +217 +00:11:42,336 --> 00:11:46,773 +for further information +on how to verify JWSs. + +218 +00:11:46,807 --> 00:11:50,344 +In essence, +the x5c chain is a chain of certificates. + +219 +00:11:50,377 --> 00:11:53,413 +Successful verification +of the certificate chain tells you + +220 +00:11:53,447 --> 00:11:57,351 +that the data can be trusted +and that the data is signed by Apple. + +221 +00:11:57,384 --> 00:12:01,421 +Order matters for the certificate chain. +First comes the root certificate. + +222 +00:12:01,455 --> 00:12:04,391 +This root certificate may be followed +by additional certificates, + +223 +00:12:04,424 --> 00:12:08,028 +where each of these certificates +are signed by the previous certificate. + +224 +00:12:08,061 --> 00:12:12,065 +I will refer to the last certificate +in the chain as the leaf certificate. + +225 +00:12:13,467 --> 00:12:16,503 +The first certificate is referred to +as the root certificate + +226 +00:12:16,537 --> 00:12:18,105 +and is self-signed. + +227 +00:12:18,138 --> 00:12:20,908 +This certificate should match +the root certificate you obtain + +228 +00:12:20,941 --> 00:12:22,743 +from Apple's Certificate Authority. + +229 +00:12:22,776 --> 00:12:27,014 +If the certificates do not match, +the chain should not be trusted. + +230 +00:12:27,047 --> 00:12:29,683 +The leaf certificate, +the last certificate in the chain, + +231 +00:12:29,716 --> 00:12:33,020 +is the certificate +that is used to sign the JWS. + +232 +00:12:33,053 --> 00:12:35,489 +Here is an example of what the header + +233 +00:12:35,522 --> 00:12:38,692 +of a JWS the App Store sends +may look like. + +234 +00:12:38,725 --> 00:12:42,296 +First is the algorithm +used to sign the JWS. + +235 +00:12:42,329 --> 00:12:47,334 +Next is the x5c certificate chain, +with the certificates listed in order. + +236 +00:12:48,168 --> 00:12:51,972 +Now, let's look at what generating +an x5c certificate chain looks like + +237 +00:12:52,005 --> 00:12:53,941 +from a high-level overview. + +238 +00:12:53,974 --> 00:12:57,711 +We start off with the root certificate +from Apple's Certificate Authority. + +239 +00:12:57,744 --> 00:13:02,216 +Then, the root certificate is used to sign +the intermediate signing certificate. + +240 +00:13:02,249 --> 00:13:07,020 +The intermediate signing certificate +is then used to sign the leaf certificate. + +241 +00:13:08,455 --> 00:13:12,426 +Now that we've covered what generating +an x5c certificate chain looks like, + +242 +00:13:12,459 --> 00:13:14,828 +let's look at what verifying +a chain looks like. + +243 +00:13:14,862 --> 00:13:17,631 +Starting at the leaf certificate, +we ensure that it was signed + +244 +00:13:17,664 --> 00:13:20,467 +by the intermediate signing certificate. + +245 +00:13:20,501 --> 00:13:23,170 +Then we ensure +that the intermediate signing certificate + +246 +00:13:23,203 --> 00:13:25,439 +was signed by the root certificate. + +247 +00:13:25,472 --> 00:13:27,140 +Additionally, +the root certificate should match + +248 +00:13:27,174 --> 00:13:29,810 +the one from +the Apple Certificate Authority. + +249 +00:13:29,843 --> 00:13:31,845 +If all of these steps are successful, + +250 +00:13:31,879 --> 00:13:35,516 +then the entire chain +is verified as legitimate. + +251 +00:13:35,549 --> 00:13:38,919 +Let's talk about a method +to verify the certificate chain. + +252 +00:13:38,952 --> 00:13:43,657 +Here is a command to verify +the x5c certificate chain using OpenSSL. + +253 +00:13:43,690 --> 00:13:46,727 +Breaking this into pieces, +the command verify, broadly speaking, + +254 +00:13:46,760 --> 00:13:49,830 +allows you to pass in +certificates for verification. + +255 +00:13:49,863 --> 00:13:53,267 +The flag trusted allows you to provide +a certificate to trust– + +256 +00:13:53,300 --> 00:13:57,738 +in other words, a certificate that will be +used to verify the following certificates. + +257 +00:13:57,771 --> 00:14:00,574 +in this case, we are passing in +the root certificate you obtained + +258 +00:14:00,607 --> 00:14:04,344 +from the Apple Certificate Authority, +and thus can be trusted. + +259 +00:14:04,378 --> 00:14:07,381 +We'll use this +to verify the WWDR certificate, + +260 +00:14:07,414 --> 00:14:09,416 +the next certificate in the chain. + +261 +00:14:10,951 --> 00:14:14,955 +The untrusted flag allows you +to provide the certificate or certificates + +262 +00:14:14,988 --> 00:14:18,892 +that you wish to verify +using the certificate that you trust. + +263 +00:14:18,926 --> 00:14:21,728 +Here, we first pass in +the WWDR certificate + +264 +00:14:21,762 --> 00:14:25,666 +from the Apple Certificate Authority, +which is signed by the root certificate. + +265 +00:14:25,699 --> 00:14:29,937 +This should match the second certificate +in the x5c chain. + +266 +00:14:29,970 --> 00:14:33,240 +And finally, the leaf certificate here +is the last certificate, + +267 +00:14:33,273 --> 00:14:36,743 +which is signed +by the previous certificate. + +268 +00:14:36,777 --> 00:14:41,215 +In the case of a successful verification, +a success code is returned. + +269 +00:14:41,248 --> 00:14:44,551 +You may then proceed +to use the decoded information. + +270 +00:14:44,585 --> 00:14:46,787 +In the case +of an unsuccessful verification, + +271 +00:14:46,820 --> 00:14:49,389 +determine the issue +based on the returned error code. + +272 +00:14:49,423 --> 00:14:53,927 +If not possible to verify, this data may +be tampered with and should not be used. + +273 +00:14:53,961 --> 00:14:56,496 +Please refer to the App Store +developer documentation + +274 +00:14:56,530 --> 00:15:01,602 +for complete instructions on verifying +an x5c certificate chain using OpenSSL. + +275 +00:15:02,402 --> 00:15:06,840 +Here is some pseudocode for how one might +go about verifying a signed transaction. + +276 +00:15:06,874 --> 00:15:10,210 +First, obtain the JWS you wish to verify. + +277 +00:15:10,244 --> 00:15:14,448 +Then, take the certificate your JWS +library requires for verification. + +278 +00:15:14,481 --> 00:15:17,417 +Call the verify function +of your JWS library, + +279 +00:15:17,451 --> 00:15:19,520 +using the appropriate certificate. + +280 +00:15:19,553 --> 00:15:22,756 +The certificate that signs +the JWS is the leaf certificate, + +281 +00:15:22,789 --> 00:15:25,826 +though some libraries require +passing in the entire chain. + +282 +00:15:27,828 --> 00:15:31,865 +In the case that the call succeeded, +then you can proceed on with your tasks. + +283 +00:15:31,899 --> 00:15:35,502 +In the case that this was the result +of a call to the App Store Server API, + +284 +00:15:35,536 --> 00:15:38,238 +then you can proceed +to store the validated data. + +285 +00:15:38,272 --> 00:15:41,408 +As for the case of notifications, +Alex will go more into this + +286 +00:15:41,441 --> 00:15:44,211 +in his portion of this session. + +287 +00:15:44,244 --> 00:15:48,115 +In the case that the JWS cannot +be validated, do not use the JWS. + +288 +00:15:48,148 --> 00:15:52,319 +This may mean that it has been tampered +with or was not sent by the App Store. + +289 +00:15:52,352 --> 00:15:57,024 +Alex will dive more into how to better +ensure security when using notifications. + +290 +00:15:57,057 --> 00:15:59,326 +Please refer to the App Store +developer documentation + +291 +00:15:59,359 --> 00:16:03,764 +for complete instructions +on verifying and handling a JWS. + +292 +00:16:04,498 --> 00:16:08,068 +Now, let's review some use cases +for migration from verifyReceipt + +293 +00:16:08,101 --> 00:16:10,437 +to the App Store Server API. + +294 +00:16:10,470 --> 00:16:13,207 +First, let's look at the case +where you want to check + +295 +00:16:13,240 --> 00:16:16,009 +what the latest status is +for any given subscriber. + +296 +00:16:16,043 --> 00:16:20,347 +This keeps you up to date with +any changes to an individual subscription. + +297 +00:16:20,380 --> 00:16:22,716 +Previously, +to obtain the most up-to-date status + +298 +00:16:22,749 --> 00:16:25,519 +of a subscriber, +one had to call verifyReceipt + +299 +00:16:25,552 --> 00:16:28,188 +and determine the status +of the subscription based on fields + +300 +00:16:28,222 --> 00:16:32,192 +such as expiration intent, +grace_period_expires_date, et cetera. + +301 +00:16:32,226 --> 00:16:36,330 +Now, with the App Store Server API, +the Get All Subscription Statuses endpoint + +302 +00:16:36,363 --> 00:16:38,966 +can be called to obtain +the latest status of a subscription, + +303 +00:16:38,999 --> 00:16:41,335 +with a status field containing +the current status, + +304 +00:16:41,368 --> 00:16:43,103 +as well as the latest, most up-to-date + +305 +00:16:43,136 --> 00:16:45,873 +signed transaction +and renewal information. + +306 +00:16:45,906 --> 00:16:48,575 +Let's look at a flow +of how you could execute on this. + +307 +00:16:48,609 --> 00:16:51,545 +First, for any decoded receipt +that you have, you can obtain + +308 +00:16:51,578 --> 00:16:55,716 +the originalTransactionIds from it +just in the way that I showed previously. + +309 +00:16:55,749 --> 00:16:58,752 +Then, you can call +the Get All Subscription Statuses endpoint + +310 +00:16:58,785 --> 00:17:01,989 +for that originalTransactionId, +which will return the latest + +311 +00:17:02,022 --> 00:17:04,825 +signed transactions +and renewals for that transaction. + +312 +00:17:04,858 --> 00:17:08,562 +Next, let's look at the case +of obtaining the latest transactions. + +313 +00:17:08,595 --> 00:17:12,466 +Obtaining the latest transactions +informs you of what a user has purchased, + +314 +00:17:12,499 --> 00:17:16,904 +what has renewed, if there are any changes +to a user's subscription, and more. + +315 +00:17:16,937 --> 00:17:19,940 +Previously, to obtain +the latest transactions for a user, + +316 +00:17:19,973 --> 00:17:21,742 +one had to call verifyReceipt + +317 +00:17:21,775 --> 00:17:24,778 +and use the in_app array +and examine latest_receipt_info, + +318 +00:17:24,811 --> 00:17:27,915 +which contained all of +the transactions for a user. + +319 +00:17:27,948 --> 00:17:31,118 +With the App Store Server API, +to obtain the latest transactions, + +320 +00:17:31,151 --> 00:17:33,887 +the Get Transaction History endpoint +allows you to fetch + +321 +00:17:33,921 --> 00:17:36,423 +the full purchase history for a user. + +322 +00:17:36,456 --> 00:17:40,160 +Furthermore, pagination combined +with the new filter and sort features + +323 +00:17:40,194 --> 00:17:43,897 +that is covered in the WWDC22 talk, +"What's new with in-app purchase," + +324 +00:17:43,931 --> 00:17:47,601 +ensures that you can efficiently fetch +precisely the data that you need. + +325 +00:17:48,902 --> 00:17:51,004 +Let's look at a flow +of what this may entail. + +326 +00:17:51,038 --> 00:17:53,874 +With any originalTransactionId +belonging to that user, + +327 +00:17:53,907 --> 00:17:56,710 +you can call +the Get Transaction History endpoint, + +328 +00:17:56,743 --> 00:17:59,479 +which will return the history +of transactions for this user + +329 +00:17:59,513 --> 00:18:04,518 +as signed transactions, filtered, sorted, +and paginated to your specifications. + +330 +00:18:06,153 --> 00:18:09,189 +Finally, let’s look at the case +of adopting appAccountToken. + +331 +00:18:09,223 --> 00:18:12,226 +The appAccountToken field +allows you to provide a UUID + +332 +00:18:12,259 --> 00:18:15,762 +that associates a StoreKit 2 transaction +with a user. + +333 +00:18:15,796 --> 00:18:18,098 +Then, on signed transactions, +signed renewals, + +334 +00:18:18,131 --> 00:18:22,336 +and notifications for that transaction, +the appAccountToken will appear. + +335 +00:18:22,369 --> 00:18:25,806 +Previously, there was not support for +appAccountToken with original StoreKit, , + +336 +00:18:25,839 --> 00:18:28,976 +as it was a feature +that was new to StoreKit2. + +337 +00:18:29,009 --> 00:18:33,380 +Now, we added support for supplying +a UUID in the field applicationUsername + +338 +00:18:33,413 --> 00:18:36,016 +in Original StoreKit +to support compatibility + +339 +00:18:36,049 --> 00:18:38,118 +with Original StoreKit clients. + +340 +00:18:38,151 --> 00:18:40,888 +Under this condition, +that UUID will support + +341 +00:18:40,921 --> 00:18:43,457 +all the functionality +that appAccountToken does. + +342 +00:18:43,490 --> 00:18:47,728 +The appAccountToken then comes back in +verifyReceipt for Original StoreKit users, + +343 +00:18:47,761 --> 00:18:51,231 +and also appears for both +Original StoreKit and StoreKit 2 users + +344 +00:18:51,265 --> 00:18:53,267 +in calls to the App Store Server API + +345 +00:18:53,300 --> 00:18:55,903 +and notifications +from App Store Server Notifications. + +346 +00:18:56,670 --> 00:18:59,706 +That's it for the App Store Server API +portion of this session. + +347 +00:18:59,740 --> 00:19:04,511 +Next, here's Alex to cover migrating to +App Store Server Notifications Version 2. + +348 +00:19:04,545 --> 00:19:05,479 +Alex: Thanks, Gabriel. + +349 +00:19:05,512 --> 00:19:07,848 +My name is Alex, +and I'm excited to be here today + +350 +00:19:07,881 --> 00:19:11,518 +to discuss +App Store Server Notifications Version 2. + +351 +00:19:11,552 --> 00:19:15,789 +First, we'll be covering how to get +started with version 2 notifications. + +352 +00:19:15,822 --> 00:19:21,395 +Next, how version 2 notifications differ +and build upon other models available. + +353 +00:19:21,428 --> 00:19:24,898 +Third, we'll talk about recovering +in the case of missed notifications + +354 +00:19:24,932 --> 00:19:29,102 +and some of the new resources +available to help accomplish this task. + +355 +00:19:29,136 --> 00:19:32,606 +Last, how notifications can provide +insight into customer behavior + +356 +00:19:32,639 --> 00:19:34,842 +and create additional opportunities +for being informed + +357 +00:19:34,875 --> 00:19:37,377 +about the subscription lifecycle. + +358 +00:19:37,411 --> 00:19:40,681 +Let's go over a brief introduction +into what notifications are + +359 +00:19:40,714 --> 00:19:42,616 +and who can use them. + +360 +00:19:42,649 --> 00:19:45,419 +App Store Server Notifications +are messages we send you + +361 +00:19:45,452 --> 00:19:48,989 +whenever certain actions +are taken by users of your app. + +362 +00:19:49,022 --> 00:19:52,025 +These notifications broadly fall +into two categories, + +363 +00:19:52,059 --> 00:19:54,428 +subscription updates and refund updates, + +364 +00:19:54,461 --> 00:19:57,564 +although we're always working +to cover additional scenarios. + +365 +00:19:57,598 --> 00:20:00,868 +We provide these notifications +to help fill in gaps into user actions + +366 +00:20:00,901 --> 00:20:03,504 +that may not be available +to you in the app. + +367 +00:20:03,537 --> 00:20:06,640 +As an example, +one of our most common use cases + +368 +00:20:06,673 --> 00:20:08,408 +is the renewal of a subscription. + +369 +00:20:08,442 --> 00:20:12,012 +A user may not be in the app +when this transaction becomes available. + +370 +00:20:12,045 --> 00:20:15,015 +App Store Server Notifications +help account for this issue + +371 +00:20:15,048 --> 00:20:17,618 +by proactively sending +the latest transaction information + +372 +00:20:17,651 --> 00:20:20,654 +directly to your servers +when the subscription renews. + +373 +00:20:20,687 --> 00:20:24,558 +Version 2 notifications share many +similarities with the StoreKit 2 model + +374 +00:20:24,591 --> 00:20:27,828 +and the App Store Server API +you just heard about from Gabriel. + +375 +00:20:27,861 --> 00:20:31,465 +However, while they work well together, +they are all independent tools + +376 +00:20:31,498 --> 00:20:33,567 +that can be adopted at different times. + +377 +00:20:33,600 --> 00:20:36,403 +Most importantly, +you can continue to support clients + +378 +00:20:36,436 --> 00:20:40,107 +where StoreKit 2 is not available, +pre iOS-15 clients, + +379 +00:20:40,140 --> 00:20:43,377 +while utilizing +Version 2 Server Notifications. + +380 +00:20:43,410 --> 00:20:46,213 +We've worked to make +Version 2 notifications one of our most + +381 +00:20:46,246 --> 00:20:49,316 +in-depth and flexible tools +for providing information + +382 +00:20:49,349 --> 00:20:52,553 +about a user throughout +the entire subscription lifecycle. + +383 +00:20:52,586 --> 00:20:54,855 +We'll go more in-depth in this +later in the presentation, + +384 +00:20:54,888 --> 00:20:58,559 +but notifications provide information +that is almost impossible to capture + +385 +00:20:58,592 --> 00:21:01,295 +for actions taken outside of the app. + +386 +00:21:01,328 --> 00:21:03,664 +I hope I've interested you +in the concept of notifications + +387 +00:21:03,697 --> 00:21:06,867 +and Version 2 notifications in particular. + +388 +00:21:06,900 --> 00:21:09,069 +Before we go further, +while this presentation + +389 +00:21:09,102 --> 00:21:11,471 +walks you through getting started +and best practices + +390 +00:21:11,505 --> 00:21:14,508 +for receiving notifications, +it doesn't tell the whole story. + +391 +00:21:14,541 --> 00:21:16,443 +Please refer to these recent videos +for more information + +392 +00:21:16,476 --> 00:21:20,447 +about notifications and how they can +meet various use cases. + +393 +00:21:20,480 --> 00:21:23,116 +Let's look at getting +Version 2 notifications set up. + +394 +00:21:23,150 --> 00:21:25,152 +We're going to walk through +how to set up your notifications + +395 +00:21:25,185 --> 00:21:27,621 +all the way to receiving your first one. + +396 +00:21:27,654 --> 00:21:32,092 +First, go to your app's page +in App Store Connect. + +397 +00:21:32,125 --> 00:21:37,064 +Scrolling down, you will see a section +for App Store Server Notifications. + +398 +00:21:37,097 --> 00:21:41,101 +Here you'll see options +for both production and sandbox. + +399 +00:21:41,134 --> 00:21:43,704 +Each environment can contain +a separate URL + +400 +00:21:43,737 --> 00:21:47,107 +and a separate notification version. + +401 +00:21:47,140 --> 00:21:50,711 +Here is an example of the options page +for production settings. + +402 +00:21:50,744 --> 00:21:53,480 +Sandbox settings are exactly the same. + +403 +00:21:53,514 --> 00:21:57,184 +We recommend, especially if you are +a Version 1 notification user, + +404 +00:21:57,217 --> 00:22:01,555 +that you first try Version 2 notifications +in the sandbox environment. + +405 +00:22:01,588 --> 00:22:04,591 +This is a great place +to become comfortable with notifications + +406 +00:22:04,625 --> 00:22:07,528 +without impacting your production setup. + +407 +00:22:07,561 --> 00:22:11,198 +Select the Set Up Sandbox button, +provide your server's URL, + +408 +00:22:11,231 --> 00:22:13,834 +and select Version 2 Notifications. + +409 +00:22:15,169 --> 00:22:18,005 +Before triggering notifications, +confirm you have a valid + +410 +00:22:18,038 --> 00:22:21,108 +HTTPS certificate +for your server endpoint. + +411 +00:22:21,141 --> 00:22:25,245 +Also confirm you have allowed +Apple's public IPs access to your server. + +412 +00:22:25,279 --> 00:22:27,848 +Some of the most common failures +when setting up notifications + +413 +00:22:27,881 --> 00:22:30,050 +relate to firewalls and certificates. + +414 +00:22:30,083 --> 00:22:33,420 +These are also great to check if you +suddenly stop receiving notifications + +415 +00:22:33,453 --> 00:22:36,423 +as an initial troubleshooting step. + +416 +00:22:36,456 --> 00:22:39,393 +Now you are ready to receive +your first notification. + +417 +00:22:39,426 --> 00:22:41,628 +In Sandbox, notifications can be triggered + +418 +00:22:41,662 --> 00:22:44,898 +by a variety of actions, +like buying an in-app subscription. + +419 +00:22:44,932 --> 00:22:47,134 +However, for ease of use while testing, + +420 +00:22:47,167 --> 00:22:49,403 +we recommend triggering +a notification using the new + +421 +00:22:49,436 --> 00:22:54,007 +Request a Test Notification endpoint, +part of the App Store Server API. + +422 +00:22:54,041 --> 00:22:57,110 +This endpoint helps to automate +the notification testing process. + +423 +00:22:57,144 --> 00:22:59,613 +After triggering the Request +a Test Notification endpoint, + +424 +00:22:59,646 --> 00:23:02,583 +you should expect to see +a notification arrive soon. + +425 +00:23:02,616 --> 00:23:04,685 +If you are having issues +receiving notifications, + +426 +00:23:04,718 --> 00:23:07,554 +please refer the to new +Get Test Notification Status endpoint, + +427 +00:23:07,588 --> 00:23:10,991 +which can provide a brief status about why +the notification failed to be delivered. + +428 +00:23:11,024 --> 00:23:14,194 +For example, +a status like SSL_ISSUE would be + +429 +00:23:14,228 --> 00:23:17,831 +a clue to double-check +your HTTPS certificates. + +430 +00:23:17,865 --> 00:23:20,267 +We recommend +triggering a test notification + +431 +00:23:20,300 --> 00:23:22,536 +whenever you are performing +a configuration change. + +432 +00:23:22,569 --> 00:23:25,272 +This is a great way to confirm +you can still receive notifications + +433 +00:23:25,305 --> 00:23:26,907 +after the change. + +434 +00:23:26,940 --> 00:23:30,811 +Now, let's move on to understanding +the notification you've just received. + +435 +00:23:32,179 --> 00:23:34,515 +Just like transactions +we saw earlier from Gabriel, + +436 +00:23:34,548 --> 00:23:37,885 +notifications are also in the JWS format. + +437 +00:23:37,918 --> 00:23:42,322 +Let's go over how to decode +and verify a notification payload. + +438 +00:23:42,356 --> 00:23:44,625 +First, when receiving a notification, + +439 +00:23:44,658 --> 00:23:49,263 +you'll want to extract +the signedPayload field of the JSON body. + +440 +00:23:49,296 --> 00:23:52,866 +Next, you'll perform the exact same steps +Gabriel walked you through earlier + +441 +00:23:52,900 --> 00:23:55,135 +for verifying a signed transaction. + +442 +00:23:55,169 --> 00:23:57,538 +You'll follow the same steps +to verify signed data + +443 +00:23:57,571 --> 00:24:00,841 +whether it is a signed notification +payload from a notification + +444 +00:24:00,874 --> 00:24:04,945 +or a signed transaction +from the App Store Server API. + +445 +00:24:04,978 --> 00:24:09,750 +Next, it is important to verify +which app the notification is for. + +446 +00:24:09,783 --> 00:24:12,553 +If you have multiple apps +sharing the same endpoint, + +447 +00:24:12,586 --> 00:24:15,856 +this is a good place +to determine the target app. + +448 +00:24:15,889 --> 00:24:19,293 +It is also important to confirm that +the app the notification is targeted for + +449 +00:24:19,326 --> 00:24:24,264 +is your app, and the notification +was not intended for another developer. + +450 +00:24:24,298 --> 00:24:27,701 +Last, one more useful check is +to make sure that the environment + +451 +00:24:27,734 --> 00:24:30,137 +of the notification matches +your expected environment, + +452 +00:24:30,170 --> 00:24:32,606 +either Production or Sandbox. + +453 +00:24:32,639 --> 00:24:35,709 +Because App Store Connect allows +separate URLs for each environment, + +454 +00:24:35,742 --> 00:24:37,945 +it is possible to enforce +this requirement, + +455 +00:24:37,978 --> 00:24:41,481 +or, if the URLs are shared, +guarantee you are + +456 +00:24:41,515 --> 00:24:46,019 +storing and processing notifications +separately based on the environment. + +457 +00:24:46,053 --> 00:24:50,691 +At this point, the JWS is fully validated +and can be stored for further processing. + +458 +00:24:50,724 --> 00:24:53,260 +We recommend, besides some basic checks, + +459 +00:24:53,293 --> 00:24:56,463 +that your server process +the notification asynchronously. + +460 +00:24:56,496 --> 00:25:00,834 +If processing of the notification takes +too long, our server will record a timeout + +461 +00:25:00,868 --> 00:25:03,670 +and assume the notification +was not delivered successfully. + +462 +00:25:03,704 --> 00:25:06,840 +We will then resend the notification. + +463 +00:25:06,874 --> 00:25:10,577 +Therefore, moving time-intensive +processing outside this function + +464 +00:25:10,611 --> 00:25:13,247 +helps to ensure that the App Store server +records your notifications + +465 +00:25:13,280 --> 00:25:16,216 +as sent successfully +and removes the need for your server + +466 +00:25:16,250 --> 00:25:19,553 +to reprocess the notifications +upon a retry. + +467 +00:25:19,586 --> 00:25:24,558 +Now, let's go over the body +of the notification after verification. + +468 +00:25:24,591 --> 00:25:28,695 +The first fields are notification type +and the optional subtype. + +469 +00:25:28,729 --> 00:25:32,032 +Combined, these tell you the scenario +the notification is for. + +470 +00:25:32,065 --> 00:25:35,903 +These fields also help show what has +changed since the last notification + +471 +00:25:35,936 --> 00:25:38,872 +and provide information about +why these changes occurred. + +472 +00:25:38,906 --> 00:25:43,377 +The notificationUUID is a unique +identifier per notification. + +473 +00:25:43,410 --> 00:25:45,479 +If the server retries a notification, + +474 +00:25:45,512 --> 00:25:49,583 +the retried notification contains +the same notificationUUID. + +475 +00:25:49,616 --> 00:25:52,920 +This helps detect cases where +your server processed the notification, + +476 +00:25:52,953 --> 00:25:57,858 +but did not respond with a successful +HTTP response code in a timely manner. + +477 +00:25:57,891 --> 00:26:01,428 +We recommend adding duplicate +notification detection due to retries, + +478 +00:26:01,461 --> 00:26:03,497 +based on this field. + +479 +00:26:03,530 --> 00:26:06,967 +The signedDate field tells you +when the notification was created. + +480 +00:26:07,000 --> 00:26:10,470 +This is especially useful +for detecting retried notifications. + +481 +00:26:10,504 --> 00:26:13,507 +Next, the appAppleId and bundleId. + +482 +00:26:13,540 --> 00:26:16,310 +These are important +for detecting the target application. + +483 +00:26:16,343 --> 00:26:19,146 +As we discussed earlier, it is important +that you check these fields + +484 +00:26:19,179 --> 00:26:23,884 +and confirm they match expected values +to prevent replay attacks. + +485 +00:26:23,917 --> 00:26:26,854 +Additionally, make sure the environment +of the notification matches + +486 +00:26:26,887 --> 00:26:29,756 +the expected environment, +that sandbox notifications aren't being + +487 +00:26:29,790 --> 00:26:32,292 +recorded as production data, +and vice versa. + +488 +00:26:33,861 --> 00:26:38,599 +Last, the actual signedTransactionInfo +and optional signedRenewalInfo. + +489 +00:26:38,632 --> 00:26:41,001 +These will be the latest status +of the underlying purchase + +490 +00:26:41,034 --> 00:26:43,103 +at the time of signing. + +491 +00:26:43,136 --> 00:26:45,439 +At this point, +having parsed the notification, + +492 +00:26:45,472 --> 00:26:48,242 +you are left with the latest transaction +and renewal information, + +493 +00:26:48,275 --> 00:26:51,411 +and the latest reason +for the change in status. + +494 +00:26:51,445 --> 00:26:55,082 +Now that we've covered setting up +and receiving a specific notification, + +495 +00:26:55,115 --> 00:26:57,451 +let's examine +the Version 2 notification model, + +496 +00:26:57,484 --> 00:27:00,854 +how notifications can fit together +to track the subscription lifecycle, + +497 +00:27:00,888 --> 00:27:03,557 +and the design decisions +behind Version 2 notifications + +498 +00:27:03,590 --> 00:27:06,660 +through a comparison with +Version 1 notifications. + +499 +00:27:06,693 --> 00:27:09,530 +Version 2 adopts a different philosophy +when sending information + +500 +00:27:09,563 --> 00:27:10,931 +about the state of a purchase. + +501 +00:27:10,964 --> 00:27:14,301 +Instead of sending the entire +recent history every notification, + +502 +00:27:14,334 --> 00:27:17,938 +Version 2 notifications focus +only on sending the latest information: + +503 +00:27:17,971 --> 00:27:19,506 +the latest transaction information, + +504 +00:27:19,540 --> 00:27:23,343 +and in the case of subscriptions, +the pending renewal information as well. + +505 +00:27:24,912 --> 00:27:26,980 +With notifications, +we work to provide information + +506 +00:27:27,014 --> 00:27:30,050 +on every step +of the subscription lifecycle. + +507 +00:27:30,083 --> 00:27:33,453 +Therefore, notifications only contain +the latest information + +508 +00:27:33,487 --> 00:27:36,123 +about the purchase or subscription. + +509 +00:27:36,156 --> 00:27:39,226 +Together, these notifications create +a complete timeline + +510 +00:27:39,259 --> 00:27:41,161 +of a subscription's status. + +511 +00:27:41,195 --> 00:27:44,598 +If you need to view the entire +transaction history and don't have access + +512 +00:27:44,631 --> 00:27:46,834 +to the notification history, +this pairs well with + +513 +00:27:46,867 --> 00:27:49,169 +the Get Transaction History endpoint +which lets you query + +514 +00:27:49,203 --> 00:27:54,074 +a user's entire transaction history +in a paginated and filterable context. + +515 +00:27:54,107 --> 00:27:58,879 +Second, version 1 notifications +do not require clients use StoreKit 2. + +516 +00:27:58,912 --> 00:28:01,515 +And that's right, neither does version 2. + +517 +00:28:01,548 --> 00:28:04,852 +In fact, no matter what framework +is used on the client side, + +518 +00:28:04,885 --> 00:28:09,122 +you can start enjoying the benefits +of version 2 notifications today. + +519 +00:28:09,156 --> 00:28:13,994 +Last, version 2 notifications work +to enhance the level of detail provided + +520 +00:28:14,027 --> 00:28:16,897 +and expand cases covered +by adding additional types + +521 +00:28:16,930 --> 00:28:19,299 +and adding the new subtype field. + +522 +00:28:19,333 --> 00:28:21,535 +Through this, +we are able to cover more scenarios + +523 +00:28:21,568 --> 00:28:24,605 +and provide notifications at every step +of the subscription lifecycle. + +524 +00:28:24,638 --> 00:28:28,041 +Some of the notable scenarios +we've added include expiration, + +525 +00:28:28,075 --> 00:28:31,411 +more granular information related +to changes in auto renewal status, + +526 +00:28:31,445 --> 00:28:34,281 +and more scenarios +around the refund process. + +527 +00:28:34,314 --> 00:28:37,651 +Now, to illustrate the complexity +of the scenarios covered + +528 +00:28:37,684 --> 00:28:40,921 +and provide a more concrete example, +let's look at how notifications + +529 +00:28:40,954 --> 00:28:43,156 +can inform you of each step +a subscription takes, + +530 +00:28:43,190 --> 00:28:45,325 +from start to finish. + +531 +00:28:45,359 --> 00:28:49,162 +Let's imagine a user +before a subscription. + +532 +00:28:49,196 --> 00:28:52,833 +Upon subscribing, the user moves +into the renewing subscription state + +533 +00:28:52,866 --> 00:28:55,969 +and a SUBSCRIBED with subtype INITIAL_BUY +notification is sent, + +534 +00:28:56,003 --> 00:29:00,107 +or an OFFER_REDEEMED with subtype +INITIAL_BUY if an offer was used. + +535 +00:29:00,140 --> 00:29:03,343 +Contained in the notification +would be the first signed transaction + +536 +00:29:03,377 --> 00:29:06,246 +and the signed renewal information. + +537 +00:29:06,280 --> 00:29:10,384 +Time passes, and the subscription renews, +staying in the renewing state. + +538 +00:29:10,417 --> 00:29:12,920 +Upon each renewal, +we send a DID_RENEW notification + +539 +00:29:12,953 --> 00:29:16,723 +with the next +signed transaction information. + +540 +00:29:16,757 --> 00:29:19,159 +Whenever the user deactivates +auto renewal, + +541 +00:29:19,193 --> 00:29:22,029 +they move to the expiring +subscription state, and you will receive + +542 +00:29:22,062 --> 00:29:23,463 +a DID_CHANGE_RENEWAL_STATUS + +543 +00:29:23,497 --> 00:29:26,700 +with subtype AUTO_RENEW_DISABLED +notification. + +544 +00:29:28,402 --> 00:29:31,405 +If they don't reenable auto renewal, +at the end of the period they move + +545 +00:29:31,438 --> 00:29:34,241 +to the expired state, +and you will receive an EXPIRED + +546 +00:29:34,274 --> 00:29:36,643 +with subtype VOLUNTARY notification. + +547 +00:29:36,677 --> 00:29:38,278 +Now, you might be wondering, + +548 +00:29:38,312 --> 00:29:40,714 +where are all the other +notification types? + +549 +00:29:43,050 --> 00:29:46,520 +Here is the subscription lifecycle, +as seen through notifications. + +550 +00:29:46,553 --> 00:29:49,289 +There is a lot going on. + +551 +00:29:49,323 --> 00:29:52,626 +And this diagram doesn't even tell +the whole story. + +552 +00:29:52,659 --> 00:29:57,130 +The refund/revocation lifecycle +isn't included here, for example. + +553 +00:29:57,164 --> 00:29:59,566 +This diagram illustrates +the vast array of scenarios + +554 +00:29:59,600 --> 00:30:02,603 +that version 2 notifications cover +and that they work to inform you + +555 +00:30:02,636 --> 00:30:05,439 +of each step +of the subscription lifecycle. + +556 +00:30:06,507 --> 00:30:08,342 +The other point I would make +is that we work to cover + +557 +00:30:08,375 --> 00:30:10,477 +every possible transition state. + +558 +00:30:10,511 --> 00:30:14,147 +This helps to increase the utility of +notifications by becoming a single source + +559 +00:30:14,181 --> 00:30:16,950 +for tracking subscriptions +and improves confidence + +560 +00:30:16,984 --> 00:30:19,987 +that you are seeing every step +of the subscriber's journey. + +561 +00:30:20,020 --> 00:30:22,623 +However, even though +all of this data is here, + +562 +00:30:22,656 --> 00:30:25,158 +you don't need to work +with every type available. + +563 +00:30:25,192 --> 00:30:28,662 +Even just processing notifications +related to renewal preference changes, + +564 +00:30:28,695 --> 00:30:31,064 +for example, can provide value. + +565 +00:30:31,098 --> 00:30:33,166 +Especially if +you are just getting started, + +566 +00:30:33,200 --> 00:30:37,271 +begin with the notification types +that are most useful for your situation. + +567 +00:30:37,304 --> 00:30:40,207 +Now, let's cover what happens +after you've got your server set up, + +568 +00:30:40,240 --> 00:30:44,011 +everything is running smoothly, +but, alas, your server goes down. + +569 +00:30:44,044 --> 00:30:47,014 +Whether it was for a few days, +a few minutes, or you think you might have + +570 +00:30:47,047 --> 00:30:50,884 +missed just one, let's go over some steps +to help resolve this issue. + +571 +00:30:50,918 --> 00:30:52,719 +Let's image your server. + +572 +00:30:52,753 --> 00:30:56,690 +It is set up successfully +and is receiving notifications. + +573 +00:30:56,723 --> 00:31:01,695 +At some point, your server has an issue +and is unable to receive notifications. + +574 +00:31:01,728 --> 00:31:04,031 +We are still attempting +to send messages to your server, + +575 +00:31:04,064 --> 00:31:06,333 +but now those requests begin to fail. + +576 +00:31:06,366 --> 00:31:08,368 +There are several ways +to deal with this scenario. + +577 +00:31:08,402 --> 00:31:10,170 +The first is just to wait. + +578 +00:31:10,204 --> 00:31:13,006 +If we don't receive +a successful status code from your server + +579 +00:31:13,040 --> 00:31:14,441 +or couldn't connect to it at all, + +580 +00:31:14,474 --> 00:31:18,645 +we will retry notifications +according to our documented retry policy. + +581 +00:31:18,679 --> 00:31:21,815 +For version 2 notifications, +we retry after each attempt, + +582 +00:31:21,849 --> 00:31:24,985 +first after a 1-hour delay, +then a 12-hour delay, + +583 +00:31:25,018 --> 00:31:28,789 +24-, 48-, and finally 72-hour delay. + +584 +00:31:28,822 --> 00:31:31,225 +Waiting works great +for outages less than an hour, + +585 +00:31:31,258 --> 00:31:34,661 +as notifications will be retried +an hour after the first failure. + +586 +00:31:36,029 --> 00:31:37,865 +At some point your server recovers, + +587 +00:31:37,898 --> 00:31:41,101 +and you start receiving +notifications again. + +588 +00:31:41,134 --> 00:31:46,306 +First, you receive a new notification, +unrelated to the missed notifications. + +589 +00:31:46,340 --> 00:31:49,776 +Notifications are retried with a delay, +so as soon as your server comes online, + +590 +00:31:49,810 --> 00:31:52,679 +you will not immediately receive +all missed notifications. + +591 +00:31:53,914 --> 00:31:57,718 +Some time passes and you start to receive +the notifications you missed, + +592 +00:31:57,751 --> 00:31:59,853 +interspersed with new notifications. + +593 +00:32:01,221 --> 00:32:04,391 +This brings up the question, +how can you detect if a notification + +594 +00:32:04,424 --> 00:32:07,628 +is the original or a retried notification? + +595 +00:32:07,661 --> 00:32:09,496 +Let's examine a notification. + +596 +00:32:10,531 --> 00:32:13,600 +In this notification, +we're just showing a few fields. + +597 +00:32:14,935 --> 00:32:17,838 +Notifications contain a signedDate field. + +598 +00:32:17,871 --> 00:32:20,007 +This field can be useful +for detecting retries, + +599 +00:32:20,040 --> 00:32:23,677 +by comparing the signing date with +the time the notification was received. + +600 +00:32:23,710 --> 00:32:26,980 +If you see notifications with +a signing date significantly earlier + +601 +00:32:27,014 --> 00:32:28,916 +than the date +you received the notification, + +602 +00:32:28,949 --> 00:32:31,518 +this indicates you may have +experienced an outage. + +603 +00:32:32,619 --> 00:32:36,256 +Imagine in this scenario +the notifications labeled 6 and 3 + +604 +00:32:36,290 --> 00:32:38,225 +were for the same subscription. + +605 +00:32:38,258 --> 00:32:41,862 +This could be determined by comparing +the originalTransactionIds. + +606 +00:32:41,895 --> 00:32:46,567 +In this case, just because notification 3 +was received after notification 6 + +607 +00:32:46,600 --> 00:32:51,238 +does not mean it contains +newer information than notification 6. + +608 +00:32:51,271 --> 00:32:54,241 +Other times, a notification +may have been received by your server, + +609 +00:32:54,274 --> 00:32:58,712 +but it failed to respond +with a successful HTTP 200 status code. + +610 +00:32:58,745 --> 00:33:02,015 +This can cause a notification +to be redelivered to your server. + +611 +00:33:02,049 --> 00:33:06,119 +As discussed earlier, make sure to check +the notificationUUID field + +612 +00:33:06,153 --> 00:33:08,155 +to deduplicate these requests. + +613 +00:33:08,188 --> 00:33:11,058 +You may see significant numbers +of retried notifications + +614 +00:33:11,091 --> 00:33:13,994 +even though you successfully +recorded the notifications. + +615 +00:33:14,027 --> 00:33:15,929 +In this case, make sure you are responding + +616 +00:33:15,963 --> 00:33:19,900 +with an HTTP 200 response +every time you receive a notification. + +617 +00:33:19,933 --> 00:33:23,103 +Additionally, make sure that you are +doing so in a timely manner, + +618 +00:33:23,136 --> 00:33:27,474 +and are not doing extensive processing +before responding successfully, + +619 +00:33:27,508 --> 00:33:30,777 +to prevent us from recording a timeout +and resending the notification. + +620 +00:33:30,811 --> 00:33:35,949 +Sometimes, especially with longer outages, +the next retry may be hours or days away, + +621 +00:33:35,983 --> 00:33:39,553 +or for an extended outage, +retries may have been exhausted. + +622 +00:33:39,586 --> 00:33:42,155 +The next option for recovering +from missed notifications + +623 +00:33:42,189 --> 00:33:44,691 +is the Get Notification History endpoint. + +624 +00:33:45,826 --> 00:33:48,729 +We've just announced the new +Get Notification History endpoint, + +625 +00:33:48,762 --> 00:33:52,232 +which provides a six-month history +of notifications we have sent your server. + +626 +00:33:52,266 --> 00:33:54,601 +Refer to "What's new +with in-app purchases" video + +627 +00:33:54,635 --> 00:33:56,069 +for an overview of this endpoint + +628 +00:33:56,103 --> 00:33:58,372 +along with other great features +we are announcing. + +629 +00:33:58,405 --> 00:34:01,642 +Here we will be focusing on +best practices when using this endpoint + +630 +00:34:01,675 --> 00:34:04,411 +and scenarios where it can assist. + +631 +00:34:04,444 --> 00:34:05,913 +After an outage is resolved, + +632 +00:34:05,946 --> 00:34:08,849 +note the start and end timestamps +of the outage. + +633 +00:34:08,882 --> 00:34:11,318 +The Get Notification History endpoint +allows queries to be made + +634 +00:34:11,351 --> 00:34:13,053 +over a specific timespan. + +635 +00:34:13,086 --> 00:34:15,455 +By specifying the start +and end times of the outage, + +636 +00:34:15,489 --> 00:34:18,325 +you can only process notifications +that you likely missed, + +637 +00:34:18,358 --> 00:34:21,295 +instead of requiring paging +through the entire history. + +638 +00:34:21,328 --> 00:34:22,996 +This will help improve +the speed of recovery + +639 +00:34:23,030 --> 00:34:26,600 +and reduce work reprocessing +already-recorded notifications. + +640 +00:34:27,534 --> 00:34:29,803 +Next, the Get Notification +History endpoint + +641 +00:34:29,837 --> 00:34:32,306 +allows you to filter +by type of notification. + +642 +00:34:32,339 --> 00:34:35,042 +If you have experience an extended +outage and expect a significant + +643 +00:34:35,075 --> 00:34:38,111 +number of notifications, +consider filtering by type, and starting + +644 +00:34:38,145 --> 00:34:42,482 +with types that may have immediate impact, +like DID_RENEW and EXPIRED. + +645 +00:34:42,516 --> 00:34:46,854 +These will help you take action +on the most relevant cases first. + +646 +00:34:46,887 --> 00:34:49,223 +One tip when passing notification types, + +647 +00:34:49,256 --> 00:34:51,758 +if the notificationSubtype +field is omitted, + +648 +00:34:51,792 --> 00:34:55,596 +this will only return notifications +that also do not have a subtype. + +649 +00:34:55,629 --> 00:34:59,766 +Therefore, for the example shown +for the DID_RENEW notificationType, + +650 +00:34:59,800 --> 00:35:01,935 +this would not return +DID_RENEW notifications + +651 +00:35:01,969 --> 00:35:03,370 +with subtype BILLING_RECOVERY. + +652 +00:35:05,105 --> 00:35:07,908 +Last, the Get Notification +History endpoint allows filtering + +653 +00:35:07,941 --> 00:35:11,712 +to a specific user by using +an originalTransactionId. + +654 +00:35:11,745 --> 00:35:13,614 +Thinking back +to the subscription lifecycle, + +655 +00:35:13,647 --> 00:35:16,016 +we've worked to make sure +every step of a user's journey + +656 +00:35:16,049 --> 00:35:17,985 +is covered by notifications. + +657 +00:35:18,018 --> 00:35:21,555 +Therefore, if you find yourself +jumping around in unexpected ways, + +658 +00:35:21,588 --> 00:35:24,825 +for example from a renewing subscription +straight to expiration, + +659 +00:35:24,858 --> 00:35:27,961 +this may indicate that you missed +a notification for that user. + +660 +00:35:27,995 --> 00:35:30,364 +This can also be useful +in a customer-support context + +661 +00:35:30,397 --> 00:35:33,734 +if a user's account is in a different +state than you would expect. + +662 +00:35:33,767 --> 00:35:37,771 +In these cases, you can send a query +for that user's notification history. + +663 +00:35:38,906 --> 00:35:42,176 +Let's go over the response from +the Get Notification History endpoint. + +664 +00:35:42,209 --> 00:35:44,878 +Only certain values are shown +in the response for simplicity. + +665 +00:35:46,613 --> 00:35:50,484 +The values returned in the response +are in the notificationHistory array. + +666 +00:35:52,119 --> 00:35:55,589 +Each entry in the array +represents a single notification. + +667 +00:35:56,823 --> 00:35:59,660 +The signed payload field contains +the exact notification + +668 +00:35:59,693 --> 00:36:00,894 +that was sent to you. + +669 +00:36:02,162 --> 00:36:05,199 +Second, we have +the firstSendAttemptResult field. + +670 +00:36:05,232 --> 00:36:08,001 +This field contains one of several values +based on the result + +671 +00:36:08,035 --> 00:36:11,638 +of the initial notification attempt +as recorded by our servers. + +672 +00:36:11,672 --> 00:36:14,741 +In the successful case, +this will be the value SUCCESS. + +673 +00:36:14,775 --> 00:36:16,777 +However, as we've just been discussing, + +674 +00:36:16,810 --> 00:36:19,780 +sometimes notifications fail +to reach your server. + +675 +00:36:19,813 --> 00:36:22,482 +These messages are meant to be +a general guide to help point you + +676 +00:36:22,516 --> 00:36:25,819 +in the direction of the issue, +to simplify the resolution process. + +677 +00:36:25,853 --> 00:36:28,522 +For example, we see SSL_ISSUE here. + +678 +00:36:28,555 --> 00:36:31,024 +This indicates there is a problem +with the SSL certificate + +679 +00:36:31,058 --> 00:36:33,126 +or process on the server. + +680 +00:36:33,160 --> 00:36:36,697 +This field provides improved visibility +into diagnosing servers issues + +681 +00:36:36,730 --> 00:36:39,233 +beyond seeing +the notification did not arrive. + +682 +00:36:39,266 --> 00:36:42,803 +We also provide this same field in +the Get Test Notification Status endpoint, + +683 +00:36:42,836 --> 00:36:45,806 +to provide this functionality +when using test notifications. + +684 +00:36:45,839 --> 00:36:48,976 +These can be used to help while +onboarding or during troubleshooting, + +685 +00:36:49,009 --> 00:36:52,446 +or retrospectively during determining +the root cause of an outage. + +686 +00:36:52,479 --> 00:36:55,949 +Notifications may not cover +all cases of a user's history. + +687 +00:36:55,983 --> 00:36:58,652 +You may have just adopted notifications +and have existing users + +688 +00:36:58,685 --> 00:37:00,354 +with histories not covered. + +689 +00:37:00,387 --> 00:37:03,290 +You also may wish to examine a history +longer than the retention period + +690 +00:37:03,323 --> 00:37:06,894 +of notifications in the Get +Notification History endpoint. + +691 +00:37:06,927 --> 00:37:09,763 +That's where the Get Transaction +History endpoint enters the picture. + +692 +00:37:09,796 --> 00:37:11,899 +This endpoint, +as we saw earlier in the presentation + +693 +00:37:11,932 --> 00:37:14,401 +from Gabriel, +solves these issues by providing histories + +694 +00:37:14,434 --> 00:37:18,605 +for your customers that cover cases +before you started using notifications. + +695 +00:37:18,639 --> 00:37:21,909 +Now, let's go over how notifications +can provide insight + +696 +00:37:21,942 --> 00:37:25,179 +and opportunities above and beyond +the purchase history. + +697 +00:37:25,212 --> 00:37:29,082 +One of the new additions in Version 2 +notifications is the subtype field, + +698 +00:37:29,116 --> 00:37:32,386 +adding additional context +to the notificationType field. + +699 +00:37:32,419 --> 00:37:35,389 +This field is meant to provide +more detail in certain scenarios, + +700 +00:37:35,422 --> 00:37:38,058 +like EXPIRED +or DID_CHANGE_RENEWAL_STATUS. + +701 +00:37:38,091 --> 00:37:41,929 +For example, with EXPIRED, +the action you take is usually the same, + +702 +00:37:41,962 --> 00:37:45,332 +mark the subscription as inactive +and revoke access to the product. + +703 +00:37:45,365 --> 00:37:49,102 +However, it can often be useful +to understand why the user expired. + +704 +00:37:49,136 --> 00:37:51,772 +Was it due to a billing issue, +voluntary choice, + +705 +00:37:51,805 --> 00:37:53,841 +or a price increase +that was never accepted? + +706 +00:37:53,874 --> 00:37:56,877 +Another notification, +DID_CHANGE_RENEWAL_STATUS, + +707 +00:37:56,910 --> 00:37:59,246 +is a great example of gaining +additional information + +708 +00:37:59,279 --> 00:38:01,815 +and opportunities +when using notifications. + +709 +00:38:01,849 --> 00:38:04,251 +On the surface, +it looks to be of low priority. + +710 +00:38:04,284 --> 00:38:06,286 +No action needs to be immediately taken. + +711 +00:38:06,320 --> 00:38:09,022 +The important notification +for revoking access to the product + +712 +00:38:09,056 --> 00:38:11,058 +is the EXPIRED notification. + +713 +00:38:11,091 --> 00:38:14,127 +Don't be fooled. +There is a lot of opportunity here. + +714 +00:38:14,161 --> 00:38:16,430 +One, this notification is +a great opportunity + +715 +00:38:16,463 --> 00:38:20,167 +to attempt to win back the customer +before their subscription expires. + +716 +00:38:20,200 --> 00:38:22,536 +Especially since deactivating +auto renewal may occur + +717 +00:38:22,569 --> 00:38:25,038 +outside the application, +this can be the only trigger + +718 +00:38:25,072 --> 00:38:29,443 +to be informed in this change in +renewal status before the expiration date. + +719 +00:38:29,476 --> 00:38:32,546 +This notification also provides insight +into customer behavior. + +720 +00:38:32,579 --> 00:38:34,014 +This notification can be used to determine + +721 +00:38:34,047 --> 00:38:36,550 +when in the renewal period +subscribers are cancelling. + +722 +00:38:36,583 --> 00:38:37,885 +Is it the day before renewal? + +723 +00:38:37,918 --> 00:38:39,786 +Are new subscribers +deactivating auto-renewal + +724 +00:38:39,820 --> 00:38:42,256 +soon after signing up for your service? + +725 +00:38:42,289 --> 00:38:43,824 +This type of information can be +important for understanding + +726 +00:38:43,857 --> 00:38:47,427 +the causes of cancellation +and improving your product. + +727 +00:38:47,461 --> 00:38:50,130 +Last, certain scenarios +may never be reflected + +728 +00:38:50,163 --> 00:38:52,900 +in a user's history without notifications. + +729 +00:38:52,933 --> 00:38:57,237 +For example, a user may deactivate +but then reactivate auto-renewal, + +730 +00:38:57,271 --> 00:38:59,940 +before their subscription period +has expired. + +731 +00:38:59,973 --> 00:39:02,242 +Because this all occurs +within a subscription period, + +732 +00:39:02,276 --> 00:39:05,345 +there is no effect +on the subscription's long-term status. + +733 +00:39:05,379 --> 00:39:07,981 +These decisions can be important +for understanding your customers, + +734 +00:39:08,015 --> 00:39:09,483 +and notifications provide information + +735 +00:39:09,516 --> 00:39:13,654 +to detect and record +these types of scenarios. + +736 +00:39:13,687 --> 00:39:16,924 +Overall, notifications work +to enhance and create opportunities + +737 +00:39:16,957 --> 00:39:19,459 +for understanding customer behavior +by providing information + +738 +00:39:19,493 --> 00:39:23,530 +at every step of the customer journey, +covering more scenarios than ever before. + +739 +00:39:23,564 --> 00:39:26,900 +In conclusion, today we've covered +both the App Store Server API + +740 +00:39:26,934 --> 00:39:29,269 +and App Store Server Notifications. + +741 +00:39:29,303 --> 00:39:30,871 +These are available +to improve the capabilities + +742 +00:39:30,904 --> 00:39:33,207 +surrounding managing +and tracking purchases. + +743 +00:39:33,240 --> 00:39:37,177 +They use updated message types +and cover more cases than ever before. + +744 +00:39:37,211 --> 00:39:39,046 +These systems are available +for all clients + +745 +00:39:39,079 --> 00:39:42,616 +and cross-compatible with both +Original StoreKit and StoreKit 2 + +746 +00:39:42,649 --> 00:39:46,086 +and can improve your ability +to monitor the subscription lifecycle. + +747 +00:39:46,119 --> 00:39:49,489 +Last, these tools are already available +in both Sandbox and Production + +748 +00:39:49,523 --> 00:39:51,458 +and are a great addition to any system. + +749 +00:39:51,491 --> 00:39:54,795 +Thank you for joining us, +and have a great WWDC. + diff --git "a/eng/2022 Session 10041 What\342\200\231s new in Wallet and Apple Pay en.srt" "b/eng/2022 Session 10041 What\342\200\231s new in Wallet and Apple Pay en.srt" new file mode 100644 index 0000000..9473be9 --- /dev/null +++ "b/eng/2022 Session 10041 What\342\200\231s new in Wallet and Apple Pay en.srt" @@ -0,0 +1,3727 @@ +1 +00:00:00,267 --> 00:00:03,337 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,337 --> 00:00:09,910 +♪ + +3 +00:00:09,910 --> 00:00:11,812 +Lais Minchillo: +Hi, my name is Lais. + +4 +00:00:11,812 --> 00:00:13,847 +David Silver: And I'm David. +And we will be presenting + +5 +00:00:13,847 --> 00:00:17,885 +this year's new features +in Wallet and Apple Pay. + +6 +00:00:17,885 --> 00:00:20,520 +We launched Apple Pay in 2014, + +7 +00:00:20,520 --> 00:00:23,657 +setting a new benchmark +for making fast, secure, + +8 +00:00:23,657 --> 00:00:27,928 +and private payments +in store, online and in-app. + +9 +00:00:27,928 --> 00:00:31,231 +Since then, we've expanded +Apple Pay across the globe. + +10 +00:00:31,231 --> 00:00:35,202 +Apple Pay is now available +in 72 countries and territories + +11 +00:00:35,202 --> 00:00:38,739 +and handles more than a million +transactions every day. + +12 +00:00:38,739 --> 00:00:42,042 +Today we're introducing exciting +new features and APIs + +13 +00:00:42,042 --> 00:00:43,710 +into Wallet and Apple Pay. + +14 +00:00:43,710 --> 00:00:45,812 +Lais will tell you more. + +15 +00:00:45,812 --> 00:00:47,180 +Lais: Thank you, David! + +16 +00:00:47,180 --> 00:00:50,284 +Let's take a look +at the main agenda for today. + +17 +00:00:50,284 --> 00:00:53,921 +First, we'll talk about +some quick updates. + +18 +00:00:53,921 --> 00:00:56,823 +We are adding support for +payments to multiple merchants + +19 +00:00:56,823 --> 00:00:59,293 +in a single transaction. + +20 +00:00:59,293 --> 00:01:02,663 +We are also greatly improving +support for automatic payments, + +21 +00:01:02,663 --> 00:01:05,299 +including subscriptions, + +22 +00:01:05,299 --> 00:01:07,467 +With order tracking, +you can enhance + +23 +00:01:07,467 --> 00:01:10,737 +the postpurchase experience +for your customers. + +24 +00:01:10,737 --> 00:01:14,107 +And finally, David will +talk about identity verification + +25 +00:01:14,107 --> 00:01:17,311 +with IDs in Wallet. + +26 +00:01:17,311 --> 00:01:20,147 +We have a few +exciting updates to share. + +27 +00:01:20,147 --> 00:01:23,250 +Tap to Pay on iPhone +was announced earlier this year + +28 +00:01:23,250 --> 00:01:27,120 +and launched +in iOS 15.4 in the US. + +29 +00:01:27,120 --> 00:01:29,990 +Tap to Pay on iPhone +provides a secure, private, + +30 +00:01:29,990 --> 00:01:33,126 +and easy way to accept +contactless payments. + +31 +00:01:33,126 --> 00:01:35,696 +You can easily integrate +this into your app + +32 +00:01:35,696 --> 00:01:39,366 +to seamlessly and securely +accept contactless payments. + +33 +00:01:39,366 --> 00:01:41,168 +This includes Apple Pay, + +34 +00:01:41,168 --> 00:01:43,503 +contactless credit +and debit cards, + +35 +00:01:43,503 --> 00:01:45,639 +and other digital wallets. + +36 +00:01:45,639 --> 00:01:46,907 +The transaction is completed + +37 +00:01:46,907 --> 00:01:49,009 +through a simple tap +to the iPhone, + +38 +00:01:49,009 --> 00:01:51,044 +removing the need +for additional hardware + +39 +00:01:51,044 --> 00:01:53,146 +or payment terminals. + +40 +00:01:53,146 --> 00:01:55,582 +Meanwhile, in macOS 13, + +41 +00:01:55,582 --> 00:01:58,885 +we redesigned +the Apple Pay experience. + +42 +00:01:58,885 --> 00:02:01,321 +The iOS payment sheet +redesign last year + +43 +00:02:01,321 --> 00:02:02,756 +was a great success + +44 +00:02:02,756 --> 00:02:06,626 +and this year we're bringing +a similar experience to macOS. + +45 +00:02:06,626 --> 00:02:09,029 +We used SwiftUI +to implement this, + +46 +00:02:09,029 --> 00:02:11,365 +which helped us +bring new features to macOS + +47 +00:02:11,365 --> 00:02:13,700 +at the same time as iOS. + +48 +00:02:13,700 --> 00:02:16,370 +All of the Apple Pay features +we are introducing today + +49 +00:02:16,370 --> 00:02:19,639 +are also supported on Mac. + +50 +00:02:19,639 --> 00:02:23,110 +We're introducing +new SwiftUI APIs. + +51 +00:02:23,110 --> 00:02:26,446 +Integrating Add to Apple Wallet +or Apple Pay buttons + +52 +00:02:26,446 --> 00:02:29,282 +in your SwiftUI app +will be much easier. + +53 +00:02:29,282 --> 00:02:31,651 +These new APIs will +significantly reduce + +54 +00:02:31,651 --> 00:02:33,920 +the amount of code +you need to write. + +55 +00:02:33,920 --> 00:02:36,056 +Let's take a look at +how you can add a button + +56 +00:02:36,056 --> 00:02:39,192 +to prompt the user +to add an airline pass. + +57 +00:02:39,192 --> 00:02:41,962 +First, create the pass. + +58 +00:02:41,962 --> 00:02:45,365 +You should handle the case where +it wasn't successfully loaded. + +59 +00:02:45,365 --> 00:02:47,901 +This could happen +if the pass data is malformed + +60 +00:02:47,901 --> 00:02:51,338 +or if it was not properly +signed, for example. + +61 +00:02:51,338 --> 00:02:56,109 +Next, call AddPassToWalletButton +with an array of passes. + +62 +00:02:56,109 --> 00:02:59,479 +In this example, we have +an array with only one element, + +63 +00:02:59,479 --> 00:03:02,983 +but you can have multiple passes +on the same button. + +64 +00:03:02,983 --> 00:03:05,919 +The result is passed +in as a Bool, and you can save, + +65 +00:03:05,919 --> 00:03:08,488 +log, or trigger other actions +in your app + +66 +00:03:08,488 --> 00:03:12,025 +based on whether the user +has added a pass or not. + +67 +00:03:12,025 --> 00:03:15,162 +In this example, +I'm saving that to a state var. + +68 +00:03:15,162 --> 00:03:16,630 +And that's it! + +69 +00:03:16,630 --> 00:03:19,599 +You can also customize +the button's size and style + +70 +00:03:19,599 --> 00:03:21,935 +within a set of minimum values. + +71 +00:03:21,935 --> 00:03:28,642 +This is the default size: +width 250 and height 50. + +72 +00:03:28,642 --> 00:03:30,911 +You could also make it +wider... + +73 +00:03:32,512 --> 00:03:33,880 +...or taller. + +74 +00:03:35,882 --> 00:03:37,284 +This wraps up how you can add + +75 +00:03:37,284 --> 00:03:40,854 +an Add to Apple Wallet button +in SwiftUI. + +76 +00:03:40,854 --> 00:03:45,192 +Now, let's see how you can add +a Pay with Apple Pay button. + +77 +00:03:45,192 --> 00:03:47,394 +First, create a payment request + +78 +00:03:47,394 --> 00:03:49,863 +using the PKPaymentRequest +class, + +79 +00:03:49,863 --> 00:03:52,899 +setting your usual +configuration on it. + +80 +00:03:52,899 --> 00:03:57,037 +Then, create an +authorizationChange method. + +81 +00:03:57,037 --> 00:03:59,005 +Now that we have +these two pieces ready, + +82 +00:03:59,005 --> 00:04:01,741 +let's add code +to show the button. + +83 +00:04:01,741 --> 00:04:04,344 +Add a call to +PayWithApplePayButton, + +84 +00:04:04,344 --> 00:04:07,914 +passing in the label, +the paymentRequest object, + +85 +00:04:07,914 --> 00:04:10,784 +and the authorizationChange +method. + +86 +00:04:10,784 --> 00:04:13,086 +To handle cases where +Apple Pay isn't supported + +87 +00:04:13,086 --> 00:04:16,656 +by the current device, +you can pass in a fallback view. + +88 +00:04:16,656 --> 00:04:18,525 +Just like the Add Pass button, + +89 +00:04:18,525 --> 00:04:23,463 +you can also customize +its size and style. + +90 +00:04:23,463 --> 00:04:26,700 +In total, +there are 17 different labels, + +91 +00:04:26,700 --> 00:04:28,635 +so you are able to customize +the pay button + +92 +00:04:28,635 --> 00:04:31,371 +to align with your use case. + +93 +00:04:31,371 --> 00:04:38,645 +These are available on iOS, +iPadOS, macOS, and watchOS. + +94 +00:04:38,645 --> 00:04:42,949 +Now, let's take a look +at multimerchant payments. + +95 +00:04:42,949 --> 00:04:46,286 +In iOS 16, we're introducing +the ability to request + +96 +00:04:46,286 --> 00:04:48,922 +multiple payment tokens +for different merchants + +97 +00:04:48,922 --> 00:04:50,724 +in the same transaction. + +98 +00:04:50,724 --> 00:04:53,894 +This is useful for things +like online marketplaces, + +99 +00:04:53,894 --> 00:04:56,596 +travel bookings, +and ticketing services. + +100 +00:04:56,596 --> 00:04:59,199 +Let's take a closer look +at an example. + +101 +00:04:59,199 --> 00:05:01,935 +Imagine Allison +is planning a trip. + +102 +00:05:01,935 --> 00:05:04,504 +She goes to a travel +agency's website + +103 +00:05:04,504 --> 00:05:06,072 +and they conveniently offer her + +104 +00:05:06,072 --> 00:05:08,141 +all of the things +she needs to book -- + +105 +00:05:08,141 --> 00:05:12,412 +flight tickets, a hotel stay, +and a car rental. + +106 +00:05:12,412 --> 00:05:16,049 +Allison just needs to pay +a total of $500 dollars. + +107 +00:05:16,049 --> 00:05:18,084 +Allison provides +the travel agency + +108 +00:05:18,084 --> 00:05:20,453 +her full credit card +information. + +109 +00:05:20,453 --> 00:05:23,256 +Now, you might imagine +that the travel agency + +110 +00:05:23,256 --> 00:05:26,293 +will charge Allison's +credit card $500 dollars, + +111 +00:05:26,293 --> 00:05:29,196 +and then pay +the other companies involved. + +112 +00:05:29,196 --> 00:05:31,865 +But what typically happens +is the travel agency + +113 +00:05:31,865 --> 00:05:34,434 +simply passes along +the credit card information + +114 +00:05:34,434 --> 00:05:38,605 +to each company to make +their individual charges. + +115 +00:05:38,605 --> 00:05:40,740 +This works, +but it's not great + +116 +00:05:40,740 --> 00:05:42,676 +for Allison's +privacy and security + +117 +00:05:42,676 --> 00:05:46,413 +to have her credit card +information shared around. + +118 +00:05:46,413 --> 00:05:49,749 +Now, with the new +multimerchant payment API, + +119 +00:05:49,749 --> 00:05:52,018 +it's possible to request +a payment token + +120 +00:05:52,018 --> 00:05:55,355 +for each merchant involved +in a transaction. + +121 +00:05:55,355 --> 00:05:57,157 +Using these payment tokens, + +122 +00:05:57,157 --> 00:06:00,327 +the multiple companies involved +can each charge Allison + +123 +00:06:00,327 --> 00:06:03,263 +for the relevant amount +she authorized. + +124 +00:06:03,263 --> 00:06:05,932 +Allison can now book +and pay for her trip + +125 +00:06:05,932 --> 00:06:09,269 +while taking advantage of the +privacy and security benefits + +126 +00:06:09,269 --> 00:06:12,005 +that Apple Pay offers. + +127 +00:06:12,005 --> 00:06:14,541 +The payment sheet has been +updated to show customers + +128 +00:06:14,541 --> 00:06:18,345 +a breakdown of the submerchants +involved in a transaction. + +129 +00:06:18,345 --> 00:06:20,313 +Customers can tap +on the total field + +130 +00:06:20,313 --> 00:06:22,549 +to navigate +to the payment summary. + +131 +00:06:22,549 --> 00:06:25,051 +Here, the customer +can see a breakdown + +132 +00:06:25,051 --> 00:06:27,520 +of all the merchants involved +in the transaction, + +133 +00:06:27,520 --> 00:06:30,590 +along with the amount authorized +for each one. + +134 +00:06:30,590 --> 00:06:32,392 +Now, let's look +at how you can add + +135 +00:06:32,392 --> 00:06:35,528 +multimerchant payments +to your app. + +136 +00:06:35,528 --> 00:06:37,764 +First, create a payment request + +137 +00:06:37,764 --> 00:06:40,233 +using the PKPaymentRequest +class, + +138 +00:06:40,233 --> 00:06:43,203 +setting your usual +configuration on it. + +139 +00:06:43,203 --> 00:06:48,341 +Then add summary items for your +payment, including the total. + +140 +00:06:48,341 --> 00:06:50,977 +Next, create +a payment token context + +141 +00:06:50,977 --> 00:06:54,414 +for each additional merchant +involved in the transaction, + +142 +00:06:54,414 --> 00:06:58,385 +using the new +PKPaymentTokenContext class. + +143 +00:06:58,385 --> 00:07:01,121 +Provide details +for each merchant, + +144 +00:07:01,121 --> 00:07:04,424 +as well as the amount +to authorize for each. + +145 +00:07:04,424 --> 00:07:09,462 +Finally, set the payment token +contexts on the payment request. + +146 +00:07:09,462 --> 00:07:11,631 +Keep in mind that the sum +of the amounts + +147 +00:07:11,631 --> 00:07:14,167 +for all of your +payment token contexts + +148 +00:07:14,167 --> 00:07:17,070 +must be less than or equal +to the total amount + +149 +00:07:17,070 --> 00:07:19,506 +of the payment request itself. + +150 +00:07:19,506 --> 00:07:23,043 +Also, you should always use +the same external identifier + +151 +00:07:23,043 --> 00:07:26,012 +for the same merchant any time +you request a payment token + +152 +00:07:26,012 --> 00:07:28,882 +for that merchant in your app. + +153 +00:07:28,882 --> 00:07:30,917 +For adopting +multi-merchant payments + +154 +00:07:30,917 --> 00:07:32,552 +with Apple Pay on the web, + +155 +00:07:32,552 --> 00:07:36,656 +check out the Apple Pay JS API +documentation. + +156 +00:07:36,656 --> 00:07:39,159 +Now, let's take a look +at our improvements + +157 +00:07:39,159 --> 00:07:41,961 +to automatic payments. + +158 +00:07:41,961 --> 00:07:45,732 +In iOS 16, we're introducing +the ability for people to view + +159 +00:07:45,732 --> 00:07:48,702 +and manage automatic payments +they've set up with merchants, + +160 +00:07:48,702 --> 00:07:50,670 +right from the Wallet app. + +161 +00:07:50,670 --> 00:07:51,738 +In this release, + +162 +00:07:51,738 --> 00:07:54,641 +we're supporting two types +of automatic payments: + +163 +00:07:54,641 --> 00:07:57,043 +recurring payments, +which includes things like + +164 +00:07:57,043 --> 00:08:00,547 +subscriptions, installments, +or recurring billing; + +165 +00:08:00,547 --> 00:08:02,582 +and automatic reload payments, + +166 +00:08:02,582 --> 00:08:05,418 +such as store card +balance top-ups. + +167 +00:08:05,418 --> 00:08:08,221 +We're introducing new APIs +to allow you to request + +168 +00:08:08,221 --> 00:08:12,125 +to set up an automatic payment +when you make a payment request. + +169 +00:08:12,125 --> 00:08:15,562 +We're also introducing +Apple Pay merchant tokens, + +170 +00:08:15,562 --> 00:08:19,532 +a new kind of payment token +tied to a customer's Apple ID, + +171 +00:08:19,532 --> 00:08:22,168 +which can help you more reliably +charge your customers + +172 +00:08:22,168 --> 00:08:24,437 +on an ongoing basis. + +173 +00:08:24,437 --> 00:08:27,040 +Let's take a closer look +at Apple Pay merchant tokens + +174 +00:08:27,040 --> 00:08:29,542 +to see how +they can be useful. + +175 +00:08:29,542 --> 00:08:32,445 +Imagine Julie is paying +for a book club membership + +176 +00:08:32,445 --> 00:08:34,981 +using Apple Pay on her iPhone. + +177 +00:08:34,981 --> 00:08:37,350 +The book club makes +a payment request, + +178 +00:08:37,350 --> 00:08:39,552 +and when Julie authorizes +the payment, + +179 +00:08:39,552 --> 00:08:41,921 +the book club receives +a payment token, + +180 +00:08:41,921 --> 00:08:44,524 +and every month they can +use that to charge Julie + +181 +00:08:44,524 --> 00:08:46,526 +her membership fee. + +182 +00:08:46,526 --> 00:08:49,796 +This payment token is linked +to the device Julie used + +183 +00:08:49,796 --> 00:08:51,865 +to authorize the payment. + +184 +00:08:51,865 --> 00:08:55,235 +But what happens if Julie +gets a new iPhone? + +185 +00:08:55,235 --> 00:08:57,737 +With the new +automatic payments feature, + +186 +00:08:57,737 --> 00:08:59,639 +the book club +will instead receive + +187 +00:08:59,639 --> 00:09:01,641 +an Apple Pay merchant token, + +188 +00:09:01,641 --> 00:09:04,544 +if Julie's payment network +supports it. + +189 +00:09:04,544 --> 00:09:07,847 +This payment token is tied +to Julie's Apple ID, + +190 +00:09:07,847 --> 00:09:09,649 +rather than to her iPhone, + +191 +00:09:09,649 --> 00:09:14,220 +which provides better assurances +for ongoing authorizations. + +192 +00:09:14,220 --> 00:09:16,823 +This means that if Julie +upgrades her iPhone, + +193 +00:09:16,823 --> 00:09:18,758 +or resets her current phone, + +194 +00:09:18,758 --> 00:09:21,594 +the book club can continue +to reliably charge Julie + +195 +00:09:21,594 --> 00:09:23,997 +her monthly membership fee. + +196 +00:09:23,997 --> 00:09:26,666 +If you accept Apple Pay +for these types of payments, + +197 +00:09:26,666 --> 00:09:30,003 +it's a great idea to adopt +automatic payments to ensure + +198 +00:09:30,003 --> 00:09:32,972 +you can continue reliably +charging your customers, + +199 +00:09:32,972 --> 00:09:36,509 +as well as avoid any +interruption to their services. + +200 +00:09:36,509 --> 00:09:38,978 +The first type of automatic +payment we're supporting + +201 +00:09:38,978 --> 00:09:41,981 +in this release +is recurring payments. + +202 +00:09:41,981 --> 00:09:45,218 +Recurring payments have +a fixed or variable amount + +203 +00:09:45,218 --> 00:09:47,353 +that is charged on +a regular schedule, + +204 +00:09:47,353 --> 00:09:51,157 +such as weekly, +monthly, or annually. + +205 +00:09:51,157 --> 00:09:53,560 +These payments can end +on a certain date + +206 +00:09:53,560 --> 00:09:57,063 +or they can be ongoing +until canceled. + +207 +00:09:57,063 --> 00:10:01,568 +A trial or introductory period +is also supported. + +208 +00:10:01,568 --> 00:10:03,970 +Subscriptions, +installment plans, + +209 +00:10:03,970 --> 00:10:06,272 +and regular billing +are perfect uses + +210 +00:10:06,272 --> 00:10:08,508 +for this type of payment. + +211 +00:10:08,508 --> 00:10:11,010 +Let's take a look at how you can +set up a recurring payment + +212 +00:10:11,010 --> 00:10:14,547 +in your app using +automatic payments. + +213 +00:10:14,547 --> 00:10:17,183 +Start by specifying +the amount and duration + +214 +00:10:17,183 --> 00:10:19,219 +of the recurring payment, +using the + +215 +00:10:19,219 --> 00:10:22,655 +PKRecurringPaymentSummaryItem +class. + +216 +00:10:22,655 --> 00:10:24,157 +For recurring payments, + +217 +00:10:24,157 --> 00:10:27,126 +you can specify +both a regular billing period + +218 +00:10:27,126 --> 00:10:30,997 +as well as an introductory +or trial period. + +219 +00:10:30,997 --> 00:10:34,400 +You can use the startDate +and endDate properties + +220 +00:10:34,400 --> 00:10:36,970 +to indicate when +the trial period ends + +221 +00:10:36,970 --> 00:10:40,473 +and the regular billing period +starts. + +222 +00:10:40,473 --> 00:10:43,710 +Next, create a +recurringPaymentRequest + +223 +00:10:43,710 --> 00:10:47,514 +using the new +PKRecurringPaymentRequest class. + +224 +00:10:47,514 --> 00:10:49,816 +Provide a description +of the payment, + +225 +00:10:49,816 --> 00:10:51,784 +the regular billing period, + +226 +00:10:51,784 --> 00:10:54,787 +as well as a managementURL +to a web page + +227 +00:10:54,787 --> 00:10:57,891 +where the customer can update +or delete the payment method + +228 +00:10:57,891 --> 00:10:59,926 +for the recurring payment. + +229 +00:10:59,926 --> 00:11:03,429 +You can also optionally +provide a trial billing period + +230 +00:11:03,429 --> 00:11:05,765 +as well as +billingAgreement text + +231 +00:11:05,765 --> 00:11:09,302 +to help explain the terms +of the payment to the customer. + +232 +00:11:09,302 --> 00:11:13,873 +Finally, you can optionally +provide a tokenNotificationURL + +233 +00:11:13,873 --> 00:11:16,910 +where your server can receive +life cycle notifications + +234 +00:11:16,910 --> 00:11:19,779 +about the Apple Pay +merchant token for the payment, + +235 +00:11:19,779 --> 00:11:21,414 +if one was issued. + +236 +00:11:21,414 --> 00:11:23,950 +For example, +you can receive notifications + +237 +00:11:23,950 --> 00:11:27,687 +if the card issuer or the user +deletes the token. + +238 +00:11:27,687 --> 00:11:30,056 +For more information +about merchant token + +239 +00:11:30,056 --> 00:11:32,025 +life cycle notifications, + +240 +00:11:32,025 --> 00:11:36,930 +see the Apple Pay Merchant Token +Management API documentation. + +241 +00:11:36,930 --> 00:11:40,300 +Finally, set the +recurringPaymentRequest + +242 +00:11:40,300 --> 00:11:42,335 +on the paymentRequest object. + +243 +00:11:42,335 --> 00:11:45,605 +A quick note on summary items, +your recurring payment + +244 +00:11:45,605 --> 00:11:48,575 +will not be added to the payment +request's summary items + +245 +00:11:48,575 --> 00:11:49,776 +automatically. + +246 +00:11:49,776 --> 00:11:54,080 +So be sure to add items for it +to the summary items array. + +247 +00:11:54,080 --> 00:11:56,015 +The total for +the payment request + +248 +00:11:56,015 --> 00:11:57,450 +should be the first amount + +249 +00:11:57,450 --> 00:11:59,352 +that the customer +will be charged. + +250 +00:11:59,352 --> 00:12:02,088 +So in this example, +the total is set to display + +251 +00:12:02,088 --> 00:12:03,957 +the trial period amount, + +252 +00:12:03,957 --> 00:12:07,794 +as that's the first amount +the customer will be charged. + +253 +00:12:07,794 --> 00:12:09,529 +The payment sheet +will show details + +254 +00:12:09,529 --> 00:12:11,965 +of your recurring payment +to customers, + +255 +00:12:11,965 --> 00:12:14,300 +and they can tap +on the Billing Details section + +256 +00:12:14,300 --> 00:12:17,136 +to read further. + +257 +00:12:17,136 --> 00:12:20,306 +Now, let's take a look at the +second type of automatic payment + +258 +00:12:20,306 --> 00:12:22,075 +we're supporting +in this release: + +259 +00:12:22,075 --> 00:12:24,277 +automatic reload payments. + +260 +00:12:24,277 --> 00:12:25,645 +With this type of payment, + +261 +00:12:25,645 --> 00:12:27,580 +a balance is +automatically topped up + +262 +00:12:27,580 --> 00:12:30,583 +with a fixed amount whenever +the balance drops below + +263 +00:12:30,583 --> 00:12:32,852 +a certain threshold amount. + +264 +00:12:32,852 --> 00:12:35,622 +Automatic reload payments +are perfect for things like + +265 +00:12:35,622 --> 00:12:39,459 +store card top-ups +and prepaid balances. + +266 +00:12:39,459 --> 00:12:42,595 +To request to set up +an automatic reload payment, + +267 +00:12:42,595 --> 00:12:46,032 +start by specifying the reload +and threshold amounts, + +268 +00:12:46,032 --> 00:12:51,237 +using the new PKAutomatic +ReloadPaymentSummaryItem class. + +269 +00:12:51,237 --> 00:12:55,208 +Next, create an +automaticReloadPaymentRequest + +270 +00:12:55,208 --> 00:12:59,579 +using the new PKAutomatic +ReloadPaymentRequest class, + +271 +00:12:59,579 --> 00:13:02,915 +passing in a description +of the payment, the billing, + +272 +00:13:02,915 --> 00:13:04,517 +and a management URL, + +273 +00:13:04,517 --> 00:13:06,819 +just like for +recurring payments. + +274 +00:13:06,819 --> 00:13:10,156 +You can also optionally +provide billing agreement text + +275 +00:13:10,156 --> 00:13:13,660 +and a token notification URL. + +276 +00:13:13,660 --> 00:13:16,829 +Finally, set the automatic +reload payment request + +277 +00:13:16,829 --> 00:13:19,065 +on the payment request object. + +278 +00:13:19,065 --> 00:13:22,435 +Again, be sure to include +your automatic reload payment + +279 +00:13:22,435 --> 00:13:24,137 +in the summary items + +280 +00:13:24,137 --> 00:13:27,940 +and set the total of the +paymentRequest appropriately. + +281 +00:13:27,940 --> 00:13:31,044 +For adopting automatic payments +with Apple Pay on the web, + +282 +00:13:31,044 --> 00:13:35,248 +check out the Apple Pay JS API +documentation. + +283 +00:13:35,248 --> 00:13:37,550 +Here's how the automatic reload +payment appears + +284 +00:13:37,550 --> 00:13:41,854 +in the payment sheet +for your customers. + +285 +00:13:41,854 --> 00:13:44,223 +Finally, here's +a few things to remember + +286 +00:13:44,223 --> 00:13:47,193 +to help give your customers +the best possible experience + +287 +00:13:47,193 --> 00:13:50,229 +when you adopt +automatic payments in your app. + +288 +00:13:50,229 --> 00:13:52,165 +Remember to include +summary items + +289 +00:13:52,165 --> 00:13:56,469 +for your automatic payment +as these are not added for you. + +290 +00:13:56,469 --> 00:13:59,038 +The total amount of your +payment request should be + +291 +00:13:59,038 --> 00:14:02,341 +the first amount +the customer will be charged. + +292 +00:14:02,341 --> 00:14:05,178 +You should keep the billing +agreement text short. + +293 +00:14:05,178 --> 00:14:09,515 +The payment sheet will only show +the first 500 characters. + +294 +00:14:09,515 --> 00:14:12,452 +The billing agreement text +is not intended to replace + +295 +00:14:12,452 --> 00:14:14,987 +your normal billing +and legal agreements. + +296 +00:14:14,987 --> 00:14:16,556 +It's up to you to be compliant + +297 +00:14:16,556 --> 00:14:19,225 +with your local +recurring billing laws. + +298 +00:14:19,225 --> 00:14:22,128 +If you have a legal agreement +to show the customer, + +299 +00:14:22,128 --> 00:14:24,230 +that might mean +displaying it to the customer + +300 +00:14:24,230 --> 00:14:27,166 +before you present +the payment sheet. + +301 +00:14:27,166 --> 00:14:30,069 +You can only request +one type of automatic payment + +302 +00:14:30,069 --> 00:14:32,438 +in a single transaction. + +303 +00:14:32,438 --> 00:14:34,974 +Also, automatic payments +cannot be used + +304 +00:14:34,974 --> 00:14:37,810 +with multi-merchant payments. + +305 +00:14:37,810 --> 00:14:40,880 +Finally, if you want to receive +life cycle notifications + +306 +00:14:40,880 --> 00:14:44,250 +for the Apple Pay merchant token +issued for the payment, + +307 +00:14:44,250 --> 00:14:47,386 +be sure to provide +a token notification URL + +308 +00:14:47,386 --> 00:14:50,923 +and adopt the Apple Pay Merchant +Token Management API + +309 +00:14:50,923 --> 00:14:52,391 +on your server. + +310 +00:14:52,391 --> 00:14:54,327 +We think you'll love +these new APIs + +311 +00:14:54,327 --> 00:14:57,196 +and the benefits +of Apple Pay merchant tokens. + +312 +00:14:57,196 --> 00:14:58,998 +Here are just a few +of our partners + +313 +00:14:58,998 --> 00:15:03,536 +that will also be adopting +support for automatic payments. + +314 +00:15:03,536 --> 00:15:06,105 +Apple Pay merchant tokens +will be supported by + +315 +00:15:06,105 --> 00:15:10,009 +American Express, Discover, +Mastercard, and Visa, + +316 +00:15:10,009 --> 00:15:14,313 +with support for other payment +networks in the future. + +317 +00:15:14,313 --> 00:15:16,783 +We are excited to introduce +Order Tracking + +318 +00:15:16,783 --> 00:15:20,253 +to enhance the postpurchase +experience. + +319 +00:15:20,253 --> 00:15:24,457 +New in iOS 16, order tracking +allows users to track orders + +320 +00:15:24,457 --> 00:15:26,893 +placed with participating +merchants. + +321 +00:15:26,893 --> 00:15:29,295 +Wallet now provides +an intuitive overview + +322 +00:15:29,295 --> 00:15:32,598 +of active orders, +recently completed orders, + +323 +00:15:32,598 --> 00:15:34,300 +and past orders. + +324 +00:15:34,300 --> 00:15:38,104 +I currently have a single active +order for some bakery goods. + +325 +00:15:38,104 --> 00:15:42,241 +My order is still in processing; +I'll get back to that later. + +326 +00:15:42,241 --> 00:15:46,012 +For now, I want to buy some toys +and accessories for my cat + +327 +00:15:46,012 --> 00:15:47,580 +from Pet Avenue. + +328 +00:15:47,580 --> 00:15:50,283 +I choose to check out +with Apple Pay. + +329 +00:15:50,283 --> 00:15:52,485 +Shortly after +I authorize the payment, + +330 +00:15:52,485 --> 00:15:55,922 +I receive a notification +to track my order in Wallet. + +331 +00:15:55,922 --> 00:15:58,124 +Interacting with +that notification + +332 +00:15:58,124 --> 00:16:00,393 +takes me to the details +of my order + +333 +00:16:00,393 --> 00:16:02,628 +where I can check +the current status. + +334 +00:16:02,628 --> 00:16:04,263 +I can see the order status, + +335 +00:16:04,263 --> 00:16:06,899 +including shipping +and tracking information, + +336 +00:16:06,899 --> 00:16:09,802 +and the list of line items +I have ordered. + +337 +00:16:09,802 --> 00:16:13,973 +Further below, I have multiple +options to contact Pet Avenue, + +338 +00:16:13,973 --> 00:16:18,678 +check the payment information, +and go back to Pet Avenue's app. + +339 +00:16:18,678 --> 00:16:21,113 +Now, imagine +Pet Avenue is really fast + +340 +00:16:21,113 --> 00:16:25,384 +in processing incoming orders +and they just shipped my items. + +341 +00:16:25,384 --> 00:16:27,753 +As soon as Pet Avenue +shipped my order, + +342 +00:16:27,753 --> 00:16:30,389 +they have updated +the available information. + +343 +00:16:30,389 --> 00:16:33,192 +I can see the status changed +to "On the Way," + +344 +00:16:33,192 --> 00:16:36,362 +with an estimated delivery date +of June 10th. + +345 +00:16:36,362 --> 00:16:38,397 +They also included +a custom message + +346 +00:16:38,397 --> 00:16:41,667 +and tracking information +for the shipment. + +347 +00:16:41,667 --> 00:16:43,569 +Oh, remember my baking goods? + +348 +00:16:43,569 --> 00:16:46,272 +I just got a notification +that they are ready for pickup. + +349 +00:16:46,272 --> 00:16:48,007 +Let's check it out. + +350 +00:16:48,007 --> 00:16:50,610 +I have ordered +my bakery goods for pick up. + +351 +00:16:50,610 --> 00:16:52,912 +They are ready for pick up, +that's great! + +352 +00:16:52,912 --> 00:16:55,681 +Bake My Breath Away +has provided my pickup window, + +353 +00:16:55,681 --> 00:16:57,183 +pickup instructions, + +354 +00:16:57,183 --> 00:16:59,852 +and a barcode +to present upon arrival. + +355 +00:16:59,852 --> 00:17:03,522 +We've seen that order tracking +works seamlessly with Apple Pay. + +356 +00:17:03,522 --> 00:17:06,292 +Let's have a look at how +you can integrate order tracking + +357 +00:17:06,292 --> 00:17:08,961 +into your customer experience. + +358 +00:17:08,961 --> 00:17:10,997 +To get started +with order tracking, + +359 +00:17:10,997 --> 00:17:13,733 +first, you must create +an Order Type ID + +360 +00:17:13,733 --> 00:17:15,768 +in your developer account. + +361 +00:17:15,768 --> 00:17:19,672 +An Order Type ID identifies +your organization as an entity + +362 +00:17:19,672 --> 00:17:22,141 +that provides order information. + +363 +00:17:22,141 --> 00:17:24,911 +You can register +multiple Order Type IDs -- + +364 +00:17:24,911 --> 00:17:26,979 +for example, +to provide order information + +365 +00:17:26,979 --> 00:17:29,882 +on behalf of multiple merchants. + +366 +00:17:29,882 --> 00:17:32,985 +Also create an Order Type ID +Certificate. + +367 +00:17:32,985 --> 00:17:35,688 +You will use the certificate +to build order packages + +368 +00:17:35,688 --> 00:17:38,157 +and update orders. + +369 +00:17:38,157 --> 00:17:41,227 +Orders are distributed +as order packages. + +370 +00:17:41,227 --> 00:17:44,664 +An order package includes +all the metadata and information + +371 +00:17:44,664 --> 00:17:46,032 +for an order. + +372 +00:17:46,032 --> 00:17:48,701 +It can represent +a wide range of scenarios, + +373 +00:17:48,701 --> 00:17:53,105 +including shipping, pickup, +and multifulfillment orders. + +374 +00:17:53,105 --> 00:17:55,775 +An order package +also includes images, + +375 +00:17:55,775 --> 00:17:58,678 +like your logo +and line item images. + +376 +00:17:58,678 --> 00:18:00,446 +You can also add localizations + +377 +00:18:00,446 --> 00:18:03,950 +to support your diverse +range of customers. + +378 +00:18:03,950 --> 00:18:06,786 +Every order package must be +cryptographically signed + +379 +00:18:06,786 --> 00:18:08,955 +to verify its origin. + +380 +00:18:08,955 --> 00:18:10,690 +Once everything is in place, + +381 +00:18:10,690 --> 00:18:14,093 +order packages are compressed +for distribution. + +382 +00:18:14,093 --> 00:18:15,695 +Check out +the sample order packages + +383 +00:18:15,695 --> 00:18:17,296 +attached to this session. + +384 +00:18:17,296 --> 00:18:19,398 +And for more information +on order packages, + +385 +00:18:19,398 --> 00:18:22,234 +see the developer +documentation. + +386 +00:18:22,234 --> 00:18:25,838 +Adding an order to Wallet +works seamlessly with Apple Pay. + +387 +00:18:25,838 --> 00:18:28,040 +When your customer +authorized a payment, + +388 +00:18:28,040 --> 00:18:31,444 +your app or web page +receives payment information, + +389 +00:18:31,444 --> 00:18:34,847 +then sends it to your server +for processing. + +390 +00:18:34,847 --> 00:18:37,550 +If processing the payment +information succeeds, + +391 +00:18:37,550 --> 00:18:41,220 +your server creates an order +and some metadata. + +392 +00:18:41,220 --> 00:18:43,789 +Your server then returns details +about that order + +393 +00:18:43,789 --> 00:18:47,526 +to your app or web page +to include in the result. + +394 +00:18:47,526 --> 00:18:50,997 +The order details enable the +device to asynchronously request + +395 +00:18:50,997 --> 00:18:53,332 +the order from your server. + +396 +00:18:53,332 --> 00:18:57,370 +Your server then returns +the order package to the device. + +397 +00:18:57,370 --> 00:18:59,372 +When your server +creates an order, + +398 +00:18:59,372 --> 00:19:01,407 +assign an Order ID +that is unique + +399 +00:19:01,407 --> 00:19:04,477 +within the namespace +of your Order Type ID. + +400 +00:19:04,477 --> 00:19:08,581 +Your server must also generate +a secure authentication token. + +401 +00:19:08,581 --> 00:19:12,651 +This is a shared secret that is +part of the order details. + +402 +00:19:12,651 --> 00:19:15,454 +The device will use the token +to authenticate itself + +403 +00:19:15,454 --> 00:19:17,857 +when it requests the order. + +404 +00:19:17,857 --> 00:19:19,291 +Let's take a look at an example + +405 +00:19:19,291 --> 00:19:22,128 +for returning a payment +authorization result. + +406 +00:19:22,128 --> 00:19:24,330 +When your customer +authorized a payment, + +407 +00:19:24,330 --> 00:19:27,566 +your app sends the payment +information to your server + +408 +00:19:27,566 --> 00:19:29,935 +and asks it to create an order. + +409 +00:19:29,935 --> 00:19:32,938 +Check whether the server +result indicates success + +410 +00:19:32,938 --> 00:19:36,042 +and handle any error +returned by your server. + +411 +00:19:36,042 --> 00:19:39,278 +If the server result +does indicate success, + +412 +00:19:39,278 --> 00:19:41,180 +complete the payment +with an appropriate + +413 +00:19:41,180 --> 00:19:44,050 +authorization result. + +414 +00:19:44,050 --> 00:19:46,252 +To return a payment +authorization result + +415 +00:19:46,252 --> 00:19:47,987 +with order details, + +416 +00:19:47,987 --> 00:19:51,624 +first, extract them +from the server result. + +417 +00:19:51,624 --> 00:19:55,828 +Then create a +PKPaymentOrderDetails object + +418 +00:19:55,828 --> 00:19:59,265 +with the Order Ttype ID, +Order ID, + +419 +00:19:59,265 --> 00:20:03,502 +the URL to your server, +and the authenticationToken. + +420 +00:20:03,502 --> 00:20:06,739 +Assign the PKPaymentOrderDetails +object + +421 +00:20:06,739 --> 00:20:12,211 +to the new orderDetails property +on PKPaymentAuthorizationResult. + +422 +00:20:12,211 --> 00:20:13,612 +That's it! + +423 +00:20:13,612 --> 00:20:17,216 +You can complete a payment with +order details on the web too. + +424 +00:20:17,216 --> 00:20:22,188 +As before, extract the order +details from the server result. + +425 +00:20:22,188 --> 00:20:24,457 +Then include the order details + +426 +00:20:24,457 --> 00:20:27,793 +in the data you complete +the payment with. + +427 +00:20:27,793 --> 00:20:29,528 +To be able to update an order, + +428 +00:20:29,528 --> 00:20:31,964 +create an order package +that indicates support + +429 +00:20:31,964 --> 00:20:34,200 +for automatic updates. + +430 +00:20:34,200 --> 00:20:36,068 +When the order is added, + +431 +00:20:36,068 --> 00:20:38,871 +the device will register +for updates to it. + +432 +00:20:38,871 --> 00:20:42,975 +Your server must store +information about registrations. + +433 +00:20:42,975 --> 00:20:45,978 +Later on, when your server +updates the order, + +434 +00:20:45,978 --> 00:20:49,215 +use the registration information +to notify devices + +435 +00:20:49,215 --> 00:20:52,518 +that registered for updates +to it. + +436 +00:20:52,518 --> 00:20:55,154 +When the device receives +the push notification, + +437 +00:20:55,154 --> 00:20:58,757 +it will again request the order +from your server. + +438 +00:20:58,757 --> 00:21:01,427 +Your server then returns +the updated order package + +439 +00:21:01,427 --> 00:21:03,629 +to the device. + +440 +00:21:03,629 --> 00:21:05,231 +Only your customers and you + +441 +00:21:05,231 --> 00:21:07,266 +should know +what they have ordered. + +442 +00:21:07,266 --> 00:21:10,269 +We designed order tracking +with privacy in mind. + +443 +00:21:10,269 --> 00:21:12,972 +Order information +is exchanged directly + +444 +00:21:12,972 --> 00:21:15,508 +between devices and your server. + +445 +00:21:15,508 --> 00:21:18,077 +When orders are synchronized +via iCloud, + +446 +00:21:18,077 --> 00:21:20,246 +they are end-to-end encrypted. + +447 +00:21:20,246 --> 00:21:21,914 +Follow these practices +to provide + +448 +00:21:21,914 --> 00:21:24,717 +the best customer +experience possible. + +449 +00:21:24,717 --> 00:21:27,720 +Associate your app +with orders that you provide. + +450 +00:21:27,720 --> 00:21:30,990 +If your app delivers +notifications and is installed, + +451 +00:21:30,990 --> 00:21:34,026 +you can disable order tracking +notifications. + +452 +00:21:34,026 --> 00:21:37,696 +This helps to prevent +duplicate notifications. + +453 +00:21:37,696 --> 00:21:40,900 +Use the knowledge you have +about your customer preferences + +454 +00:21:40,900 --> 00:21:44,203 +to provide relevant +localizations only. + +455 +00:21:44,203 --> 00:21:46,872 +Be mindful of +the order package size. + +456 +00:21:46,872 --> 00:21:48,374 +Try to keep the size small + +457 +00:21:48,374 --> 00:21:51,043 +to reduce expensive +networking cost. + +458 +00:21:51,043 --> 00:21:52,378 +When you update an order, + +459 +00:21:52,378 --> 00:21:56,215 +promptly notify devices that +registered for updates to it. + +460 +00:21:56,215 --> 00:22:00,186 +Orders in Wallet should match +the actual state of the order. + +461 +00:22:00,186 --> 00:22:05,024 +Make sure to also check out +the HIG for order tracking. + +462 +00:22:05,024 --> 00:22:07,793 +Platforms can make your +integration of order tracking + +463 +00:22:07,793 --> 00:22:09,161 +much simpler. + +464 +00:22:09,161 --> 00:22:12,998 +We are happy to announce +Shopify, Narvar, and Route + +465 +00:22:12,998 --> 00:22:15,467 +will support order tracking +by fall. + +466 +00:22:15,467 --> 00:22:18,170 +Look out for more platforms +supporting order tracking + +467 +00:22:18,170 --> 00:22:19,838 +in the coming months. + +468 +00:22:19,838 --> 00:22:21,840 +Order tracking is +a great way to enhance + +469 +00:22:21,840 --> 00:22:24,944 +the postpurchase experience +for your customers. + +470 +00:22:24,944 --> 00:22:26,579 +With automatic updates, + +471 +00:22:26,579 --> 00:22:28,647 +your customers +will always be up to date + +472 +00:22:28,647 --> 00:22:31,016 +about the status +of their orders. + +473 +00:22:31,016 --> 00:22:33,953 +We believe your customers +will love this experience + +474 +00:22:33,953 --> 00:22:36,722 +and we look forward +to ordering with you soon! + +475 +00:22:36,722 --> 00:22:39,258 +Now, I'll pass over to David. + +476 +00:22:39,258 --> 00:22:41,060 +David: Thanks, Lais! + +477 +00:22:41,060 --> 00:22:44,363 +I'm super excited to talk about +the new functionality + +478 +00:22:44,363 --> 00:22:48,300 +we've added to IDs in Wallet +in iOS 16. + +479 +00:22:48,300 --> 00:22:52,938 +We launched IDs in Wallet +earlier this year in iOS 15.4. + +480 +00:22:52,938 --> 00:22:55,307 +It allows users +in supported US states + +481 +00:22:55,307 --> 00:22:58,677 +to add their driver's license +or State ID to Wallet. + +482 +00:22:58,677 --> 00:23:01,981 +IDs in Wallet are issued +by the same issuing authorities + +483 +00:23:01,981 --> 00:23:04,149 +as users' physical IDs. + +484 +00:23:04,149 --> 00:23:07,419 +in the US, that's their State's +Department of Motor Vehicles + +485 +00:23:07,419 --> 00:23:10,322 +or equivalent organization. + +486 +00:23:10,322 --> 00:23:14,660 +In iOS 16, we've added +a new API that allows apps + +487 +00:23:14,660 --> 00:23:17,930 +and App Clips to request +information from IDs in Wallet + +488 +00:23:17,930 --> 00:23:21,634 +in order to verify +a user's age or identity. + +489 +00:23:21,634 --> 00:23:24,103 +Your app will request +the information, + +490 +00:23:24,103 --> 00:23:26,805 +the user will review +and approve the request, + +491 +00:23:26,805 --> 00:23:29,341 +Then your app will send +the response to your server + +492 +00:23:29,341 --> 00:23:32,244 +for decryption and verification. + +493 +00:23:32,244 --> 00:23:34,179 +You can request +a number of data elements + +494 +00:23:34,179 --> 00:23:35,781 +from the user's ID. + +495 +00:23:35,781 --> 00:23:39,752 +These include their name, +their address, + +496 +00:23:39,752 --> 00:23:42,154 +their date of birth, +their photo -- + +497 +00:23:42,154 --> 00:23:43,756 +known as the portrait -- + +498 +00:23:43,756 --> 00:23:46,625 +the issuing authority +that issued their ID, + +499 +00:23:46,625 --> 00:23:49,995 +the number and expiration +date of their physical ID, + +500 +00:23:49,995 --> 00:23:52,598 +and the driving privileges +granted by their ID, + +501 +00:23:52,598 --> 00:23:54,667 +if there are any. + +502 +00:23:54,667 --> 00:23:58,704 +A very common use case for IDs +is to verify someone's age. + +503 +00:23:58,704 --> 00:24:02,641 +With a physical ID, that means +looking at their date of birth. + +504 +00:24:02,641 --> 00:24:04,977 +But the date of birth reveals +far more information + +505 +00:24:04,977 --> 00:24:08,113 +than is necessary +just to verify age. + +506 +00:24:08,113 --> 00:24:10,616 +If you're checking my age, +you don't actually need to know + +507 +00:24:10,616 --> 00:24:12,985 +the exact day or year +I was born, + +508 +00:24:12,985 --> 00:24:14,753 +or even how old I am. + +509 +00:24:14,753 --> 00:24:17,723 +You just need to know +if I'm old enough. + +510 +00:24:17,723 --> 00:24:21,493 +With IDs in Wallet, you can ask +that question directly. + +511 +00:24:21,493 --> 00:24:23,962 +Your app can request +a Boolean data element + +512 +00:24:23,962 --> 00:24:27,099 +indicating whether the user +is above a certain age, + +513 +00:24:27,099 --> 00:24:29,268 +providing a more +privacy-preserving way + +514 +00:24:29,268 --> 00:24:33,505 +to do age verification than +checking the full date of birth. + +515 +00:24:33,505 --> 00:24:36,775 +When your app invokes the API, +a sheet will show the user + +516 +00:24:36,775 --> 00:24:39,378 +what information +you're requesting. + +517 +00:24:39,378 --> 00:24:41,080 +It will also show +whether you intend + +518 +00:24:41,080 --> 00:24:42,548 +to store that information, + +519 +00:24:42,548 --> 00:24:44,817 +and for how long +you intend to store it. + +520 +00:24:44,817 --> 00:24:47,119 +This lets the user make +an informed decision + +521 +00:24:47,119 --> 00:24:49,822 +about whether to share +the information with your app. + +522 +00:24:49,822 --> 00:24:52,091 +No information is shared +until they give + +523 +00:24:52,091 --> 00:24:56,328 +their explicit approval +using Face ID or Touch ID. + +524 +00:24:56,328 --> 00:24:58,831 +The response you receive +contains just the elements + +525 +00:24:58,831 --> 00:25:00,199 +you requested. + +526 +00:25:00,199 --> 00:25:02,501 +Other identity verification +mechanisms, + +527 +00:25:02,501 --> 00:25:04,937 +such as scanning +a physical ID card, + +528 +00:25:04,937 --> 00:25:07,272 +share everything +that's on the ID. + +529 +00:25:07,272 --> 00:25:09,708 +By limiting the sharing +to just what you need, + +530 +00:25:09,708 --> 00:25:12,945 +IDs in Wallet is more +privacy-preserving for the user + +531 +00:25:12,945 --> 00:25:15,147 +and reduces the amount +of sensitive information + +532 +00:25:15,147 --> 00:25:18,751 +you need to keep secure +on your server. + +533 +00:25:18,751 --> 00:25:21,653 +The response is signed +by the ID's issuing authority, + +534 +00:25:21,653 --> 00:25:23,555 +making it straightforward +to verify + +535 +00:25:23,555 --> 00:25:26,725 +that the information +in the response is authentic. + +536 +00:25:26,725 --> 00:25:29,228 +Note that the issuing authority +creates the ID + +537 +00:25:29,228 --> 00:25:32,398 +but is not involved +at the time you invoke the API. + +538 +00:25:32,398 --> 00:25:34,933 +They do not learn when users +share their information + +539 +00:25:34,933 --> 00:25:39,204 +or to whom they share it with. + +540 +00:25:39,204 --> 00:25:42,007 +To use the API, you need +to request an entitlement + +541 +00:25:42,007 --> 00:25:44,910 +through your developer account. + +542 +00:25:44,910 --> 00:25:46,745 +You'll then need to set up +a merchant ID + +543 +00:25:46,745 --> 00:25:48,814 +and encryption certificate. + +544 +00:25:48,814 --> 00:25:51,850 +This process is very similar +to setting up in-app payment + +545 +00:25:51,850 --> 00:25:53,252 +with Apple Pay. + +546 +00:25:53,252 --> 00:25:58,123 +We'll talk more about how to use +the ID and certificate in a little bit. + +547 +00:25:58,123 --> 00:26:01,427 +For now, let's talk about +the verification flow. + +548 +00:26:01,427 --> 00:26:04,296 +At a high level, +it consists of four steps. + +549 +00:26:04,296 --> 00:26:07,966 +First, your app will invoke the +API in the PassKit framework + +550 +00:26:07,966 --> 00:26:11,770 +and specify the information +you're requesting. + +551 +00:26:11,770 --> 00:26:13,439 +The system will then display +a sheet + +552 +00:26:13,439 --> 00:26:16,408 +prompting the user +to approve the request. + +553 +00:26:16,408 --> 00:26:18,610 +Once they do, +your app will receive + +554 +00:26:18,610 --> 00:26:21,113 +an encrypted response. + +555 +00:26:21,113 --> 00:26:23,816 +Your app will then pass +that response to your server + +556 +00:26:23,816 --> 00:26:27,186 +for decryption and verification. + +557 +00:26:27,186 --> 00:26:32,124 +First, let's talk about +how to use the API in PassKit. + +558 +00:26:32,124 --> 00:26:34,660 +If your app uses SwiftUI, +you should use the + +559 +00:26:34,660 --> 00:26:37,963 +VerifyIdentityWithWalletButton +SwiftUI view. + +560 +00:26:37,963 --> 00:26:39,665 +This displays a button +that triggers + +561 +00:26:39,665 --> 00:26:43,101 +the identity verification +flow when pressed. + +562 +00:26:43,101 --> 00:26:46,839 +Just like the Pay with Apple Pay +and Add Pass to Wallet buttons, + +563 +00:26:46,839 --> 00:26:50,342 +the Verify Identity with Wallet +button provides a familiar, + +564 +00:26:50,342 --> 00:26:54,179 +consistent experience +across apps using the API. + +565 +00:26:54,179 --> 00:26:56,248 +You can choose between +four different labels + +566 +00:26:56,248 --> 00:26:58,650 +to display a button +that fits your use case. + +567 +00:26:58,650 --> 00:27:00,853 +It automatically switches +between single + +568 +00:27:00,853 --> 00:27:05,924 +and multiline versions depending +on the space available to it. + +569 +00:27:05,924 --> 00:27:07,426 +When creating the button, + +570 +00:27:07,426 --> 00:27:10,896 +you need to specify +a PKIdentityRequest object, + +571 +00:27:10,896 --> 00:27:13,665 +which describes the information +you want to request + +572 +00:27:13,665 --> 00:27:15,534 +and how it should be returned. + +573 +00:27:15,534 --> 00:27:18,770 +Let's take a look +at how to create it. + +574 +00:27:18,770 --> 00:27:19,905 +You begin by creating a + +575 +00:27:19,905 --> 00:27:22,841 +PKIdentityDrivers +LicenseDescriptor, + +576 +00:27:22,841 --> 00:27:24,476 +which describes +the data elements + +577 +00:27:24,476 --> 00:27:26,044 +that you're looking for. + +578 +00:27:26,044 --> 00:27:28,480 +Use the addElements method +to specify elements + +579 +00:27:28,480 --> 00:27:29,715 +you want to request, + +580 +00:27:29,715 --> 00:27:32,417 +along with whether you intend +to store them. + +581 +00:27:32,417 --> 00:27:35,387 +You can invoke the addElements +method multiple times + +582 +00:27:35,387 --> 00:27:37,189 +to specify different +sets of elements + +583 +00:27:37,189 --> 00:27:39,224 +with different intents to store. + +584 +00:27:39,224 --> 00:27:42,427 +In this example, +I'm calling it twice. + +585 +00:27:42,427 --> 00:27:45,364 +First, I add an +age(atLeast: element, + +586 +00:27:45,364 --> 00:27:48,367 +which is not going to be stored. + +587 +00:27:48,367 --> 00:27:51,303 +Then, I call the addElements +method again to request + +588 +00:27:51,303 --> 00:27:54,640 +the user's givenName, +familyName, and portrait, + +589 +00:27:54,640 --> 00:27:58,343 +all of which may be stored +for up to 30 days. + +590 +00:27:58,343 --> 00:28:03,315 +The descriptor then goes +into a PKIdentityRequest. + +591 +00:28:03,315 --> 00:28:07,185 +The next step is to specify +a merchantIdentifier to use. + +592 +00:28:07,185 --> 00:28:08,754 +The merchantIdentifier +indicates + +593 +00:28:08,754 --> 00:28:11,557 +the encryption certificate +to which the API response + +594 +00:28:11,557 --> 00:28:13,425 +will be encrypted. + +595 +00:28:13,425 --> 00:28:15,627 +You'll configure both +the merchantIdentifier + +596 +00:28:15,627 --> 00:28:19,765 +and its encryption certificate +through your developer account. + +597 +00:28:19,765 --> 00:28:22,200 +Finally, you'll need +to specify a nonce, + +598 +00:28:22,200 --> 00:28:23,936 +which will be tied +to the response you receive + +599 +00:28:23,936 --> 00:28:25,437 +from the API. + +600 +00:28:25,437 --> 00:28:27,172 +Its an important +security feature + +601 +00:28:27,172 --> 00:28:29,374 +used to prevent replays +of a response + +602 +00:28:29,374 --> 00:28:32,611 +and to bind it +to a specific user session. + +603 +00:28:32,611 --> 00:28:35,347 +Exactly how you manage the nonce +is up to you, + +604 +00:28:35,347 --> 00:28:37,649 +based on your own +security requirements. + +605 +00:28:37,649 --> 00:28:40,652 +Often, it comes from +your server, because later on, + +606 +00:28:40,652 --> 00:28:42,854 +your server will be +responsible for enforcing + +607 +00:28:42,854 --> 00:28:45,457 +that the nonce is valid. + +608 +00:28:45,457 --> 00:28:47,059 +With all of those +properties set, + +609 +00:28:47,059 --> 00:28:50,362 +you have your +PKIdentityRequest. + +610 +00:28:50,362 --> 00:28:52,731 +Now, let's get back +to the button. + +611 +00:28:52,731 --> 00:28:54,999 +If identity verification +is available, + +612 +00:28:54,999 --> 00:28:57,002 +the button will be displayed +in your app, + +613 +00:28:57,002 --> 00:28:59,671 +and tapping it will start +the identity verification flow + +614 +00:28:59,671 --> 00:29:02,074 +with your request. + +615 +00:29:02,074 --> 00:29:04,743 +If identity verification +is not available, + +616 +00:29:04,743 --> 00:29:08,347 +a fallback view you specify +will be displayed instead. + +617 +00:29:08,347 --> 00:29:11,416 +For example, this will happen +if there isn't an ID in Wallet + +618 +00:29:11,416 --> 00:29:12,951 +on this iPhone. + +619 +00:29:12,951 --> 00:29:14,252 +You can use +the fallback view + +620 +00:29:14,252 --> 00:29:18,190 +to offer other ways +to verify identity. + +621 +00:29:18,190 --> 00:29:20,993 +Let's assume identity +verification is available, + +622 +00:29:20,993 --> 00:29:22,928 +and the user taps the button. + +623 +00:29:22,928 --> 00:29:25,831 +The system will then show +a sheet with your request, + +624 +00:29:25,831 --> 00:29:27,766 +including the elements +you requested + +625 +00:29:27,766 --> 00:29:30,135 +and your intent to store them. + +626 +00:29:30,135 --> 00:29:33,772 +The user can approve the request +with Face ID or Touch ID, + +627 +00:29:33,772 --> 00:29:37,109 +or close the sheet +without approving. + +628 +00:29:37,109 --> 00:29:39,378 +Your code will then receive +a result object + +629 +00:29:39,378 --> 00:29:42,948 +containing the outcome +of the request. + +630 +00:29:42,948 --> 00:29:46,818 +If the request was approved, +you'll receive a success result. + +631 +00:29:46,818 --> 00:29:49,554 +This comes with +a PKIdentityDocument object + +632 +00:29:49,554 --> 00:29:51,556 +containing the +encrypted response, + +633 +00:29:51,556 --> 00:29:53,191 +which your app +will send to your server + +634 +00:29:53,191 --> 00:29:56,895 +for decryption and verification. + +635 +00:29:56,895 --> 00:29:58,897 +If the request +was not successful, + +636 +00:29:58,897 --> 00:30:00,932 +you'll receive a failure result. + +637 +00:30:00,932 --> 00:30:02,300 +The most common cause +of failure + +638 +00:30:02,300 --> 00:30:04,269 +is that the request +was not approved, + +639 +00:30:04,269 --> 00:30:07,606 +in which case you'll receive +a cancelled error. + +640 +00:30:07,606 --> 00:30:10,275 +That was +VerifyIdentityWithWalletButton, + +641 +00:30:10,275 --> 00:30:12,644 +the SwiftUI version of the API. + +642 +00:30:12,644 --> 00:30:14,246 +Use it to display a button + +643 +00:30:14,246 --> 00:30:16,648 +that launches +the identity verification flow + +644 +00:30:16,648 --> 00:30:19,851 +and requests information +from IDs in Wallet. + +645 +00:30:19,851 --> 00:30:22,287 +If you're not using +SwiftUI in your app, + +646 +00:30:22,287 --> 00:30:24,790 +you can also use +the PKIdentityButton + +647 +00:30:24,790 --> 00:30:27,793 +and PKIdentityAuthorization +Controller classes + +648 +00:30:27,793 --> 00:30:31,096 +to accomplish the same thing. + +649 +00:30:31,096 --> 00:30:34,533 +OK, so, now you've +requested the information, + +650 +00:30:34,533 --> 00:30:36,802 +the user's approved the request, + +651 +00:30:36,802 --> 00:30:38,704 +and your app has sent +the encrypted response + +652 +00:30:38,704 --> 00:30:40,238 +to your server. + +653 +00:30:40,238 --> 00:30:42,641 +Now, let's talk about +what your server needs to do + +654 +00:30:42,641 --> 00:30:46,511 +to decrypt and verify +that response. + +655 +00:30:46,511 --> 00:30:49,181 +I'm only going to skim +the surface on this topic, + +656 +00:30:49,181 --> 00:30:53,218 +so please check the developer +documentation for more details. + +657 +00:30:53,218 --> 00:30:56,188 +The response format uses +several international standards, + +658 +00:30:56,188 --> 00:30:58,590 +so I strongly recommend +familiarizing yourself + +659 +00:30:58,590 --> 00:31:01,493 +with those as well. + +660 +00:31:01,493 --> 00:31:03,095 +The response data you'll receive + +661 +00:31:03,095 --> 00:31:06,098 +is in a CBOR-encoded +encryption envelope. + +662 +00:31:06,098 --> 00:31:10,235 +CBOR is a data format +defined in RFC 8949. + +663 +00:31:10,235 --> 00:31:11,636 +It's similar to JSON + +664 +00:31:11,636 --> 00:31:14,473 +but uses binary data +to encode objects. + +665 +00:31:14,473 --> 00:31:16,675 +The encryption envelope +contains metadata + +666 +00:31:16,675 --> 00:31:18,610 +needed for +the decryption process, + +667 +00:31:18,610 --> 00:31:21,713 +along with the encrypted data +itself. + +668 +00:31:21,713 --> 00:31:24,316 +The data is encrypted +using HPKE, + +669 +00:31:24,316 --> 00:31:28,019 +an encryption scheme +defined in RFC 9180. + +670 +00:31:28,019 --> 00:31:32,023 +Your server will decrypt +this data using its private key. + +671 +00:31:32,023 --> 00:31:35,327 +Once decrypted, you'll get +an mdoc response object. + +672 +00:31:35,327 --> 00:31:39,364 +The mdoc response is defined +in ISO 18013 part five, + +673 +00:31:39,364 --> 00:31:44,002 +the ISO standard for mobile +Driver's Licenses and State IDs. + +674 +00:31:44,002 --> 00:31:46,638 +The mdoc response object +contains the data elements + +675 +00:31:46,638 --> 00:31:48,573 +that you requested. + +676 +00:31:48,573 --> 00:31:51,076 +It also includes a number +of security features + +677 +00:31:51,076 --> 00:31:52,711 +that your server +needs to validate + +678 +00:31:52,711 --> 00:31:55,413 +to ensure that the response +is authentic. + +679 +00:31:55,413 --> 00:31:57,682 +Note that your server +will perform the decryption + +680 +00:31:57,682 --> 00:31:59,584 +and validation itself. + +681 +00:31:59,584 --> 00:32:02,587 +Neither Apple servers nor +the issuing authority's servers + +682 +00:32:02,587 --> 00:32:04,322 +are involved. + +683 +00:32:04,322 --> 00:32:06,057 +Before we can talk +about decryption + +684 +00:32:06,057 --> 00:32:07,526 +and response validation, + +685 +00:32:07,526 --> 00:32:09,995 +we need to talk about +the session transcript. + +686 +00:32:09,995 --> 00:32:13,131 +This is a CBOR structure +that binds a response payload + +687 +00:32:13,131 --> 00:32:16,168 +to a specific request +from a specific app. + +688 +00:32:16,168 --> 00:32:18,770 +Your server will need to build +this structure and use it + +689 +00:32:18,770 --> 00:32:23,041 +during both +decryption and validation. + +690 +00:32:23,041 --> 00:32:26,478 +The session transcript contains +the same nonce and merchant ID + +691 +00:32:26,478 --> 00:32:29,681 +you used earlier in your +PKIdentityRequest, + +692 +00:32:29,681 --> 00:32:32,184 +as well as the team ID +of your developer team + +693 +00:32:32,184 --> 00:32:35,253 +and the SHA256 hash +of your encryption certificate's + +694 +00:32:35,253 --> 00:32:37,756 +public key. + +695 +00:32:37,756 --> 00:32:39,491 +When building +the session transcript, + +696 +00:32:39,491 --> 00:32:41,793 +your server should check +that the inputs you're using + +697 +00:32:41,793 --> 00:32:43,061 +are all valid. + +698 +00:32:43,061 --> 00:32:45,630 +That means the nonce shouldn't +have been used already, + +699 +00:32:45,630 --> 00:32:47,966 +and should be tied +to the current user. + +700 +00:32:47,966 --> 00:32:50,068 +The other values should +match what's expected + +701 +00:32:50,068 --> 00:32:52,270 +on your developer account. + +702 +00:32:52,270 --> 00:32:55,574 +Now let's talk about decrypting +the encrypted data. + +703 +00:32:55,574 --> 00:32:57,909 +You'll need the session +transcript you just created, + +704 +00:32:57,909 --> 00:33:01,279 +along with the metadata +from the encryption envelope. + +705 +00:33:01,279 --> 00:33:03,849 +You'll also need +your private key. + +706 +00:33:03,849 --> 00:33:04,950 +This is the private key + +707 +00:33:04,950 --> 00:33:07,786 +corresponding to the certificate +you setup earlier + +708 +00:33:07,786 --> 00:33:09,454 +in your developer account. + +709 +00:33:09,454 --> 00:33:12,290 +To protect the confidentiality +of user information, + +710 +00:33:12,290 --> 00:33:15,260 +you need to make sure +your private key stays private. + +711 +00:33:15,260 --> 00:33:17,162 +Store it securely +on your server + +712 +00:33:17,162 --> 00:33:19,331 +and never include it +in your app. + +713 +00:33:19,331 --> 00:33:21,600 +If your private key +is ever compromised, + +714 +00:33:21,600 --> 00:33:25,871 +revoke the certificate in your +developer account immediately. + +715 +00:33:25,871 --> 00:33:27,939 +After decrypting +the encrypted data, + +716 +00:33:27,939 --> 00:33:30,108 +you'll receive +an mdoc response object + +717 +00:33:30,108 --> 00:33:32,277 +containing two +cryptographic signatures, + +718 +00:33:32,277 --> 00:33:34,479 +plus the data elements +you requested. + +719 +00:33:34,479 --> 00:33:37,349 +You need to check both +signatures in the mdoc response + +720 +00:33:37,349 --> 00:33:39,851 +before you can use +its data elements. + +721 +00:33:39,851 --> 00:33:42,921 +First, you need to check +the issuer signature. + +722 +00:33:42,921 --> 00:33:45,090 +This is a signature +from the issuing authority + +723 +00:33:45,090 --> 00:33:46,958 +of the user's ID. + +724 +00:33:46,958 --> 00:33:48,460 +By checking this signature, + +725 +00:33:48,460 --> 00:33:50,662 +you're verifying that the data +in the response + +726 +00:33:50,662 --> 00:33:52,697 +came from the real +issuing authority + +727 +00:33:52,697 --> 00:33:54,599 +and wasn't tampered with. + +728 +00:33:54,599 --> 00:33:57,535 +You should check that not only +is the signature valid, + +729 +00:33:57,535 --> 00:34:01,273 +but also that it is signed by +an issuer certificate you trust. + +730 +00:34:01,273 --> 00:34:03,842 +Take a look at the documentation +for more details + +731 +00:34:03,842 --> 00:34:07,779 +about the issuer certificates +used by IDs in Wallet. + +732 +00:34:07,779 --> 00:34:11,016 +Next, you need to verify +the device signature. + +733 +00:34:11,016 --> 00:34:13,051 +This is a signature +created by a key + +734 +00:34:13,051 --> 00:34:15,954 +in the Secure Element +of the user's iPhone. + +735 +00:34:15,954 --> 00:34:18,156 +It proves that the response +you received + +736 +00:34:18,156 --> 00:34:20,625 +came from the same iPhone +that the issuing authority + +737 +00:34:20,625 --> 00:34:22,994 +originally issued the ID to. + +738 +00:34:22,994 --> 00:34:25,730 +Here, you'll need to use +your session transcript again, + +739 +00:34:25,730 --> 00:34:29,968 +along with some information +covered by the issuer signature. + +740 +00:34:29,968 --> 00:34:32,404 +Finally, you're ready +to use the data elements + +741 +00:34:32,404 --> 00:34:33,905 +that you requested. + +742 +00:34:33,905 --> 00:34:35,407 +You should never +use these elements + +743 +00:34:35,407 --> 00:34:38,710 +without first verifying the +issuer and device signatures, + +744 +00:34:38,710 --> 00:34:41,713 +because otherwise you don't know +whether the data you received + +745 +00:34:41,713 --> 00:34:44,115 +is authentic. + +746 +00:34:44,115 --> 00:34:46,618 +And with all those steps +completed, you're done! + +747 +00:34:46,618 --> 00:34:48,753 +Your app has requested +the information, + +748 +00:34:48,753 --> 00:34:52,691 +and your server has decrypted +and verified the response. + +749 +00:34:52,691 --> 00:34:55,260 +You might be wondering +how to test your implementation + +750 +00:34:55,260 --> 00:34:57,262 +if you don't have +an ID in Wallet. + +751 +00:34:57,262 --> 00:35:01,433 +We've provided a few mechanisms +to help you do that. + +752 +00:35:01,433 --> 00:35:04,269 +First, you can test +in the iOS Simulator, + +753 +00:35:04,269 --> 00:35:07,238 +where the API will return +a mock response. + +754 +00:35:07,238 --> 00:35:09,641 +This response is similar +to a real one, + +755 +00:35:09,641 --> 00:35:12,978 +but lacks real signatures. + +756 +00:35:12,978 --> 00:35:15,480 +Similarly, you can use +a test profile + +757 +00:35:15,480 --> 00:35:18,016 +to receive a mock response +on a real iPhone, + +758 +00:35:18,016 --> 00:35:21,319 +even If you don't have an ID +in Wallet on that iPhone. + +759 +00:35:21,319 --> 00:35:24,956 +See the documentation for more +details about how to do this. + +760 +00:35:24,956 --> 00:35:26,691 +Note that your server +should never treat + +761 +00:35:26,691 --> 00:35:30,495 +either of these mock responses +like a real one. + +762 +00:35:30,495 --> 00:35:32,964 +To help you with +your server implementation, + +763 +00:35:32,964 --> 00:35:36,167 +the documentation also +includes an example response, + +764 +00:35:36,167 --> 00:35:41,439 +along with everything you need +to decrypt and validate it. + +765 +00:35:41,439 --> 00:35:44,376 +And that's how you can perform +identity verification + +766 +00:35:44,376 --> 00:35:47,178 +with IDs in Wallet in iOS 16. + +767 +00:35:47,178 --> 00:35:49,948 +We discussed how to use +the API in your app, + +768 +00:35:49,948 --> 00:35:52,083 +how to process the response +on your server, + +769 +00:35:52,083 --> 00:35:54,886 +and how to test +your implementation. + +770 +00:35:54,886 --> 00:35:57,622 +Lais: This year, we introduced +many great new features + +771 +00:35:57,622 --> 00:35:59,391 +to Wallet and Apple Pay. + +772 +00:35:59,391 --> 00:36:01,760 +These include +multi-merchant payments, + +773 +00:36:01,760 --> 00:36:04,362 +improved support +for automatic payments, + +774 +00:36:04,362 --> 00:36:07,565 +order tracking, +and identity verification. + +775 +00:36:07,565 --> 00:36:09,434 +Please check out +the developer documentation + +776 +00:36:09,434 --> 00:36:10,802 +for more information. + +777 +00:36:10,802 --> 00:36:14,973 +David: Thank you for watching, +and have a great WWDC! + +778 +00:36:14,973 --> 00:36:19,077 +♪ + diff --git a/eng/2022 Session 10043 What's new in App Store Connect en.srt b/eng/2022 Session 10043 What's new in App Store Connect en.srt new file mode 100644 index 0000000..be60817 --- /dev/null +++ b/eng/2022 Session 10043 What's new in App Store Connect en.srt @@ -0,0 +1,1029 @@ +1 +00:00:00,267 --> 00:00:03,670 +♪ Mellow instrumental +hip-hip music ♪ + +2 +00:00:03,670 --> 00:00:09,710 +♪ + +3 +00:00:09,710 --> 00:00:12,746 +Hello, and welcome to "What's +New in App Store Connect." + +4 +00:00:12,746 --> 00:00:14,815 +My name's Will Averill, +and I'm an engineering manager + +5 +00:00:14,815 --> 00:00:17,150 +on the App Store Connect team. + +6 +00:00:17,150 --> 00:00:20,053 +App Store Connect has grown +considerably over the years. + +7 +00:00:20,053 --> 00:00:22,489 +You use it to create, +manage, and grow your apps + +8 +00:00:22,489 --> 00:00:25,425 +on the App Store +across all of our platforms. + +9 +00:00:25,425 --> 00:00:27,895 +And we continue to bring new +features to App Store Connect + +10 +00:00:27,895 --> 00:00:33,500 +on web, our iOS and iPadOS app, +and the App Store Connect API. + +11 +00:00:33,500 --> 00:00:36,637 +Just last year, we launched +a bunch of great new features. + +12 +00:00:36,637 --> 00:00:39,273 +You've probably heard all about +things like in-app events, + +13 +00:00:39,273 --> 00:00:41,942 +custom product pages, +and TestFlight for Mac. + +14 +00:00:41,942 --> 00:00:43,744 +So instead, +let me quickly highlight + +15 +00:00:43,744 --> 00:00:46,546 +a couple recent updates +you might have missed. + +16 +00:00:46,546 --> 00:00:48,916 +In TestFlight, we made +managing your build's + +17 +00:00:48,916 --> 00:00:51,518 +associated tester groups easier. + +18 +00:00:51,518 --> 00:00:54,721 +With just one click, you can +now quickly add or remove + +19 +00:00:54,721 --> 00:00:56,290 +a tester group to a build + +20 +00:00:56,290 --> 00:00:59,493 +directly from the Versions +or Build Groups tab. + +21 +00:00:59,493 --> 00:01:02,162 +We also heard your feedback +loud and clear, + +22 +00:01:02,162 --> 00:01:04,798 +so we made an update +allowing you to transfer apps + +23 +00:01:04,798 --> 00:01:06,867 +that use Apple Wallet. + +24 +00:01:06,867 --> 00:01:09,603 +And finally, to facilitate +the submission process + +25 +00:01:09,603 --> 00:01:11,972 +for in-app events, +custom product pages, + +26 +00:01:11,972 --> 00:01:13,840 +and product page optimization, + +27 +00:01:13,840 --> 00:01:17,044 +we launched the enhanced +App Store submission experience. + +28 +00:01:17,044 --> 00:01:19,179 +This is one the biggest +enhancements we've made + +29 +00:01:19,179 --> 00:01:20,714 +to the submission workflow, + +30 +00:01:20,714 --> 00:01:24,184 +so let's explore this together +in a bit more detail. + +31 +00:01:24,184 --> 00:01:27,054 +First, you're now able +to group multiple items + +32 +00:01:27,054 --> 00:01:29,323 +into a single submission. + +33 +00:01:29,323 --> 00:01:31,224 +Additionally -- +in most cases -- + +34 +00:01:31,224 --> 00:01:34,861 +you can choose to submit +without a new app version. + +35 +00:01:34,861 --> 00:01:37,864 +And we introduced +a dedicated App Review page + +36 +00:01:37,864 --> 00:01:39,999 +where you can manage +in-progress submissions, + +37 +00:01:39,999 --> 00:01:41,535 +communicate with App Review, + +38 +00:01:41,535 --> 00:01:44,471 +and even view recently +completed submissions. + +39 +00:01:44,471 --> 00:01:46,440 +Let's start by talking +about what it means + +40 +00:01:46,440 --> 00:01:49,242 +to group items +in a review submission. + +41 +00:01:49,242 --> 00:01:51,478 +Say we have a number +of custom product pages + +42 +00:01:51,478 --> 00:01:54,781 +or any other review items +we want to publish to the store. + +43 +00:01:54,781 --> 00:01:56,650 +Since review items +can only be submitted + +44 +00:01:56,650 --> 00:01:58,518 +as part of a review submission, + +45 +00:01:58,518 --> 00:02:01,154 +the first step is +to add them to one. + +46 +00:02:01,154 --> 00:02:03,290 +Think of a review submission +as the vehicle + +47 +00:02:03,290 --> 00:02:06,893 +that carries review items +to and from App Review. + +48 +00:02:06,893 --> 00:02:10,397 +Now, a review submission only +needs one item to be submitted, + +49 +00:02:10,397 --> 00:02:12,399 +but the advantage +to grouping several items + +50 +00:02:12,399 --> 00:02:14,868 +is they are all reviewed +in context together. + +51 +00:02:14,868 --> 00:02:17,871 +This helps ensure a consistent +and efficient review. + +52 +00:02:17,871 --> 00:02:19,806 +In fact, all review submissions + +53 +00:02:19,806 --> 00:02:22,209 +are typically reviewed +within 24 hours, + +54 +00:02:22,209 --> 00:02:26,113 +regardless of the number +or type of review items. + +55 +00:02:26,113 --> 00:02:28,048 +After review, +each item in the submission + +56 +00:02:28,048 --> 00:02:30,784 +will be marked as +accepted or rejected. + +57 +00:02:30,784 --> 00:02:32,052 +It's very important +to understand + +58 +00:02:32,052 --> 00:02:34,321 +that no review +items are approved + +59 +00:02:34,321 --> 00:02:37,591 +until all items in the review +submission are accepted. + +60 +00:02:37,591 --> 00:02:39,493 +So let's look at two ways +to move forward + +61 +00:02:39,493 --> 00:02:42,529 +with a review submission +that has rejected items. + +62 +00:02:42,529 --> 00:02:45,832 +The first option is to edit any +of these rejected review items + +63 +00:02:45,832 --> 00:02:47,534 +and then resubmit. + +64 +00:02:47,534 --> 00:02:49,936 +If those items +now come back accepted, + +65 +00:02:49,936 --> 00:02:51,671 +the review submission +is complete + +66 +00:02:51,671 --> 00:02:52,839 +and every item is approved + +67 +00:02:52,839 --> 00:02:55,675 +and able to be published +to the App Store. + +68 +00:02:55,675 --> 00:02:57,210 +The other option +is to simply remove + +69 +00:02:57,210 --> 00:02:59,913 +any rejected items +from the submission. + +70 +00:02:59,913 --> 00:03:01,515 +This leaves +the review submission + +71 +00:03:01,515 --> 00:03:03,083 +with only approved items, + +72 +00:03:03,083 --> 00:03:06,119 +once again completing +the review process. + +73 +00:03:06,119 --> 00:03:08,789 +However, keep in mind +that any removed items + +74 +00:03:08,789 --> 00:03:11,992 +will need to be resubmitted +as a part of a new submission + +75 +00:03:11,992 --> 00:03:14,227 +in order to become approved. + +76 +00:03:14,227 --> 00:03:15,495 +And before I move on, + +77 +00:03:15,495 --> 00:03:17,431 +let me specify that +review submission items + +78 +00:03:17,431 --> 00:03:20,133 +can be app versions, +in-app events, + +79 +00:03:20,133 --> 00:03:24,438 +custom product pages, or +product page optimization tests. + +80 +00:03:24,438 --> 00:03:26,773 +Now, let's take a look +at how you're able to submit + +81 +00:03:26,773 --> 00:03:28,742 +without a new app version. + +82 +00:03:28,742 --> 00:03:30,944 +To understand how this works, + +83 +00:03:30,944 --> 00:03:32,379 +let's discuss +a few more specifics + +84 +00:03:32,379 --> 00:03:34,714 +about review submissions. + +85 +00:03:34,714 --> 00:03:39,486 +First, each submission +has an associated platform. + +86 +00:03:39,486 --> 00:03:41,455 +In addition, +each platform supports + +87 +00:03:41,455 --> 00:03:44,024 +a specific set of review items. + +88 +00:03:44,024 --> 00:03:46,526 +Although, as you can see, +most items are reviewed + +89 +00:03:46,526 --> 00:03:49,930 +and grouped as part +of an iOS submission. + +90 +00:03:49,930 --> 00:03:52,933 +And lastly, you can have one +in-progress review submission + +91 +00:03:52,933 --> 00:03:54,367 +per platform. + +92 +00:03:54,367 --> 00:03:56,503 +In this example, +you can see we're working + +93 +00:03:56,503 --> 00:03:59,272 +towards submitting all three +versions of our app. + +94 +00:03:59,272 --> 00:04:01,341 +But, let's go ahead +and take a closer look + +95 +00:04:01,341 --> 00:04:04,177 +at the iOS review submission. + +96 +00:04:04,177 --> 00:04:06,780 +App Review reviews +all items in a submission + +97 +00:04:06,780 --> 00:04:10,650 +against an app version to ensure +everything is consistent. + +98 +00:04:10,650 --> 00:04:12,752 +If there's an app version +in the submission, + +99 +00:04:12,752 --> 00:04:15,088 +that becomes the version +used for review. + +100 +00:04:15,088 --> 00:04:16,590 +But like I mentioned earlier, + +101 +00:04:16,590 --> 00:04:20,427 +you can submit without adding a +new version to your submission. + +102 +00:04:20,427 --> 00:04:22,929 +To do this requires +a previously approved version + +103 +00:04:22,929 --> 00:04:24,030 +of your app. + +104 +00:04:24,030 --> 00:04:25,565 +Of course, +once submitted, + +105 +00:04:25,565 --> 00:04:28,835 +items will be reviewed +against this version. + +106 +00:04:28,835 --> 00:04:31,171 +This means you're able +to submit in-app events, + +107 +00:04:31,171 --> 00:04:34,541 +custom product pages and +product page optimization tests + +108 +00:04:34,541 --> 00:04:37,511 +anytime after your first +iOS version is approved, + +109 +00:04:37,511 --> 00:04:40,447 +without needing +a new app binary. + +110 +00:04:40,447 --> 00:04:43,016 +So now that I've explained +how this experience works, + +111 +00:04:43,016 --> 00:04:44,317 +let me show you +where the dedicated + +112 +00:04:44,317 --> 00:04:47,821 +App Review submission page +lives in App Store Connect. + +113 +00:04:47,821 --> 00:04:49,856 +Once you've logged in +and selected an app, + +114 +00:04:49,856 --> 00:04:53,326 +click the App Review link +on the left nav menu. + +115 +00:04:53,326 --> 00:04:55,629 +This is the App Review page + +116 +00:04:55,629 --> 00:04:58,532 +where you can manage +your entire review workflow. + +117 +00:04:58,532 --> 00:05:00,734 +Here, you can see an overview +of your submissions + +118 +00:05:00,734 --> 00:05:04,171 +and click into any of them +for a more detailed look. + +119 +00:05:04,171 --> 00:05:06,873 +Now obviously, +using the web UI is great, + +120 +00:05:06,873 --> 00:05:08,775 +but wouldn't it be nice +to submit and track + +121 +00:05:08,775 --> 00:05:11,378 +submission status on the go? + +122 +00:05:11,378 --> 00:05:12,746 +That's why we're excited +to share + +123 +00:05:12,746 --> 00:05:14,915 +that we've brought the +enhanced submission experience + +124 +00:05:14,915 --> 00:05:20,520 +to App Store Connect on iPadOS +and iOS with this week's update. + +125 +00:05:20,520 --> 00:05:21,888 +You'll now be able to submit + +126 +00:05:21,888 --> 00:05:24,457 +any of your Ready for Review +submissions to App Review + +127 +00:05:24,457 --> 00:05:27,327 +from anywhere, +with just one tap. + +128 +00:05:27,327 --> 00:05:28,628 +Once submitted, + +129 +00:05:28,628 --> 00:05:31,965 +you can track the progress +of your review submissions. + +130 +00:05:31,965 --> 00:05:34,868 +And you can opt in +to receive timely notifications + +131 +00:05:34,868 --> 00:05:37,370 +about status updates + +132 +00:05:37,370 --> 00:05:41,608 +as well as manage your +submission by removing items, + +133 +00:05:41,608 --> 00:05:46,213 +viewing rejection reasons, +and replying to App Review. + +134 +00:05:46,213 --> 00:05:48,949 +So that's the enhanced +App Store submission experience, + +135 +00:05:48,949 --> 00:05:50,951 +available now +on App Store Connect + +136 +00:05:50,951 --> 00:05:53,853 +for iPadOS and iOS. + +137 +00:05:53,853 --> 00:05:55,388 +Let's shift gears a bit +and focus on + +138 +00:05:55,388 --> 00:05:57,224 +the App Store Connect API + +139 +00:05:57,224 --> 00:05:59,559 +which represents a great way +for you to customize + +140 +00:05:59,559 --> 00:06:01,928 +and automate your app workflows. + +141 +00:06:01,928 --> 00:06:04,664 +Best of all, we're always +working to add new capabilities + +142 +00:06:04,664 --> 00:06:06,633 +to the API. + +143 +00:06:06,633 --> 00:06:10,370 +Last year, we added support +for App Clips, Xcode Cloud, + +144 +00:06:10,370 --> 00:06:12,772 +in-app events, +custom product pages, + +145 +00:06:12,772 --> 00:06:14,641 +product page optimization, + +146 +00:06:14,641 --> 00:06:17,043 +and even the enhanced App Store +submission experience + +147 +00:06:17,043 --> 00:06:18,511 +I just described. + +148 +00:06:18,511 --> 00:06:20,413 +And this year is no different. + +149 +00:06:20,413 --> 00:06:23,283 +With our huge 2.0 release +coming this summer, + +150 +00:06:23,283 --> 00:06:25,685 +we're expanding the number +of resources in the API + +151 +00:06:25,685 --> 00:06:27,721 +by 60 percent. + +152 +00:06:27,721 --> 00:06:28,955 +In this summer's release, + +153 +00:06:28,955 --> 00:06:30,790 +we're adding some +great new functionality + +154 +00:06:30,790 --> 00:06:32,859 +that has been heavily requested. + +155 +00:06:32,859 --> 00:06:35,562 +Beginning with +a comprehensive set of APIs + +156 +00:06:35,562 --> 00:06:37,664 +to manage your entire +in-app purchase + +157 +00:06:37,664 --> 00:06:39,933 +and subscription lifecycle. + +158 +00:06:39,933 --> 00:06:41,935 +We started by breaking +subscriptions + +159 +00:06:41,935 --> 00:06:43,770 +into their own resource + +160 +00:06:43,770 --> 00:06:47,607 +and giving you full control +to create, edit, or delete + +161 +00:06:47,607 --> 00:06:50,710 +any of your in-app purchases +or subscriptions. + +162 +00:06:50,710 --> 00:06:54,881 +You'll also be able to manage +pricing, submit for review, + +163 +00:06:54,881 --> 00:06:57,851 +and create special offers +and promo codes. + +164 +00:06:57,851 --> 00:06:59,653 +We can't wait to see +how you take advantage + +165 +00:06:59,653 --> 00:07:02,122 +of this new opportunity +to automate your in-app purchase + +166 +00:07:02,122 --> 00:07:04,491 +and subscription workflows. + +167 +00:07:04,491 --> 00:07:05,759 +We're also adding the ability + +168 +00:07:05,759 --> 00:07:08,862 +to fetch and respond +to your app's customer reviews. + +169 +00:07:08,862 --> 00:07:10,697 +The goal here +is to allow you to build + +170 +00:07:10,697 --> 00:07:14,267 +some great custom workflows +around customer interaction. + +171 +00:07:14,267 --> 00:07:16,536 +And lastly, we're adding +additional reporting data + +172 +00:07:16,536 --> 00:07:19,439 +to the power and performance +metrics and diagnostics APIs + +173 +00:07:19,439 --> 00:07:21,141 +for app hangs. + +174 +00:07:21,141 --> 00:07:23,576 +Identifying and eliminating +hangs in your app + +175 +00:07:23,576 --> 00:07:25,612 +is a great way +to increase performance + +176 +00:07:25,612 --> 00:07:28,014 +and improve user experience. + +177 +00:07:28,014 --> 00:07:30,317 +But until now, +you could only view metrics + +178 +00:07:30,317 --> 00:07:33,186 +like your app's hang rate +through the API. + +179 +00:07:33,186 --> 00:07:34,988 +That changes this summer +as we're adding + +180 +00:07:34,988 --> 00:07:38,325 +a new diagnostic type +for app hangs. + +181 +00:07:38,325 --> 00:07:39,626 +You'll be able use this type + +182 +00:07:39,626 --> 00:07:42,362 +with the existing +diagnostic signatures resource + +183 +00:07:42,362 --> 00:07:43,863 +to discover the places +in your app + +184 +00:07:43,863 --> 00:07:46,032 +that contribute most to hangs. + +185 +00:07:46,032 --> 00:07:48,001 +Not only that, +but you'll also be able to see + +186 +00:07:48,001 --> 00:07:50,470 +detailed stack traces +for these hang signatures + +187 +00:07:50,470 --> 00:07:53,707 +through the diagnostic logs +relationship. + +188 +00:07:53,707 --> 00:07:55,842 +I've just barely scratched +the surface here, + +189 +00:07:55,842 --> 00:07:58,178 +so if you'd like to learn more +about how these APIs work + +190 +00:07:58,178 --> 00:08:00,914 +or how to use this data +to discover additional insights + +191 +00:08:00,914 --> 00:08:02,282 +into your app's behavior, + +192 +00:08:02,282 --> 00:08:05,085 +definitely check out +these two related sessions. + +193 +00:08:05,085 --> 00:08:08,722 +Overall, the App Store Connect +API 2.0 release + +194 +00:08:08,722 --> 00:08:10,757 +represents a major milestone. + +195 +00:08:10,757 --> 00:08:14,361 +After four years of development, +we've fully embraced REST APIs + +196 +00:08:14,361 --> 00:08:17,097 +as the future of +App Store Connect automation. + +197 +00:08:17,097 --> 00:08:19,432 +As a result, +we'll begin to decommission + +198 +00:08:19,432 --> 00:08:21,735 +the XML feed this fall. + +199 +00:08:21,735 --> 00:08:25,171 +Therefore, we highly encourage +you to align your integration + +200 +00:08:25,171 --> 00:08:28,007 +with the App Store Connect API +moving forward. + +201 +00:08:28,007 --> 00:08:30,443 +In summary, we recommend +taking full advantage + +202 +00:08:30,443 --> 00:08:32,512 +of the enhanced App Store +submission experience + +203 +00:08:32,512 --> 00:08:35,615 +in order streamline +your review process. + +204 +00:08:35,615 --> 00:08:38,184 +Also, be sure to download +the latest release + +205 +00:08:38,184 --> 00:08:41,221 +of App Store Connect +for iOS and iPadOS, + +206 +00:08:41,221 --> 00:08:43,857 +which adds support for the +enhanced submission experience + +207 +00:08:43,857 --> 00:08:46,393 +and several other updates. + +208 +00:08:46,393 --> 00:08:49,162 +And finally, we're adding +a bunch of new capabilities + +209 +00:08:49,162 --> 00:08:50,730 +to the App Store Connect API + +210 +00:08:50,730 --> 00:08:53,400 +with the 2.0 release +this summer. + +211 +00:08:53,400 --> 00:08:56,503 +We strongly suggest working +to integrate with our API, + +212 +00:08:56,503 --> 00:08:59,305 +especially as we begin +to decommission the XML feed + +213 +00:08:59,305 --> 00:09:01,040 +later this fall. + +214 +00:09:01,040 --> 00:09:03,209 +As always, we look forward +to hearing your feedback + +215 +00:09:03,209 --> 00:09:06,379 +and hope you have a great WWDC! + +216 +00:09:06,379 --> 00:09:07,881 +Thanks for watching. + +217 +00:09:07,881 --> 00:09:12,051 +♪ + diff --git a/eng/2022 Session 10044 Discover Benchmarks in App Analytics en.srt b/eng/2022 Session 10044 Discover Benchmarks in App Analytics en.srt new file mode 100644 index 0000000..85976e8 --- /dev/null +++ b/eng/2022 Session 10044 Discover Benchmarks in App Analytics en.srt @@ -0,0 +1,889 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,643 --> 00:00:11,612 +Mahesh Molakalapalli: +My name is Mahesh Molakalapalli. + +3 +00:00:11,645 --> 00:00:16,083 +I am an engineering manager on +the App Store and App Analytics team. + +4 +00:00:16,116 --> 00:00:20,020 +Our goal with App Analytics +is to help you grow your business + +5 +00:00:20,053 --> 00:00:24,024 +by providing you with the unique +insights about your app. + +6 +00:00:24,057 --> 00:00:28,629 +Today, I am going to introduce you +to an exciting new feature + +7 +00:00:28,662 --> 00:00:31,098 +called App Benchmarking + +8 +00:00:31,131 --> 00:00:35,636 +that will help you get better insights +than ever before. + +9 +00:00:35,669 --> 00:00:39,706 +We will begin with an overview +of benchmarking. + +10 +00:00:39,740 --> 00:00:45,779 +Then we'll talk about the metrics for +which you can see benchmarking data. + +11 +00:00:45,812 --> 00:00:50,684 +Next, we'll show you how we create +relevant benchmarks + +12 +00:00:50,717 --> 00:00:55,155 +while protecting the privacy of all apps. + +13 +00:00:55,189 --> 00:00:59,526 +And we will wrap up by showing you +how you can take action + +14 +00:00:59,560 --> 00:01:03,530 +on insights gained from benchmarking. + +15 +00:01:03,564 --> 00:01:05,132 +Let's get started. + +16 +00:01:05,165 --> 00:01:07,868 +Many of you may already use App Analytics. + +17 +00:01:07,901 --> 00:01:10,537 +In this tool, we share data with you + +18 +00:01:10,571 --> 00:01:15,409 +about your app's acquisition, +usage, and monetization. + +19 +00:01:15,442 --> 00:01:19,546 +Let's say that you are a developer +of an app called Mountain Climber, + +20 +00:01:19,580 --> 00:01:23,650 +which is a travel app +offering subscriptions. + +21 +00:01:23,684 --> 00:01:28,655 +The overview page shows how +Mountain Climber is performing over time, + +22 +00:01:28,689 --> 00:01:31,558 +across key performance metrics + +23 +00:01:31,592 --> 00:01:36,864 +that allows you to monitor results +in each stage of the customer lifecycle, + +24 +00:01:36,897 --> 00:01:42,236 +from awareness +to conversion and on to retention. + +25 +00:01:42,269 --> 00:01:44,771 +Since you're focused on growth, + +26 +00:01:44,805 --> 00:01:49,643 +let's say that one of your top +business priorities for your app + +27 +00:01:49,676 --> 00:01:52,479 +has been to improve your conversion rate. + +28 +00:01:52,513 --> 00:01:56,483 +Looking at the data, +you have been making steady improvement, + +29 +00:01:56,517 --> 00:02:00,888 +with a 5.5% increase +over the last 90 days. + +30 +00:02:00,921 --> 00:02:04,758 +But what is unclear +is how much more improvement + +31 +00:02:04,791 --> 00:02:08,262 +you might be able to achieve +with continued effort. + +32 +00:02:08,295 --> 00:02:10,664 +Do you have more opportunity to grow, + +33 +00:02:10,697 --> 00:02:14,535 +or should you focus your efforts +somewhere else? + +34 +00:02:14,568 --> 00:02:17,838 +That's where peer group benchmarking +comes in. + +35 +00:02:17,871 --> 00:02:20,607 +You can learn +about the performance of apps + +36 +00:02:20,641 --> 00:02:24,144 +that are similar to your app +in a privacy-friendly way, + +37 +00:02:24,178 --> 00:02:28,415 +and check how you are performing +relative to them. + +38 +00:02:28,448 --> 00:02:30,584 +Like your Mountain Climber app, + +39 +00:02:30,617 --> 00:02:34,621 +all the apps +on the right hand side are travel apps, + +40 +00:02:34,655 --> 00:02:37,724 +with a subscription business model. + +41 +00:02:37,758 --> 00:02:42,963 +Let's take a look at how your App is doing +compared to this group. + +42 +00:02:44,064 --> 00:02:48,836 +You can see the same apps, +arranged left to right, + +43 +00:02:48,869 --> 00:02:52,673 +from lowest to highest conversion rates. + +44 +00:02:52,706 --> 00:02:56,610 +So how does your app stack up +against all the others? + +45 +00:02:56,643 --> 00:03:00,347 +Although your conversion rate +has been improving over time, + +46 +00:03:00,380 --> 00:03:03,784 +you are still in the bottom half +of your peer group. + +47 +00:03:03,817 --> 00:03:06,119 +There's plenty of room for improvement. + +48 +00:03:06,153 --> 00:03:08,989 +Benchmarking provides insights like this + +49 +00:03:09,022 --> 00:03:14,728 +without revealing the performance +of any individual app in your peer group. + +50 +00:03:15,495 --> 00:03:20,501 +We do this by including enough apps +to provide meaningful results + +51 +00:03:20,534 --> 00:03:21,869 +in the aggregate + +52 +00:03:21,902 --> 00:03:26,373 +so that it doesn't reveal +any individual app's performance. + +53 +00:03:26,406 --> 00:03:29,543 +And in order to show +your relative position, + +54 +00:03:29,576 --> 00:03:34,381 +we show you what the distribution +of your peer group looks like + +55 +00:03:34,414 --> 00:03:40,053 +such as the 25th, 50th, +and 75th percentiles of your group. + +56 +00:03:41,455 --> 00:03:44,324 +You will then be able to see +how you are performing + +57 +00:03:44,358 --> 00:03:46,293 +relative to your peer group, + +58 +00:03:46,326 --> 00:03:49,363 +and understand +whether you have growth opportunity, + +59 +00:03:49,396 --> 00:03:52,900 +or whether +you're already beating the competition. + +60 +00:03:52,933 --> 00:03:57,437 +Now that you have an idea +of what peer group benchmarking is + +61 +00:03:57,471 --> 00:03:59,640 +and why it is so important, + +62 +00:03:59,673 --> 00:04:04,611 +let us talk about the metrics that you +will be able to use in this feature. + +63 +00:04:05,512 --> 00:04:09,449 +One of our goals with benchmarking +is to give you insight + +64 +00:04:09,483 --> 00:04:14,288 +into how your app is performing +throughout the customer lifecycle. + +65 +00:04:14,321 --> 00:04:20,160 +That's why we will be offering benchmarks +for metrics related to acquisition, + +66 +00:04:20,194 --> 00:04:22,963 +usage, and monetization. + +67 +00:04:22,996 --> 00:04:25,699 +To help you measure +your acquisition efforts, + +68 +00:04:25,732 --> 00:04:29,036 +we will provide you with +conversion rate benchmarks. + +69 +00:04:29,069 --> 00:04:33,440 +Conversion rate helps you understand +how often people download + +70 +00:04:33,473 --> 00:04:37,978 +or redownload your app +after seeing it on the App Store. + +71 +00:04:38,011 --> 00:04:39,847 +The higher your conversion rate, + +72 +00:04:39,880 --> 00:04:43,617 +the more efficiently +you are acquiring your customers. + +73 +00:04:44,785 --> 00:04:49,089 +To help you measure usage, +you will be able to see Day 1, + +74 +00:04:49,122 --> 00:04:52,359 +Day 7, and Day 28 retention. + +75 +00:04:52,392 --> 00:04:56,129 +Retention helps you understand +what percentage of people return + +76 +00:04:56,163 --> 00:04:58,699 +to your app one day after download, + +77 +00:04:58,732 --> 00:05:00,133 +seven days after download, + +78 +00:05:00,167 --> 00:05:02,369 +and 28 days after download, + +79 +00:05:02,402 --> 00:05:06,807 +and is a good indicator +of how engaging your app is + +80 +00:05:06,840 --> 00:05:08,408 +for your customers. + +81 +00:05:09,676 --> 00:05:13,747 +You will also be able to see +how your crash rate stacks up + +82 +00:05:13,780 --> 00:05:15,682 +against your peers. + +83 +00:05:15,716 --> 00:05:19,186 +If your app is prone to crashing +more often than normal, + +84 +00:05:19,219 --> 00:05:23,390 +it may negatively impact your engagement +and monetization metrics, + +85 +00:05:23,423 --> 00:05:26,627 +so it's good to keep this +as low as possible. + +86 +00:05:27,928 --> 00:05:32,032 +Finally, to help you evaluate +your monetization strategy, + +87 +00:05:32,065 --> 00:05:37,471 +you will be able to see benchmarks +around average proceeds per paying user. + +88 +00:05:37,504 --> 00:05:42,075 +This will allow you to see how much money +your peers take home + +89 +00:05:42,109 --> 00:05:44,444 +from each of their paying customers, + +90 +00:05:44,478 --> 00:05:47,314 +so you can see how you compare. + +91 +00:05:47,347 --> 00:05:51,385 +Now that you know what metrics +you will be able to see, + +92 +00:05:51,418 --> 00:05:55,222 +let us talk about relevancy and privacy. + +93 +00:05:55,255 --> 00:05:57,424 +Throughout the development +of this feature, + +94 +00:05:57,457 --> 00:06:00,260 +we have strived to create peer groups + +95 +00:06:00,294 --> 00:06:03,463 +that are highly relevant to you +and your business, + +96 +00:06:03,497 --> 00:06:08,802 +while also protecting the privacy +of each app within that group. + +97 +00:06:08,836 --> 00:06:14,141 +This helps us meet our goals +of providing you with helpful insights, + +98 +00:06:14,174 --> 00:06:18,478 +without revealing the performance +of any individual app. + +99 +00:06:18,512 --> 00:06:20,881 +Let us discuss two examples + +100 +00:06:20,914 --> 00:06:24,585 +of how we will make the peer groups +relevant to you, + +101 +00:06:24,618 --> 00:06:29,289 +no matter what kind of app +that you have on the App Store. + +102 +00:06:29,323 --> 00:06:34,094 +First, we will use a number of attributes +that we have on the App Store + +103 +00:06:34,127 --> 00:06:36,096 +to create your peer groups, + +104 +00:06:36,129 --> 00:06:39,199 +including the app's App Store category. + +105 +00:06:39,233 --> 00:06:43,003 +For example, if you have put your app +in the travel category, + +106 +00:06:43,036 --> 00:06:44,938 +we will group you with other apps + +107 +00:06:44,972 --> 00:06:48,509 +that also put themselves +in the travel category. + +108 +00:06:48,542 --> 00:06:53,547 +Second, we will also look at +how your app monetizes on the store. + +109 +00:06:53,580 --> 00:06:56,350 +For example, is it a free app, + +110 +00:06:56,383 --> 00:07:00,320 +freemium, paid, +paidmium, or subscription? + +111 +00:07:00,354 --> 00:07:04,024 +Since apps with different business models +have different expectations + +112 +00:07:04,057 --> 00:07:05,759 +about user behavior, + +113 +00:07:05,792 --> 00:07:10,564 +this is also a very important factor +for creating relevant peer groups. + +114 +00:07:10,597 --> 00:07:14,468 +Each attribute that we use to create +your peer groups + +115 +00:07:14,501 --> 00:07:19,173 +has been tested to ensure +it allows for meaningful comparisons + +116 +00:07:19,206 --> 00:07:23,143 +over time between your app +and its peer group. + +117 +00:07:23,177 --> 00:07:25,546 +Next, let's talk about privacy. + +118 +00:07:25,579 --> 00:07:30,217 +At Apple, we have significant experience +with building great products + +119 +00:07:30,250 --> 00:07:32,286 +that also have great privacy, + +120 +00:07:32,319 --> 00:07:36,023 +and we used that experience here. + +121 +00:07:36,056 --> 00:07:38,192 +In order to accomplish our privacy goals, + +122 +00:07:38,225 --> 00:07:41,361 +we use a technique called +differential privacy, + +123 +00:07:41,395 --> 00:07:44,932 +which is the gold standard +for ensuring that aggregated data + +124 +00:07:44,965 --> 00:07:50,037 +can remain both helpful +and private at the same time. + +125 +00:07:50,070 --> 00:07:54,074 +When we calculate your peer +group's overall conversion rate, + +126 +00:07:54,107 --> 00:07:56,643 +we add small amounts of noise + +127 +00:07:56,677 --> 00:08:01,181 +to each data point +we share about your peer group. + +128 +00:08:01,215 --> 00:08:05,485 +We also ensure +that enough apps are in your peer group + +129 +00:08:05,519 --> 00:08:08,522 +so that it is not possible to know + +130 +00:08:08,555 --> 00:08:13,427 +whether a particular app is in +or out of your peer group, + +131 +00:08:13,460 --> 00:08:16,597 +because the noise added to the data set + +132 +00:08:16,630 --> 00:08:21,201 +is large enough to obscure +the exact makeup of the peer group, + +133 +00:08:21,235 --> 00:08:24,238 +while still providing useful information + +134 +00:08:24,271 --> 00:08:27,074 +about your peer group in the aggregate. + +135 +00:08:27,107 --> 00:08:30,043 +This protects the privacy of your app + +136 +00:08:30,077 --> 00:08:33,046 +and all the other apps in the peer group. + +137 +00:08:33,080 --> 00:08:37,751 +Let us end by taking a look at +how you can use other tools + +138 +00:08:37,784 --> 00:08:40,153 +to take action on this information. + +139 +00:08:41,288 --> 00:08:44,725 +The App Store provides a number +of developer features + +140 +00:08:44,758 --> 00:08:49,530 +that enable you to optimize +and improve your performance + +141 +00:08:49,563 --> 00:08:52,332 +across the customer lifecycle. + +142 +00:08:52,366 --> 00:08:55,569 +For example, +to have a great conversion rate, + +143 +00:08:55,602 --> 00:08:58,639 +you need to have +a great App Store product page, + +144 +00:08:58,672 --> 00:09:02,409 +and the App Store offers you +tools to help. + +145 +00:09:02,442 --> 00:09:04,611 +With product page optimization, + +146 +00:09:04,645 --> 00:09:07,314 +you can test different app icons, + +147 +00:09:07,347 --> 00:09:09,683 +screenshots, and app previews + +148 +00:09:09,716 --> 00:09:15,689 +to find out which combination of features +works best with your customers. + +149 +00:09:15,722 --> 00:09:19,960 +And with custom product pages, +you can create unique product pages + +150 +00:09:19,993 --> 00:09:21,562 +for different audience + +151 +00:09:21,595 --> 00:09:24,097 +that focus on their specific interests, + +152 +00:09:24,131 --> 00:09:28,235 +rather than having one product page +for all customers. + +153 +00:09:28,268 --> 00:09:33,807 +Both of these features can help you +boost your conversion rate. + +154 +00:09:33,841 --> 00:09:36,410 +In order to improve your usage metrics, + +155 +00:09:36,443 --> 00:09:39,947 +you can utilize in-app events +and App Clips. + +156 +00:09:39,980 --> 00:09:43,984 +In-app events are timely events +within apps and games– + +157 +00:09:44,017 --> 00:09:46,019 +such as game competitions, + +158 +00:09:46,053 --> 00:09:49,456 +movie premieres, +or livestreamed experiences– + +159 +00:09:49,489 --> 00:09:51,992 +that you can showcase on the App Store + +160 +00:09:52,025 --> 00:09:56,730 +to engage new customers +and re-engage existing customers. + +161 +00:09:56,763 --> 00:09:59,933 +And an App Clip is +a small part of your app + +162 +00:09:59,967 --> 00:10:03,403 +that is discoverable +in relevant public places + +163 +00:10:03,437 --> 00:10:07,441 +so your customers can complete +a quick task from your app. + +164 +00:10:07,474 --> 00:10:10,944 +For example, whether ordering take-out +from a restaurant, + +165 +00:10:10,978 --> 00:10:14,214 +renting a scooter, +or setting up a new connected appliance + +166 +00:10:14,248 --> 00:10:17,251 +for the first time, +people can launch the App Clip + +167 +00:10:17,284 --> 00:10:20,854 +to easily start and finish the task. + +168 +00:10:20,888 --> 00:10:23,891 +And finally, to improve your monetization, + +169 +00:10:23,924 --> 00:10:26,426 +you can try out different pricing tiers + +170 +00:10:26,460 --> 00:10:29,396 +so that users can customize +their experiences + +171 +00:10:29,429 --> 00:10:34,067 +based on their preferences +and the amount they want to pay. + +172 +00:10:34,101 --> 00:10:37,271 +Or you can create +a promoted in-app purchase, + +173 +00:10:37,304 --> 00:10:41,975 +which lets users browse the items for sale +directly on the App Store, + +174 +00:10:42,009 --> 00:10:45,078 +even before they download your app. + +175 +00:10:45,112 --> 00:10:48,715 +To conclude, +by combining peer group benchmarking + +176 +00:10:48,749 --> 00:10:52,719 +with other App Store tools, +you can make meaningful progress + +177 +00:10:52,753 --> 00:10:57,191 +in the areas +where your app needs it the most. + +178 +00:10:57,224 --> 00:11:01,795 +We invite you to go to +developer.apple.com/app-store + +179 +00:11:01,828 --> 00:11:03,997 +to learn more. + +180 +00:11:04,031 --> 00:11:07,301 +Here is a quick summary +of what you've seen today. + +181 +00:11:07,334 --> 00:11:10,437 +Peer Group Benchmarking +is an exciting tool + +182 +00:11:10,470 --> 00:11:13,874 +that will let you compare +your App Store performance + +183 +00:11:13,907 --> 00:11:18,245 +against a group of similar apps +on the store. + +184 +00:11:18,278 --> 00:11:20,814 +We have designed Peer Group Benchmarking + +185 +00:11:20,848 --> 00:11:25,052 +using differential privacy +to provide relevant insights + +186 +00:11:25,085 --> 00:11:30,157 +without revealing the information +about any individual app. + +187 +00:11:30,190 --> 00:11:32,292 +By using Peer Group Benchmarking + +188 +00:11:32,326 --> 00:11:36,263 +in combination with other developer +features on the App Store, + +189 +00:11:36,296 --> 00:11:42,369 +you will have the ability to boost +your app's performance on the App Store. + +190 +00:11:42,402 --> 00:11:47,007 +You will be able to start using +Peer Group Benchmarking early next year. + +191 +00:11:47,040 --> 00:11:48,942 +Thank you. + diff --git a/eng/2022 Session 10046 Adopt declarative device management en.srt b/eng/2022 Session 10046 Adopt declarative device management en.srt new file mode 100644 index 0000000..69f4a35 --- /dev/null +++ b/eng/2022 Session 10046 Adopt declarative device management en.srt @@ -0,0 +1,3075 @@ +1 +00:00:00,000 --> 00:00:03,837 +♪ instrumental hip hop music ♪ + +2 +00:00:03,837 --> 00:00:09,977 +♪ + +3 +00:00:09,977 --> 00:00:12,613 +Welcome to the "Adopt +declarative device management" + +4 +00:00:12,613 --> 00:00:13,614 +session. + +5 +00:00:13,614 --> 00:00:15,249 +My name is Cyrus Daboo, + +6 +00:00:15,249 --> 00:00:18,352 +and I am an engineer +on the Device Management team. + +7 +00:00:18,352 --> 00:00:21,421 +I am here to tell you about +the exciting new features + +8 +00:00:21,421 --> 00:00:24,458 +in declarative +device management. + +9 +00:00:24,458 --> 00:00:26,760 +At WWDC21, + +10 +00:00:26,760 --> 00:00:30,530 +my colleague Melissa introduced +declarative device management, + +11 +00:00:30,530 --> 00:00:33,400 +a new paradigm +for managing Apple devices, + +12 +00:00:33,400 --> 00:00:37,337 +reenvisioning +the MDM protocol itself. + +13 +00:00:37,337 --> 00:00:39,239 +As we learned in that session, + +14 +00:00:39,239 --> 00:00:41,508 +declarative device management +is powerful + +15 +00:00:41,508 --> 00:00:45,412 +because it enables devices +to be autonomous and proactive. + +16 +00:00:45,412 --> 00:00:47,147 +The device is autonomous, + +17 +00:00:47,147 --> 00:00:49,716 +as it reacts to its own +state changes + +18 +00:00:49,716 --> 00:00:52,452 +and then applies +management logic to itself, + +19 +00:00:52,452 --> 00:00:54,788 +without prompting +from the server. + +20 +00:00:54,788 --> 00:00:56,290 +The device is proactive, + +21 +00:00:56,290 --> 00:00:58,859 +with the status channel +asynchronously reporting + +22 +00:00:58,859 --> 00:01:01,862 +to the server when important +state changes occur, + +23 +00:01:01,862 --> 00:01:06,166 +avoiding the need for servers +to poll devices. + +24 +00:01:06,166 --> 00:01:07,901 +There are two key elements + +25 +00:01:07,901 --> 00:01:10,704 +to the declarative device +management data model: + +26 +00:01:10,704 --> 00:01:13,173 +declarations and status. + +27 +00:01:13,173 --> 00:01:16,510 +Declarations encompass +activations and predicates, + +28 +00:01:16,510 --> 00:01:20,113 +configurations, assets, +and management types. + +29 +00:01:20,113 --> 00:01:24,518 +And status covers status items +and status reporting. + +30 +00:01:24,518 --> 00:01:26,987 +Let's take a moment +to talk about why this matters, + +31 +00:01:26,987 --> 00:01:28,855 +what it means for you, + +32 +00:01:28,855 --> 00:01:32,092 +and the organizations +that use your products. + +33 +00:01:32,092 --> 00:01:34,061 +We have created this technology + +34 +00:01:34,061 --> 00:01:36,897 +to support new complex +management strategies, + +35 +00:01:36,897 --> 00:01:40,567 +enhance the overall user +experience of managed devices, + +36 +00:01:40,567 --> 00:01:44,771 +alleviate the repetitive and +tedious tasks of an IT admin, + +37 +00:01:44,771 --> 00:01:47,307 +and empower devices +to be the driver + +38 +00:01:47,307 --> 00:01:50,043 +in their own +management state. + +39 +00:01:50,043 --> 00:01:53,647 +For you, as a developer +of a device management solution, + +40 +00:01:53,647 --> 00:01:56,083 +the declarative approach +allows your servers + +41 +00:01:56,083 --> 00:01:58,719 +to be lightweight and reactive. + +42 +00:01:58,719 --> 00:02:00,721 +And with +the declarative data model + +43 +00:02:00,721 --> 00:02:03,957 +more closely mapping to how +organizations are structured, + +44 +00:02:03,957 --> 00:02:08,495 +that means changes to devices +becomes more intuitive. + +45 +00:02:08,495 --> 00:02:11,264 +Status reports provide +a rich feedback channel, + +46 +00:02:11,264 --> 00:02:14,935 +which enable your servers +to monitor devices more closely, + +47 +00:02:14,935 --> 00:02:17,204 +and present +pertinent information + +48 +00:02:17,204 --> 00:02:19,740 +in a more timely +and reliable fashion, + +49 +00:02:19,740 --> 00:02:22,242 +without the need +for complex strategies + +50 +00:02:22,242 --> 00:02:24,478 +used to implement polling. + +51 +00:02:24,478 --> 00:02:27,080 +All of this means a simpler +development effort, + +52 +00:02:27,080 --> 00:02:30,417 +enabling you to focus on +the device management features + +53 +00:02:30,417 --> 00:02:33,053 +that add value +where it matters most, + +54 +00:02:33,053 --> 00:02:37,391 +and create a solution +your customers will love. + +55 +00:02:37,391 --> 00:02:39,292 +For IT admins, + +56 +00:02:39,292 --> 00:02:41,762 +the declarative approach +inspires more confidence + +57 +00:02:41,762 --> 00:02:44,498 +that the device +is in the expected state. + +58 +00:02:44,498 --> 00:02:47,234 +And in the situations +where it is not, + +59 +00:02:47,234 --> 00:02:48,935 +that it is in a safe state + +60 +00:02:48,935 --> 00:02:51,972 +that protects +any sensitive organization data, + +61 +00:02:51,972 --> 00:02:55,942 +even when connectivity +to the server is lost. + +62 +00:02:55,942 --> 00:03:00,113 +It provides critical feedback +from devices via status reports, + +63 +00:03:00,113 --> 00:03:02,849 +that also improves +efficiency for admins + +64 +00:03:02,849 --> 00:03:04,918 +through less utilization +of resources + +65 +00:03:04,918 --> 00:03:07,187 +such as network bandwidth. + +66 +00:03:07,187 --> 00:03:09,222 +For the organization's users, + +67 +00:03:09,222 --> 00:03:11,591 +device management +becomes a more responsive + +68 +00:03:11,591 --> 00:03:14,861 +and reliable experience +with faster onboarding, + +69 +00:03:14,861 --> 00:03:16,430 +quicker recovery times + +70 +00:03:16,430 --> 00:03:20,167 +and better support +from their organization. + +71 +00:03:20,167 --> 00:03:22,135 +With all these benefits in mind, + +72 +00:03:22,135 --> 00:03:25,205 +know that the focus +of future protocol features + +73 +00:03:25,205 --> 00:03:28,909 +will be declarative +device management, + +74 +00:03:28,909 --> 00:03:30,677 +making it even more important + +75 +00:03:30,677 --> 00:03:33,013 +for you to adopt +declarative device management + +76 +00:03:33,013 --> 00:03:36,383 +in your products today. + +77 +00:03:36,383 --> 00:03:39,386 +For an in-depth introduction +to declarative device management + +78 +00:03:39,386 --> 00:03:41,455 +and the steps needed +to adopt it, + +79 +00:03:41,455 --> 00:03:46,326 +make sure you watch +the WWDC21 session video. + +80 +00:03:46,326 --> 00:03:49,362 +In this release, +we have three focus areas: + +81 +00:03:49,362 --> 00:03:52,632 +expanding the scope of +declarative device management, + +82 +00:03:52,632 --> 00:03:57,304 +enhancing status reports, +and enhancing predicates. + +83 +00:03:57,304 --> 00:03:59,439 +Let's start +with expanding the scope + +84 +00:03:59,439 --> 00:04:02,542 +of declarative device +management. + +85 +00:04:02,542 --> 00:04:05,078 +When declarative device +management was introduced, + +86 +00:04:05,078 --> 00:04:08,748 +it was supported on only +iOS with user enrollments. + +87 +00:04:08,748 --> 00:04:11,485 +Now, declarative device +management is available + +88 +00:04:11,485 --> 00:04:15,355 +for every enrollment type +MDM supports: + +89 +00:04:15,355 --> 00:04:17,023 +automatic device enrollment, + +90 +00:04:17,023 --> 00:04:19,893 +which includes +supervised devices; + +91 +00:04:19,893 --> 00:04:21,795 +profile-based enrollment; + +92 +00:04:21,795 --> 00:04:25,465 +and profile and account-based +user enrollments. + +93 +00:04:25,465 --> 00:04:28,401 +Declarative device management +is now also available + +94 +00:04:28,401 --> 00:04:30,937 +on Shared iPad. + +95 +00:04:30,937 --> 00:04:34,474 +In iOS 16, users can now +find configurations + +96 +00:04:34,474 --> 00:04:38,545 +in the MDM profile details view +in the Settings app. + +97 +00:04:38,545 --> 00:04:41,748 +Tapping the Configurations row +reveals details + +98 +00:04:41,748 --> 00:04:44,851 +about the active configurations. + +99 +00:04:44,851 --> 00:04:47,354 +And I am also pleased +to announce + +100 +00:04:47,354 --> 00:04:49,656 +that declarative device +management is available + +101 +00:04:49,656 --> 00:04:53,226 +on every platform MDM supports. + +102 +00:04:53,226 --> 00:04:56,997 +macOS Ventura now supports +declarative device management, + +103 +00:04:56,997 --> 00:05:01,434 +for all MDM enrollment types +supported on macOS. + +104 +00:05:01,434 --> 00:05:04,938 +tvOS 16 now supports declarative +device management + +105 +00:05:04,938 --> 00:05:08,508 +for MDM device enrollment types. + +106 +00:05:08,508 --> 00:05:10,310 +Where supported by the OS, + +107 +00:05:10,310 --> 00:05:12,445 +the same set of declarations +and status + +108 +00:05:12,445 --> 00:05:14,247 +that are available on iOS + +109 +00:05:14,247 --> 00:05:18,852 +are also available +on macOS and tvOS. + +110 +00:05:18,852 --> 00:05:21,888 +On macOS, a Configurations +section is present + +111 +00:05:21,888 --> 00:05:24,191 +in the MDM profile details view, + +112 +00:05:24,191 --> 00:05:26,893 +revealing the active +configurations. + +113 +00:05:26,893 --> 00:05:30,430 +The same goes for tvOS, +where a Configurations section + +114 +00:05:30,430 --> 00:05:34,634 +is present in the +MDM profile details view. + +115 +00:05:34,634 --> 00:05:36,336 +One last thing to note here: + +116 +00:05:36,336 --> 00:05:39,005 +both macOS +and Shared iPad devices + +117 +00:05:39,005 --> 00:05:41,374 +each have two MDM channels. + +118 +00:05:41,374 --> 00:05:44,744 +These are the device +and user channel. + +119 +00:05:44,744 --> 00:05:46,446 +The device channel +allows management + +120 +00:05:46,446 --> 00:05:47,981 +of device level state, + +121 +00:05:47,981 --> 00:05:50,217 +whereas the user channel +targets management state + +122 +00:05:50,217 --> 00:05:52,419 +for specific users. + +123 +00:05:52,419 --> 00:05:55,322 +To use declarative device +management on any channel, + +124 +00:05:55,322 --> 00:05:58,391 +it must be enabled separately +for that channel. + +125 +00:05:58,391 --> 00:06:01,228 +That means sending the +DeclarativeManagement command + +126 +00:06:01,228 --> 00:06:03,663 +on the corresponding channel. + +127 +00:06:03,663 --> 00:06:07,100 +Also, declarative device +management status reports + +128 +00:06:07,100 --> 00:06:09,869 +are separately generated +for each channel, + +129 +00:06:09,869 --> 00:06:14,374 +so they need to be +separately monitored as well. + +130 +00:06:14,374 --> 00:06:19,846 +Now on to our second focus area: +status reports. + +131 +00:06:19,846 --> 00:06:23,116 +Let's do a quick review +of status reports. + +132 +00:06:23,116 --> 00:06:26,486 +Devices can incrementally report +status to the server, + +133 +00:06:26,486 --> 00:06:29,022 +for subscribed status items. + +134 +00:06:29,022 --> 00:06:32,092 +The device tracks successful +responses from the server + +135 +00:06:32,092 --> 00:06:34,461 +to ensure status updates +are reliable + +136 +00:06:34,461 --> 00:06:36,730 +and not missed in the case +of networking + +137 +00:06:36,730 --> 00:06:39,232 +or other types of problem. + +138 +00:06:39,232 --> 00:06:42,502 +Status reporting +makes the device proactive. + +139 +00:06:42,502 --> 00:06:45,605 +There is no need for servers +to continuously poll the device + +140 +00:06:45,605 --> 00:06:47,741 +to watch for state changes. + +141 +00:06:47,741 --> 00:06:51,645 +In iOS 15, we introduced +a set of status items + +142 +00:06:51,645 --> 00:06:54,347 +for device properties, +such as model type + +143 +00:06:54,347 --> 00:06:56,916 +and operating system version. + +144 +00:06:56,916 --> 00:07:01,054 +For this release we are +expanding status in three areas: + +145 +00:07:01,054 --> 00:07:02,922 +passcode state, + +146 +00:07:02,922 --> 00:07:05,358 +accounts installed +by configurations, + +147 +00:07:05,358 --> 00:07:08,895 +and MDM installed apps. + +148 +00:07:08,895 --> 00:07:12,232 +Let's start +with passcode status. + +149 +00:07:12,232 --> 00:07:16,803 +In iOS 15, we introduced +a passcode policy configuration. + +150 +00:07:16,803 --> 00:07:20,473 +There can be some lag between +the policy being applied, + +151 +00:07:20,473 --> 00:07:22,642 +and the passcode +becoming compliant + +152 +00:07:22,642 --> 00:07:24,477 +when changed by the user, + +153 +00:07:24,477 --> 00:07:28,148 +just as there is with +MDM passcode policy profiles. + +154 +00:07:28,148 --> 00:07:31,651 +So, MDM servers have to poll +the device to determine + +155 +00:07:31,651 --> 00:07:34,020 +when the passcode +becomes compliant. + +156 +00:07:34,020 --> 00:07:36,089 +But with the new declarative +device management + +157 +00:07:36,089 --> 00:07:40,794 +passcode status items, +there is no need to do that. + +158 +00:07:40,794 --> 00:07:43,897 +We have added two status items: + +159 +00:07:43,897 --> 00:07:48,835 +Passcode.is-compliant +and passcode.is-present. + +160 +00:07:48,835 --> 00:07:51,338 +Compliance indicates +if the passcode is compliant + +161 +00:07:51,338 --> 00:07:54,741 +with all passcode policies +applied via MDM profiles + +162 +00:07:54,741 --> 00:07:57,210 +or configurations. + +163 +00:07:57,210 --> 00:07:59,546 +These status items +have Boolean values + +164 +00:07:59,546 --> 00:08:01,481 +that mirror +the equivalent properties + +165 +00:08:01,481 --> 00:08:05,452 +that can be retrieved +via MDM queries. + +166 +00:08:05,452 --> 00:08:08,855 +Let's explore +a typical server behavior. + +167 +00:08:08,855 --> 00:08:12,359 +Often, an organization +has security sensitive state + +168 +00:08:12,359 --> 00:08:14,060 +to apply to a device. + +169 +00:08:14,060 --> 00:08:17,364 +For example, +VPN or Wi-Fi profiles + +170 +00:08:17,364 --> 00:08:21,000 +to allow access +to protected networks. + +171 +00:08:21,000 --> 00:08:23,837 +That state should only +be active on a device, + +172 +00:08:23,837 --> 00:08:26,906 +when a strong passcode +policy is present, + +173 +00:08:26,906 --> 00:08:31,077 +and the passcode is compliant +with that policy. + +174 +00:08:31,077 --> 00:08:33,780 +With traditional MDM, +a server has to send + +175 +00:08:33,780 --> 00:08:37,183 +a passcode policy profile +then poll the device, + +176 +00:08:37,183 --> 00:08:39,586 +to wait for the passcode +to become compliant + +177 +00:08:39,586 --> 00:08:41,454 +when the user changes it. + +178 +00:08:41,454 --> 00:08:44,557 +Initially the passcode +is likely not compliant, + +179 +00:08:44,557 --> 00:08:48,194 +so the Wi-Fi profile +cannot be sent. + +180 +00:08:48,194 --> 00:08:50,764 +Eventually, the user +changes the passcode + +181 +00:08:50,764 --> 00:08:52,599 +to bring it into compliance. + +182 +00:08:52,599 --> 00:08:54,134 +On the server's next poll, + +183 +00:08:54,134 --> 00:08:57,337 +it detects the changed +compliant state + +184 +00:08:57,337 --> 00:09:01,107 +and determines it is OK +to send the Wi-Fi profile, + +185 +00:09:01,107 --> 00:09:04,444 +which then gets installed +on the device. + +186 +00:09:04,444 --> 00:09:06,946 +Declarative device management +removes the need + +187 +00:09:06,946 --> 00:09:10,350 +for the server to poll by using +an activation predicate + +188 +00:09:10,350 --> 00:09:13,520 +that is triggered by +the passcode compliance state. + +189 +00:09:13,520 --> 00:09:15,622 +The server sends both +the passcode policy + +190 +00:09:15,622 --> 00:09:18,858 +and the Wi-Fi profile +as configurations, + +191 +00:09:18,858 --> 00:09:21,961 +with the Wi-Fi configuration +tied to an activation + +192 +00:09:21,961 --> 00:09:25,198 +predicated on +the passcode compliance. + +193 +00:09:25,198 --> 00:09:27,834 +The passcode configuration +is immediately activated + +194 +00:09:27,834 --> 00:09:31,204 +and applies a strong +passcode policy. + +195 +00:09:31,204 --> 00:09:34,007 +Initially, the passcode +is likely not compliant, + +196 +00:09:34,007 --> 00:09:36,910 +so the activation predicate +evaluates to false, + +197 +00:09:36,910 --> 00:09:40,346 +and the Wi-Fi configuration +is not activated. + +198 +00:09:40,346 --> 00:09:44,551 +At some point, the user updates +the passcode to be compliant. + +199 +00:09:44,551 --> 00:09:47,821 +This triggers reevaluation +of the activations + +200 +00:09:47,821 --> 00:09:50,590 +and the predicate +now evaluates to true, + +201 +00:09:50,590 --> 00:09:54,627 +resulting in the Wi-Fi +configuration being activated. + +202 +00:09:54,627 --> 00:09:58,364 +All this happens without any +intervention from the server, + +203 +00:09:58,364 --> 00:10:01,034 +and in fact can happen +without any connection + +204 +00:10:01,034 --> 00:10:03,670 +to the server being present. + +205 +00:10:03,670 --> 00:10:06,272 +The server does automatically +get a status report + +206 +00:10:06,272 --> 00:10:09,175 +from the device when +the configuration activates, + +207 +00:10:09,175 --> 00:10:11,845 +so it knows when +the change takes place. + +208 +00:10:11,845 --> 00:10:14,747 +This illustrates how +we have successfully moved + +209 +00:10:14,747 --> 00:10:17,750 +business logic from +the server to the device, + +210 +00:10:17,750 --> 00:10:19,319 +to avoid the need to poll + +211 +00:10:19,319 --> 00:10:24,290 +and get a more responsive +and reliable device behavior. + +212 +00:10:24,290 --> 00:10:27,894 +Now, let's turn +to account status. + +213 +00:10:27,894 --> 00:10:31,397 +In iOS 15, we introduced +account configurations + +214 +00:10:31,397 --> 00:10:34,968 +to install accounts +of various types on a device. + +215 +00:10:34,968 --> 00:10:37,537 +These are typically +organization accounts, + +216 +00:10:37,537 --> 00:10:41,007 +giving the user access +to organization data. + +217 +00:10:41,007 --> 00:10:44,043 +It is useful for the admin +to know when accounts + +218 +00:10:44,043 --> 00:10:45,812 +have been +successfully installed, + +219 +00:10:45,812 --> 00:10:47,547 +and what state they are in, + +220 +00:10:47,547 --> 00:10:51,451 +to help support users +who might be having problems. + +221 +00:10:51,451 --> 00:10:55,722 +This release adds eight +account status items for mail, + +222 +00:10:55,722 --> 00:10:58,324 +calendar, +and other account types. + +223 +00:10:58,324 --> 00:11:01,261 +Note, that status +is only reported for accounts + +224 +00:11:01,261 --> 00:11:03,062 +installed by configurations + +225 +00:11:03,062 --> 00:11:05,665 +and won't include +accounts created manually + +226 +00:11:05,665 --> 00:11:08,968 +or installed via MDM profiles. + +227 +00:11:08,968 --> 00:11:11,104 +Each new status item corresponds + +228 +00:11:11,104 --> 00:11:13,072 +to an account +configuration type, + +229 +00:11:13,072 --> 00:11:16,042 +with status for incoming +and outgoing mail accounts + +230 +00:11:16,042 --> 00:11:18,344 +reported separately. + +231 +00:11:18,344 --> 00:11:22,248 +The new status items each use +a different type of JSON object, + +232 +00:11:22,248 --> 00:11:26,252 +to represent the status of +the corresponding account type. + +233 +00:11:26,252 --> 00:11:29,556 +Here are examples +of an incoming mail status item, + +234 +00:11:29,556 --> 00:11:32,992 +and a subscribed +calendar status item. + +235 +00:11:32,992 --> 00:11:36,062 +The value of the identifier key +is a unique identifier + +236 +00:11:36,062 --> 00:11:39,599 +for an object within the array +of status item objects -- + +237 +00:11:39,599 --> 00:11:42,001 +more on this in a minute. + +238 +00:11:42,001 --> 00:11:44,871 +The value of the +declaration identifier key, + +239 +00:11:44,871 --> 00:11:46,773 +matches the identifier +property value + +240 +00:11:46,773 --> 00:11:49,342 +of the configuration +that installed the account, + +241 +00:11:49,342 --> 00:11:51,210 +making it easy +to cross-reference + +242 +00:11:51,210 --> 00:11:55,415 +the status item object and +its associated configuration. + +243 +00:11:55,415 --> 00:11:57,850 +These two keys +are always present + +244 +00:11:57,850 --> 00:12:01,187 +in all types of account +status item object. + +245 +00:12:01,187 --> 00:12:03,790 +The other keys are specific +to the type of account. + +246 +00:12:03,790 --> 00:12:06,960 +For example, hostname and port +for the mail server, + +247 +00:12:06,960 --> 00:12:11,164 +or calendar-URL +for the subscribed calendar. + +248 +00:12:11,164 --> 00:12:15,468 +This release introduces status +items whose value is an array, + +249 +00:12:15,468 --> 00:12:17,704 +to support reporting +on one or more accounts + +250 +00:12:17,704 --> 00:12:19,472 +of the same type. + +251 +00:12:19,472 --> 00:12:22,809 +Such array values +have special behavior. + +252 +00:12:22,809 --> 00:12:25,712 +Each item in the array +is a JSON object + +253 +00:12:25,712 --> 00:12:30,083 +with the same schema used for +all objects in a single array. + +254 +00:12:30,083 --> 00:12:32,986 +Each object type always +has an identifier key, + +255 +00:12:32,986 --> 00:12:35,822 +acting as the primary key +for locating objects + +256 +00:12:35,822 --> 00:12:37,423 +within the array. + +257 +00:12:37,423 --> 00:12:38,858 +Other keys are present + +258 +00:12:38,858 --> 00:12:42,195 +and tied to the underlying type +of status being reported. + +259 +00:12:42,195 --> 00:12:44,497 +To ensure forwards compatibility + +260 +00:12:44,497 --> 00:12:47,467 +with any keys added +in future OS releases, + +261 +00:12:47,467 --> 00:12:52,505 +your server must accept +unknown keys in array objects. + +262 +00:12:52,505 --> 00:12:55,074 +Changes to an array value +are always reported + +263 +00:12:55,074 --> 00:12:57,210 +incrementally to the server + +264 +00:12:57,210 --> 00:13:01,180 +on a per-object basis, +for performance reasons. + +265 +00:13:01,180 --> 00:13:02,715 +Let's run through an example + +266 +00:13:02,715 --> 00:13:05,885 +that shows how +this new feature works. + +267 +00:13:05,885 --> 00:13:07,186 +In this example, + +268 +00:13:07,186 --> 00:13:09,756 +the server sends +two mail account configurations + +269 +00:13:09,756 --> 00:13:10,990 +to the device. + +270 +00:13:10,990 --> 00:13:14,227 +These are both active +resulting in two mail accounts + +271 +00:13:14,227 --> 00:13:16,329 +present on the device. + +272 +00:13:16,329 --> 00:13:18,464 +The server now sends +a status subscription + +273 +00:13:18,464 --> 00:13:21,000 +for the mail account +status item. + +274 +00:13:21,000 --> 00:13:23,202 +When the subscription +is activated, + +275 +00:13:23,202 --> 00:13:25,238 +status for the accounts +is collected, + +276 +00:13:25,238 --> 00:13:29,208 +and the device sends +a status report to the server. + +277 +00:13:29,208 --> 00:13:32,545 +The status report will include +the two account status objects + +278 +00:13:32,545 --> 00:13:34,213 +in the status array, + +279 +00:13:34,213 --> 00:13:35,848 +giving the server +a complete picture + +280 +00:13:35,848 --> 00:13:38,851 +of what is currently +present on the device. + +281 +00:13:38,851 --> 00:13:42,088 +Each array object +has a different identifier. + +282 +00:13:42,088 --> 00:13:44,123 +After processing this report, + +283 +00:13:44,123 --> 00:13:46,659 +the server has status +for two mail accounts, + +284 +00:13:46,659 --> 00:13:49,529 +matching what is on the device. + +285 +00:13:49,529 --> 00:13:52,131 +When the server adds +a mail account on the device + +286 +00:13:52,131 --> 00:13:55,802 +by sending a new configuration, +the status item on the device + +287 +00:13:55,802 --> 00:13:58,905 +has a new object added +to its array value, + +288 +00:13:58,905 --> 00:14:03,142 +and another status report +is sent to the server. + +289 +00:14:03,142 --> 00:14:05,712 +Only the new item is reported. + +290 +00:14:05,712 --> 00:14:08,614 +The value of the identifier +key does not match + +291 +00:14:08,614 --> 00:14:10,349 +any the server already has, + +292 +00:14:10,349 --> 00:14:15,154 +so the server can infer this +corresponds to a new account. + +293 +00:14:15,154 --> 00:14:16,622 +After processing this report, + +294 +00:14:16,622 --> 00:14:19,358 +the server has status +for three mail accounts, + +295 +00:14:19,358 --> 00:14:21,928 +the two initial ones +and the new one, + +296 +00:14:21,928 --> 00:14:25,932 +again matching exactly +what is on the device. + +297 +00:14:25,932 --> 00:14:27,900 +When account status changes, + +298 +00:14:27,900 --> 00:14:31,704 +such as when a user toggles +the mail or notes enabled state, + +299 +00:14:31,704 --> 00:14:33,439 +the status item on the device + +300 +00:14:33,439 --> 00:14:36,642 +will have an updated object +in its array value, + +301 +00:14:36,642 --> 00:14:41,013 +and again, a status report +is sent to the server. + +302 +00:14:41,013 --> 00:14:43,516 +Only the changed item +is reported. + +303 +00:14:43,516 --> 00:14:46,319 +In this case, the user +turned off the notes feature + +304 +00:14:46,319 --> 00:14:47,920 +for the account. + +305 +00:14:47,920 --> 00:14:49,922 +The value of the identifier key + +306 +00:14:49,922 --> 00:14:52,158 +matches one +the server already has, + +307 +00:14:52,158 --> 00:14:54,961 +so the server can infer +that this is an update + +308 +00:14:54,961 --> 00:14:56,329 +to an existing account. + +309 +00:14:56,329 --> 00:14:57,530 +Consequently, + +310 +00:14:57,530 --> 00:15:00,433 +it replaces the existing +status item array object + +311 +00:15:00,433 --> 00:15:02,769 +with the new one. + +312 +00:15:02,769 --> 00:15:04,437 +After processing this report, + +313 +00:15:04,437 --> 00:15:07,073 +the server has status +for three mail accounts, + +314 +00:15:07,073 --> 00:15:09,942 +but one has changed. + +315 +00:15:09,942 --> 00:15:13,079 +When an account configuration +is removed from the device, + +316 +00:15:13,079 --> 00:15:14,614 +the status item on the device + +317 +00:15:14,614 --> 00:15:17,683 +has the corresponding object +marked for removal, + +318 +00:15:17,683 --> 00:15:21,788 +and another status report +is sent to the server. + +319 +00:15:21,788 --> 00:15:24,257 +Only the removed item +is reported. + +320 +00:15:24,257 --> 00:15:25,858 +To indicate removal, + +321 +00:15:25,858 --> 00:15:29,428 +the array item object +contains only two keys: + +322 +00:15:29,428 --> 00:15:30,997 +the identifier key -- + +323 +00:15:30,997 --> 00:15:33,766 +whose value matches +one the server already has -- + +324 +00:15:33,766 --> 00:15:37,470 +and the removed key, +set to the value true. + +325 +00:15:37,470 --> 00:15:40,373 +This allows the server +to update its representation + +326 +00:15:40,373 --> 00:15:44,510 +of the device state +by removing the existing item. + +327 +00:15:44,510 --> 00:15:46,379 +After processing this report, + +328 +00:15:46,379 --> 00:15:49,215 +the server has status +for only two mail accounts, + +329 +00:15:49,215 --> 00:15:52,952 +correctly matching +the state of the device. + +330 +00:15:52,952 --> 00:15:55,922 +One last point about +status reports. + +331 +00:15:55,922 --> 00:15:59,225 +The device will limit the rate +at which status reports are sent + +332 +00:15:59,225 --> 00:16:01,527 +to avoid performance issues. + +333 +00:16:01,527 --> 00:16:04,564 +The device aggregates +changes to status items + +334 +00:16:04,564 --> 00:16:07,333 +over a variable interval +of up to one minute + +335 +00:16:07,333 --> 00:16:10,570 +before sending a status report +to the server. + +336 +00:16:10,570 --> 00:16:13,005 +This means status +is reported quickly, + +337 +00:16:13,005 --> 00:16:15,675 +but it is not immediate. + +338 +00:16:15,675 --> 00:16:17,543 +Next, let's turn our attention + +339 +00:16:17,543 --> 00:16:20,513 +to solving a perennial +MDM-bottleneck problem: + +340 +00:16:20,513 --> 00:16:24,050 +monitoring +application install status. + +341 +00:16:24,050 --> 00:16:28,821 +MDM servers often install apps +on devices to give users access + +342 +00:16:28,821 --> 00:16:32,658 +to the tools needed +for their work or education. + +343 +00:16:32,658 --> 00:16:34,861 +Server-side logic +is often dictated + +344 +00:16:34,861 --> 00:16:37,997 +by whether an app is installed +successfully or not. + +345 +00:16:37,997 --> 00:16:42,101 +So MDM servers need to monitor +app installation progress + +346 +00:16:42,101 --> 00:16:46,105 +and watch for the possibility +of users removing managed apps + +347 +00:16:46,105 --> 00:16:48,875 +on their device. + +348 +00:16:48,875 --> 00:16:53,145 +Currently, MDM servers can use +the InstalledApplicationList + +349 +00:16:53,145 --> 00:16:56,349 +or ManagedApplicationList +commands to poll the device + +350 +00:16:56,349 --> 00:16:59,585 +to observe +app installation progress. + +351 +00:16:59,585 --> 00:17:02,088 +We can avoid polling +by having the device + +352 +00:17:02,088 --> 00:17:06,058 +proactively send app install +progress to the server. + +353 +00:17:06,058 --> 00:17:07,326 +And the tool to do that + +354 +00:17:07,326 --> 00:17:12,198 +is declarative device management +status reports. + +355 +00:17:12,198 --> 00:17:15,468 +This release adds +an mdm.app status item. + +356 +00:17:15,468 --> 00:17:19,138 +Its value is an array of objects +that each represent an app + +357 +00:17:19,138 --> 00:17:22,141 +that has been installed +by the MDM server. + +358 +00:17:22,141 --> 00:17:25,611 +Since this value is an array, +it is reported incrementally, + +359 +00:17:25,611 --> 00:17:28,114 +using the procedure +described earlier. + +360 +00:17:28,114 --> 00:17:31,984 +Note that only apps installed +by MDM are reported here, + +361 +00:17:31,984 --> 00:17:35,187 +even on supervised devices. + +362 +00:17:35,187 --> 00:17:38,357 +This status report includes +a status item for an app + +363 +00:17:38,357 --> 00:17:40,493 +that has finished installing. + +364 +00:17:40,493 --> 00:17:43,162 +The identifier key +is the unique identifier + +365 +00:17:43,162 --> 00:17:45,598 +for the array item object, +and in this case, + +366 +00:17:45,598 --> 00:17:48,167 +is the app's bundle identifier. + +367 +00:17:48,167 --> 00:17:51,170 +The name key indicates +the name of the app. + +368 +00:17:51,170 --> 00:17:54,006 +The three version keys +provide normal, short, + +369 +00:17:54,006 --> 00:17:57,043 +and external +version identifiers. + +370 +00:17:57,043 --> 00:17:58,911 +And the state key +is an enumeration + +371 +00:17:58,911 --> 00:18:02,848 +that indicates the current +install phase for the app. + +372 +00:18:02,848 --> 00:18:04,850 +The values of these keys +correspond + +373 +00:18:04,850 --> 00:18:07,053 +to the equivalent items +in the MDM + +374 +00:18:07,053 --> 00:18:10,189 +ManagedApplicationList +command response. + +375 +00:18:10,189 --> 00:18:13,926 +With all this information, the +server can immediately identify + +376 +00:18:13,926 --> 00:18:18,764 +which app is being reported +and what its state is. + +377 +00:18:18,764 --> 00:18:22,902 +Let's examine an example of the +flow of data as an app installs. + +378 +00:18:22,902 --> 00:18:25,738 +On the right side, +we have an iOS 16 device + +379 +00:18:25,738 --> 00:18:28,574 +that is being managed +by an MDM server. + +380 +00:18:28,574 --> 00:18:31,978 +The server has already enabled +declarative device management + +381 +00:18:31,978 --> 00:18:33,646 +and sent a status subscription + +382 +00:18:33,646 --> 00:18:37,049 +for the MDM-installed +app status item. + +383 +00:18:37,049 --> 00:18:39,719 +The next step for the server +is to install an app + +384 +00:18:39,719 --> 00:18:43,222 +using the MDM +InstallApplication command. + +385 +00:18:43,222 --> 00:18:46,625 +Since this is a user enrollment, +user approval is needed + +386 +00:18:46,625 --> 00:18:49,295 +to install the app, +so a prompt appears + +387 +00:18:49,295 --> 00:18:52,698 +when the device processes +the app install command. + +388 +00:18:52,698 --> 00:18:55,868 +At this point, the installation +progress is paused, + +389 +00:18:55,868 --> 00:18:58,604 +waiting for user input. + +390 +00:18:58,604 --> 00:19:01,540 +The device will send +a status report to the server, + +391 +00:19:01,540 --> 00:19:05,644 +and that will contain a single +MDM-installed app status object, + +392 +00:19:05,644 --> 00:19:10,282 +with the bundle ID of the app +and the state set to prompting. + +393 +00:19:10,282 --> 00:19:13,085 +At some point, the user taps +the Install button, + +394 +00:19:13,085 --> 00:19:16,856 +and the app install +starts on the device. + +395 +00:19:16,856 --> 00:19:20,693 +As the install proceeds, another +status report will be sent, + +396 +00:19:20,693 --> 00:19:23,829 +this time with the app state +set to installing; + +397 +00:19:23,829 --> 00:19:28,300 +indicating the app is being +downloaded and installed. + +398 +00:19:28,300 --> 00:19:30,536 +Eventually, the app +completes installation + +399 +00:19:30,536 --> 00:19:32,705 +and is ready for use. + +400 +00:19:32,705 --> 00:19:35,241 +At that point, another +status report will be sent + +401 +00:19:35,241 --> 00:19:37,443 +with the app state +set to managed, + +402 +00:19:37,443 --> 00:19:41,380 +indicating the app is properly +installed and managed. + +403 +00:19:41,380 --> 00:19:46,085 +Now, let's say the user manually +deletes the app on the device. + +404 +00:19:46,085 --> 00:19:48,554 +Again, a status report +will be sent, + +405 +00:19:48,554 --> 00:19:53,259 +this time with the app state set +to managed-but-uninstalled. + +406 +00:19:53,259 --> 00:19:55,895 +This indicates the app +is no longer installed, + +407 +00:19:55,895 --> 00:19:58,497 +but its management state +is still being tracked + +408 +00:19:58,497 --> 00:20:00,699 +on the device. + +409 +00:20:00,699 --> 00:20:02,835 +Let's assume the server +wants to remove + +410 +00:20:02,835 --> 00:20:04,770 +the app-management state. + +411 +00:20:04,770 --> 00:20:07,807 +It does that by sending +a RemoveApplication command + +412 +00:20:07,807 --> 00:20:09,341 +to the device. + +413 +00:20:09,341 --> 00:20:12,311 +That removes the internally +maintained management state, + +414 +00:20:12,311 --> 00:20:14,647 +and if the app were +still present, + +415 +00:20:14,647 --> 00:20:17,283 +it would be removed too. + +416 +00:20:17,283 --> 00:20:19,752 +Another status report will be +sent with the app object + +417 +00:20:19,752 --> 00:20:22,822 +marked as removed +from the app status array. + +418 +00:20:22,822 --> 00:20:26,725 +This illustrates the power of +the new MDM status item + +419 +00:20:26,725 --> 00:20:28,594 +to help improve +the responsiveness + +420 +00:20:28,594 --> 00:20:30,963 +and reliability of app installs, + +421 +00:20:30,963 --> 00:20:35,000 +and it only takes a few steps +to implement. + +422 +00:20:35,000 --> 00:20:40,372 +Now, let's examine our third +focus area: predicates. + +423 +00:20:40,372 --> 00:20:43,342 +Let's quickly review +activation predicates. + +424 +00:20:43,342 --> 00:20:45,911 +Activations can include +an optional predicate + +425 +00:20:45,911 --> 00:20:47,980 +that determines +whether the configurations + +426 +00:20:47,980 --> 00:20:52,118 +referenced in the activation +will be applied to the device. + +427 +00:20:52,118 --> 00:20:53,853 +Predicates can reference +status items + +428 +00:20:53,853 --> 00:20:57,823 +to allow the values of those +status items to be tested. + +429 +00:20:57,823 --> 00:21:00,593 +When a status item referenced +in a predicate changes, + +430 +00:21:00,593 --> 00:21:03,662 +the device will reprocess +all of the activations, + +431 +00:21:03,662 --> 00:21:06,565 +reevaluating any predicates. + +432 +00:21:06,565 --> 00:21:08,400 +Predicates are specified +as a string + +433 +00:21:08,400 --> 00:21:10,402 +using the NSPredicate syntax + +434 +00:21:10,402 --> 00:21:13,572 +documented on +the Apple Developer site. + +435 +00:21:13,572 --> 00:21:16,675 +To support more complex +predicate expressions, + +436 +00:21:16,675 --> 00:21:19,812 +we have extended the predicate +syntax to make it easier + +437 +00:21:19,812 --> 00:21:23,682 +to detect status items +in the expression. + +438 +00:21:23,682 --> 00:21:26,318 +The new syntax places +the status item name + +439 +00:21:26,318 --> 00:21:30,456 +inside an @status term +in the predicate string. + +440 +00:21:30,456 --> 00:21:33,492 +In the example, +the serial number status item + +441 +00:21:33,492 --> 00:21:35,327 +appears in +the predicate expression, + +442 +00:21:35,327 --> 00:21:38,330 +using the new syntax. + +443 +00:21:38,330 --> 00:21:40,266 +The previous syntax +will continue to work + +444 +00:21:40,266 --> 00:21:44,537 +for backwards compatibility, +however, it is now deprecated, + +445 +00:21:44,537 --> 00:21:47,640 +so please switch +to the new one. + +446 +00:21:47,640 --> 00:21:49,575 +Let's examine how +predicates can be used + +447 +00:21:49,575 --> 00:21:52,011 +with status item array values. + +448 +00:21:52,011 --> 00:21:55,047 +As we just described, +we now have status item values + +449 +00:21:55,047 --> 00:21:56,882 +that are arrays +for the accounts + +450 +00:21:56,882 --> 00:21:59,785 +and MDM-installed +app status items. + +451 +00:21:59,785 --> 00:22:02,521 +It is useful to be able +to predicate an activation + +452 +00:22:02,521 --> 00:22:05,157 +on an item in the array. + +453 +00:22:05,157 --> 00:22:08,561 +For example, we might want +an activation to be triggered + +454 +00:22:08,561 --> 00:22:10,963 +when an app with a particular +bundle identifier + +455 +00:22:10,963 --> 00:22:14,567 +is installed and managed +on the device. + +456 +00:22:14,567 --> 00:22:16,869 +NSPredicate has +a SUBQUERY term + +457 +00:22:16,869 --> 00:22:20,239 +that can be used +to operate on arrays. + +458 +00:22:20,239 --> 00:22:23,142 +This NSPredicate expression +uses a SUBQUERY + +459 +00:22:23,142 --> 00:22:26,812 +targeting the MDM-installed +app status item. + +460 +00:22:26,812 --> 00:22:30,983 +The status item is used as the +first argument to the SUBQUERY. + +461 +00:22:30,983 --> 00:22:33,085 +The second argument +defines a variable + +462 +00:22:33,085 --> 00:22:35,754 +that will refer to each +element of the array. + +463 +00:22:35,754 --> 00:22:38,023 +The third argument is +a predicate expression + +464 +00:22:38,023 --> 00:22:42,494 +that tests each element +identified by that variable. + +465 +00:22:42,494 --> 00:22:45,397 +The SUBQUERY expression +returns an array of elements + +466 +00:22:45,397 --> 00:22:48,701 +that match the predicate +in the third argument. + +467 +00:22:48,701 --> 00:22:51,637 +The @count operator +then returns the length + +468 +00:22:51,637 --> 00:22:53,772 +of that array, +and the length is checked + +469 +00:22:53,772 --> 00:22:57,476 +to determine if there is +one resulting match. + +470 +00:22:57,476 --> 00:23:00,446 +When the specified app +is installed and managed, + +471 +00:23:00,446 --> 00:23:02,748 +this SUBQUERY expression +will return an array + +472 +00:23:02,748 --> 00:23:04,450 +with a single element, + +473 +00:23:04,450 --> 00:23:07,453 +and the predicate +will evaluate to true. + +474 +00:23:07,453 --> 00:23:10,356 +When the app is not installed, +the SUBQUERY expression + +475 +00:23:10,356 --> 00:23:12,091 +will return an empty array, + +476 +00:23:12,091 --> 00:23:15,694 +and the predicate +will evaluate to false. + +477 +00:23:15,694 --> 00:23:18,230 +Note that in order +to reference the keys + +478 +00:23:18,230 --> 00:23:21,800 +in the status item array object, +the @key extension term + +479 +00:23:21,800 --> 00:23:25,838 +must be used to ensure the key +paths are properly processed. + +480 +00:23:25,838 --> 00:23:28,540 +The new predicate syntax +is extensible, + +481 +00:23:28,540 --> 00:23:30,976 +and we will now discuss +how it can be used + +482 +00:23:30,976 --> 00:23:35,781 +to add predicate terms +for a new type of data. + +483 +00:23:35,781 --> 00:23:38,417 +Servers need to be able +to more directly control + +484 +00:23:38,417 --> 00:23:42,221 +the evaluation of predicates, so +that complex server-side logic + +485 +00:23:42,221 --> 00:23:45,591 +can translate into simple +state changes on the device, + +486 +00:23:45,591 --> 00:23:48,894 +without the need to synchronize +large sets of configurations + +487 +00:23:48,894 --> 00:23:51,130 +to trigger those changes. + +488 +00:23:51,130 --> 00:23:54,400 +An example of this might be +an organization that has users + +489 +00:23:54,400 --> 00:23:57,236 +with multiple roles +and wants efficient, + +490 +00:23:57,236 --> 00:23:59,071 +just-in-time assignment +of devices + +491 +00:23:59,071 --> 00:24:03,008 +as they are handed out to users, +or organizations that need + +492 +00:24:03,008 --> 00:24:05,644 +to quickly distribute +replacement devices, + +493 +00:24:05,644 --> 00:24:07,813 +or quickly put devices +into a safe mode + +494 +00:24:07,813 --> 00:24:10,516 +to protect organization data. + +495 +00:24:10,516 --> 00:24:13,185 +To support this, +I am pleased to say + +496 +00:24:13,185 --> 00:24:16,355 +we are adding a new declaration +to allow servers + +497 +00:24:16,355 --> 00:24:19,058 +to set arbitrary properties +on the device, + +498 +00:24:19,058 --> 00:24:22,328 +that can be directly used +in activation predicates. + +499 +00:24:22,328 --> 00:24:26,865 +This is the new management +properties declaration. + +500 +00:24:26,865 --> 00:24:29,501 +The declaration consists +of a JSON object + +501 +00:24:29,501 --> 00:24:33,005 +whose key names +are defined by the server. + +502 +00:24:33,005 --> 00:24:36,642 +The JSON object values +can be any JSON value type, + +503 +00:24:36,642 --> 00:24:39,645 +including arrays or objects. + +504 +00:24:39,645 --> 00:24:41,580 +The management properties +declaration here, + +505 +00:24:41,580 --> 00:24:44,950 +includes three properties: +the name and age properties + +506 +00:24:44,950 --> 00:24:47,252 +that have a string +and integer value, + +507 +00:24:47,252 --> 00:24:51,724 +and the roles property +that is an array of strings. + +508 +00:24:51,724 --> 00:24:54,093 +This is an activation +with a predicate + +509 +00:24:54,093 --> 00:24:56,929 +that references some +management properties. + +510 +00:24:56,929 --> 00:24:59,965 +First, it tests the age property +to determine + +511 +00:24:59,965 --> 00:25:03,702 +if its integer value is +greater than or equal to 18, + +512 +00:25:03,702 --> 00:25:06,472 +then it tests the roles property +to determine + +513 +00:25:06,472 --> 00:25:11,577 +if the string Grade12 is +in the property array value. + +514 +00:25:11,577 --> 00:25:12,845 +Each property is referenced + +515 +00:25:12,845 --> 00:25:15,447 +using the @property +extension term, + +516 +00:25:15,447 --> 00:25:19,651 +with the property key name +inside the term. + +517 +00:25:19,651 --> 00:25:21,687 +Multiple management +properties declarations + +518 +00:25:21,687 --> 00:25:23,288 +can be sent to the device, + +519 +00:25:23,288 --> 00:25:26,692 +but the keys should be unique +across all of them. + +520 +00:25:26,692 --> 00:25:29,461 +If there are duplicate keys, +one of the values + +521 +00:25:29,461 --> 00:25:32,464 +will be arbitrarily chosen +when the property is referenced + +522 +00:25:32,464 --> 00:25:35,834 +in a predicate, leading +to unpredictable results. + +523 +00:25:35,834 --> 00:25:39,471 +So please avoid using +duplicate key names. + +524 +00:25:39,471 --> 00:25:43,075 +Let's explore +an example use case. + +525 +00:25:43,075 --> 00:25:45,411 +This example involves a school. + +526 +00:25:45,411 --> 00:25:48,647 +And of course, the school +has a set of teachers. + +527 +00:25:48,647 --> 00:25:52,885 +The school has two divisions: +Upper and Lower. + +528 +00:25:52,885 --> 00:25:57,322 +Each division has its own campus +with its own Wi-Fi network. + +529 +00:25:57,322 --> 00:25:59,858 +Some teachers function +as an IT Admin + +530 +00:25:59,858 --> 00:26:02,895 +and require access +to a shared mail account. + +531 +00:26:02,895 --> 00:26:05,631 +Some teachers also function +as a sports coach + +532 +00:26:05,631 --> 00:26:07,499 +and should have +a subscribed calendar + +533 +00:26:07,499 --> 00:26:10,369 +for all the team +game schedules. + +534 +00:26:10,369 --> 00:26:13,839 +There are thus four different +roles that a teacher may have, + +535 +00:26:13,839 --> 00:26:16,842 +and sometimes +they have multiple roles. + +536 +00:26:16,842 --> 00:26:19,077 +Each role has a set +of configurations + +537 +00:26:19,077 --> 00:26:22,014 +that must be applied to devices +based on the roles + +538 +00:26:22,014 --> 00:26:25,417 +of the teacher +assigned to the device. + +539 +00:26:25,417 --> 00:26:28,887 +Let's consider two teachers +in our example. + +540 +00:26:28,887 --> 00:26:30,889 +Teacher one teaches +in Lower school + +541 +00:26:30,889 --> 00:26:33,425 +and is also a sports coach. + +542 +00:26:33,425 --> 00:26:39,298 +Teacher two teaches in Upper +school and is also an IT admin. + +543 +00:26:39,298 --> 00:26:41,066 +How might such a use case +be handled + +544 +00:26:41,066 --> 00:26:44,169 +by a traditional MDM server? + +545 +00:26:44,169 --> 00:26:46,638 +Typically, the server has +to wait for a device + +546 +00:26:46,638 --> 00:26:48,340 +to be assigned to a teacher + +547 +00:26:48,340 --> 00:26:51,577 +before it can fully +configure that device. + +548 +00:26:51,577 --> 00:26:55,247 +The server has to determine +what roles the teacher has. + +549 +00:26:55,247 --> 00:26:59,585 +It then determines what profiles +are linked to each role. + +550 +00:26:59,585 --> 00:27:02,488 +It then has to install +each profile on the device + +551 +00:27:02,488 --> 00:27:05,157 +one at a time. + +552 +00:27:05,157 --> 00:27:07,993 +If a teacher changes roles, +the server has to add + +553 +00:27:07,993 --> 00:27:11,330 +or remove profiles +to match the new roles. + +554 +00:27:11,330 --> 00:27:14,099 +This is time-consuming +and can introduce + +555 +00:27:14,099 --> 00:27:17,336 +significant bottlenecks +to a device-management system + +556 +00:27:17,336 --> 00:27:19,471 +particularly at peak times, + +557 +00:27:19,471 --> 00:27:21,874 +which in our case would be +the first day of school + +558 +00:27:21,874 --> 00:27:24,409 +when assignments are done. + +559 +00:27:24,409 --> 00:27:26,745 +With the new management +properties declaration, + +560 +00:27:26,745 --> 00:27:30,015 +we have a more efficient +alternative to this. + +561 +00:27:30,015 --> 00:27:32,951 +This involves preloading +a full set of declarations + +562 +00:27:32,951 --> 00:27:35,954 +on the device ahead of time. + +563 +00:27:35,954 --> 00:27:38,323 +Configurations +are assigned to activations, + +564 +00:27:38,323 --> 00:27:41,260 +with predicates that are +triggered for different roles + +565 +00:27:41,260 --> 00:27:44,363 +via management properties. + +566 +00:27:44,363 --> 00:27:47,799 +When a device is assigned to +a teacher, the server sends only + +567 +00:27:47,799 --> 00:27:49,935 +a management properties +declaration + +568 +00:27:49,935 --> 00:27:52,871 +with the teacher's roles, +which triggers activation + +569 +00:27:52,871 --> 00:27:56,375 +of the configurations +for those roles. + +570 +00:27:56,375 --> 00:27:58,844 +This method minimizes +the overall server + +571 +00:27:58,844 --> 00:28:01,847 +and network traffic +and reduces the complexity + +572 +00:28:01,847 --> 00:28:05,617 +of making rapid changes +to device state. + +573 +00:28:05,617 --> 00:28:07,953 +Let's go back +to our school example. + +574 +00:28:07,953 --> 00:28:11,590 +The server will preload the +following sets of declarations: + +575 +00:28:11,590 --> 00:28:13,992 +two activation/configuration +pairs + +576 +00:28:13,992 --> 00:28:17,362 +that set up the Wi-Fi network +for each division. + +577 +00:28:17,362 --> 00:28:19,865 +Then, we have an +activation/configuration pair + +578 +00:28:19,865 --> 00:28:23,001 +for the IT admin role, +that installs a mail account. + +579 +00:28:23,001 --> 00:28:26,071 +Finally, we have an activation +and configuration + +580 +00:28:26,071 --> 00:28:29,341 +that installs +a subscribed calendar. + +581 +00:28:29,341 --> 00:28:32,444 +Each activation has a predicate +that tests the division + +582 +00:28:32,444 --> 00:28:36,515 +or function's name using +the roles management property. + +583 +00:28:36,515 --> 00:28:39,384 +When initially loaded +on an unassigned device, + +584 +00:28:39,384 --> 00:28:44,189 +all the predicates evaluate +to false, so nothing is applied. + +585 +00:28:44,189 --> 00:28:48,427 +Now, let's examine what happens +on the day of assignment. + +586 +00:28:48,427 --> 00:28:50,696 +All the server needs to do +is create + +587 +00:28:50,696 --> 00:28:52,598 +management properties +declarations + +588 +00:28:52,598 --> 00:28:55,467 +customized to each teacher. + +589 +00:28:55,467 --> 00:28:59,938 +Teacher one has a roles property +that lists Lower and Sports. + +590 +00:28:59,938 --> 00:29:05,210 +Teacher two has a roles property +that lists Upper and IT Admin. + +591 +00:29:05,210 --> 00:29:07,479 +When these declarations +are separately sent + +592 +00:29:07,479 --> 00:29:09,281 +to each assigned device, + +593 +00:29:09,281 --> 00:29:13,418 +the preloaded activations +will all be reevaluated. + +594 +00:29:13,418 --> 00:29:16,555 +So teacher one's device +has the configurations + +595 +00:29:16,555 --> 00:29:20,325 +for Lower and Sports +roles activated. + +596 +00:29:20,325 --> 00:29:23,195 +And teacher two's device +has the configurations + +597 +00:29:23,195 --> 00:29:27,199 +for the Upper and IT Admin +roles activated. + +598 +00:29:27,199 --> 00:29:29,868 +Only a single declaration +is needed to trigger + +599 +00:29:29,868 --> 00:29:33,438 +the application +of many configurations. + +600 +00:29:33,438 --> 00:29:34,773 +Finally, let's examine + +601 +00:29:34,773 --> 00:29:37,643 +what happens when +a teacher changes roles. + +602 +00:29:37,643 --> 00:29:40,512 +In this case, teacher two +has become a sports coach + +603 +00:29:40,512 --> 00:29:43,448 +in addition +to their existing roles. + +604 +00:29:43,448 --> 00:29:45,283 +The management +properties declaration + +605 +00:29:45,283 --> 00:29:47,919 +for the teacher's assigned +device is now updated + +606 +00:29:47,919 --> 00:29:50,956 +to include +the additional role name. + +607 +00:29:50,956 --> 00:29:53,458 +When that declaration +is updated on the device, + +608 +00:29:53,458 --> 00:29:56,328 +all the activations +are reevaluated. + +609 +00:29:56,328 --> 00:29:58,997 +In this case, the subscribed +calendar configuration + +610 +00:29:58,997 --> 00:30:01,733 +for the new Sports role +will be applied. + +611 +00:30:01,733 --> 00:30:06,838 +Again, only a single declaration +change is needed as a trigger. + +612 +00:30:06,838 --> 00:30:07,973 +This illustrates + +613 +00:30:07,973 --> 00:30:10,108 +how the management +properties declaration + +614 +00:30:10,108 --> 00:30:13,512 +provides a powerful way +to quickly and easily switch + +615 +00:30:13,512 --> 00:30:16,682 +between sets of configurations +on a device, + +616 +00:30:16,682 --> 00:30:18,817 +so that complex +server-side logic + +617 +00:30:18,817 --> 00:30:23,555 +can translate into simple +state changes on the device. + +618 +00:30:23,555 --> 00:30:25,824 +Now, let's wrap up. + +619 +00:30:25,824 --> 00:30:29,027 +We have extended the scope of +declarative device management + +620 +00:30:29,027 --> 00:30:34,499 +on iOS 16, tvOS 16, +and macOS Ventura, + +621 +00:30:34,499 --> 00:30:37,402 +as well as making it available +for all applicable types + +622 +00:30:37,402 --> 00:30:41,173 +of MDM enrollment, +including Shared iPad. + +623 +00:30:41,173 --> 00:30:45,077 +This provides full support for +declarative device management + +624 +00:30:45,077 --> 00:30:49,948 +across all Apple devices +that support MDM. + +625 +00:30:49,948 --> 00:30:53,919 +We have added new status items +for passcode, accounts, + +626 +00:30:53,919 --> 00:30:56,321 +and MDM-installed apps. + +627 +00:30:56,321 --> 00:31:00,358 +The MDM-installed app status +provides a great solution + +628 +00:31:00,358 --> 00:31:03,895 +for one of MDM's +key bottlenecks. + +629 +00:31:03,895 --> 00:31:06,798 +Finally, we have enhanced +the predicate syntax + +630 +00:31:06,798 --> 00:31:10,102 +to make it more extensible +and easy to use + +631 +00:31:10,102 --> 00:31:13,305 +and added the new management +properties declaration + +632 +00:31:13,305 --> 00:31:16,007 +that gives servers +even more opportunity + +633 +00:31:16,007 --> 00:31:19,811 +to move complex business logic +to the device. + +634 +00:31:19,811 --> 00:31:22,414 +Now is the time to add +declarative device management + +635 +00:31:22,414 --> 00:31:24,049 +to your products. + +636 +00:31:24,049 --> 00:31:26,284 +And we're excited to learn +what you'll do + +637 +00:31:26,284 --> 00:31:28,587 +to reimagine device +management solutions + +638 +00:31:28,587 --> 00:31:32,357 +using declarative +device management! + +639 +00:31:32,357 --> 00:31:35,627 +As always, your feedback +is greatly appreciated. + +640 +00:31:35,627 --> 00:31:38,463 +Thank you and enjoy +the rest of WWDC. + +641 +00:31:38,463 --> 00:31:43,568 +♪ ♪ + diff --git a/eng/2022 Session 10048 What's new in Safari and WebKit en.srt b/eng/2022 Session 10048 What's new in Safari and WebKit en.srt new file mode 100644 index 0000000..0f5b42b --- /dev/null +++ b/eng/2022 Session 10048 What's new in Safari and WebKit en.srt @@ -0,0 +1,2882 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,710 +♪ + +3 +00:00:09,710 --> 00:00:12,145 +Hi there! I'm Kendall Bagley, + +4 +00:00:12,145 --> 00:00:14,982 +a software engineer +on the Safari team. + +5 +00:00:14,982 --> 00:00:19,486 +It's been a full year since +we last got together at WWDC, + +6 +00:00:19,486 --> 00:00:21,488 +and today, we're going +to be talking about + +7 +00:00:21,488 --> 00:00:23,957 +all the amazing features +and enhancements + +8 +00:00:23,957 --> 00:00:25,425 +to Safari and WebKit + +9 +00:00:25,425 --> 00:00:28,629 +from both what's new here +at this year's WWDC + +10 +00:00:28,629 --> 00:00:32,032 +and from what we've seen +throughout this whole past year. + +11 +00:00:32,032 --> 00:00:35,335 +In fact, it's been +quite a busy year! + +12 +00:00:35,335 --> 00:00:38,372 +Since last fall, each release +of Safari has delivered + +13 +00:00:38,372 --> 00:00:42,209 +new and exciting features that +we know y'all as web developers + +14 +00:00:42,209 --> 00:00:44,211 +have been asking for. + +15 +00:00:44,211 --> 00:00:47,014 +Each of the new enhancements +delivered this year + +16 +00:00:47,014 --> 00:00:49,349 +aimed to address some of +the biggest points of feedback + +17 +00:00:49,349 --> 00:00:51,451 +you've been sharing with us. + +18 +00:00:51,451 --> 00:00:55,022 +Like adding a parent selector +with the :has() pseudo class, + +19 +00:00:55,022 --> 00:00:59,059 +the new flexbox inspector, +and even container queries. + +20 +00:00:59,059 --> 00:01:02,362 +We want to make your daily work +that much better and easier + +21 +00:01:02,362 --> 00:01:05,032 +while building the best +and most powerful software + +22 +00:01:05,032 --> 00:01:06,400 +for the web. + +23 +00:01:06,400 --> 00:01:09,670 +In fact, what's here +is just some of the new content + +24 +00:01:09,670 --> 00:01:11,872 +that we'll be going over today. + +25 +00:01:11,872 --> 00:01:15,242 +But there's so much more that +we wouldn't possibly be able + +26 +00:01:15,242 --> 00:01:18,378 +to cover it all +in this one session. + +27 +00:01:18,378 --> 00:01:23,150 +There's been a total +of 162 new web platform features + +28 +00:01:23,150 --> 00:01:26,019 +and improvements across +the seven Safari releases + +29 +00:01:26,019 --> 00:01:27,721 +this past year. + +30 +00:01:27,721 --> 00:01:31,692 +We've been proud to provide +so many new tools for you to use + +31 +00:01:31,692 --> 00:01:34,361 +to make your websites +and web apps. + +32 +00:01:34,361 --> 00:01:38,932 +And for macOS, the best way +to see what's new and exciting + +33 +00:01:38,932 --> 00:01:42,836 +as soon as possible is through +Safari Technology Preview, + +34 +00:01:42,836 --> 00:01:45,339 +where you can try out +the latest and greatest + +35 +00:01:45,339 --> 00:01:48,308 +for Safari and WebKit +and also help us know + +36 +00:01:48,308 --> 00:01:51,011 +what we should have come next. + +37 +00:01:51,011 --> 00:01:55,148 +But like you saw, there's +a whole bunch of new features, + +38 +00:01:55,148 --> 00:01:56,249 +so let's take a look + +39 +00:01:56,249 --> 00:01:59,086 +at everything that +we're going to cover. + +40 +00:01:59,086 --> 00:02:03,557 +Today we'll be looking +at new HTML features, + +41 +00:02:03,557 --> 00:02:05,192 +CSS enhancements -- + +42 +00:02:05,192 --> 00:02:09,463 +including a bunch to help +your code architecture -- + +43 +00:02:09,463 --> 00:02:15,435 +new Web Inspector tools, +a wide selection of new web API, + +44 +00:02:15,435 --> 00:02:19,206 +great JavaScript +and WebAssembly features, + +45 +00:02:19,206 --> 00:02:23,210 +and improvements +to security and privacy. + +46 +00:02:23,210 --> 00:02:27,280 +So, let's get started +with what's new with HTML + +47 +00:02:27,280 --> 00:02:29,750 +by taking a look +at a web page I'm creating + +48 +00:02:29,750 --> 00:02:32,753 +for my coworkers and I to use. + +49 +00:02:32,753 --> 00:02:36,223 +I personally really like to +thrift and repurpose my clothes + +50 +00:02:36,223 --> 00:02:39,226 +as a way to make my wardrobe +more sustainable, + +51 +00:02:39,226 --> 00:02:41,461 +and I thought +a clothing swap website + +52 +00:02:41,461 --> 00:02:46,233 +would be a great way +to have my team try it out too. + +53 +00:02:46,233 --> 00:02:49,669 +My design for the site includes +a Request Item button + +54 +00:02:49,669 --> 00:02:51,338 +that should show you +a form to fill out + +55 +00:02:51,338 --> 00:02:54,174 +when you spot a piece +of clothing that you like. + +56 +00:02:54,174 --> 00:02:56,576 +And I want that form +to show up in an overlay + +57 +00:02:56,576 --> 00:02:58,812 +over top of the whole page. + +58 +00:02:58,812 --> 00:03:01,882 +The new dialog element +provides a really easy way + +59 +00:03:01,882 --> 00:03:06,253 +to create overlays in a robust +and accessible manner + +60 +00:03:06,253 --> 00:03:08,789 +that we can use +for our request form. + +61 +00:03:08,789 --> 00:03:11,958 +And the new backdrop +pseudo-element in CSS + +62 +00:03:11,958 --> 00:03:14,461 +makes it possible +to style the background + +63 +00:03:14,461 --> 00:03:16,329 +behind the modal. + +64 +00:03:16,329 --> 00:03:19,866 +Let's bring up the dialog +by requesting an item. + +65 +00:03:19,866 --> 00:03:22,369 +See that shadow and animation? + +66 +00:03:22,369 --> 00:03:24,471 +It's really looking great! + +67 +00:03:24,471 --> 00:03:27,374 +Then, once an item +is requested on our site, + +68 +00:03:27,374 --> 00:03:28,575 +the person who posted it + +69 +00:03:28,575 --> 00:03:32,979 +needs to be able +to accept the request. + +70 +00:03:32,979 --> 00:03:35,415 +At the bottom of the page, +there's a carousel + +71 +00:03:35,415 --> 00:03:38,452 +to flip through +all your received requests. + +72 +00:03:38,452 --> 00:03:41,788 +But I don't want someone +to accidentally interact + +73 +00:03:41,788 --> 00:03:44,591 +with a button or a text field +for one of the items + +74 +00:03:44,591 --> 00:03:46,359 +that's not the frontmost, + +75 +00:03:46,359 --> 00:03:49,463 +with either clicks +or keyboard navigation. + +76 +00:03:49,463 --> 00:03:52,766 +I can use the inert attribute +to fix this. + +77 +00:03:52,766 --> 00:03:55,202 +By dynamically applying +the inert attribute + +78 +00:03:55,202 --> 00:03:56,937 +with the JavaScript here, + +79 +00:03:56,937 --> 00:04:00,540 +I'm disabling any interactions +for elements on slides + +80 +00:04:00,540 --> 00:04:02,843 +that aren't the currently +selected slide + +81 +00:04:02,843 --> 00:04:04,945 +as we switch between them. + +82 +00:04:04,945 --> 00:04:08,782 +And, using inert +includes disabling interactions + +83 +00:04:08,782 --> 00:04:11,885 +for assistive technologies +and prevents screen readers + +84 +00:04:11,885 --> 00:04:14,688 +from reading those +disabled items aloud, + +85 +00:04:14,688 --> 00:04:16,523 +giving much clearer guidance + +86 +00:04:16,523 --> 00:04:20,660 +on which elements +are intended for interaction. + +87 +00:04:20,660 --> 00:04:25,398 +And lastly for HTML, there's +the new lazy loading for images. + +88 +00:04:25,398 --> 00:04:27,534 +On my site, +there's some icons in the header + +89 +00:04:27,534 --> 00:04:32,339 +that I need to load right away, +but for the clothing item images + +90 +00:04:32,339 --> 00:04:34,875 +that are offscreen +on that first load, + +91 +00:04:34,875 --> 00:04:37,511 +we can utilize +lazy loading for them, + +92 +00:04:37,511 --> 00:04:40,881 +so the images only load +when the user scrolls to them, + +93 +00:04:40,881 --> 00:04:44,684 +making the page feel faster +and more responsive. + +94 +00:04:44,684 --> 00:04:47,254 +I'm really loving +how the site's looking so far, + +95 +00:04:47,254 --> 00:04:48,989 +and it's going to work great + +96 +00:04:48,989 --> 00:04:52,392 +for those using +assistive technologies as well. + +97 +00:04:52,392 --> 00:04:56,696 +And those HTML features +are just getting us started, + +98 +00:04:56,696 --> 00:04:58,965 +because there's so much +to check out + +99 +00:04:58,965 --> 00:05:02,269 +with CSS this year too. + +100 +00:05:02,269 --> 00:05:04,838 +A huge part of our CSS focus + +101 +00:05:04,838 --> 00:05:07,974 +has been around +making your CSS easier to reuse + +102 +00:05:07,974 --> 00:05:10,410 +through more +powerful architecture. + +103 +00:05:10,410 --> 00:05:13,280 +With that, we know +the number one request + +104 +00:05:13,280 --> 00:05:16,650 +for new web technology +has been container queries. + +105 +00:05:16,650 --> 00:05:19,419 +And we're thrilled to announce +container queries + +106 +00:05:19,419 --> 00:05:22,589 +will ship in Safari 16! + +107 +00:05:22,589 --> 00:05:24,758 +You'll be able to use +both size queries + +108 +00:05:24,758 --> 00:05:27,260 +and container query units. + +109 +00:05:27,260 --> 00:05:30,197 +Here, I'm experimenting +with an alternative layout + +110 +00:05:30,197 --> 00:05:32,499 +for the clothing swap website. + +111 +00:05:32,499 --> 00:05:35,235 +I'm making the card that +presents a piece of clothing + +112 +00:05:35,235 --> 00:05:38,672 +into a reusable component +and dropping that component + +113 +00:05:38,672 --> 00:05:42,175 +into several different +places in the page layout. + +114 +00:05:42,175 --> 00:05:43,610 +Here in the sidebar, + +115 +00:05:43,610 --> 00:05:46,580 +the available space +is a bit narrow, + +116 +00:05:46,580 --> 00:05:49,149 +so I want all the content +inside my component + +117 +00:05:49,149 --> 00:05:51,451 +to stack vertically. + +118 +00:05:51,451 --> 00:05:53,086 +In the main grid of items, + +119 +00:05:53,086 --> 00:05:56,189 +I want to feature the first one +as a hero graphic + +120 +00:05:56,189 --> 00:05:59,659 +that should take up all +the available space horizontally + +121 +00:05:59,659 --> 00:06:02,896 +and arrange the content in a +fashion that makes more sense + +122 +00:06:02,896 --> 00:06:05,232 +for a wide layout. + +123 +00:06:05,232 --> 00:06:07,968 +The rest of the items +in the main content area + +124 +00:06:07,968 --> 00:06:10,870 +should be divided +into smaller columns. + +125 +00:06:10,870 --> 00:06:12,872 +So I've created another layout + +126 +00:06:12,872 --> 00:06:17,110 +that works when there's a medium +amount of horizontal space. + +127 +00:06:17,110 --> 00:06:20,747 +Using container queries to +handle the change in the layout, + +128 +00:06:20,747 --> 00:06:22,415 +rather than media queries, + +129 +00:06:22,415 --> 00:06:26,419 +I can write the layout code +for this component just once + +130 +00:06:26,419 --> 00:06:29,522 +and use that component +any place on my site + +131 +00:06:29,522 --> 00:06:31,858 +in a container of any size, + +132 +00:06:31,858 --> 00:06:35,395 +and the correct layout +will always get applied. + +133 +00:06:35,395 --> 00:06:38,798 +I specified which element +to use for the container + +134 +00:06:38,798 --> 00:06:41,935 +and whether I want to measure +against just the inline size + +135 +00:06:41,935 --> 00:06:45,805 +or both inline and block size +at the same time, + +136 +00:06:45,805 --> 00:06:48,908 +by using +the container-type property. + +137 +00:06:48,908 --> 00:06:51,444 +I can optionally +name my container + +138 +00:06:51,444 --> 00:06:53,613 +using +the container-name property, + +139 +00:06:53,613 --> 00:06:58,518 +which gives me more flexibility +in how I structure the HTML. + +140 +00:06:58,518 --> 00:07:03,089 +Then I use the @container rule +to apply styles conditionally, + +141 +00:07:03,089 --> 00:07:05,358 +based on the size +of the container. + +142 +00:07:05,358 --> 00:07:08,094 +Here, if the clothing card +component is in a container + +143 +00:07:08,094 --> 00:07:10,897 +that's wider than 250 pixels, + +144 +00:07:10,897 --> 00:07:14,968 +the grid will change to have +two columns instead of one. + +145 +00:07:14,968 --> 00:07:18,738 +Next up with CSS architecture: +cascade layers. + +146 +00:07:18,738 --> 00:07:22,809 +This is a powerful change +to the CSS cascade. + +147 +00:07:22,809 --> 00:07:24,911 +Since the beginning of CSS, + +148 +00:07:24,911 --> 00:07:28,348 +the cascade has been made up +of these different layers. + +149 +00:07:28,348 --> 00:07:32,052 +But no matter what specificity +of any given selector + +150 +00:07:32,052 --> 00:07:34,621 +inside each layer, +author styles -- + +151 +00:07:34,621 --> 00:07:37,023 +the styles you write +as web developers -- + +152 +00:07:37,023 --> 00:07:39,859 +always beats UA styles. + +153 +00:07:39,859 --> 00:07:43,596 +Inline styles are always more +powerful than author styles, + +154 +00:07:43,596 --> 00:07:46,599 +and so on +with the rest of the hierarchy. + +155 +00:07:46,599 --> 00:07:49,369 +Cascade layers takes +this same concept + +156 +00:07:49,369 --> 00:07:52,339 +and allows you to create +your own custom layers + +157 +00:07:52,339 --> 00:07:57,277 +where specificity is calculated +independently inside each layer. + +158 +00:07:57,277 --> 00:08:00,380 +One entire layer +beats another entire layer, + +159 +00:08:00,380 --> 00:08:04,984 +no matter what the specificity +is of the selectors being used. + +160 +00:08:04,984 --> 00:08:08,388 +And you determine which layer +has power over the others + +161 +00:08:08,388 --> 00:08:13,293 +through the order of how you +define the layers in your CSS. + +162 +00:08:13,293 --> 00:08:15,395 +Cascade layers +will be a useful tool + +163 +00:08:15,395 --> 00:08:18,298 +for architecting CSS +on large projects + +164 +00:08:18,298 --> 00:08:20,767 +and maintaining +that code over time. + +165 +00:08:20,767 --> 00:08:22,202 +Perhaps your team will use them + +166 +00:08:22,202 --> 00:08:24,938 +to separate a design system +from overrides + +167 +00:08:24,938 --> 00:08:28,775 +or a framework you're using for +custom styles for your project. + +168 +00:08:28,775 --> 00:08:30,910 +It's totally up to you! + +169 +00:08:30,910 --> 00:08:33,780 +And, to round out all +the amazing new enhancements + +170 +00:08:33,780 --> 00:08:36,916 +for your CSS architecture +is :has(), + +171 +00:08:36,916 --> 00:08:40,920 +a pseudo-class that can act as +the long-wanted parent selector + +172 +00:08:40,920 --> 00:08:42,689 +and much more. + +173 +00:08:42,689 --> 00:08:45,625 +Combined with +any other selector in CSS, + +174 +00:08:45,625 --> 00:08:48,628 +:has() can look for siblings, +attributes, + +175 +00:08:48,628 --> 00:08:51,131 +states of form fields, +and much more. + +176 +00:08:51,131 --> 00:08:54,033 +It's really powerful. + +177 +00:08:54,033 --> 00:08:56,970 +Here, I want to highlight +the entire message box + +178 +00:08:56,970 --> 00:08:59,706 +whenever someone has checked +the "Urgent?" checkbox + +179 +00:08:59,706 --> 00:09:01,608 +for one of their messages. + +180 +00:09:01,608 --> 00:09:05,145 +I can use the :has pseudo-class +here to say that anytime + +181 +00:09:05,145 --> 00:09:08,715 +the form element has an input +of type checkbox + +182 +00:09:08,715 --> 00:09:12,719 +with that checkbox checked, +apply this CSS. + +183 +00:09:12,719 --> 00:09:16,890 +And I don't even need +to use any JavaScript. + +184 +00:09:16,890 --> 00:09:19,359 +We hope all these +great improvements + +185 +00:09:19,359 --> 00:09:21,995 +to handling +your CSS architecture, + +186 +00:09:21,995 --> 00:09:25,999 +with :has(), cascade layers, +and container queries, + +187 +00:09:25,999 --> 00:09:28,134 +make your work +as a web developer + +188 +00:09:28,134 --> 00:09:30,470 +that much better. + +189 +00:09:30,470 --> 00:09:33,239 +But these aren't +the only CSS additions + +190 +00:09:33,239 --> 00:09:35,975 +that we're excited about. + +191 +00:09:35,975 --> 00:09:39,179 +You've wanted a tool similar +to existing viewport units + +192 +00:09:39,179 --> 00:09:41,915 +but would be more useful +on devices where scrolling + +193 +00:09:41,915 --> 00:09:44,617 +causes the size of the viewport +to change. + +194 +00:09:44,617 --> 00:09:48,588 +And for that, there's new +viewport units for y'all. + +195 +00:09:48,588 --> 00:09:50,457 +When you want to know +the height of the viewport + +196 +00:09:50,457 --> 00:09:53,526 +when it's at its smallest, +use svh. + +197 +00:09:53,526 --> 00:09:54,828 +For the height of the viewport + +198 +00:09:54,828 --> 00:09:57,430 +when it's at its largest, +use lvh. + +199 +00:09:57,430 --> 00:10:00,867 +Just remember: s for small, +l for large. + +200 +00:10:00,867 --> 00:10:03,636 +For a dynamic number +that changes to always match + +201 +00:10:03,636 --> 00:10:07,740 +the current actual height +of the viewport, use dvh. + +202 +00:10:07,740 --> 00:10:10,210 +And it's not just for height. + +203 +00:10:10,210 --> 00:10:14,581 +We've got you covered +with even more viewport units. + +204 +00:10:14,581 --> 00:10:17,617 +There's width units, +which are good for completeness + +205 +00:10:17,617 --> 00:10:20,687 +to match up with +the highly used height units. + +206 +00:10:20,687 --> 00:10:24,090 +We've got block and inline -- +both being useful + +207 +00:10:24,090 --> 00:10:25,792 +when writing +for multiple languages + +208 +00:10:25,792 --> 00:10:28,728 +with differing ways +in which text can flow. + +209 +00:10:28,728 --> 00:10:32,332 +And we didn't forget, +min and max, too. + +210 +00:10:32,332 --> 00:10:35,735 +But what about when you want +to create some movement + +211 +00:10:35,735 --> 00:10:39,038 +on your page, +not just react to it? + +212 +00:10:39,038 --> 00:10:42,509 +Animation has previously +been very declarative + +213 +00:10:42,509 --> 00:10:46,079 +where you can specify a start, +an end, and a duration + +214 +00:10:46,079 --> 00:10:48,014 +to get objects moving. + +215 +00:10:48,014 --> 00:10:51,784 +But it's been a challenge +to animate elements on a page + +216 +00:10:51,784 --> 00:10:54,654 +either when trying to get it +to follow a curved path + +217 +00:10:54,654 --> 00:10:59,425 +or even just being able +to move it around by an offset. + +218 +00:10:59,425 --> 00:11:02,161 +And I'd like to add +a secret animation + +219 +00:11:02,161 --> 00:11:04,531 +for the header +when you click on it, + +220 +00:11:04,531 --> 00:11:07,800 +Really just thought it'd be fun. + +221 +00:11:07,800 --> 00:11:10,637 +With the new offset-path, +you can define a path + +222 +00:11:10,637 --> 00:11:13,573 +that you want your object +to animate along. + +223 +00:11:13,573 --> 00:11:17,677 +Set the path with offset-path +and use offset-distance + +224 +00:11:17,677 --> 00:11:19,946 +for the keyframe effect. + +225 +00:11:19,946 --> 00:11:24,217 +Then use the animation property +to apply the keyframe effect, + +226 +00:11:24,217 --> 00:11:27,854 +giving you all the control +you'd want with your animations, + +227 +00:11:27,854 --> 00:11:30,189 +all in CSS. + +228 +00:11:30,189 --> 00:11:33,660 +We also want to give you +more control over your page + +229 +00:11:33,660 --> 00:11:36,429 +even with the parts of the web +that have typically been + +230 +00:11:36,429 --> 00:11:39,132 +defined by +the browser engine itself, + +231 +00:11:39,132 --> 00:11:43,469 +and over scroll-behavior is just +our first example of this. + +232 +00:11:43,469 --> 00:11:46,472 +Since the beginning of the web, +if you click on a link + +233 +00:11:46,472 --> 00:11:49,108 +that moves you to another part +of a web page, + +234 +00:11:49,108 --> 00:11:51,811 +it visually appears as a jump. + +235 +00:11:51,811 --> 00:11:54,981 +Sometimes this is disorienting +to your users. + +236 +00:11:54,981 --> 00:11:58,084 +The scroll-behavior property +in CSS provides a way + +237 +00:11:58,084 --> 00:12:01,788 +to specify if you want +this behavior or not. + +238 +00:12:01,788 --> 00:12:06,759 +By default, it's set to auto, +and it'll appear as that jump. + +239 +00:12:06,759 --> 00:12:09,629 +By specifying scroll-behavior +as smooth, + +240 +00:12:09,629 --> 00:12:12,699 +you can ask the browser +to instead scroll smoothly + +241 +00:12:12,699 --> 00:12:14,901 +to the next place on the page. + +242 +00:12:14,901 --> 00:12:17,604 +You can also do this +with the JavaScript methods + +243 +00:12:17,604 --> 00:12:21,941 +window.scroll(), scrollTo(), +or scrollBy(). + +244 +00:12:21,941 --> 00:12:24,043 +You know your customers best + +245 +00:12:24,043 --> 00:12:27,246 +and should be able to define +your own web page experience + +246 +00:12:27,246 --> 00:12:30,416 +outside of the browser +engine defaults, + +247 +00:12:30,416 --> 00:12:33,653 +which is also where +the use of :focus-visible + +248 +00:12:33,653 --> 00:12:37,156 +as well as accent-color +can come into play. + +249 +00:12:37,156 --> 00:12:39,859 +You're probably familiar +with the focus selector + +250 +00:12:39,859 --> 00:12:42,495 +if you've ever wanted +to apply a specific style + +251 +00:12:42,495 --> 00:12:44,364 +to the focus indicator, + +252 +00:12:44,364 --> 00:12:47,934 +likely to have it more in line +with your overall design. + +253 +00:12:47,934 --> 00:12:50,570 +But there are some +accessibility pitfalls + +254 +00:12:50,570 --> 00:12:55,575 +of losing the browser-based +heuristic when you do that. + +255 +00:12:55,575 --> 00:12:59,045 +And on my site, instead +of the built-in form colors, + +256 +00:12:59,045 --> 00:13:02,348 +I'd love to use a custom color. + +257 +00:13:02,348 --> 00:13:05,284 +Let's use the teal color +that's already in my header + +258 +00:13:05,284 --> 00:13:09,155 +for both the focus highlight +and the checkbox. + +259 +00:13:09,155 --> 00:13:11,457 +With the :focus-visible +pseudo-class, + +260 +00:13:11,457 --> 00:13:14,527 +you can style the focus +indicator how you choose + +261 +00:13:14,527 --> 00:13:18,097 +while also having that +stylized indicator only show + +262 +00:13:18,097 --> 00:13:21,801 +if it would be shown natively +by the browser. + +263 +00:13:21,801 --> 00:13:25,171 +And to add another layer of +customization to your forms, + +264 +00:13:25,171 --> 00:13:27,707 +you can use accent-color +to change the color + +265 +00:13:27,707 --> 00:13:30,843 +of different parts +of the form control UI. + +266 +00:13:30,843 --> 00:13:32,912 +It'll take affect +on that checkbox + +267 +00:13:32,912 --> 00:13:35,982 +as well as radio buttons, +and so much more. + +268 +00:13:35,982 --> 00:13:38,017 +Also with CSS, + +269 +00:13:38,017 --> 00:13:42,255 +we've been replacing more +and more of the WebKit prefixes. + +270 +00:13:42,255 --> 00:13:44,123 +These used to be the perfect way + +271 +00:13:44,123 --> 00:13:47,193 +to try out experimental +features, but now, + +272 +00:13:47,193 --> 00:13:50,296 +we're able to move towards their +standards-defined properties + +273 +00:13:50,296 --> 00:13:55,201 +to make your CSS easier to write +and more interoperable. + +274 +00:13:55,201 --> 00:13:59,939 +But don't worry, your existing +CSS with WebKit prefixes + +275 +00:13:59,939 --> 00:14:02,008 +will keep working +as you transition + +276 +00:14:02,008 --> 00:14:04,577 +to their web standards +counterparts. + +277 +00:14:04,577 --> 00:14:08,681 +Backface-visibility, +print-color-adjust, + +278 +00:14:08,681 --> 00:14:11,918 +and text-align: match-parent +are all exactly the same + +279 +00:14:11,918 --> 00:14:14,520 +as their prefixed counterpart. + +280 +00:14:14,520 --> 00:14:17,557 +Both mask +and text-combine-upright + +281 +00:14:17,557 --> 00:14:20,827 +have had their syntax updated +from the prefixed version + +282 +00:14:20,827 --> 00:14:23,162 +to match the standard. + +283 +00:14:23,162 --> 00:14:26,632 +And the nonprefixed appearance +property also adds support + +284 +00:14:26,632 --> 00:14:29,302 +for the new auto value +but has removed + +285 +00:14:29,302 --> 00:14:32,305 +the WebKit-specific values +in Safari 16, + +286 +00:14:32,305 --> 00:14:34,540 +like caret or listitem, + +287 +00:14:34,540 --> 00:14:38,344 +as it got brought up +to standards specifications. + +288 +00:14:38,344 --> 00:14:39,612 +There's been a lot to note + +289 +00:14:39,612 --> 00:14:42,248 +about our typography additions +as well. + +290 +00:14:42,248 --> 00:14:45,418 +In particular, we've added +the font-palette property + +291 +00:14:45,418 --> 00:14:48,154 +that allows for easy selection +of a color palette + +292 +00:14:48,154 --> 00:14:50,256 +within a color font. + +293 +00:14:50,256 --> 00:14:52,191 +It's something that I think +would be really cool + +294 +00:14:52,191 --> 00:14:55,795 +to try out with some +potential logos for my site. + +295 +00:14:55,795 --> 00:14:57,263 +We can test out how it looks + +296 +00:14:57,263 --> 00:15:00,133 +with the built-in +dark or light palettes + +297 +00:15:00,133 --> 00:15:02,368 +or even what it'd be like +to customize it + +298 +00:15:02,368 --> 00:15:05,404 +to exactly what I want +with color overrides + +299 +00:15:05,404 --> 00:15:08,908 +and get some yellow in there +to brighten it up. + +300 +00:15:08,908 --> 00:15:11,778 +And with typography, +there's been the addition of + +301 +00:15:11,778 --> 00:15:15,281 +text-decoration-skip-ink, +which allows you to control + +302 +00:15:15,281 --> 00:15:18,050 +what happens when +an underline or overline + +303 +00:15:18,050 --> 00:15:21,220 +intersects +with a letter or character. + +304 +00:15:21,220 --> 00:15:25,491 +Plus the ic unit, which makes it +possible to precisely line up + +305 +00:15:25,491 --> 00:15:28,828 +CJK characters +in the block direction. + +306 +00:15:28,828 --> 00:15:31,764 +It's useful for creating +a clean typography grid + +307 +00:15:31,764 --> 00:15:36,569 +in languages like Chinese, +Japanese, and Korean. + +308 +00:15:36,569 --> 00:15:40,740 +To wrap up our discussion +of all these great CSS features, + +309 +00:15:40,740 --> 00:15:43,776 +we've definitely +got to talk about subgrid. + +310 +00:15:43,776 --> 00:15:47,413 +For years, layout on the web +was pretty hard. + +311 +00:15:47,413 --> 00:15:50,216 +CSS Grid has been revolutionary, + +312 +00:15:50,216 --> 00:15:55,454 +but it only affects the direct +children of a grid container. + +313 +00:15:55,454 --> 00:15:59,525 +Here, I'm using CSS Grid +to layout these cards, + +314 +00:15:59,525 --> 00:16:01,527 +and to automatically +adjust the layout + +315 +00:16:01,527 --> 00:16:04,964 +to fit the viewport width +by adding and removing columns + +316 +00:16:04,964 --> 00:16:08,067 +without any media queries. + +317 +00:16:08,067 --> 00:16:12,438 +But the size of the content +on each card isn't the same; + +318 +00:16:12,438 --> 00:16:14,540 +some headlines are longer, + +319 +00:16:14,540 --> 00:16:17,243 +the photos have +different aspect ratios, + +320 +00:16:17,243 --> 00:16:21,047 +and that's causing the visuals +to look really messy. + +321 +00:16:21,047 --> 00:16:23,683 +I'd like for all +the Request Item buttons + +322 +00:16:23,683 --> 00:16:27,453 +and the message boxes +to line up across the page, + +323 +00:16:27,453 --> 00:16:31,424 +and I'd like a longer title +on one card to affect the layout + +324 +00:16:31,424 --> 00:16:35,695 +on the other cards, so they all +get the same spacing. + +325 +00:16:35,695 --> 00:16:39,131 +Now, we can accomplish +this using subgrid. + +326 +00:16:39,131 --> 00:16:41,400 +I've put a grid +on each article, + +327 +00:16:41,400 --> 00:16:44,904 +and I've tied all of those grids +to the grid of their parent + +328 +00:16:44,904 --> 00:16:49,842 +simply by writing +"grid-template-rows: subgrid." + +329 +00:16:49,842 --> 00:16:53,379 +You can see how all the content +on each clothing card now + +330 +00:16:53,379 --> 00:16:57,183 +perfectly lines up by using the +Grid Inspector in Web Inspector + +331 +00:16:57,183 --> 00:17:01,621 +where I can turn on all the +grids I could possibly need too. + +332 +00:17:01,621 --> 00:17:06,726 +A lot of CSS work becomes easier +when we use the Web Inspector. + +333 +00:17:06,726 --> 00:17:10,162 +In fact, there's been some +amazing additions + +334 +00:17:10,162 --> 00:17:11,497 +to the Web Inspector + +335 +00:17:11,497 --> 00:17:14,967 +that I think you'll be +really excited to try out. + +336 +00:17:14,967 --> 00:17:17,270 +First off, +layout is easier to write + +337 +00:17:17,270 --> 00:17:19,405 +when you can see +what's going on, + +338 +00:17:19,405 --> 00:17:23,743 +which is exactly what makes +the Web Inspector so important. + +339 +00:17:23,743 --> 00:17:26,145 +And with the new +Flexbox Inspector, + +340 +00:17:26,145 --> 00:17:30,316 +you can actually visualize +the spacing between elements. + +341 +00:17:30,316 --> 00:17:32,818 +Here on my website, +I was having some trouble + +342 +00:17:32,818 --> 00:17:35,588 +adding these icons to my header. + +343 +00:17:35,588 --> 00:17:39,625 +All I need to do +is inspect the element, + +344 +00:17:39,625 --> 00:17:42,161 +and go to the Layout tab, + +345 +00:17:42,161 --> 00:17:44,931 +and since I'm not concerned +with my grids right now, + +346 +00:17:44,931 --> 00:17:47,533 +I can go ahead +and collapse that section + +347 +00:17:47,533 --> 00:17:51,203 +to get right to the new +Flexbox Inspector. + +348 +00:17:51,203 --> 00:17:53,239 +I can even turn on +all the views + +349 +00:17:53,239 --> 00:17:57,910 +with just a single click and +still have smooth performance. + +350 +00:17:57,910 --> 00:18:00,146 +And with all the views +turned on, + +351 +00:18:00,146 --> 00:18:01,914 +I can clearly see +with the hash marks + +352 +00:18:01,914 --> 00:18:05,418 +and container boxes how +my elements are being arranged + +353 +00:18:05,418 --> 00:18:09,588 +and how the empty space +is taking up the view. + +354 +00:18:09,588 --> 00:18:13,392 +So now I want to make sure +I'm getting my alignment right, + +355 +00:18:13,392 --> 00:18:16,162 +which I can use +the new alignment editor for. + +356 +00:18:16,162 --> 00:18:18,064 +I can go to the Styles tab + +357 +00:18:18,064 --> 00:18:21,133 +to find a new button +next to align-items. + +358 +00:18:21,133 --> 00:18:24,136 +Here, I'm able to toggle +through the different options + +359 +00:18:24,136 --> 00:18:26,906 +to find what works best +for my header, + +360 +00:18:26,906 --> 00:18:30,676 +and I can do the same +with justify-content as well. + +361 +00:18:30,676 --> 00:18:33,879 +Again, just toggling through +each of the options + +362 +00:18:33,879 --> 00:18:39,251 +and then landing on the one +that I think looks just right. + +363 +00:18:39,251 --> 00:18:43,089 +I also think the yellow icons +are a bit too small, + +364 +00:18:43,089 --> 00:18:47,059 +and I want to try making them +the same size as the red icons, + +365 +00:18:47,059 --> 00:18:49,395 +which I believe +are using a variable + +366 +00:18:49,395 --> 00:18:50,930 +with "medium" in the name, + +367 +00:18:50,930 --> 00:18:54,767 +but I really can't remember +the full name. + +368 +00:18:54,767 --> 00:18:56,802 +I can try out +changing the size + +369 +00:18:56,802 --> 00:18:58,804 +by inspecting +one of the yellow icons + +370 +00:18:58,804 --> 00:19:02,008 +and editing its height +in the inspector. + +371 +00:19:02,008 --> 00:19:05,311 +And, thanks to our new +CSS fuzzy autocompletion, + +372 +00:19:05,311 --> 00:19:10,850 +I can go ahead and type "medium" +and the variable I want pops up + +373 +00:19:10,850 --> 00:19:14,353 +even though "medium" +is at the end of the name. + +374 +00:19:14,353 --> 00:19:16,255 +And those yellow icons + +375 +00:19:16,255 --> 00:19:20,092 +definitely aren't too small +anymore. + +376 +00:19:20,092 --> 00:19:23,029 +And when those other variables +for the different icons + +377 +00:19:23,029 --> 00:19:26,232 +aren't being used +for the element I'm inspecting, + +378 +00:19:26,232 --> 00:19:29,368 +they get hidden away +with our new CSS tooling. + +379 +00:19:29,368 --> 00:19:32,338 +But don't worry, +there's a button to reveal them + +380 +00:19:32,338 --> 00:19:34,306 +when you need them. + +381 +00:19:34,306 --> 00:19:38,144 +And probably most excitingly +for Web Inspector this year, + +382 +00:19:38,144 --> 00:19:40,513 +we are happy +to announce support + +383 +00:19:40,513 --> 00:19:44,517 +for developer tool extensions +for the Safari Web Inspector. + +384 +00:19:44,517 --> 00:19:47,686 +The creators of your favorite +developer tools extensions + +385 +00:19:47,686 --> 00:19:50,389 +will now be able +to port them to Safari, + +386 +00:19:50,389 --> 00:19:55,327 +using the same underlying APIs +that they use in other browsers. + +387 +00:19:55,327 --> 00:19:58,531 +If you're interested in learning +how to make an extension + +388 +00:19:58,531 --> 00:20:00,366 +for the Web Inspector, + +389 +00:20:00,366 --> 00:20:03,636 +exploring the new APIs, +and getting set up + +390 +00:20:03,636 --> 00:20:06,705 +to start using them yourself, +make sure to watch + +391 +00:20:06,705 --> 00:20:12,411 +"Create Safari Web Inspector +Extensions" at this year's WWDC. + +392 +00:20:12,411 --> 00:20:15,081 +Now we've covered +a lot of what's new + +393 +00:20:15,081 --> 00:20:18,484 +with our front-end technologies, +so let's switch gears + +394 +00:20:18,484 --> 00:20:22,254 +and get into +what's new with our web APIs. + +395 +00:20:22,254 --> 00:20:26,826 +Which we are so excited to +announce support for web push. + +396 +00:20:26,826 --> 00:20:31,363 +It'll be available in Safari 16 +on macOS Ventura. + +397 +00:20:31,363 --> 00:20:35,768 +It's coming to iOS +and iPadOS next year. + +398 +00:20:35,768 --> 00:20:39,405 +Web push lets you remotely +send notifications to your users + +399 +00:20:39,405 --> 00:20:41,774 +from your website or web app. + +400 +00:20:41,774 --> 00:20:46,512 +This is a fully interoperable, +standards-based implementation. + +401 +00:20:46,512 --> 00:20:48,881 +If you've implemented +web push already + +402 +00:20:48,881 --> 00:20:50,916 +and it works in other browsers, + +403 +00:20:50,916 --> 00:20:55,254 +it should just work in Safari +without any modifications. + +404 +00:20:55,254 --> 00:20:58,824 +And you don't need an +Apple Developer account either. + +405 +00:20:58,824 --> 00:21:00,793 +To learn all about the details, + +406 +00:21:00,793 --> 00:21:06,432 +watch "Meet Web Push for Safari" +here at WWDC22. + +407 +00:21:06,432 --> 00:21:08,567 +If you're excited +about web push, + +408 +00:21:08,567 --> 00:21:10,202 +then you'll probably +be excited about + +409 +00:21:10,202 --> 00:21:13,272 +new web app manifest +improvements too. + +410 +00:21:13,272 --> 00:21:16,809 +Now, you can define the icon +that's used when people save + +411 +00:21:16,809 --> 00:21:20,246 +your web app to the Home Screen +in your manifest file. + +412 +00:21:20,246 --> 00:21:23,315 +To have the icons +in the manifest take precedence, + +413 +00:21:23,315 --> 00:21:24,917 +you'll need to ensure +that there is no + +414 +00:21:24,917 --> 00:21:28,921 +apple-touch-icon +defined in the HTML head. + +415 +00:21:28,921 --> 00:21:32,825 +If you want to deliver +one icon to iOS and iPadOS, + +416 +00:21:32,825 --> 00:21:36,462 +while delivering a different +icon to other mobile platforms, + +417 +00:21:36,462 --> 00:21:39,965 +you can still do so by defining +the icon for Apple devices + +418 +00:21:39,965 --> 00:21:44,036 +in that HTML head +using the apple-touch-icon. + +419 +00:21:44,036 --> 00:21:47,173 +And if you don't declare +an icon in either place, + +420 +00:21:47,173 --> 00:21:50,042 +then when a user saves +your site to the Home Screen, + +421 +00:21:50,042 --> 00:21:52,545 +they'll simply get +a screenshot of your site. + +422 +00:21:52,545 --> 00:21:56,282 +Excitingly, we also no longer +wait for the user to choose + +423 +00:21:56,282 --> 00:21:58,684 +"Add to Home Screen" +from the Share menu + +424 +00:21:58,684 --> 00:22:01,887 +to load the manifest file, +which means you can use + +425 +00:22:01,887 --> 00:22:05,324 +that manifest file to define +characteristics of your web page + +426 +00:22:05,324 --> 00:22:08,427 +on all your sites +and even further reduce + +427 +00:22:08,427 --> 00:22:11,630 +the need to use meta tags. + +428 +00:22:11,630 --> 00:22:15,234 +Continuing with our APIs, +we've done a lot to improve + +429 +00:22:15,234 --> 00:22:18,170 +the use of web pages +in multiple browsing contexts + +430 +00:22:18,170 --> 00:22:20,005 +with the same origins. + +431 +00:22:20,005 --> 00:22:22,841 +Broadcast channels allow you +to send notifications + +432 +00:22:22,841 --> 00:22:25,811 +between those different +browsing contexts. + +433 +00:22:25,811 --> 00:22:29,248 +Let's imagine someone is using +the clothing swap website + +434 +00:22:29,248 --> 00:22:32,618 +and they have it open in +two windows at the same time. + +435 +00:22:32,618 --> 00:22:35,921 +Then they claim a piece +of clothing in one window. + +436 +00:22:35,921 --> 00:22:39,625 +We'll be able to post a message +and sync that unavailable state + +437 +00:22:39,625 --> 00:22:43,395 +to any other +open tabs or windows. + +438 +00:22:43,395 --> 00:22:47,132 +But maybe it's not updating +a tab in the background, + +439 +00:22:47,132 --> 00:22:49,802 +but updating a file +saved for your site. + +440 +00:22:49,802 --> 00:22:51,604 +For that, +there's been the addition + +441 +00:22:51,604 --> 00:22:54,673 +of the File System Access API. + +442 +00:22:54,673 --> 00:22:57,176 +We've had incremental updates +to this API + +443 +00:22:57,176 --> 00:22:59,778 +across multiple +releases this year, + +444 +00:22:59,778 --> 00:23:02,982 +starting with origin +private file system, + +445 +00:23:02,982 --> 00:23:05,784 +which is private storage +based on origin. + +446 +00:23:05,784 --> 00:23:06,852 +So for instance, + +447 +00:23:06,852 --> 00:23:09,855 +my clothing swap site +wouldn't have other sites, + +448 +00:23:09,855 --> 00:23:13,692 +like apple.com, +reading its files. + +449 +00:23:13,692 --> 00:23:17,162 +We then added to the API +with the getFile() method + +450 +00:23:17,162 --> 00:23:21,066 +of FileSystemFileHandle, +which reads an existing file + +451 +00:23:21,066 --> 00:23:24,069 +retrieved from your site's +root directory, + +452 +00:23:24,069 --> 00:23:26,538 +like we're doing +with a draft file here + +453 +00:23:26,538 --> 00:23:29,908 +that we also happened +to have just created. + +454 +00:23:29,908 --> 00:23:33,145 +Now let's take a look +at our most vibrant + +455 +00:23:33,145 --> 00:23:37,716 +API addition this year +with some new color richness. + +456 +00:23:37,716 --> 00:23:40,853 +The Display P3 color space +makes it possible + +457 +00:23:40,853 --> 00:23:45,157 +to represent colors +that just don't exist in RGB. + +458 +00:23:45,157 --> 00:23:48,294 +Here, we've got some examples +of the color picker. + +459 +00:23:48,294 --> 00:23:50,462 +On the left +of the squiggly white line + +460 +00:23:50,462 --> 00:23:53,265 +is color that exists in RGB. + +461 +00:23:53,265 --> 00:23:58,237 +And on the right of the line +are colors only available in P3. + +462 +00:23:58,237 --> 00:24:03,442 +In 2016, we added P3 support +for videos and photos. + +463 +00:24:03,442 --> 00:24:06,945 +Last year, we were thrilled to +be the first browser engine to + +464 +00:24:06,945 --> 00:24:12,351 +implement the new color syntax +defined in CSS Color Level 4. + +465 +00:24:12,351 --> 00:24:15,487 +This year, +we've added support for P3 color + +466 +00:24:15,487 --> 00:24:18,257 +for content inside +the canvas element. + +467 +00:24:18,257 --> 00:24:21,460 +So, no need to use +the colors based on devices + +468 +00:24:21,460 --> 00:24:24,563 +all the way from the 90s, +when you can now start utilizing + +469 +00:24:24,563 --> 00:24:28,167 +the full color capabilities +of all the amazing devices + +470 +00:24:28,167 --> 00:24:29,868 +of today. + +471 +00:24:29,868 --> 00:24:33,739 +But there's even more to +check out with our new Web APIs + +472 +00:24:33,739 --> 00:24:38,877 +from this past year, including +shadow realms, web locks, + +473 +00:24:38,877 --> 00:24:42,915 +and updated support +to the ResizeObserver API + +474 +00:24:42,915 --> 00:24:45,951 +for the ResizeObserverSize +interface, + +475 +00:24:45,951 --> 00:24:47,286 +which will help you observe + +476 +00:24:47,286 --> 00:24:50,356 +changes to an element's +box-sizing properties. + +477 +00:24:50,356 --> 00:24:52,558 +There's so much to try out + +478 +00:24:52,558 --> 00:24:55,260 +across all of our +new API additions, + +479 +00:24:55,260 --> 00:24:58,697 +and of course, with all of our +new features too. + +480 +00:24:58,697 --> 00:25:02,000 +In fact, we've still +got more to cover. + +481 +00:25:02,000 --> 00:25:05,037 +So let's next get into +all that's new + +482 +00:25:05,037 --> 00:25:08,140 +in JavaScript & WebAssembly. + +483 +00:25:08,140 --> 00:25:11,143 +If your website uses workers, +and you want instances + +484 +00:25:11,143 --> 00:25:14,680 +of these workers to be shared +across tabs and windows, + +485 +00:25:14,680 --> 00:25:17,683 +then the new shared workers +interface will definitely help + +486 +00:25:17,683 --> 00:25:20,886 +and potentially +reduce memory usage. + +487 +00:25:20,886 --> 00:25:24,490 +Instead of spawning new workers +for every task that you want + +488 +00:25:24,490 --> 00:25:27,559 +to happen in the background, +you can have just one worker + +489 +00:25:27,559 --> 00:25:31,497 +in use for each browsing context +with the same origin. + +490 +00:25:31,497 --> 00:25:35,200 +Each script would create +a shared worker in the same way, + +491 +00:25:35,200 --> 00:25:37,603 +and then they can receive +and post messages + +492 +00:25:37,603 --> 00:25:40,139 +using the same port. + +493 +00:25:40,139 --> 00:25:42,441 +The shared worker +would be able to receive + +494 +00:25:42,441 --> 00:25:44,376 +and respond to messages + +495 +00:25:44,376 --> 00:25:47,646 +sent from all +of the different scripts. + +496 +00:25:47,646 --> 00:25:50,749 +This will result in less +demand on your servers, + +497 +00:25:50,749 --> 00:25:52,284 +while also making your webpage + +498 +00:25:52,284 --> 00:25:56,221 +fast and responsive +for your customers. + +499 +00:25:56,221 --> 00:26:00,359 +We've also got a whole array +of array features to show you. + +500 +00:26:00,359 --> 00:26:03,762 +Instead of having to mutate +an array using reverse() + +501 +00:26:03,762 --> 00:26:05,898 +when you want to search +from the end, + +502 +00:26:05,898 --> 00:26:10,369 +you now can use the findLast() +and findLastIndex() methods, + +503 +00:26:10,369 --> 00:26:13,272 +like I've done here to find +the item and index + +504 +00:26:13,272 --> 00:26:17,342 +for the last item +containing a "shoestring." + +505 +00:26:17,342 --> 00:26:19,878 +The new at() method +also makes searching + +506 +00:26:19,878 --> 00:26:22,781 +from the end +of an array even easier. + +507 +00:26:22,781 --> 00:26:26,385 +Using braces works great +when the index is positive, + +508 +00:26:26,385 --> 00:26:28,921 +but with at(), +we get the additional feature + +509 +00:26:28,921 --> 00:26:31,256 +of indexing with negative values + +510 +00:26:31,256 --> 00:26:35,260 +making your code +more concise and readable. + +511 +00:26:35,260 --> 00:26:39,498 +But even with that good number +of new array features, + +512 +00:26:39,498 --> 00:26:41,967 +nothing much can beat +the sheer number + +513 +00:26:41,967 --> 00:26:46,538 +of new internationalization +features we got for you. + +514 +00:26:46,538 --> 00:26:49,675 +WebKit has continued +to add regular updates + +515 +00:26:49,675 --> 00:26:54,146 +to our Intl implementation +throughout this past year. + +516 +00:26:54,146 --> 00:26:57,616 +There's been added support +for different numbering systems + +517 +00:26:57,616 --> 00:27:00,185 +with new methods +in NumberFormat, + +518 +00:27:00,185 --> 00:27:03,555 +calendars, thanks to updates +with Locale as well as + +519 +00:27:03,555 --> 00:27:09,628 +DisplayNames, and currency +with the Intl Enumeration API. + +520 +00:27:09,628 --> 00:27:13,098 +And like I said, +there's a lot that's been added + +521 +00:27:13,098 --> 00:27:15,834 +to our Intl implementation +this year + +522 +00:27:15,834 --> 00:27:20,038 +that you'll have no shortage +of things to try out and explore + +523 +00:27:20,038 --> 00:27:24,109 +to cater to your users +across the world. + +524 +00:27:24,109 --> 00:27:27,679 +And for all those that have +existing code in all sorts + +525 +00:27:27,679 --> 00:27:32,885 +of different coding languages, +like C, Objective C, or Swift, + +526 +00:27:32,885 --> 00:27:34,987 +that they'd like +to bring to the web, + +527 +00:27:34,987 --> 00:27:38,957 +WebAssembly gets them running +without any need to rewrite. + +528 +00:27:38,957 --> 00:27:40,626 +And with this year's +improvements, + +529 +00:27:40,626 --> 00:27:45,364 +your web apps using WebAssembly +are only getting more powerful + +530 +00:27:45,364 --> 00:27:49,201 +with the addressable memory +being expanded to 4GB, + +531 +00:27:49,201 --> 00:27:51,503 +and the performance +gains that come + +532 +00:27:51,503 --> 00:27:54,573 +with the new zero-cost +exception handling. + +533 +00:27:54,573 --> 00:27:58,010 +Overall, there's definitely +some exciting stuff + +534 +00:27:58,010 --> 00:28:01,580 +for JavaScript and WebAssembly +to try out here. + +535 +00:28:01,580 --> 00:28:05,517 +And speaking of WebAssembly, +we also have some security + +536 +00:28:05,517 --> 00:28:08,153 +and privacy enhancements +that not only + +537 +00:28:08,153 --> 00:28:11,256 +will protect the users +of the web who we develop for, + +538 +00:28:11,256 --> 00:28:16,395 +but will also bring new +potential for you as developers. + +539 +00:28:16,395 --> 00:28:19,831 +With both of the new +Cross Origin Opener Policy + +540 +00:28:19,831 --> 00:28:25,304 +and Cross Origin Embedder Policy +HTTP response headers, + +541 +00:28:25,304 --> 00:28:28,540 +your site can opt in +to process isolation, + +542 +00:28:28,540 --> 00:28:30,108 +which means your site will run + +543 +00:28:30,108 --> 00:28:33,679 +in its own dedicated +webContent process. + +544 +00:28:33,679 --> 00:28:36,848 +We know that a lot of apps +can benefit from running + +545 +00:28:36,848 --> 00:28:40,886 +on multiple threads in parallel +using WebAssembly threading, + +546 +00:28:40,886 --> 00:28:46,024 +and with these new headers, +you're able to do so securely. + +547 +00:28:46,024 --> 00:28:51,029 +Our second security enhancement +also involves HTTP headers + +548 +00:28:51,029 --> 00:28:55,500 +with our improved support for +content security policy level 3. + +549 +00:28:55,500 --> 00:28:58,270 +CSP provides enhanced +security control + +550 +00:28:58,270 --> 00:29:01,306 +over your loading content +and mitigates risk + +551 +00:29:01,306 --> 00:29:04,743 +of cross-site scripting +and other vulnerabilities. + +552 +00:29:04,743 --> 00:29:08,013 +With the level 3 updates, +the most exciting addition + +553 +00:29:08,013 --> 00:29:11,483 +has been the new strict-dynamic +source expression. + +554 +00:29:11,483 --> 00:29:15,387 +The designers of strict-dynamic +realized you can use nonces + +555 +00:29:15,387 --> 00:29:18,123 +to allow certain scripts, +then extend that trust + +556 +00:29:18,123 --> 00:29:21,059 +to scripts loaded +by the already trusted ones. + +557 +00:29:21,059 --> 00:29:24,329 +No explicit allow list needed. + +558 +00:29:24,329 --> 00:29:26,765 +Look how much simpler +the header becomes. + +559 +00:29:26,765 --> 00:29:29,301 +Going from that original +long list of domains + +560 +00:29:29,301 --> 00:29:32,537 +that could potentially end up +allowing too much. + +561 +00:29:32,537 --> 00:29:37,009 +And with that, we wrap up our +security and privacy features, + +562 +00:29:37,009 --> 00:29:39,344 +which also brings us to the end + +563 +00:29:39,344 --> 00:29:41,747 +of all that we'll get +to cover today, + +564 +00:29:41,747 --> 00:29:45,017 +but there's even more +to explore on your own. + +565 +00:29:45,017 --> 00:29:47,586 +For instance, +we've had media updates + +566 +00:29:47,586 --> 00:29:51,556 +including support for capturing +a specific Safari window + +567 +00:29:51,556 --> 00:29:57,562 +with the getUserDisplay() API, +WebRTC Perfect Negotiation, + +568 +00:29:57,562 --> 00:30:03,001 +In-band chapter tracks, +and requestVideoFrameCallback(). + +569 +00:30:03,001 --> 00:30:06,171 +As well as a lot of cool +additions for web extensions + +570 +00:30:06,171 --> 00:30:08,640 +with manifest version 3 support, + +571 +00:30:08,640 --> 00:30:12,611 +and a bunch of new +web extensions APIs. + +572 +00:30:12,611 --> 00:30:16,348 +To dive deeper into all these +features covered here today, + +573 +00:30:16,348 --> 00:30:20,218 +and to explore all the 162 +features and improvements + +574 +00:30:20,218 --> 00:30:23,755 +developed in Safari +and WebKit in the past year, + +575 +00:30:23,755 --> 00:30:26,458 +make sure to download +Safari Technology Preview + +576 +00:30:26,458 --> 00:30:29,928 +to keep up with what's +coming in the future, + +577 +00:30:29,928 --> 00:30:33,298 +explore web technology by +checking out our release notes, + +578 +00:30:33,298 --> 00:30:37,069 +blog posts, and all the great +content on webkit.org, + +579 +00:30:37,069 --> 00:30:41,640 +including extensive +documentation for Web Inspector. + +580 +00:30:41,640 --> 00:30:44,176 +And as always, +let us know what you think + +581 +00:30:44,176 --> 00:30:47,879 +and what you'd like to see next +by filing your bug reports. + +582 +00:30:47,879 --> 00:30:50,282 +If you come across +a bug in WebKit -- + +583 +00:30:50,282 --> 00:30:55,887 +something about HTML, CSS, +JavaScript, DOM APIs, + +584 +00:30:55,887 --> 00:30:58,590 +or the Web Inspector -- +make sure to send your feedback + +585 +00:30:58,590 --> 00:31:03,829 +through WebKit's bug tracking +system at bugs.webkit.org. + +586 +00:31:03,829 --> 00:31:07,365 +And for suggestions or bugs +with the Safari interface, + +587 +00:31:07,365 --> 00:31:11,169 +file issues in Apple's +Feedback Assistant. + +588 +00:31:11,169 --> 00:31:14,773 +We look forward to delivering +more of the amazing features + +589 +00:31:14,773 --> 00:31:17,042 +that make the work +of web developers like you + +590 +00:31:17,042 --> 00:31:19,678 +that much better +with all the Safari + +591 +00:31:19,678 --> 00:31:22,347 +and Safari Technology Preview +releases + +592 +00:31:22,347 --> 00:31:24,449 +to come in this next year. + +593 +00:31:24,449 --> 00:31:26,685 +Thank you for joining me today, + +594 +00:31:26,685 --> 00:31:30,188 +and I hope you have +the best time here at WWDC. + +595 +00:31:30,188 --> 00:31:32,257 +Bye now! + +596 +00:31:32,257 --> 00:31:35,560 +♪ + diff --git a/eng/2022 Session 10049 What's new in WKWebView en.srt b/eng/2022 Session 10049 What's new in WKWebView en.srt new file mode 100644 index 0000000..fdf5d4f --- /dev/null +++ b/eng/2022 Session 10049 What's new in WKWebView en.srt @@ -0,0 +1,773 @@ +1 +00:00:00,300 --> 00:00:05,339 +♪ instrumental hip hop music ♪ + +2 +00:00:05,339 --> 00:00:09,476 +♪ + +3 +00:00:09,476 --> 00:00:12,980 +Hello, and welcome to +"What's new in WKWebView" + +4 +00:00:12,980 --> 00:00:16,450 +I'm Alex Christensen, +an engineer on the WebKit team. + +5 +00:00:16,450 --> 00:00:19,653 +We've been busy, and we've added +a lot of new web technology + +6 +00:00:19,653 --> 00:00:23,223 +for you to use +in your apps this year. + +7 +00:00:23,223 --> 00:00:25,158 +Before we start, though, + +8 +00:00:25,158 --> 00:00:27,494 +let's make sure you are +using the right technology + +9 +00:00:27,494 --> 00:00:29,830 +for your application. + +10 +00:00:29,830 --> 00:00:33,467 +If you want a browser-like +experience inside your app, + +11 +00:00:33,467 --> 00:00:36,003 +and don't need +deep customization, + +12 +00:00:36,003 --> 00:00:40,974 +you'll definitely want to +use SFSafariViewController. + +13 +00:00:40,974 --> 00:00:45,712 +If your app still uses +the deprecated UIWebView, + +14 +00:00:45,712 --> 00:00:48,248 +now is the time to migrate +to the faster + +15 +00:00:48,248 --> 00:00:51,051 +and more responsive +WKWebView. + +16 +00:00:51,051 --> 00:00:56,189 +UIWebView will be removed +in a future release. + +17 +00:00:56,189 --> 00:00:58,759 +WKWebView is the API to use + +18 +00:00:58,759 --> 00:01:02,663 +to write an application +that interacts with web content. + +19 +00:01:02,663 --> 00:01:05,532 +You could be using it +for CSS-based UI + +20 +00:01:05,532 --> 00:01:09,102 +or writing some of your app +in JavaScript. + +21 +00:01:09,102 --> 00:01:11,705 +You might be interacting +with your own web content + +22 +00:01:11,705 --> 00:01:14,274 +using app-bound domains. + +23 +00:01:14,274 --> 00:01:17,978 +You may also be developing +your own browser. + +24 +00:01:17,978 --> 00:01:20,213 +Whatever your application is, + +25 +00:01:20,213 --> 00:01:22,215 +we are constantly adding +new capabilities + +26 +00:01:22,215 --> 00:01:25,786 +to allow you to make +richer web applications. + +27 +00:01:25,786 --> 00:01:28,622 +The new features available +to WKWebView this year + +28 +00:01:28,622 --> 00:01:30,624 +come in four categories: + +29 +00:01:30,624 --> 00:01:33,160 +new ways to interact +with web content, + +30 +00:01:33,160 --> 00:01:35,762 +new capabilities +for content blockers, + +31 +00:01:35,762 --> 00:01:41,601 +encrypted media, +and use of Remote Web Inspector. + +32 +00:01:41,601 --> 00:01:44,371 +First, we're going +to cover new APIs + +33 +00:01:44,371 --> 00:01:47,708 +for interacting +with web content. + +34 +00:01:47,708 --> 00:01:50,444 +There are three new ways +your app can interact + +35 +00:01:50,444 --> 00:01:55,615 +with web content in iOS 16: +using the full-screen API, + +36 +00:01:55,615 --> 00:02:01,154 +using new CSS viewport units, +and using find interactions. + +37 +00:02:01,154 --> 00:02:03,490 +Let's get started +with full screen. + +38 +00:02:03,490 --> 00:02:04,791 +For many years now, + +39 +00:02:04,791 --> 00:02:07,627 +JavaScript has been able +to make HTML elements, + +40 +00:02:07,627 --> 00:02:11,698 +like videos or canvas games, +full screen in browsers, + +41 +00:02:11,698 --> 00:02:15,469 +and now they can do +that in your apps too. + +42 +00:02:15,469 --> 00:02:19,172 +Here's what a simple example +looks like on a phone. + +43 +00:02:19,172 --> 00:02:21,708 +JavaScript can request +full screen, + +44 +00:02:21,708 --> 00:02:26,113 +then the user or JavaScript +can exit full screen. + +45 +00:02:26,113 --> 00:02:27,748 +All you need to do +in your app + +46 +00:02:27,748 --> 00:02:32,519 +is set WKPreferences +.isElementFullscreenEnabled + +47 +00:02:32,519 --> 00:02:37,024 +to true and load web content +that uses full-screen APIs + +48 +00:02:37,024 --> 00:02:39,893 +like webkitRequestFullscreen. + +49 +00:02:39,893 --> 00:02:41,595 +It works pretty well +out of the box, + +50 +00:02:41,595 --> 00:02:44,798 +but if you'd like to customize +the transitions in your app, + +51 +00:02:44,798 --> 00:02:49,036 +you can observe the value +of WKWebView.fullscreenState, + +52 +00:02:49,036 --> 00:02:52,105 +which will let your app know +when the web content + +53 +00:02:52,105 --> 00:02:55,275 +is becoming full screen +or returning. + +54 +00:02:55,275 --> 00:02:58,412 +And that's all there is to +using WebKit's full-screen APIs + +55 +00:02:58,412 --> 00:02:59,913 +in your app. + +56 +00:03:01,248 --> 00:03:04,251 +We also have new CSS units +to allow web content + +57 +00:03:04,251 --> 00:03:08,221 +to lay out according +to dynamic viewport sizes. + +58 +00:03:08,221 --> 00:03:14,861 +These new CSS units include +svh, lvh, dvh, and many others. + +59 +00:03:14,861 --> 00:03:17,064 +They allow web developers +to modify layout + +60 +00:03:17,064 --> 00:03:22,102 +based on the smallest, largest, +and dynamic viewport sizes. + +61 +00:03:22,102 --> 00:03:24,004 +Let's take a look +at what Safari does + +62 +00:03:24,004 --> 00:03:28,742 +to get an idea of how +this can be used in your app. + +63 +00:03:28,742 --> 00:03:31,311 +When you first open +a page in Safari, + +64 +00:03:31,311 --> 00:03:35,348 +you see the webpage host +and some buttons at the bottom. + +65 +00:03:35,348 --> 00:03:38,452 +As you scroll, +the viewport increases in size + +66 +00:03:38,452 --> 00:03:41,621 +when the buttons slide +out of the way. + +67 +00:03:41,621 --> 00:03:45,659 +Svh, lvh, and dvh +provide useful units + +68 +00:03:45,659 --> 00:03:49,896 +to measure these different +sizes of the viewport. + +69 +00:03:49,896 --> 00:03:53,900 +If your app changes the viewport +of your WKWebView, + +70 +00:03:53,900 --> 00:03:56,536 +then you should inform +WebKit up front + +71 +00:03:56,536 --> 00:03:59,639 +what the viewport +size ranges are. + +72 +00:03:59,639 --> 00:04:02,642 +In Swift, one function call +informs WebKit + +73 +00:04:02,642 --> 00:04:05,912 +of the maximum and minimum +edge insets + +74 +00:04:05,912 --> 00:04:08,715 +to allow this web content +to lay out correctly + +75 +00:04:08,715 --> 00:04:10,717 +in your app. + +76 +00:04:10,717 --> 00:04:13,753 +We are also introducing support +for Find interactions + +77 +00:04:13,753 --> 00:04:17,824 +in WKWebView in iOS 16. + +78 +00:04:17,824 --> 00:04:21,428 +Many WKWebView applications +load lots of text, + +79 +00:04:21,428 --> 00:04:24,331 +and users want +to search this text. + +80 +00:04:24,331 --> 00:04:27,667 +If you set WKWebView +.findInteractionEnabled + +81 +00:04:27,667 --> 00:04:28,969 +to true, + +82 +00:04:28,969 --> 00:04:32,139 +then your users will be able +to use familiar UI + +83 +00:04:32,139 --> 00:04:36,409 +and shortcuts like Command-F +to search the text + +84 +00:04:36,409 --> 00:04:38,845 +on the open page. + +85 +00:04:38,845 --> 00:04:40,947 +That one line of code +is all you need + +86 +00:04:40,947 --> 00:04:42,883 +to turn on the feature, + +87 +00:04:42,883 --> 00:04:46,286 +but you can also access +the UIFindInteraction object + +88 +00:04:46,286 --> 00:04:48,889 +through WKWebView +.findInteraction + +89 +00:04:48,889 --> 00:04:52,058 +to do things like present +and dismiss the Find panel, + +90 +00:04:52,058 --> 00:04:55,929 +or move to the next or previous +results programmatically. + +91 +00:04:55,929 --> 00:04:59,132 +Try it out and see +what it can do in your app. + +92 +00:04:59,132 --> 00:05:00,634 +For content blocking, + +93 +00:05:00,634 --> 00:05:04,738 +we added a new capability +to WKContentRuleList, + +94 +00:05:04,738 --> 00:05:08,975 +the API used to implement +content blockers in Safari. + +95 +00:05:08,975 --> 00:05:14,147 +Here, we have Wikipedia embedded +in an iframe on an example site. + +96 +00:05:14,147 --> 00:05:17,651 +Previously, you could run +regular expressions on the URL + +97 +00:05:17,651 --> 00:05:20,754 +being requested +and the top-frame URL, + +98 +00:05:20,754 --> 00:05:24,758 +to decide whether to block +a load or perform other actions. + +99 +00:05:24,758 --> 00:05:26,193 +Sometimes, though, + +100 +00:05:26,193 --> 00:05:29,029 +what you really wanted +was for a certain rule + +101 +00:05:29,029 --> 00:05:33,934 +to apply only to loads +inside certain iframes. + +102 +00:05:33,934 --> 00:05:36,469 +Now you can run +regular expressions + +103 +00:05:36,469 --> 00:05:39,339 +on the URL of the current frame. + +104 +00:05:39,339 --> 00:05:41,908 +We are going to write a rule +to block images + +105 +00:05:41,908 --> 00:05:47,147 +but only from frames +containing Wikipedia. + +106 +00:05:47,147 --> 00:05:48,215 +To do this, + +107 +00:05:48,215 --> 00:05:52,686 +we add if-frame-url +to the JSON like this. + +108 +00:05:52,686 --> 00:05:55,455 +You then compile the JSON +as you have before + +109 +00:05:55,455 --> 00:05:58,959 +and apply it to +a WKWebViewConfiguration. + +110 +00:05:58,959 --> 00:06:01,027 +The regular expression then runs + +111 +00:06:01,027 --> 00:06:06,199 +on the URL of the frame +that is making the request. + +112 +00:06:06,199 --> 00:06:10,203 +This blocking rule will now only +apply to requests from frames + +113 +00:06:10,203 --> 00:06:14,541 +that match the if-frame-url +regular expression. + +114 +00:06:14,541 --> 00:06:16,910 +Here we see that it has +blocked the image loads + +115 +00:06:16,910 --> 00:06:20,347 +inside the Wikipedia iframe. + +116 +00:06:20,347 --> 00:06:23,450 +If you're serious about +implementing content blockers, + +117 +00:06:23,450 --> 00:06:28,188 +you should check out the WWDC +session about Safari Extensions + +118 +00:06:28,188 --> 00:06:30,323 +which includes some +new possibilities + +119 +00:06:30,323 --> 00:06:33,059 +in declarativeNetRequest. + +120 +00:06:33,059 --> 00:06:36,796 +Another new capability +in WKWebView in iPadOS 16 + +121 +00:06:36,796 --> 00:06:38,898 +is encrypted media. + +122 +00:06:38,898 --> 00:06:42,202 +If you have content that uses +the Encrypted Media Extensions + +123 +00:06:42,202 --> 00:06:45,305 +and Media Source +Extensions APIs, + +124 +00:06:45,305 --> 00:06:49,276 +you can now use it +in your apps on iPadOS. + +125 +00:06:49,276 --> 00:06:53,480 +This means that if you have +premium content like AppleTV+, + +126 +00:06:53,480 --> 00:06:57,684 +it will work on iPadOS +like it has in macOS. + +127 +00:06:57,684 --> 00:07:00,887 +If your app has +the web-browser entitlement, + +128 +00:07:00,887 --> 00:07:03,223 +then Remote Web Inspector +will just work + +129 +00:07:03,223 --> 00:07:07,260 +with your production app +like it has on Safari on iOS; + +130 +00:07:07,260 --> 00:07:11,931 +no need to add +or change any code. + +131 +00:07:11,931 --> 00:07:15,001 +To enable Web Inspector +in third-party browsers, + +132 +00:07:15,001 --> 00:07:17,671 +the process is the same +as with Safari. + +133 +00:07:17,671 --> 00:07:20,006 +You'll first need to turn on +Web Inspector + +134 +00:07:20,006 --> 00:07:23,276 +in Safari settings +on the iOS device, + +135 +00:07:23,276 --> 00:07:26,846 +then enable the Develop menu +in Advanced Settings + +136 +00:07:26,846 --> 00:07:29,416 +in Safari on your Mac. + +137 +00:07:29,416 --> 00:07:33,019 +Attach your phone to the Mac +and look for your device + +138 +00:07:33,019 --> 00:07:36,489 +in Safari's Develop menu +on macOS. + +139 +00:07:36,489 --> 00:07:40,060 +Web Inspector has many tools +for debugging web content. + +140 +00:07:40,060 --> 00:07:42,062 +You can explore the DOM, + +141 +00:07:42,062 --> 00:07:45,198 +run and debug +JavaScript execution, + +142 +00:07:45,198 --> 00:07:48,702 +view timelines +of your page-loading, and more. + +143 +00:07:48,702 --> 00:07:50,370 +If you have a website, + +144 +00:07:50,370 --> 00:07:53,106 +you can now inspect +and debug it yourself + +145 +00:07:53,106 --> 00:07:55,241 +in third-party browsers +on iOS, + +146 +00:07:55,241 --> 00:07:57,844 +using Remote Web Inspector. + +147 +00:07:57,844 --> 00:08:00,146 +Those are the main +new API additions + +148 +00:08:00,146 --> 00:08:02,582 +to WKWebView this year. + +149 +00:08:02,582 --> 00:08:06,252 +Try them out and see +what they can do in your apps. + +150 +00:08:06,252 --> 00:08:10,056 +Remember to use the best APIs +for your application, + +151 +00:08:10,056 --> 00:08:13,360 +and if there's something you're +unable to do with WKWebView, + +152 +00:08:13,360 --> 00:08:17,364 +please file a feature request +using Feedback Assistant. + +153 +00:08:17,364 --> 00:08:19,399 +We read the feature requests +we get + +154 +00:08:19,399 --> 00:08:23,770 +and prioritize our development +based on your input. + +155 +00:08:23,770 --> 00:08:24,938 +Don't forget to check out + +156 +00:08:24,938 --> 00:08:27,440 +"What's new in Safari +Web Extensions”, + +157 +00:08:27,440 --> 00:08:30,343 +and for more additions +to the web platform, + +158 +00:08:30,343 --> 00:08:31,711 +remember to check out + +159 +00:08:31,711 --> 00:08:34,447 +"What's new in Safari +and WebKit”. + +160 +00:08:34,447 --> 00:08:37,684 +Thanks for watching, +and enjoy the rest of WWDC. + +161 +00:08:37,684 --> 00:08:41,921 +♪ ♪ + diff --git a/eng/2022 Session 10050 Complications and widgets - Reloaded en.srt b/eng/2022 Session 10050 Complications and widgets - Reloaded en.srt new file mode 100644 index 0000000..c098f47 --- /dev/null +++ b/eng/2022 Session 10050 Complications and widgets - Reloaded en.srt @@ -0,0 +1,1584 @@ +1 +00:00:00,200 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,543 +♪ + +3 +00:00:09,543 --> 00:00:11,578 +Hi! +My name is Devon. + +4 +00:00:11,578 --> 00:00:13,180 +I'm an engineer +on the watchOS team, + +5 +00:00:13,180 --> 00:00:15,315 +and today, +I'll be talking about iOS. + +6 +00:00:15,315 --> 00:00:17,985 +And I'm Graham, +an engineer from iOS, + +7 +00:00:17,985 --> 00:00:21,355 +and today, +I'll be talking about watchOS. + +8 +00:00:21,355 --> 00:00:23,924 +We'll be talking about +API additions to WidgetKit + +9 +00:00:23,924 --> 00:00:25,726 +that enable you to write +accessory widgets + +10 +00:00:25,726 --> 00:00:29,162 +for the Lock Screen +and complications for watchOS. + +11 +00:00:29,162 --> 00:00:31,365 +We'll show how you can +develop both together, + +12 +00:00:31,365 --> 00:00:34,701 +along with additions to SwiftUI +to help you along the way. + +13 +00:00:34,701 --> 00:00:36,203 +If you're unfamiliar +with widgets, + +14 +00:00:36,203 --> 00:00:37,604 +timelines, and reloading, + +15 +00:00:37,604 --> 00:00:41,141 +I encourage you to seek out +prior WidgetKit sessions. + +16 +00:00:41,141 --> 00:00:43,644 +First, we'll talk about +the history of complications + +17 +00:00:43,644 --> 00:00:45,579 +and how they've evolved. + +18 +00:00:45,579 --> 00:00:48,482 +Then, we'll talk about new API +to color your widgets + +19 +00:00:48,482 --> 00:00:51,652 +and complications +in their new environments. + +20 +00:00:51,652 --> 00:00:53,854 +After that, Graham will demo +how to get started + +21 +00:00:53,854 --> 00:00:54,922 +making your own widgets + +22 +00:00:54,922 --> 00:00:58,792 +and moving your existing +widget extension to watchOS. + +23 +00:00:58,792 --> 00:01:00,360 +Next, Graham will +walk you through + +24 +00:01:00,360 --> 00:01:02,863 +how to make the most +of these smaller views. + +25 +00:01:02,863 --> 00:01:05,599 +And lastly, we'll talk about the +different privacy environments + +26 +00:01:05,599 --> 00:01:07,935 +your widgets may appear in. + +27 +00:01:07,935 --> 00:01:10,771 +Complications are a key piece +of the watchOS platform, + +28 +00:01:10,771 --> 00:01:14,741 +presenting quick, glanceable +information on the watch face. + +29 +00:01:14,741 --> 00:01:16,076 +They convey +immediately accessible, + +30 +00:01:16,076 --> 00:01:18,545 +high-value information +and a tap takes you + +31 +00:01:18,545 --> 00:01:22,149 +to the relevant location +in the app. + +32 +00:01:22,149 --> 00:01:24,284 +In watchOS 2, +ClockKit enabled you + +33 +00:01:24,284 --> 00:01:26,453 +to create your own +complications. + +34 +00:01:26,453 --> 00:01:28,689 +Complications have come +a long way since then. + +35 +00:01:30,824 --> 00:01:33,460 +Rich complications were +introduced in watchOS 5, + +36 +00:01:33,460 --> 00:01:37,798 +with graphic content +and a suite of new families. + +37 +00:01:37,798 --> 00:01:40,300 +SwiftUI complications +and multiple complications + +38 +00:01:40,300 --> 00:01:42,502 +were introduced in watchOS 7, +which enabled you + +39 +00:01:42,502 --> 00:01:44,671 +to take your complications +to the next level + +40 +00:01:44,671 --> 00:01:48,742 +and provide more options +than ever. + +41 +00:01:48,742 --> 00:01:50,944 +Today, complications +have been reimagined + +42 +00:01:50,944 --> 00:01:53,680 +and remade with WidgetKit, +embracing SwiftUI + +43 +00:01:53,680 --> 00:01:55,716 +and bringing the glanceable +complication experience + +44 +00:01:55,716 --> 00:01:58,952 +to iOS in the form of widgets. + +45 +00:01:58,952 --> 00:02:01,922 +With WidgetKit in iOS 16 +and watchOS 9, + +46 +00:02:01,922 --> 00:02:04,391 +you can build great glanceable +widgets and complications + +47 +00:02:04,391 --> 00:02:05,892 +across both platforms, + +48 +00:02:05,892 --> 00:02:08,462 +enabling you to write your code +once and share infrastructure + +49 +00:02:08,462 --> 00:02:11,231 +with your existing +Home Screen widgets. + +50 +00:02:11,231 --> 00:02:13,333 +To do this, we've added +new widget families + +51 +00:02:13,333 --> 00:02:15,268 +to the existing +WidgetFamily type, + +52 +00:02:15,268 --> 00:02:18,905 +prefixed with the word +"accessory." + +53 +00:02:18,905 --> 00:02:20,841 +The new accessoryRectangular +family can be used + +54 +00:02:20,841 --> 00:02:23,877 +to show multiple lines of text +or small graphs and charts, + +55 +00:02:23,877 --> 00:02:27,981 +similar to the existing ClockKit +graphicRectangular family. + +56 +00:02:27,981 --> 00:02:30,951 +The accessoryCircular family +is great for brief information, + +57 +00:02:30,951 --> 00:02:32,953 +gauges, and progress views. + +58 +00:02:32,953 --> 00:02:37,624 +This family also replaces the +graphicCircular ClockKit family. + +59 +00:02:37,624 --> 00:02:40,660 +The all-new accessoryInline +is a text-only slot + +60 +00:02:40,660 --> 00:02:45,132 +present on many faces on watchOS +and above the time on iOS. + +61 +00:02:45,132 --> 00:02:47,234 +The inline slot +comes in many sizes, + +62 +00:02:47,234 --> 00:02:48,502 +and we'll talk about how +to make the best use + +63 +00:02:48,502 --> 00:02:50,037 +of them all later on. + +64 +00:02:52,339 --> 00:02:55,609 +Specific to watchOS is the new +accessoryCorner family, + +65 +00:02:55,609 --> 00:02:59,146 +mixing a small circle of widget +content with gauges and text. + +66 +00:02:59,146 --> 00:03:03,784 +This talk focuses on families +common between iOS and watchOS. + +67 +00:03:03,784 --> 00:03:05,952 +For more details +on this new watchOS family + +68 +00:03:05,952 --> 00:03:08,255 +and complication-specific +features, check out + +69 +00:03:08,255 --> 00:03:11,992 +the "Go further with WidgetKit +complications" session. + +70 +00:03:11,992 --> 00:03:14,261 +Let's talk about colors +and rendering modes. + +71 +00:03:14,261 --> 00:03:15,896 +You may have noticed +that accessory widgets + +72 +00:03:15,896 --> 00:03:18,298 +take on a few +different appearances. + +73 +00:03:18,298 --> 00:03:21,001 +The system controls the look +of accessory family widgets, + +74 +00:03:21,001 --> 00:03:22,769 +and we've given you some tools +to help adapt them + +75 +00:03:22,769 --> 00:03:25,105 +to the rendering style. + +76 +00:03:25,105 --> 00:03:26,239 +There are three different +rendering modes + +77 +00:03:26,239 --> 00:03:28,375 +your widget +may be shown in. + +78 +00:03:28,375 --> 00:03:34,981 +Your widget can be full color, +accented, or vibrant. + +79 +00:03:34,981 --> 00:03:36,983 +We've introduced the +WidgetRenderingMode type + +80 +00:03:36,983 --> 00:03:40,787 +to represent these three +different presentations. + +81 +00:03:40,787 --> 00:03:42,956 +You can access this value +from the Environment, + +82 +00:03:42,956 --> 00:03:46,093 +using the widgetRenderingMode +keypath. + +83 +00:03:46,093 --> 00:03:48,662 +After that, you can change +your content conditionally + +84 +00:03:48,662 --> 00:03:53,800 +to make sure it looks just right +everywhere that it'll show up. + +85 +00:03:53,800 --> 00:03:55,569 +In watchOS's full-color mode, + +86 +00:03:55,569 --> 00:03:58,772 +your content is displayed +exactly as you specify. + +87 +00:03:58,772 --> 00:04:01,308 +Many existing complications +take on a colorful appearance + +88 +00:04:01,308 --> 00:04:04,277 +in full color, like the gradient +in Weather's gauges, + +89 +00:04:04,277 --> 00:04:08,381 +or the Activity rings' colors. + +90 +00:04:08,381 --> 00:04:10,784 +In the accented rendering mode, +your views are split + +91 +00:04:10,784 --> 00:04:13,787 +into two groups +and colored independently. + +92 +00:04:13,787 --> 00:04:15,722 +The two coloring groups +are flatly colored, + +93 +00:04:15,722 --> 00:04:19,226 +preserving only +their original opacities. + +94 +00:04:19,226 --> 00:04:21,061 +You can tell the system +how to group your views + +95 +00:04:21,061 --> 00:04:23,063 +with the .widgetAccentable() +view modifier, + +96 +00:04:23,063 --> 00:04:24,898 +or switch out your content +based on + +97 +00:04:24,898 --> 00:04:26,700 +the Widget Rendering Mode +environment value + +98 +00:04:26,700 --> 00:04:29,970 +to look perfect when flattened. + +99 +00:04:29,970 --> 00:04:31,638 +Note that the system +can tint your content + +100 +00:04:31,638 --> 00:04:35,008 +in a number of ways, +some of which are inverted. + +101 +00:04:35,008 --> 00:04:37,010 +Some are on a black background, +while others are + +102 +00:04:37,010 --> 00:04:42,115 +on the new full-color +backgrounds in watchOS 9. + +103 +00:04:42,115 --> 00:04:44,017 +In the iOS vibrant +rendering mode, + +104 +00:04:44,017 --> 00:04:46,419 +your content is desaturated +then colored appropriately + +105 +00:04:46,419 --> 00:04:48,588 +for the Lock Screen background. + +106 +00:04:48,588 --> 00:04:50,290 +The system maps +your greyscale content + +107 +00:04:50,290 --> 00:04:52,225 +in to a material appearance. + +108 +00:04:52,225 --> 00:04:54,661 +This material is adaptive +to the content behind it, + +109 +00:04:54,661 --> 00:04:57,430 +appearing just right +in its environment. + +110 +00:04:57,430 --> 00:04:59,466 +Additionally, the Lock Screen +can be configured + +111 +00:04:59,466 --> 00:05:02,936 +to give the vibrant +rendering mode a colored tint. + +112 +00:05:02,936 --> 00:05:05,772 +A light source color ends up +mostly opaque and brighter. + +113 +00:05:05,772 --> 00:05:07,574 +On the other end, +a dark source color + +114 +00:05:07,574 --> 00:05:10,310 +appears as a less prominent blur +of the background behind it, + +115 +00:05:10,310 --> 00:05:12,512 +with only a slight amount +of brightening. + +116 +00:05:12,512 --> 00:05:13,813 +To ensure legibility, + +117 +00:05:13,813 --> 00:05:16,750 +avoid using transparent colors +in this mode. + +118 +00:05:16,750 --> 00:05:18,885 +Instead, use darker colors +or black + +119 +00:05:18,885 --> 00:05:20,587 +to represent less +prominent content + +120 +00:05:20,587 --> 00:05:23,323 +while maintaining legibility. + +121 +00:05:23,323 --> 00:05:24,991 +To help you with +some of this nuance, + +122 +00:05:24,991 --> 00:05:27,761 +we've also introduced the +AccessoryWidgetBackground view + +123 +00:05:27,761 --> 00:05:30,230 +to give a consistent backdrop +to widgets that need them, + +124 +00:05:30,230 --> 00:05:32,866 +like this circular calendar. + +125 +00:05:32,866 --> 00:05:34,901 +While most accessory widgets +have no background, + +126 +00:05:34,901 --> 00:05:38,038 +some styles can be +enhanced with one. + +127 +00:05:38,038 --> 00:05:39,906 +The background view takes on +different appearances + +128 +00:05:39,906 --> 00:05:41,841 +in the various +widget rendering modes + +129 +00:05:41,841 --> 00:05:43,577 +and is tuned by the system +to look right + +130 +00:05:43,577 --> 00:05:46,346 +for the style of the face +or Lock Screen. + +131 +00:05:46,346 --> 00:05:49,216 +This is a soft transparent view +in full color and accented, + +132 +00:05:49,216 --> 00:05:51,184 +and black in +the vibrant environment, + +133 +00:05:51,184 --> 00:05:54,588 +which results in +a low brightness and full blur. + +134 +00:05:54,588 --> 00:05:57,057 +Graham is super excited to get +started making some new widgets + +135 +00:05:57,057 --> 00:05:59,259 +for the Lock Screen +and complications on watchOS -- + +136 +00:05:59,259 --> 00:06:01,294 +I'll hand it off to him. + +137 +00:06:01,294 --> 00:06:02,462 +Hi again! + +138 +00:06:02,462 --> 00:06:04,598 +I'll be adding support +for our new widget families + +139 +00:06:04,598 --> 00:06:07,300 +to an existing app -- +Emoji Rangers -- + +140 +00:06:07,300 --> 00:06:10,937 +which some of you might be +familiar with from WWDC2020's + +141 +00:06:10,937 --> 00:06:13,240 +"Widgets Code-along." + +142 +00:06:13,240 --> 00:06:14,774 +Before I begin, a note for those + +143 +00:06:14,774 --> 00:06:17,510 +with existing +widget-free projects. + +144 +00:06:17,510 --> 00:06:20,080 +You can get started by adding +the Widget Extension target + +145 +00:06:20,080 --> 00:06:23,250 +to your project, +which already exists on iOS + +146 +00:06:23,250 --> 00:06:26,052 +and has been brought +to watchOS. + +147 +00:06:26,052 --> 00:06:28,722 +But I know that many of you +have apps with widgets already, + +148 +00:06:28,722 --> 00:06:30,557 +so today let's start there +and talk about + +149 +00:06:30,557 --> 00:06:33,093 +adding new widgets +and complications. + +150 +00:06:35,895 --> 00:06:38,531 +We'll continue +the Emoji Rangers project. + +151 +00:06:38,531 --> 00:06:41,234 +This app keeps track +of our favorite Emoji Rangers + +152 +00:06:41,234 --> 00:06:43,737 +and keeps you up to date with +their health and recharge time + +153 +00:06:43,737 --> 00:06:46,072 +with the use +of Home Screen widgets. + +154 +00:06:46,072 --> 00:06:48,742 +We've already brought Emoji +Rangers over to watchOS, + +155 +00:06:48,742 --> 00:06:51,177 +bringing our favorite app +to our wrists. + +156 +00:06:51,177 --> 00:06:53,480 +Today we'll be extending +Emoji Rangers with support + +157 +00:06:53,480 --> 00:06:55,181 +for our new widget families + +158 +00:06:55,181 --> 00:06:59,152 +and bringing its widget +extension to the watch. + +159 +00:06:59,152 --> 00:07:01,154 +Let's get started with getting +the widget extension + +160 +00:07:01,154 --> 00:07:02,689 +onto the watch. + +161 +00:07:02,689 --> 00:07:05,358 +We'll add a new watchOS target +that shares code + +162 +00:07:05,358 --> 00:07:07,961 +with the existing iOS target. + +163 +00:07:07,961 --> 00:07:10,664 +We'll duplicate the iOS +widget extension target, + +164 +00:07:13,066 --> 00:07:14,334 +give it a better name, + +165 +00:07:19,439 --> 00:07:20,640 +change the bundle identifier + +166 +00:07:20,640 --> 00:07:24,844 +to be prefixed +with the watch app's, + +167 +00:07:24,844 --> 00:07:27,647 +target watchOS, + +168 +00:07:29,716 --> 00:07:32,952 +and embed our new extension +in our watch app. + +169 +00:07:44,764 --> 00:07:47,200 +Now we need to get our code +building on watchOS -- + +170 +00:07:47,200 --> 00:07:48,935 +let's get on with that. + +171 +00:07:53,907 --> 00:07:56,676 +Glancing through the +EmojiRangerWidget code, + +172 +00:07:56,676 --> 00:07:58,578 +we can see +the timeline provider, + +173 +00:07:58,578 --> 00:08:01,548 +which is used when the +system reloads content, + +174 +00:08:08,288 --> 00:08:10,824 +the view which uses SwiftUI +to generate content + +175 +00:08:10,824 --> 00:08:13,493 +for our different families, + +176 +00:08:16,529 --> 00:08:19,966 +the widget configuration, + +177 +00:08:19,966 --> 00:08:22,736 +and the Xcode preview provider. + +178 +00:08:22,736 --> 00:08:23,903 +The Emoji Rangers app + +179 +00:08:23,903 --> 00:08:26,806 +already supports +iOS Home Screen widgets. + +180 +00:08:26,806 --> 00:08:29,376 +It offers the system small +and medium families, + +181 +00:08:29,376 --> 00:08:31,444 +and here in the +widget configuration, + +182 +00:08:31,444 --> 00:08:34,080 +I'm going to add +the new families. + +183 +00:08:38,051 --> 00:08:41,054 +Because system families are +unavailable on the watch, + +184 +00:08:41,054 --> 00:08:42,622 +we'll need to use +a platform macro + +185 +00:08:42,622 --> 00:08:45,291 +to specify our +supportedFamilies. + +186 +00:08:55,535 --> 00:08:57,904 +In the preview provider, +I'm going to add previews + +187 +00:08:57,904 --> 00:08:59,906 +for the new families. + +188 +00:09:12,952 --> 00:09:16,356 +Next, we need to implement the +new IntentRecommendation API + +189 +00:09:16,356 --> 00:09:19,859 +before we can successfully +build for watchOS. + +190 +00:09:19,859 --> 00:09:21,828 +While Intents are +fully configurable + +191 +00:09:21,828 --> 00:09:24,464 +in the widgets editing UI +on iOS, + +192 +00:09:24,464 --> 00:09:28,401 +on watchOS, we need to provide +a preconfigured list. + +193 +00:09:28,401 --> 00:09:31,604 +We can do that by overriding +the new recommendations method + +194 +00:09:31,604 --> 00:09:34,107 +on our IntentTimelineProvider. + +195 +00:09:46,119 --> 00:09:47,887 +Now we're building successfully. + +196 +00:09:47,887 --> 00:09:49,189 +Let's resume the previews + +197 +00:09:49,189 --> 00:09:51,591 +and see what +our circular widget looks like. + +198 +00:09:56,296 --> 00:09:58,631 +The content intended +for even a small widget + +199 +00:09:58,631 --> 00:10:01,534 +doesn't fit well inside +our new form factor. + +200 +00:10:01,534 --> 00:10:03,937 +The new widget families +are smaller than iOS widgets + +201 +00:10:03,937 --> 00:10:06,206 +found on the Home Screen, +and you'll need to consider + +202 +00:10:06,206 --> 00:10:09,175 +the content of +your complications. + +203 +00:10:09,175 --> 00:10:11,611 +Now let's talk about +some new views we can use + +204 +00:10:11,611 --> 00:10:14,814 +to make our complications +stand out. + +205 +00:10:14,814 --> 00:10:16,516 +Let's go to the view. + +206 +00:10:16,516 --> 00:10:19,452 +We can see code for the +systemSmall and other widgets; + +207 +00:10:19,452 --> 00:10:22,288 +let's add code for +the accessoryCircular case. + +208 +00:10:22,288 --> 00:10:25,158 +I think it would look good +just with the avatar. + +209 +00:10:32,298 --> 00:10:34,634 +This provides a quick shortcut +in to our app + +210 +00:10:34,634 --> 00:10:37,403 +but doesn't offer users +any information. + +211 +00:10:37,403 --> 00:10:39,305 +Let's add a progress view +around the edge, + +212 +00:10:39,305 --> 00:10:41,641 +which will give the users +an idea of when the Ranger + +213 +00:10:41,641 --> 00:10:44,310 +will be ready for combat again. + +214 +00:10:46,579 --> 00:10:49,282 +Trouble is animating +this progress view to be current + +215 +00:10:49,282 --> 00:10:53,419 +will require a lot of timeline +entries in short succession. + +216 +00:10:53,419 --> 00:10:57,524 +Instead, we can use SwiftUI's +new auto-updating ProgressView. + +217 +00:10:57,524 --> 00:10:59,559 +That takes a date interval +over which our Ranger + +218 +00:10:59,559 --> 00:11:01,060 +will be fully healed. + +219 +00:11:01,060 --> 00:11:03,463 +The system will keep +our progress view updated, + +220 +00:11:03,463 --> 00:11:06,599 +meaning we only need +one timeline entry here. + +221 +00:11:11,471 --> 00:11:14,774 +Much better. + +222 +00:11:14,774 --> 00:11:16,843 +Now let's add +the rectangular family. + +223 +00:11:19,846 --> 00:11:25,652 +We'll select +the rectangular preview. + +224 +00:11:25,652 --> 00:11:28,555 +This gives us more space, +so we'll make a three-line view + +225 +00:11:28,555 --> 00:11:31,224 +in the style of a complication. + +226 +00:11:31,224 --> 00:11:33,927 +First the character's name, +then their level, + +227 +00:11:33,927 --> 00:11:35,895 +and then the time +until fully healed, + +228 +00:11:35,895 --> 00:11:39,933 +for which we'll use +an auto-updating date field. + +229 +00:11:39,933 --> 00:11:41,834 +I'd like the character's name +to stand out, + +230 +00:11:41,834 --> 00:11:45,004 +so I'll size the text, +using a font style of headline, + +231 +00:11:45,004 --> 00:11:47,040 +and add the +widgetAccentable modifier + +232 +00:11:47,040 --> 00:11:50,443 +which will adjust its color. + +233 +00:11:59,385 --> 00:12:01,287 +Our view looks great here +in vibrant, + +234 +00:12:01,287 --> 00:12:03,823 +now let's see how it looks +in our other rendering modes + +235 +00:12:03,823 --> 00:12:06,826 +on the watch. + +236 +00:12:18,338 --> 00:12:22,041 +You can see how the character's +name takes on the accent color. + +237 +00:12:22,041 --> 00:12:24,177 +To make your widgets +and complications feel at home + +238 +00:12:24,177 --> 00:12:26,112 +in their environment, +it's important + +239 +00:12:26,112 --> 00:12:28,348 +that you use +the default font parameters + +240 +00:12:28,348 --> 00:12:30,950 +and make use of font styles. + +241 +00:12:30,950 --> 00:12:32,585 +The font styles and sizes +are different + +242 +00:12:32,585 --> 00:12:34,487 +between iOS and watchOS. + +243 +00:12:34,487 --> 00:12:36,322 +iOS uses the regular +text design, + +244 +00:12:36,322 --> 00:12:38,558 +while watchOS uses +a rounded design + +245 +00:12:38,558 --> 00:12:40,293 +with a heavier weight. + +246 +00:12:40,293 --> 00:12:42,462 +Your widgets and complications +will sit onscreen + +247 +00:12:42,462 --> 00:12:43,830 +adjacent to others. + +248 +00:12:43,830 --> 00:12:46,065 +And so they look consistent, +we recommend using + +249 +00:12:46,065 --> 00:12:50,036 +the font styles Title, Headline, +Body, and Caption. + +250 +00:12:53,373 --> 00:12:55,541 +Xcode's preview shows +we still have room leftover + +251 +00:12:55,541 --> 00:12:58,177 +to add the avatar too. + +252 +00:13:04,550 --> 00:13:07,086 +Let's see how this looks +on the iPhone. + +253 +00:13:11,290 --> 00:13:13,026 +That looks great! + +254 +00:13:13,026 --> 00:13:16,095 +Finally, let's add the third +style, accessoryInline, + +255 +00:13:16,095 --> 00:13:19,999 +which displays a line of text +and optionally an image. + +256 +00:13:19,999 --> 00:13:21,868 +Note that inline accessories +are drawn + +257 +00:13:21,868 --> 00:13:25,271 +according to system-defined +coloring and font. + +258 +00:13:25,271 --> 00:13:27,540 +Let's select the preview. + +259 +00:13:38,317 --> 00:13:41,454 +Let's show our hero's name +and recharge countdown. + +260 +00:13:45,224 --> 00:13:47,860 +This text is too long +for our watch slot. + +261 +00:13:47,860 --> 00:13:51,064 +So now's a good time +to show you ViewThatFits. + +262 +00:13:51,064 --> 00:13:54,534 +I can supply multiple views, +from lengthy to concise, + +263 +00:13:54,534 --> 00:13:56,736 +and ViewThatFits will choose +the first content view + +264 +00:13:56,736 --> 00:14:00,673 +that fits the available space +without truncation or clipping. + +265 +00:14:00,673 --> 00:14:02,208 +Let's shorten the text. + +266 +00:14:19,859 --> 00:14:22,595 +Even that might be too long +for the shortest watch slot, + +267 +00:14:22,595 --> 00:14:24,497 +so let's offer +a third alternative + +268 +00:14:24,497 --> 00:14:27,333 +by switching out the avatar +for the name. + +269 +00:14:42,048 --> 00:14:44,851 +Let's see what that looks like. + +270 +00:14:49,188 --> 00:14:52,225 +Refer to the "Compose Custom +layouts with SwiftUI" session + +271 +00:14:52,225 --> 00:14:54,660 +for more about this. + +272 +00:14:54,660 --> 00:14:56,262 +Awesome! + +273 +00:14:56,262 --> 00:14:59,232 +Even Emoji Rangers like +to enjoy some privacy, + +274 +00:14:59,232 --> 00:15:02,735 +so I'll hand it back +to Devon to talk about that. + +275 +00:15:02,735 --> 00:15:05,738 +Hello again! +Let's talk about privacy. + +276 +00:15:05,738 --> 00:15:08,007 +So far in this talk, +we've discussed the active state + +277 +00:15:08,007 --> 00:15:10,009 +of your widgets +and complications. + +278 +00:15:10,009 --> 00:15:12,678 +However, across our platforms, +you'll need to consider + +279 +00:15:12,678 --> 00:15:14,647 +whether the device +is redacting content + +280 +00:15:14,647 --> 00:15:17,550 +or in a low-luminance state. + +281 +00:15:17,550 --> 00:15:19,685 +On the iOS Lock Screen, +the default behavior + +282 +00:15:19,685 --> 00:15:22,789 +is to show your content even +while the device is locked, + +283 +00:15:22,789 --> 00:15:25,725 +which is the top-left cell +in our grid here. + +284 +00:15:25,725 --> 00:15:28,227 +However, this is configurable +in Settings, + +285 +00:15:28,227 --> 00:15:30,997 +and users can choose to redact +their widget when locked, + +286 +00:15:30,997 --> 00:15:34,167 +much like Notifications. + +287 +00:15:34,167 --> 00:15:36,369 +On watchOS, +the device stays unlocked + +288 +00:15:36,369 --> 00:15:38,671 +as long as the watch +is being worn. + +289 +00:15:38,671 --> 00:15:41,474 +When inactive, the watch +transitions into always-on, + +290 +00:15:41,474 --> 00:15:43,676 +with content in +a low-luminance presentation + +291 +00:15:43,676 --> 00:15:45,645 +and a lower update cadence. + +292 +00:15:45,645 --> 00:15:48,681 +By default, your content is not +redacted in low luminance, + +293 +00:15:48,681 --> 00:15:52,718 +which is the state +on the bottom left. + +294 +00:15:52,718 --> 00:15:55,054 +Like the Lock Screen, +your users can configure + +295 +00:15:55,054 --> 00:15:57,089 +their complication content +to be redacted + +296 +00:15:57,089 --> 00:15:59,091 +in this always-on state. + +297 +00:15:59,091 --> 00:16:01,294 +In this state, you'll need +to make sure your content + +298 +00:16:01,294 --> 00:16:05,698 +is prepared for both redaction +and low luminance. + +299 +00:16:05,698 --> 00:16:08,100 +Together, the platforms cover +each of the four states + +300 +00:16:08,100 --> 00:16:09,635 +shown here. + +301 +00:16:09,635 --> 00:16:11,737 +Consider all these possible +states and ensure + +302 +00:16:11,737 --> 00:16:14,674 +your complications and widgets +work well in all cases. + +303 +00:16:14,674 --> 00:16:18,411 +Let's talk about +how you can do that. + +304 +00:16:18,411 --> 00:16:20,279 +On the watch, your widget +needs to support + +305 +00:16:20,279 --> 00:16:23,182 +the always-on +display experience. + +306 +00:16:23,182 --> 00:16:25,051 +You can adapt your content +for always-on + +307 +00:16:25,051 --> 00:16:28,521 +with the \.isLuminanceReduced +environment value. + +308 +00:16:28,521 --> 00:16:31,023 +If you're coming from ClockKit, +note that you now can prepare + +309 +00:16:31,023 --> 00:16:35,027 +always-on content for every +timeline entry, not just one. + +310 +00:16:35,027 --> 00:16:37,597 +When in always-on, +your time-relative text + +311 +00:16:37,597 --> 00:16:40,233 +and progress views will change +to a reduced fidelity mode + +312 +00:16:40,233 --> 00:16:43,169 +to support the low update +cadence of always-on. + +313 +00:16:43,169 --> 00:16:45,771 +To support this mode, use the +environment value to remove + +314 +00:16:45,771 --> 00:16:48,341 +any time-sensitive content +and optimize your content + +315 +00:16:48,341 --> 00:16:52,211 +for the lower update frequency. + +316 +00:16:52,211 --> 00:16:54,647 +Now let's talk about redaction. + +317 +00:16:54,647 --> 00:16:57,450 +By default, the privacy mode +will show a redacted version + +318 +00:16:57,450 --> 00:17:01,888 +of the placeholder view +your TimelineProvider creates. + +319 +00:17:01,888 --> 00:17:03,756 +If you have some elements +that are sensitive + +320 +00:17:03,756 --> 00:17:05,625 +and others that don't need +to be redacted, + +321 +00:17:05,625 --> 00:17:07,560 +you can use the +.privacySensitive modifier + +322 +00:17:07,560 --> 00:17:10,696 +to mark only some of +the views to be redacted. + +323 +00:17:10,696 --> 00:17:13,432 +In this example, we've redacted +the heart rate in our widget + +324 +00:17:13,432 --> 00:17:16,235 +but left the image unredacted. + +325 +00:17:16,235 --> 00:17:18,571 +Now you're ready to make awesome +widgets for the Lock Screen + +326 +00:17:18,571 --> 00:17:20,907 +and WidgetKit complications. + +327 +00:17:20,907 --> 00:17:22,675 +For more on what's new +in SwiftUI, + +328 +00:17:22,675 --> 00:17:26,145 +check out the "Compose Custom +Layouts with SwiftUI" talk. + +329 +00:17:26,145 --> 00:17:27,580 +Thanks for watching. + +330 +00:17:27,580 --> 00:17:31,784 +♪ + diff --git a/eng/2022 Session 10051 Go further with Complications in WidgetKit en.srt b/eng/2022 Session 10051 Go further with Complications in WidgetKit en.srt new file mode 100644 index 0000000..b30e52a --- /dev/null +++ b/eng/2022 Session 10051 Go further with Complications in WidgetKit en.srt @@ -0,0 +1,824 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,211 +August Joki: Hello, I'm August Joki, + +3 +00:00:11,245 --> 00:00:13,113 +a software engineer on watchOS, + +4 +00:00:13,146 --> 00:00:18,519 +and I'm here to show you how to go further +with WidgetKit complications. + +5 +00:00:18,552 --> 00:00:22,189 +I hope you've seen the wonderful +Complications and Widgets: Reloaded talk + +6 +00:00:22,222 --> 00:00:26,260 +first, covering the basics +of complications in WidgetKit. + +7 +00:00:26,293 --> 00:00:29,029 +This talk expands +on the concepts covered there + +8 +00:00:29,062 --> 00:00:33,066 +as they relate to complications +on the watch faces. + +9 +00:00:33,100 --> 00:00:38,238 +And my WWDC 2020 talk: +Build complications in SwiftUI + +10 +00:00:38,272 --> 00:00:43,477 +covers more specifics about tinting +and SwiftUI drawing in complications. + +11 +00:00:44,211 --> 00:00:46,713 +In this talk, +I'll be discussing the WidgetKit features + +12 +00:00:46,747 --> 00:00:48,782 +unique to watchOS, + +13 +00:00:48,815 --> 00:00:51,351 +as well as how to migrate your, +and your users', + +14 +00:00:51,385 --> 00:00:54,421 +existing ClockKit complications +to WidgetKit. + +15 +00:00:54,454 --> 00:00:57,424 +I've taken inspiration from +the Coffee Tracker sample app + +16 +00:00:57,457 --> 00:00:59,793 +to use as an example +throughout this talk. + +17 +00:00:59,826 --> 00:01:02,896 +The app records the number of coffees, +teas, and sodas + +18 +00:01:02,930 --> 00:01:04,598 +you drink throughout the day + +19 +00:01:04,631 --> 00:01:08,669 +and tracks the amount of caffeine +in your body over time. + +20 +00:01:08,702 --> 00:01:12,072 +Let's start with what's unique to watchOS. + +21 +00:01:12,105 --> 00:01:14,975 +In iOS 16 +we brought complication style widgets + +22 +00:01:15,008 --> 00:01:16,810 +to the phone's lock screen + +23 +00:01:16,844 --> 00:01:21,682 +and in watchOS 9 we brought WidgetKit +to the watch's complications. + +24 +00:01:21,715 --> 00:01:25,352 +On the watch faces we have +a unique complication presentation + +25 +00:01:25,385 --> 00:01:27,087 +for the corners of the watch screen. + +26 +00:01:27,120 --> 00:01:29,623 +And it requires a unique WidgetKit family + +27 +00:01:29,656 --> 00:01:33,060 +called accessoryCorner, to describe it. + +28 +00:01:33,093 --> 00:01:36,230 +Part of that unique presentation +is auxiliary content + +29 +00:01:36,263 --> 00:01:38,298 +specified by your SwiftUI view, + +30 +00:01:38,332 --> 00:01:40,400 +but not rendered as part of your content. + +31 +00:01:40,434 --> 00:01:42,836 +Instead, it is rendered by the watch face. + +32 +00:01:44,271 --> 00:01:47,407 +The circular part of the corner +is standard SwiftUI rendering + +33 +00:01:47,441 --> 00:01:50,878 +and the auxiliary content +is the curved part in the corners. + +34 +00:01:53,080 --> 00:01:55,682 +Or in the dial on the Infograph face. + +35 +00:01:58,318 --> 00:02:02,856 +The accessoryInline family +has a unique behavior on watch faces. + +36 +00:02:02,890 --> 00:02:07,361 +It has multiple ways of being rendered +depending on the face. + +37 +00:02:07,394 --> 00:02:10,697 +Sometimes flat, + +38 +00:02:10,731 --> 00:02:12,766 +sometimes curved to match the dial. + +39 +00:02:14,234 --> 00:02:17,237 +Let's talk about how to support +these unique features + +40 +00:02:17,271 --> 00:02:21,475 +by looking at how the coffee tracker app +might be updated to use WidgetKit. + +41 +00:02:23,510 --> 00:02:26,580 +In addition to the three new +complication-styled widget families + +42 +00:02:26,613 --> 00:02:30,017 +on iOS 16: +AccessoryRectangular, + +43 +00:02:30,050 --> 00:02:34,021 +accessoryCircular, +and accessoryInline, + +44 +00:02:34,054 --> 00:02:37,958 +we have a fourth family on watchOS 9 +called accessoryCorner. + +45 +00:02:39,426 --> 00:02:43,330 +accessoryCorner can either be shown +as a large circular content, + +46 +00:02:43,363 --> 00:02:47,768 +like the maps and heart rate +complications shown in the lower corners, + +47 +00:02:47,801 --> 00:02:51,939 +or as smaller circular content +with a curved label or gauge + +48 +00:02:51,972 --> 00:02:56,009 +like the coffee tracker and moonphase +complications shown in the upper corners. + +49 +00:02:57,911 --> 00:03:01,081 +To control whether +the inner auxiliary content is shown, + +50 +00:03:01,114 --> 00:03:06,153 +watchOS 9 has added a new view modifier +you can use, which I'll show you now. + +51 +00:03:08,055 --> 00:03:11,625 +Let's look at building a corner +complication for my coffee tracker app. + +52 +00:03:13,060 --> 00:03:16,163 +Starting with the larger +circular content style, + +53 +00:03:16,196 --> 00:03:19,933 +I have a ZStack +with an SF Symbol and a background. + +54 +00:03:19,967 --> 00:03:22,936 +The SwiftUI content is +automatically clipped to a circle + +55 +00:03:22,970 --> 00:03:26,807 +to keep in line with the design +of the other corner complications. + +56 +00:03:28,408 --> 00:03:32,212 +To add the inner curved content, +we use the new to watchOS 9 + +57 +00:03:32,246 --> 00:03:34,581 +widgetLabel view modifier. + +58 +00:03:34,615 --> 00:03:38,886 +The watch face extracts the contents +of the modifier to draw the control + +59 +00:03:38,919 --> 00:03:42,890 +appropriate for the family +and the style of the watch face. + +60 +00:03:42,923 --> 00:03:47,261 +And the circular content +automatically scales down to make room. + +61 +00:03:47,294 --> 00:03:51,231 +For accessoryCorner +you can specify a SwiftUI text, gauge, + +62 +00:03:51,265 --> 00:03:54,201 +or progressView in your widget's label. + +63 +00:03:55,736 --> 00:03:59,373 +AccessoryCorner isn't the only family +that supports widgetLabel. + +64 +00:03:59,406 --> 00:04:03,510 +Let's look at how it is used +on the accessoryCircular family. + +65 +00:04:04,378 --> 00:04:08,482 +On the Infograph watch face, +in addition to the corner complications, + +66 +00:04:08,515 --> 00:04:12,586 +there are four circular complications +inside the dial. + +67 +00:04:12,619 --> 00:04:16,256 +My coffee tracker circular complication, +in the middle top, + +68 +00:04:16,290 --> 00:04:19,560 +looks very similar to the corner +complication we just saw, + +69 +00:04:19,593 --> 00:04:21,295 +but with text in the dial. + +70 +00:04:21,328 --> 00:04:23,430 +I'll show you how to add that text now. + +71 +00:04:24,665 --> 00:04:28,168 +For my circular complication design, +I thought it more appropriate + +72 +00:04:28,202 --> 00:04:30,370 +to move the gauge +that was in the widgetLabel + +73 +00:04:30,404 --> 00:04:34,107 +in my corner complication, +to be front and center. + +74 +00:04:34,141 --> 00:04:37,311 +To take advantage of the top +middle position on Infograph, + +75 +00:04:37,344 --> 00:04:41,481 +I add a widgetLabel to the gauge +in order to display additional text + +76 +00:04:41,515 --> 00:04:46,453 +in the longer bezel area that wouldn't +otherwise fit in the circular content. + +77 +00:04:46,486 --> 00:04:49,156 +But now I have redundant information +between the main view + +78 +00:04:49,189 --> 00:04:51,892 +and the text above it. + +79 +00:04:51,925 --> 00:04:54,695 +I can clean that up +by switching the circular content + +80 +00:04:54,728 --> 00:04:59,733 +to that good looking coffee cup SF Symbol +from my corner complication + +81 +00:04:59,766 --> 00:05:02,903 +But when I switch to a face +showing my circular complication + +82 +00:05:02,936 --> 00:05:04,438 +that does not have the bezel, + +83 +00:05:04,471 --> 00:05:06,540 +then I've lost all of my caffeine info. + +84 +00:05:06,573 --> 00:05:10,110 +Luckily, there's a piece of API I can add +to make my complication work + +85 +00:05:10,143 --> 00:05:11,311 +in both cases. + +86 +00:05:13,247 --> 00:05:16,350 +I update my complication +to add the Environment property + +87 +00:05:16,383 --> 00:05:19,052 +called showsWidgetLabel to my view. + +88 +00:05:19,086 --> 00:05:22,422 +This will be true whenever +the complication is in a position + +89 +00:05:22,456 --> 00:05:26,226 +on the watch face that shows +the content in the widget's label. + +90 +00:05:28,095 --> 00:05:33,500 +And then I can change the content +to depend on the value of showsWidgetLabel + +91 +00:05:33,534 --> 00:05:38,972 +so I am able to have the appropriate level +of information in each complication spot. + +92 +00:05:39,006 --> 00:05:42,776 +I just demonstrated two different ways +that the accessoryCircular family + +93 +00:05:42,809 --> 00:05:44,378 +can show up on watch faces, + +94 +00:05:44,411 --> 00:05:47,014 +and there is one more way +you need to be aware of. + +95 +00:05:47,047 --> 00:05:51,552 +The Extra Large watch face has long been +a great way for people to see the time + +96 +00:05:51,585 --> 00:05:54,688 +in an extra large format. + +97 +00:05:54,721 --> 00:05:58,091 +And it supports a single, +large circular complication. + +98 +00:05:58,125 --> 00:06:01,295 +The Extra Large face uses +the accessoryCircular family + +99 +00:06:01,328 --> 00:06:06,533 +and automatically scales up the content +to match the style of the face. + +100 +00:06:06,567 --> 00:06:08,769 +Please note: +as this face is designed + +101 +00:06:08,802 --> 00:06:10,737 +to have a single, large complication, + +102 +00:06:10,771 --> 00:06:13,340 +do not use the increased canvas size + +103 +00:06:13,373 --> 00:06:16,710 +as an opportunity +to densely pack your complication. + +104 +00:06:16,743 --> 00:06:22,216 +The content should be identical to +the normal circular family, only larger. + +105 +00:06:22,249 --> 00:06:25,285 +As I mentioned earlier, +there are two more widget families used + +106 +00:06:25,319 --> 00:06:29,857 +on watch faces: +accessoryRectangular and accessoryInline. + +107 +00:06:29,890 --> 00:06:33,560 +There are no faces with rectangular +complications that show the widgetLabel. + +108 +00:06:33,594 --> 00:06:37,931 +And the accessoryInline family acts, +already, as a widgetLabel. + +109 +00:06:37,965 --> 00:06:42,002 +The watch face extracts Images +and Texts from your inline content + +110 +00:06:42,035 --> 00:06:45,506 +and renders them itself +to match the look of the face. + +111 +00:06:45,539 --> 00:06:48,075 +Next up is Migration. + +112 +00:06:48,108 --> 00:06:49,676 +There are two parts to migration: + +113 +00:06:49,710 --> 00:06:53,247 +rewriting your existing ClockKit +complication code in WidgetKit; + +114 +00:06:53,280 --> 00:06:56,617 +and providing a mapping +to let the system know how to upgrade + +115 +00:06:56,650 --> 00:07:00,554 +your complications people have set +on their watch faces. + +116 +00:07:00,587 --> 00:07:04,925 +When you adopt WidgetKit the system +will stop asking your ClockKit data source + +117 +00:07:04,958 --> 00:07:10,264 +for new content and show only your new +complications in the face editing picker. + +118 +00:07:11,732 --> 00:07:13,867 +As well as bringing +WidgetKit to the watch, + +119 +00:07:13,901 --> 00:07:18,572 +watchOS 9 has updated every face +to support rich complications, + +120 +00:07:18,605 --> 00:07:21,909 +which allowed us to dramatically reduce +the number of complication families + +121 +00:07:21,942 --> 00:07:25,445 +from 12 to only 4. + +122 +00:07:25,479 --> 00:07:28,916 +Rectangular and Corner +map directly across + +123 +00:07:28,949 --> 00:07:32,486 +to accessoryRectangular +and accessoryCorner. + +124 +00:07:32,519 --> 00:07:35,622 +All three graphic Circular +styled ClockKit families + +125 +00:07:35,656 --> 00:07:39,860 +are now a single +accessoryCircular WidgetKit family. + +126 +00:07:39,893 --> 00:07:44,398 +And the accessoryInline family +is used where the old utilitarianSmallFlat + +127 +00:07:44,431 --> 00:07:47,334 +or utilitarianLarge used to be. + +128 +00:07:48,836 --> 00:07:52,139 +And many places that used to be +utilitarianSmall + +129 +00:07:52,172 --> 00:07:56,443 +have been updated +to use the accessoryCorner family. + +130 +00:07:57,611 --> 00:08:00,914 +With WidgetKit, SwiftUI views +and their state driven layout + +131 +00:08:00,948 --> 00:08:03,317 +have replaced ClockKit's templates. + +132 +00:08:03,350 --> 00:08:06,019 +WidgetKit still has +familiar timelines and entries. + +133 +00:08:06,053 --> 00:08:09,590 +In fact, they were originally inspired +by ClockKit itself, + +134 +00:08:09,623 --> 00:08:12,826 +which means that your complication +data source will nicely migrate + +135 +00:08:12,860 --> 00:08:16,730 +to one of either a static or intent based +WidgetKit configuration. + +136 +00:08:17,998 --> 00:08:20,968 +Please see the original WidgetKit talk +for more details + +137 +00:08:21,001 --> 00:08:23,837 +about the types of configurations +WidgetKit supports + +138 +00:08:23,871 --> 00:08:25,906 +as well as general family support. + +139 +00:08:25,939 --> 00:08:30,143 +We've added one last API to ClockKit +to allow a person's complications + +140 +00:08:30,177 --> 00:08:33,313 +to be migrated by +the system automatically. + +141 +00:08:33,347 --> 00:08:36,450 +This allows for your existing +complications that are already + +142 +00:08:36,483 --> 00:08:40,754 +on watch faces to automatically be +upgraded to your new WidgetKit based + +143 +00:08:40,787 --> 00:08:44,625 +complications +without any user interaction. + +144 +00:08:44,658 --> 00:08:46,793 +When your app gets updated on a watch, + +145 +00:08:46,827 --> 00:08:49,496 +the Watch Faces will check +for the presence of widgets + +146 +00:08:49,530 --> 00:08:51,365 +in your app's bundle. + +147 +00:08:51,398 --> 00:08:55,936 +If it finds any, it will then launch +your ClockKit complication data source + +148 +00:08:55,969 --> 00:09:00,140 +to generate the migrations +for the existing complications. + +149 +00:09:00,174 --> 00:09:03,410 +From this point forward, +your CLKComplicationDataSource + +150 +00:09:03,443 --> 00:09:07,881 +will only be run to ask for migrations +when a person receives a shared face + +151 +00:09:07,915 --> 00:09:10,551 +with your ClockKit complications on it. + +152 +00:09:10,584 --> 00:09:14,855 +The system will ask for your migrations +every time a new face is shared, + +153 +00:09:14,888 --> 00:09:18,592 +so for a consistent experience you should +keep your migrations consistent. + +154 +00:09:18,625 --> 00:09:22,062 +Once you've finished creating +your beautiful WidgetKit complications, + +155 +00:09:22,095 --> 00:09:25,065 +you can add the new property, +widgetMigrator, + +156 +00:09:25,098 --> 00:09:29,136 +to provide the object that conforms +to the new Migrator protocol. + +157 +00:09:29,169 --> 00:09:33,473 +Be that your complication data source +itself or some other type you provide. + +158 +00:09:35,008 --> 00:09:39,213 +The CLKComplication WidgetMigrator +protocol has a single function + +159 +00:09:39,246 --> 00:09:43,150 +to provide to the watch faces +widget migration configurations + +160 +00:09:43,183 --> 00:09:47,321 +from existing CLKComplicationDescriptors. + +161 +00:09:47,354 --> 00:09:50,123 +The most straightforward way +to adopt the new API + +162 +00:09:50,157 --> 00:09:53,660 +is to have your data source conform +to the new Migrator protocol. + +163 +00:09:55,028 --> 00:09:58,632 +If your WidgetKit complication +uses the static configuration, + +164 +00:09:58,665 --> 00:10:02,569 +you provide a static migration +configuration. + +165 +00:10:02,603 --> 00:10:04,838 +And there's an equivalent +migration configuration + +166 +00:10:04,872 --> 00:10:08,275 +if you use intents +in your widget complication. + +167 +00:10:08,308 --> 00:10:12,045 +Note that if you provide intent based +migration configurations, + +168 +00:10:12,079 --> 00:10:15,716 +you will need to also include your intent +definitions in your watch app + +169 +00:10:15,749 --> 00:10:17,651 +as well as your widget extensions, + +170 +00:10:17,684 --> 00:10:20,554 +so you can create your intent objects +in both places. + +171 +00:10:21,488 --> 00:10:25,292 +WidgetKit enables new and creative ways +to make complications for the watch, + +172 +00:10:25,325 --> 00:10:28,161 +while dramatically simplifying +the experience. + +173 +00:10:28,195 --> 00:10:30,430 +Thanks for watching. + diff --git a/eng/2022 Session 10052 What's new in SwiftUI en.srt b/eng/2022 Session 10052 What's new in SwiftUI en.srt new file mode 100644 index 0000000..7811f6d --- /dev/null +++ b/eng/2022 Session 10052 What's new in SwiftUI en.srt @@ -0,0 +1,2799 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:10,310 +♪ + +3 +00:00:10,310 --> 00:00:12,446 +Hi, I'm Nick. + +4 +00:00:12,446 --> 00:00:13,480 +And I'm Franck, + +5 +00:00:13,480 --> 00:00:15,382 +and we are SwiftUI engineers. + +6 +00:00:15,382 --> 00:00:19,753 +Today, we're going to +cover "What's new in SwiftUI." + +7 +00:00:19,753 --> 00:00:23,891 +SwiftUI is growing alongside +our operating systems, + +8 +00:00:23,891 --> 00:00:28,028 +each pushing the bounds +of the other. + +9 +00:00:28,028 --> 00:00:31,164 +We continue to be amazed +and delighted + +10 +00:00:31,164 --> 00:00:34,301 +by what you are making +with SwiftUI. + +11 +00:00:34,301 --> 00:00:38,472 +We take to heart all flavors of +feedback from the community. + +12 +00:00:38,472 --> 00:00:40,607 +That's why we're especially +excited to share + +13 +00:00:40,607 --> 00:00:44,411 +what we've focused +on this year. + +14 +00:00:44,411 --> 00:00:47,814 +With this year's APIs, +we've gone deeper. + +15 +00:00:47,814 --> 00:00:50,784 +We've made more custom +experiences possible. + +16 +00:00:50,784 --> 00:00:54,688 +We've introduced some amazing +new graphical techniques. + +17 +00:00:54,688 --> 00:01:01,261 +We've architected a new SwiftUI +app structure, and much more. + +18 +00:01:01,261 --> 00:01:04,965 +SwiftUI enabled us to build +designs and features + +19 +00:01:04,965 --> 00:01:07,834 +that reflect the future +of our platforms. + +20 +00:01:07,834 --> 00:01:11,838 +From redesigns of classic apps +to completely new features + +21 +00:01:11,838 --> 00:01:14,508 +to deep system integrations. + +22 +00:01:14,508 --> 00:01:17,311 +This comprehensive adoption +within Apple + +23 +00:01:17,311 --> 00:01:20,747 +further pushes evolution +of SwiftUI. + +24 +00:01:20,747 --> 00:01:23,150 +Many of these new designs +and features + +25 +00:01:23,150 --> 00:01:26,954 +are only possible because +of how SwiftUI has evolved + +26 +00:01:26,954 --> 00:01:29,890 +how we write apps at Apple. + +27 +00:01:29,890 --> 00:01:32,726 +Today we're celebrating +these APIs, + +28 +00:01:32,726 --> 00:01:34,328 +[NOISE MAKER BLOWS] + +29 +00:01:34,328 --> 00:01:37,597 +And we're also celebrating +SwiftUI's birthday, + +30 +00:01:37,597 --> 00:01:40,167 +[NOISE MAKER BLOWS] + +31 +00:01:40,167 --> 00:01:42,402 +Franck and I are +the lucky cochairs + +32 +00:01:42,402 --> 00:01:44,338 +of the party-planning committee. + +33 +00:01:44,338 --> 00:01:46,306 +Let me tell you +about the activities + +34 +00:01:46,306 --> 00:01:49,042 +we've got planned +for the party. + +35 +00:01:49,042 --> 00:01:51,611 +I'll introduce you +to a brand-new framework + +36 +00:01:51,611 --> 00:01:54,348 +called Swift Charts +that allows you to create + +37 +00:01:54,348 --> 00:02:00,187 +delightful data visualizations +across all of our platforms. + +38 +00:02:00,187 --> 00:02:02,756 +I'll show off SwiftUI's +data-driven, + +39 +00:02:02,756 --> 00:02:05,892 +strongly-typed model +for navigation + +40 +00:02:05,892 --> 00:02:09,029 +and new window techniques. + +41 +00:02:09,029 --> 00:02:12,065 +Franck will take you +through a suite of new controls + +42 +00:02:12,065 --> 00:02:16,269 +and deeper customizations +of existing controls. + +43 +00:02:16,269 --> 00:02:19,306 +Then he'll show you how +we've brought sharing elegantly + +44 +00:02:19,306 --> 00:02:24,378 +into the universe of SwiftUI +with the Transferable protocol. + +45 +00:02:24,378 --> 00:02:27,347 +Finally, I'll close with +whimsical new graphics APIs + +46 +00:02:27,347 --> 00:02:31,351 +and advanced +new layout APIs. + +47 +00:02:31,351 --> 00:02:35,122 +Let's get going +with Swift Charts. + +48 +00:02:35,122 --> 00:02:37,824 +Swift Charts +is a declarative framework + +49 +00:02:37,824 --> 00:02:42,162 +for building beautiful +state-driven charts. + +50 +00:02:42,162 --> 00:02:44,097 +The fundamental +design principles + +51 +00:02:44,097 --> 00:02:48,735 +that make SwiftUI great +and the process of plotting data + +52 +00:02:48,735 --> 00:02:52,606 +have been composed harmoniously +to create Swift Charts -- + +53 +00:02:52,606 --> 00:02:57,744 +a world-class data-visualization +framework. + +54 +00:02:57,744 --> 00:03:01,114 +This is a bar chart plotting +the number of party tasks + +55 +00:03:01,114 --> 00:03:04,818 +Franck and I need to complete +before the party can start. + +56 +00:03:04,818 --> 00:03:07,254 +Swift Charts has built +a magnificent, + +57 +00:03:07,254 --> 00:03:11,291 +customizable chart from +only a few lines of code. + +58 +00:03:11,291 --> 00:03:15,495 +Like SwiftUI, Swift Charts +picks intelligent defaults. + +59 +00:03:15,495 --> 00:03:18,565 +Here, the framework chose +satisfyingly round numbers + +60 +00:03:18,565 --> 00:03:20,767 +for the y-axis values, + +61 +00:03:20,767 --> 00:03:23,370 +and provided a default color +for the bar marks. + +62 +00:03:26,273 --> 00:03:30,210 +If you know SwiftUI, you can +already read the declarative, + +63 +00:03:30,210 --> 00:03:33,580 +state-driven syntax +of Swift Charts. + +64 +00:03:33,580 --> 00:03:36,950 +Chart is just some View, + +65 +00:03:36,950 --> 00:03:40,587 +and you declare it just like +you do lists and tables -- + +66 +00:03:40,587 --> 00:03:44,024 +by providing data and then +building the chart content + +67 +00:03:44,024 --> 00:03:47,627 +with that data. + +68 +00:03:47,627 --> 00:03:50,397 +For this chart, +I've chosen a BarMark, + +69 +00:03:50,397 --> 00:03:54,367 +but if I switch to a LineMark +and add a foreground style + +70 +00:03:54,367 --> 00:03:57,771 +to group by category, +I can see more to the story + +71 +00:03:57,771 --> 00:04:02,576 +as Swift Charts draws individual +lines for each category + +72 +00:04:02,576 --> 00:04:07,347 +and adds a legend +to the chart automatically. + +73 +00:04:07,347 --> 00:04:10,517 +It's fun to give these +charts a little personality. + +74 +00:04:10,517 --> 00:04:13,553 +I can add points to the line +with the symbol modifier + +75 +00:04:13,553 --> 00:04:16,223 +on the LineMark. + +76 +00:04:16,223 --> 00:04:21,328 +These modifiers are no different +from SwiftUI modifiers. + +77 +00:04:21,328 --> 00:04:25,832 +You can even use +SwiftUI views within a chart. + +78 +00:04:25,832 --> 00:04:28,435 +Like List, +the data argument to Chart + +79 +00:04:28,435 --> 00:04:32,606 +can instead be passed +to a ForEach. + +80 +00:04:32,606 --> 00:04:36,343 +This allows adding +more marks to the Chart builder, + +81 +00:04:36,343 --> 00:04:39,179 +like a RuleMark +to show our daily goal. + +82 +00:04:41,715 --> 00:04:44,851 +The spirit of SwiftUI +shines through again, + +83 +00:04:44,851 --> 00:04:48,688 +as Swift Charts handles +localization, Dark Mode, + +84 +00:04:48,688 --> 00:04:51,658 +and Dynamic Type automatically, + +85 +00:04:51,658 --> 00:04:56,563 +and of course, works across +all of our platforms. + +86 +00:04:56,563 --> 00:04:59,032 +If you want to see how +to make your own charts, + +87 +00:04:59,032 --> 00:05:01,334 +check out "Hello +Swift Charts." + +88 +00:05:01,334 --> 00:05:04,237 +If you're interested in advanced +plotting techniques, + +89 +00:05:04,237 --> 00:05:07,941 +follow it up with +the "Raise the bar" session. + +90 +00:05:07,941 --> 00:05:11,978 +Next up, let's talk about +navigation and windows. + +91 +00:05:11,978 --> 00:05:13,847 +SwiftUI already supports + +92 +00:05:13,847 --> 00:05:16,583 +the most common app +navigation patterns, + +93 +00:05:16,583 --> 00:05:20,820 +such as immersive +push-and-pop navigation stacks; + +94 +00:05:20,820 --> 00:05:24,024 +expansive, +detail-rich split views; + +95 +00:05:24,024 --> 00:05:27,027 +and powerful, +multi-window experiences. + +96 +00:05:31,131 --> 00:05:34,267 +This year, +SwiftUI has big updates + +97 +00:05:34,267 --> 00:05:37,304 +for all three of these patterns. + +98 +00:05:37,304 --> 00:05:40,674 +Let's start with stacks. + +99 +00:05:40,674 --> 00:05:43,944 +SwiftUI is introducing +a new container view, + +100 +00:05:43,944 --> 00:05:46,947 +simply called +NavigationStack, + +101 +00:05:46,947 --> 00:05:51,218 +for supporting +push-and-pop-style navigation. + +102 +00:05:51,218 --> 00:05:54,321 +A NavigationStack wraps +a root content view, + +103 +00:05:54,321 --> 00:05:58,992 +like this food inventory list +for our party-planning app. + +104 +00:05:58,992 --> 00:06:02,495 +As you'd expect, it works great +with existing APIs + +105 +00:06:02,495 --> 00:06:06,700 +like NavigationLink +and navigationTitle(). + +106 +00:06:06,700 --> 00:06:08,134 +When we select a link, + +107 +00:06:08,134 --> 00:06:14,874 +SwiftUI pushes its detail view +on top of the stack. + +108 +00:06:14,874 --> 00:06:18,511 +In our app, each detail view +contains more links + +109 +00:06:18,511 --> 00:06:21,047 +for related food items +for quick browsing. + +110 +00:06:25,819 --> 00:06:28,521 +This approach might be +all you need. + +111 +00:06:28,521 --> 00:06:31,024 +But there is a new way +to present views + +112 +00:06:31,024 --> 00:06:35,762 +and have programmatic control +over that presented state. + +113 +00:06:35,762 --> 00:06:39,733 +If you need control +of a navigation stack's state, + +114 +00:06:39,733 --> 00:06:43,970 +adopt the new data-driven APIs. + +115 +00:06:43,970 --> 00:06:47,140 +The new navigationDestination() +modifier + +116 +00:06:47,140 --> 00:06:49,876 +lets us associate +navigation destinations + +117 +00:06:49,876 --> 00:06:52,279 +with specific data types. + +118 +00:06:54,914 --> 00:06:57,484 +And this year, +we taught NavigationLink + +119 +00:06:57,484 --> 00:07:01,588 +a new party trick: +instead of a destination view, + +120 +00:07:01,588 --> 00:07:04,958 +it can take a value +that represents a destination. + +121 +00:07:04,958 --> 00:07:11,598 +When tapping on a link, SwiftUI +will use its value's type + +122 +00:07:11,598 --> 00:07:14,968 +to find the right destination +and push it on the stack, + +123 +00:07:14,968 --> 00:07:18,838 +just like before. + +124 +00:07:18,838 --> 00:07:21,841 +Because we now use data +to drive our stack, + +125 +00:07:21,841 --> 00:07:24,844 +it's possible to represent +the current navigation path + +126 +00:07:24,844 --> 00:07:27,247 +as explicit state. + +127 +00:07:27,247 --> 00:07:30,817 +In this case, the navigation +path is simply an array + +128 +00:07:30,817 --> 00:07:35,555 +of all the food items +that we've visited. + +129 +00:07:35,555 --> 00:07:37,724 +With direct access +to this state, + +130 +00:07:37,724 --> 00:07:39,993 +it couldn't be easier +to add a button + +131 +00:07:39,993 --> 00:07:45,031 +for quickly jumping back +to the first selected item. + +132 +00:07:45,031 --> 00:07:47,400 +As views are pushed +onto the stack, + +133 +00:07:47,400 --> 00:07:50,870 +items are appended to +the selectedFoodItems array. + +134 +00:07:50,870 --> 00:07:54,774 +In the button's action, +we can just remove all the items + +135 +00:07:54,774 --> 00:07:57,811 +from the path +except for the first one. + +136 +00:08:00,547 --> 00:08:04,017 +And with a tap, we're right back +where we started. + +137 +00:08:07,687 --> 00:08:13,760 +Now let's talk about split views +for multicolumn navigation. + +138 +00:08:13,760 --> 00:08:15,762 +We're introducing +another new container + +139 +00:08:15,762 --> 00:08:20,333 +called NavigationSplitView +for multicolumn navigation. + +140 +00:08:20,333 --> 00:08:25,004 +NavigationSplitView can declare +two- and three-column layouts. + +141 +00:08:25,004 --> 00:08:27,507 +Party Planner uses +a simple two-column layout, + +142 +00:08:27,507 --> 00:08:31,077 +wrapping a sidebar list +of our party-planning tasks + +143 +00:08:31,077 --> 00:08:33,680 +and a detail view +that changes its content + +144 +00:08:33,680 --> 00:08:37,450 +with the selected task. + +145 +00:08:37,450 --> 00:08:41,788 +Split views work great with the +new value-based NavigationLinks + +146 +00:08:41,788 --> 00:08:44,557 +we saw earlier, +using the link's value + +147 +00:08:44,557 --> 00:08:49,362 +to drive +the list's selection. + +148 +00:08:49,362 --> 00:08:52,065 +NavigationSplitView will +automatically collapse + +149 +00:08:52,065 --> 00:08:55,802 +into a stack on smaller-size +classes or devices, + +150 +00:08:55,802 --> 00:08:58,705 +making it a great tool +for building adaptive, + +151 +00:08:58,705 --> 00:09:02,409 +multiplatform apps. + +152 +00:09:02,409 --> 00:09:05,278 +NavigationSplitView +and NavigationStack + +153 +00:09:05,278 --> 00:09:09,916 +are designed to work together +and can be directly composed + +154 +00:09:09,916 --> 00:09:15,455 +to build more complex +navigation structures. + +155 +00:09:15,455 --> 00:09:19,426 +We use this in the Party Planner +app to turn the detail column + +156 +00:09:19,426 --> 00:09:23,563 +into its own, self-contained +navigation stack, + +157 +00:09:23,563 --> 00:09:25,865 +which also shows off +the new support + +158 +00:09:25,865 --> 00:09:29,202 +for navigation stacks +on macOS. + +159 +00:09:35,141 --> 00:09:38,044 +Well, we've talked a lot +about food, + +160 +00:09:38,044 --> 00:09:41,314 +but I hear that my colleague +Curt is cooking up a storm + +161 +00:09:41,314 --> 00:09:45,418 +over in his talk, "The SwiftUI +cookbook for navigation." + +162 +00:09:45,418 --> 00:09:49,055 +Check it out to learn even +more about navigation stacks + +163 +00:09:49,055 --> 00:09:51,624 +and navigation split views. + +164 +00:09:51,624 --> 00:09:54,461 +But for now, +let's step outside the box + +165 +00:09:54,461 --> 00:09:58,331 +and talk about new scene APIs. + +166 +00:09:58,331 --> 00:10:00,800 +You're likely already familiar +with WindowGroup, + +167 +00:10:00,800 --> 00:10:04,037 +which is a great way to build +the main interface of your app, + +168 +00:10:04,037 --> 00:10:06,105 +and can generate +multiple windows + +169 +00:10:06,105 --> 00:10:10,743 +to allow different perspectives +into your app's data. + +170 +00:10:10,743 --> 00:10:13,980 +New this year, +we're adding window, + +171 +00:10:13,980 --> 00:10:16,950 +which -- you guessed it -- +declares a single, + +172 +00:10:16,950 --> 00:10:19,853 +unique window for your app. + +173 +00:10:19,853 --> 00:10:22,222 +Here, I've added +a Party Budget window + +174 +00:10:22,222 --> 00:10:24,624 +that shows the total cost +of the party. + +175 +00:10:26,893 --> 00:10:30,363 +By default the window +is available and can be shown + +176 +00:10:30,363 --> 00:10:34,868 +by selecting its name +in the app's Window menu. + +177 +00:10:34,868 --> 00:10:37,570 +But we can make +that even easier by assigning + +178 +00:10:37,570 --> 00:10:43,009 +a Command-0 keyboard +shortcut to open the window. + +179 +00:10:43,009 --> 00:10:45,879 +To make sure I stay a +budget-conscious party planner, + +180 +00:10:45,879 --> 00:10:47,680 +I'll add a toolbar button + +181 +00:10:47,680 --> 00:10:50,884 +with an action +that also shows this window. + +182 +00:10:50,884 --> 00:10:53,453 +Using the environment action +openWindow, + +183 +00:10:53,453 --> 00:11:00,393 +I can now programmatically open +new SwiftUI-managed windows. + +184 +00:11:00,393 --> 00:11:03,029 +In fact, we've added +a whole suite + +185 +00:11:03,029 --> 00:11:05,665 +of new window customizations +this year, + +186 +00:11:05,665 --> 00:11:08,101 +including modifiers +for default size, + +187 +00:11:08,101 --> 00:11:13,406 +position, resizability, +and more. + +188 +00:11:13,406 --> 00:11:15,909 +I don't want the party budget +getting in the way, + +189 +00:11:15,909 --> 00:11:19,479 +so by default it appears +as a small corner window. + +190 +00:11:19,479 --> 00:11:22,248 +But if I adjust +its position or size, + +191 +00:11:22,248 --> 00:11:24,450 +SwiftUI will automatically +remember that + +192 +00:11:24,450 --> 00:11:27,654 +across app launches. + +193 +00:11:27,654 --> 00:11:30,056 +The new standalone +window scene is great + +194 +00:11:30,056 --> 00:11:33,426 +for little auxiliary windows +like this one on the Mac, + +195 +00:11:33,426 --> 00:11:36,429 +but Party Planner +is a multiplatform app, + +196 +00:11:36,429 --> 00:11:40,500 +and we need a better design +for smaller screens. + +197 +00:11:40,500 --> 00:11:44,470 +For example, on iOS we've chosen +to display our budget + +198 +00:11:44,470 --> 00:11:47,340 +within a resizable sheet +instead. + +199 +00:11:47,340 --> 00:11:51,544 +This is possible with the new +presentationDetents() modifier. + +200 +00:11:51,544 --> 00:11:53,947 +In this case, I configured +a resizable sheet + +201 +00:11:53,947 --> 00:11:59,152 +that sticks to two different +sizes: one at 250 points, + +202 +00:11:59,152 --> 00:12:03,856 +and another at a system-defined +medium height. + +203 +00:12:03,856 --> 00:12:06,960 +It is simple to iterate +between platforms this year + +204 +00:12:06,960 --> 00:12:09,329 +with multiplatform targets +in Xcode + +205 +00:12:09,329 --> 00:12:12,532 +powering up +your SwiftUI-based apps. + +206 +00:12:12,532 --> 00:12:17,003 +One target can be deployed +to multiple platforms. + +207 +00:12:17,003 --> 00:12:19,973 +Just pick your platform +from the usual pull-down menu + +208 +00:12:19,973 --> 00:12:23,142 +in Xcode's toolbar. + +209 +00:12:23,142 --> 00:12:26,045 +Watch "What's new in Xcode" +and follow it up + +210 +00:12:26,045 --> 00:12:28,881 +with "Use Xcode to develop +a multiplatform app" + +211 +00:12:28,881 --> 00:12:31,951 +to learn more. + +212 +00:12:31,951 --> 00:12:33,786 +For the final new scene type, + +213 +00:12:33,786 --> 00:12:36,322 +we can turn our attention +to the menu bar. + +214 +00:12:36,322 --> 00:12:39,759 +With macOS Ventura, you can +now build MenuBarExtras + +215 +00:12:39,759 --> 00:12:44,163 +entirely in SwiftUI! + +216 +00:12:44,163 --> 00:12:46,399 +These can be defined +alongside other scene types + +217 +00:12:46,399 --> 00:12:49,969 +in your application and will +always be shown in the menu bar + +218 +00:12:49,969 --> 00:12:53,206 +while your app is running. + +219 +00:12:53,206 --> 00:12:57,777 +Or, you can build an entire app +using just a MenuBarExtra! + +220 +00:12:57,777 --> 00:13:01,247 +These are such a fun way to +bring even the simplest of ideas + +221 +00:13:01,247 --> 00:13:04,450 +to life on macOS. + +222 +00:13:04,450 --> 00:13:07,053 +"Bring Multiple Windows +to your SwiftUI App" + +223 +00:13:07,053 --> 00:13:09,122 +has more detail +on how to take advantage + +224 +00:13:09,122 --> 00:13:12,392 +of all the new scene types +and features. + +225 +00:13:12,392 --> 00:13:14,627 +Now that we've got control +of windows, + +226 +00:13:14,627 --> 00:13:18,331 +I'll pass it over to Franck, +to put controls in windows. + +227 +00:13:18,331 --> 00:13:19,499 +Franck: Thanks, Nick! + +228 +00:13:19,499 --> 00:13:21,934 +This year, we have +a variety of enhancements + +229 +00:13:21,934 --> 00:13:26,806 +across all our APIs for building +interactive content. + +230 +00:13:26,806 --> 00:13:30,109 +We have a lot to cover, +so let's get this party started + +231 +00:13:30,109 --> 00:13:33,413 +with some fun enhancements +to forms. + +232 +00:13:33,413 --> 00:13:36,949 +macOS Ventura comes with a +brand-new System Settings app, + +233 +00:13:36,949 --> 00:13:40,219 +that features a streamlined +navigation structure, + +234 +00:13:40,219 --> 00:13:42,722 +built using the navigation +split view and stacks + +235 +00:13:42,722 --> 00:13:46,192 +that Nick just +walked us through. + +236 +00:13:46,192 --> 00:13:50,263 +It also sports a fresh +and modern interface style. + +237 +00:13:50,263 --> 00:13:53,099 +Settings interfaces +are control-heavy, + +238 +00:13:53,099 --> 00:13:55,802 +so this style was +specifically designed + +239 +00:13:55,802 --> 00:13:58,204 +to present forms +containing many controls + +240 +00:13:58,204 --> 00:14:02,341 +in a consistent and +well-organized fashion. + +241 +00:14:02,341 --> 00:14:04,177 +We've also adopted +this new design + +242 +00:14:04,177 --> 00:14:06,112 +within our Party Planner app. + +243 +00:14:06,112 --> 00:14:08,381 +Let's take a look. + +244 +00:14:08,381 --> 00:14:10,783 +Our Event Details view +also features + +245 +00:14:10,783 --> 00:14:13,920 +many different types of controls +grouped into sections, + +246 +00:14:13,920 --> 00:14:18,825 +serving a similar purpose +as a settings interface. + +247 +00:14:18,825 --> 00:14:22,228 +This makes it a great candidate +to adopt the new visual style + +248 +00:14:22,228 --> 00:14:23,596 +from System Settings. + +249 +00:14:25,865 --> 00:14:27,633 +You can enable this design + +250 +00:14:27,633 --> 00:14:31,838 +using the new grouped +formStyle on macOS. + +251 +00:14:31,838 --> 00:14:35,842 +And thanks to the flexibility +of SwiftUI's declarative APIs, + +252 +00:14:35,842 --> 00:14:38,211 +content and controls +within the form + +253 +00:14:38,211 --> 00:14:42,482 +will automatically adapt +to the new style. + +254 +00:14:42,482 --> 00:14:45,785 +For example, sections will +visually group their content + +255 +00:14:45,785 --> 00:14:47,653 +below their headers; + +256 +00:14:47,653 --> 00:14:51,390 +and controls will consistently +align their labels and values + +257 +00:14:51,390 --> 00:14:54,594 +to the leading +and trailing edges. + +258 +00:14:54,594 --> 00:14:57,964 +Some controls may adapt +their visual appearance as well, + +259 +00:14:57,964 --> 00:15:01,768 +such as how toggles display +as trailing mini switches + +260 +00:15:01,768 --> 00:15:05,905 +for consistent layout +and alignment. + +261 +00:15:05,905 --> 00:15:07,406 +And since the form +itself provides + +262 +00:15:07,406 --> 00:15:09,575 +a lot of visual structure, + +263 +00:15:09,575 --> 00:15:10,743 +other controls adapt +to this context + +264 +00:15:10,743 --> 00:15:14,247 +with a lighter-weight +visual appearance, + +265 +00:15:14,247 --> 00:15:18,317 +and reveal more prominent +control backings on rollover. + +266 +00:15:18,317 --> 00:15:21,154 +SwiftUI makes it easy to align +other types of content + +267 +00:15:21,154 --> 00:15:25,224 +to this new style, using the new +LabeledContent view, + +268 +00:15:25,224 --> 00:15:27,693 +which can be used +to build new controls + +269 +00:15:27,693 --> 00:15:32,031 +or even just display +some read-only information. + +270 +00:15:32,031 --> 00:15:33,166 +In this case, + +271 +00:15:33,166 --> 00:15:36,269 +we're displaying some text +for the location of the event, + +272 +00:15:36,269 --> 00:15:39,138 +and SwiftUI automatically +adjusts the styling + +273 +00:15:39,138 --> 00:15:41,340 +and allows selection +of that text. + +274 +00:15:43,976 --> 00:15:48,014 +But LabeledContent can +also wrap any kind of view, + +275 +00:15:48,014 --> 00:15:50,516 +like if we wanted +to use a custom view + +276 +00:15:50,516 --> 00:15:54,587 +for displaying +more entire addresses. + +277 +00:15:54,587 --> 00:15:58,424 +SwiftUI is now smarter about +applying default styling to text + +278 +00:15:58,424 --> 00:16:00,860 +in other cases too. + +279 +00:16:00,860 --> 00:16:04,363 +It will hierarchically format +multiple pieces of text + +280 +00:16:04,363 --> 00:16:10,469 +within a control's label +to form titles and subtitles. + +281 +00:16:10,469 --> 00:16:13,239 +This new form design +looks great on macOS, + +282 +00:16:13,239 --> 00:16:15,842 +but we can also share +a lot of this same code + +283 +00:16:15,842 --> 00:16:17,777 +with the iOS version of our app. + +284 +00:16:19,846 --> 00:16:23,349 +You'll notice some improved +designs on iOS as well, + +285 +00:16:23,349 --> 00:16:26,219 +like these pop-up menu +pickers with a visual style + +286 +00:16:26,219 --> 00:16:29,989 +inspired by macOS, +but with their interactions + +287 +00:16:29,989 --> 00:16:32,825 +and appearance optimized +to fit beautifully + +288 +00:16:32,825 --> 00:16:37,496 +within a touch-based interface. + +289 +00:16:37,496 --> 00:16:42,001 +Of course, the same code works +great on iPad's larger screen, + +290 +00:16:42,001 --> 00:16:43,502 +and together with the Mac, + +291 +00:16:43,502 --> 00:16:46,505 +you can see how SwiftUI's +declarative model helps you + +292 +00:16:46,505 --> 00:16:49,642 +share code when building +shared interfaces, + +293 +00:16:49,642 --> 00:16:53,980 +helping you bring the party +to every platform. + +294 +00:16:53,980 --> 00:16:56,148 +Of course, we're also +improving controls + +295 +00:16:56,148 --> 00:16:59,185 +beyond just form styles. + +296 +00:16:59,185 --> 00:17:00,920 +So let's take +a lightning-round tour + +297 +00:17:00,920 --> 00:17:03,589 +of some other new control +features we're using + +298 +00:17:03,589 --> 00:17:06,392 +in the Party Planner app. + +299 +00:17:06,392 --> 00:17:10,963 +Let's start with the New +Activity page in our iOS app. + +300 +00:17:10,963 --> 00:17:14,166 +Text fields can be configured +to expand vertically + +301 +00:17:14,166 --> 00:17:16,669 +using the new axis parameter, + +302 +00:17:16,669 --> 00:17:19,272 +growing their height +to fit the text + +303 +00:17:19,272 --> 00:17:24,343 +and, if specified, capping +their height to the line limit. + +304 +00:17:24,343 --> 00:17:27,046 +But the lineLimit modifier +now also supports + +305 +00:17:27,046 --> 00:17:30,783 +more advanced behaviors, +like reserving a minimum amount + +306 +00:17:30,783 --> 00:17:35,187 +of space and expanding +as more content is added, + +307 +00:17:35,187 --> 00:17:41,394 +and then scrolling once the +content exceeds the upper limit. + +308 +00:17:41,394 --> 00:17:44,664 +Below our text fields, +we also see an example + +309 +00:17:44,664 --> 00:17:47,833 +of the new +MultiDatePicker control, + +310 +00:17:47,833 --> 00:17:50,569 +supporting noncontiguous +date selection + +311 +00:17:50,569 --> 00:17:53,673 +to help us spread our party +activities throughout the week. + +312 +00:17:55,608 --> 00:17:58,711 +Now at this point, maybe you're +having some mixed feelings + +313 +00:17:58,711 --> 00:18:01,614 +about the party theme +for this talk. + +314 +00:18:01,614 --> 00:18:04,250 +The great news is you can +now express those feelings + +315 +00:18:04,250 --> 00:18:08,955 +in SwiftUI, +using mixed-state controls! + +316 +00:18:08,955 --> 00:18:12,058 +Here we have a group of toggles +that can be collapsed + +317 +00:18:12,058 --> 00:18:16,062 +into a single aggregate toggle. + +318 +00:18:16,062 --> 00:18:19,398 +The inner toggles +each take a single binding + +319 +00:18:19,398 --> 00:18:22,435 +whereas the aggregate Toggle +takes a collection + +320 +00:18:22,435 --> 00:18:25,738 +of all the bindings +displaying a mixed state + +321 +00:18:25,738 --> 00:18:27,974 +if their values don't all match. + +322 +00:18:30,109 --> 00:18:32,645 +Pickers work the same way. + +323 +00:18:32,645 --> 00:18:35,247 +This decoration theme picker +changes its value + +324 +00:18:35,247 --> 00:18:38,851 +to reflect the currently +selected decoration. + +325 +00:18:38,851 --> 00:18:41,587 +But if we select +multiple decorations, + +326 +00:18:41,587 --> 00:18:43,789 +it will show the themes +for all them + +327 +00:18:43,789 --> 00:18:47,159 +using a mixed-state indicator. + +328 +00:18:47,159 --> 00:18:51,998 +Now, let's switch back +to our iOS app. + +329 +00:18:51,998 --> 00:18:54,166 +We have a few +button-style toggles + +330 +00:18:54,166 --> 00:18:57,870 +for choosing the event hashtags. + +331 +00:18:57,870 --> 00:18:59,872 +We can help +differentiate each toggle + +332 +00:18:59,872 --> 00:19:04,010 +by simply adding +a bordered button style. + +333 +00:19:04,010 --> 00:19:07,213 +Button styles like this will +now apply to any control + +334 +00:19:07,213 --> 00:19:10,116 +that supports +a button-like appearance, + +335 +00:19:10,116 --> 00:19:15,054 +including toggles, +menus, and pickers. + +336 +00:19:15,054 --> 00:19:17,023 +Moving on to steppers, + +337 +00:19:17,023 --> 00:19:20,893 +you can now provide +a format for its value. + +338 +00:19:20,893 --> 00:19:24,964 +On macOS, a formatted stepper +will display its value + +339 +00:19:24,964 --> 00:19:27,733 +in an editable field. + +340 +00:19:27,733 --> 00:19:33,506 +And steppers are also now +available on watchOS. + +341 +00:19:33,506 --> 00:19:36,709 +Apple Watch sports +one of my favorite new features: + +342 +00:19:36,709 --> 00:19:40,246 +Accessibility Quick Actions, +an alternative way + +343 +00:19:40,246 --> 00:19:44,683 +to perform actions +by clenching your hand. + +344 +00:19:44,683 --> 00:19:49,688 +A Quick Action can be defined +just like any other UI action, + +345 +00:19:49,688 --> 00:19:53,793 +using a button, allowing us +to share the same code + +346 +00:19:53,793 --> 00:19:59,298 +for both visible buttons and +their equivalent Quick Actions. + +347 +00:19:59,298 --> 00:20:03,436 +All right, we just covered +a lot of different controls, + +348 +00:20:03,436 --> 00:20:06,272 +but of course, controls +are not the only sources + +349 +00:20:06,272 --> 00:20:08,240 +of interactivity. + +350 +00:20:08,240 --> 00:20:09,742 +So let's take a look +at what's new + +351 +00:20:09,742 --> 00:20:12,144 +with larger interactive +containers, + +352 +00:20:12,144 --> 00:20:13,779 +like tables and lists. + +353 +00:20:14,847 --> 00:20:15,881 +I am excited to share + +354 +00:20:15,881 --> 00:20:20,586 +that tables are now +supported on iPadOS. + +355 +00:20:20,586 --> 00:20:24,256 +As you would expect, +tables on iPadOS are defined + +356 +00:20:24,256 --> 00:20:29,228 +using the same Table API we +introduced last year for macOS, + +357 +00:20:29,228 --> 00:20:32,865 +making it easy to share code +between platforms. + +358 +00:20:32,865 --> 00:20:36,702 +Our Invitations table shows +three columns for the name, + +359 +00:20:36,702 --> 00:20:40,539 +city, and invitation status +of each person, + +360 +00:20:40,539 --> 00:20:45,644 +taking advantage +of the iPad's large display. + +361 +00:20:45,644 --> 00:20:48,681 +But this table will also render +appropriately + +362 +00:20:48,681 --> 00:20:52,318 +in compact size classes, +including on iPhone, + +363 +00:20:52,318 --> 00:20:58,090 +showing just the primary column +within the smaller screen space. + +364 +00:20:58,090 --> 00:21:02,561 +Let's switch contexts and +check out this table on macOS. + +365 +00:21:02,561 --> 00:21:03,896 +It's looking great! + +366 +00:21:03,896 --> 00:21:07,633 +But speaking on contexts, I'd +love to add some context menus + +367 +00:21:07,633 --> 00:21:11,904 +for performing common actions +within the table. + +368 +00:21:11,904 --> 00:21:12,805 +This is a job + +369 +00:21:12,805 --> 00:21:17,376 +for the new selection-based +contentMenu modifier. + +370 +00:21:17,376 --> 00:21:19,745 +The modifier takes +a selection type, + +371 +00:21:19,745 --> 00:21:22,781 +and will be enabled +within any compatible table + +372 +00:21:22,781 --> 00:21:26,519 +or list that supports selection. + +373 +00:21:26,519 --> 00:21:29,655 +Within the menu builder, +you are given a collection + +374 +00:21:29,655 --> 00:21:32,758 +of the current selection, +allowing you to build + +375 +00:21:32,758 --> 00:21:35,661 +advanced context menus +that can operate + +376 +00:21:35,661 --> 00:21:39,198 +on a single selected row, +multiple selected rows, + +377 +00:21:39,198 --> 00:21:41,267 +or even no row selected, + +378 +00:21:41,267 --> 00:21:46,005 +such as when clicking +on the empty area of the table. + +379 +00:21:46,005 --> 00:21:49,608 +Context menus reveal actions +directly within the table, + +380 +00:21:49,608 --> 00:21:52,778 +which is great for speed +and efficiency. + +381 +00:21:52,778 --> 00:21:57,149 +But I would also like to make +these actions more discoverable. + +382 +00:21:57,149 --> 00:21:59,919 +A great way to improve +discoverability + +383 +00:21:59,919 --> 00:22:04,023 +is by displaying common actions +as buttons in the toolbar, + +384 +00:22:04,023 --> 00:22:07,626 +and iPadOS has a new +and improved toolbar design + +385 +00:22:07,626 --> 00:22:12,932 +to help achieve +that extra level of polish. + +386 +00:22:12,932 --> 00:22:16,302 +iPad toolbars can now support +user customization + +387 +00:22:16,302 --> 00:22:18,103 +and reordering, + +388 +00:22:18,103 --> 00:22:22,341 +which your app can implement by +providing explicit identifiers + +389 +00:22:22,341 --> 00:22:28,681 +for each toolbar item, +the same API available on macOS. + +390 +00:22:28,681 --> 00:22:32,351 +These identifiers allow SwiftUI +to automatically save + +391 +00:22:32,351 --> 00:22:35,287 +and restore custom +toolbar configurations + +392 +00:22:35,287 --> 00:22:37,790 +across app launches. + +393 +00:22:37,790 --> 00:22:39,558 +Note that on iPadOS, + +394 +00:22:39,558 --> 00:22:44,196 +not all toolbar items +allow customization. + +395 +00:22:44,196 --> 00:22:46,365 +Customizable actions +are configured + +396 +00:22:46,365 --> 00:22:50,703 +using the new secondaryAction +toolbar item placement, + +397 +00:22:50,703 --> 00:22:54,139 +which shows up in the center +of the toolbar by default, + +398 +00:22:54,139 --> 00:22:58,577 +or in an overflow menu +in compact-size classes. + +399 +00:22:58,577 --> 00:22:59,778 +All right! + +400 +00:22:59,778 --> 00:23:02,014 +The word is spreading around +and it looks like + +401 +00:23:02,014 --> 00:23:06,352 +the number of attendees +is growing exponentially. + +402 +00:23:06,352 --> 00:23:08,487 +Let's help our table +manage the scale + +403 +00:23:08,487 --> 00:23:12,191 +by adding support for search. + +404 +00:23:12,191 --> 00:23:14,660 +SwiftUI already +supports basic search + +405 +00:23:14,660 --> 00:23:17,930 +with a searchable modifier. + +406 +00:23:17,930 --> 00:23:21,900 +And new this year, search fields +can support tokenized inputs + +407 +00:23:21,900 --> 00:23:28,007 +and suggestions to help build +more structured search queries. + +408 +00:23:28,007 --> 00:23:31,543 +To help with filtering results, +SwiftUI now supports + +409 +00:23:31,543 --> 00:23:34,780 +search scopes, +which appear in a scope bar + +410 +00:23:34,780 --> 00:23:39,485 +beneath the toolbar on macOS +and as a segmented control + +411 +00:23:39,485 --> 00:23:43,289 +within the navigation bar +on iOS. + +412 +00:23:43,289 --> 00:23:45,057 +We have only scratched +the surface + +413 +00:23:45,057 --> 00:23:48,794 +of what is possible with SwiftUI +on iPad this year. + +414 +00:23:48,794 --> 00:23:52,531 +Check out the "SwiftUI on iPad" +series and learn more. + +415 +00:23:52,531 --> 00:23:55,067 +Now that we have a bit more +control over the event details + +416 +00:23:55,067 --> 00:23:57,603 +and logistics, +let's share the news + +417 +00:23:57,603 --> 00:24:00,873 +and get people +even more excited. + +418 +00:24:00,873 --> 00:24:02,975 +Sharing content +with other people, + +419 +00:24:02,975 --> 00:24:05,611 +as well as sharing data +across applications + +420 +00:24:05,611 --> 00:24:09,148 +are essential parts +of many apps. + +421 +00:24:09,148 --> 00:24:11,150 +Taking advantage +of these features + +422 +00:24:11,150 --> 00:24:13,719 +makes your app even more +integrated into the workflows + +423 +00:24:13,719 --> 00:24:16,889 +of the people who use them. + +424 +00:24:16,889 --> 00:24:19,892 +This year we have +a few exciting areas + +425 +00:24:19,892 --> 00:24:22,928 +to make that even easier. + +426 +00:24:22,928 --> 00:24:26,265 +Let's start with PhotosPicker, +a new multiplatform + +427 +00:24:26,265 --> 00:24:31,737 +and privacy-preserving API +for picking photos and videos. + +428 +00:24:31,737 --> 00:24:35,708 +Since photos are an essential +part of any party, + +429 +00:24:35,708 --> 00:24:38,544 +I've added a feature +to the Party Planner app + +430 +00:24:38,544 --> 00:24:43,716 +that adds fun birthday effects +to photos that were taken. + +431 +00:24:43,716 --> 00:24:48,220 +The new PhotosPicker view can +be placed anywhere in your app, + +432 +00:24:48,220 --> 00:24:52,424 +and on activation, presents +the standard photos-picking UI + +433 +00:24:52,424 --> 00:24:57,696 +to select photos or videos +from the user's library. + +434 +00:24:57,696 --> 00:25:01,900 +PhotosPicker take a binding +to a selected item, + +435 +00:25:01,900 --> 00:25:05,671 +which provides access to +the actual photo and video data. + +436 +00:25:07,206 --> 00:25:10,676 +It also has additional +rich configuration options, + +437 +00:25:10,676 --> 00:25:13,345 +such as filtering +the type of content, + +438 +00:25:13,345 --> 00:25:16,181 +preferred photo encoding, +and more. + +439 +00:25:18,450 --> 00:25:22,454 +This is the most photogenic +cupcake I have ever seen. + +440 +00:25:22,454 --> 00:25:25,324 +But one cupcake +isn't enough. + +441 +00:25:25,324 --> 00:25:29,762 +Let's apply the special effect +as we move on. + +442 +00:25:29,762 --> 00:25:32,231 +Now that we have +our customized photo, + +443 +00:25:32,231 --> 00:25:36,769 +we're ready to share it +with the new ShareLink API. + +444 +00:25:36,769 --> 00:25:40,706 +Each platform has a standard +interface for allowing people + +445 +00:25:40,706 --> 00:25:43,742 +to share content from your apps. + +446 +00:25:43,742 --> 00:25:47,813 +With watchOS 9, you can now +also present the share sheet + +447 +00:25:47,813 --> 00:25:50,883 +from within your watch apps. + +448 +00:25:50,883 --> 00:25:53,585 +The new ShareLink view +enables presenting + +449 +00:25:53,585 --> 00:25:57,423 +that system share sheet +from within your app. + +450 +00:25:57,423 --> 00:26:00,592 +You can simply provide it +with the content to be shared + +451 +00:26:00,592 --> 00:26:04,029 +and a preview to use +in the share sheet, + +452 +00:26:04,029 --> 00:26:08,033 +and it automatically creates +a standard share icon button. + +453 +00:26:10,068 --> 00:26:13,071 +On tap, it presents +the standard share sheet + +454 +00:26:13,071 --> 00:26:15,774 +to send off the content. + +455 +00:26:15,774 --> 00:26:18,811 +Share links adapt to the +context they're applied to, + +456 +00:26:18,811 --> 00:26:23,782 +such as in context menus +and across platforms. + +457 +00:26:23,782 --> 00:26:28,086 +PhotosPicker, ShareLink, +and more all take advantage + +458 +00:26:28,086 --> 00:26:30,823 +of the new +Transferable protocol, + +459 +00:26:30,823 --> 00:26:34,693 +a Swift-first declarative way +to describe how types + +460 +00:26:34,693 --> 00:26:37,563 +are transferred +across applications. + +461 +00:26:37,563 --> 00:26:41,266 +Transferable types are used +to power SwiftUI features + +462 +00:26:41,266 --> 00:26:45,571 +like drag-and-drop, which +makes it easy to drop images + +463 +00:26:45,571 --> 00:26:50,375 +from other apps into +the Party Planner gallery. + +464 +00:26:50,375 --> 00:26:53,846 +This makes use of the new +dropDestination API, + +465 +00:26:53,846 --> 00:26:58,617 +which accepts a payload type, +in this case, just an image. + +466 +00:26:58,617 --> 00:27:00,986 +The completion block +provides a collection + +467 +00:27:00,986 --> 00:27:04,957 +of the received images together +with the drop location. + +468 +00:27:07,493 --> 00:27:10,796 +Many standard types, +such as string and image, + +469 +00:27:10,796 --> 00:27:14,066 +already conform to Transferable. + +470 +00:27:14,066 --> 00:27:17,836 +So it wasn't much work to get +the ball rolling in our app, + +471 +00:27:17,836 --> 00:27:20,038 +but you can easily +take things further + +472 +00:27:20,038 --> 00:27:23,575 +and implement Transferable +in your own custom types. + +473 +00:27:23,575 --> 00:27:26,879 +When it's time to do that, +your conformance declares + +474 +00:27:26,879 --> 00:27:29,982 +the representations +appropriate for your type, + +475 +00:27:29,982 --> 00:27:34,853 +such as using Codable support +and a custom content type. + +476 +00:27:34,853 --> 00:27:36,955 +To learn more +about Transferable, + +477 +00:27:36,955 --> 00:27:41,026 +other representations, +and advanced tips and tricks, + +478 +00:27:41,026 --> 00:27:44,029 +check out the +"Meet Transferable" talk. + +479 +00:27:44,029 --> 00:27:46,365 +While we were preparing +the cupcakes, + +480 +00:27:46,365 --> 00:27:49,067 +Nick was laying out +all the supplies. + +481 +00:27:49,067 --> 00:27:51,336 +Nick, how's it +going over there? + +482 +00:27:51,336 --> 00:27:53,672 +Nick: Almost done! + +483 +00:27:53,672 --> 00:27:56,775 +I'm arranging these party horns +in a completely custom layout, + +484 +00:27:56,775 --> 00:27:59,077 +but I'll need +a little more time. + +485 +00:27:59,077 --> 00:28:02,481 +Let's talk graphics first. + +486 +00:28:02,481 --> 00:28:04,550 +ShapeStyle has new APIs + +487 +00:28:04,550 --> 00:28:08,253 +to achieve rich +graphical effects this year. + +488 +00:28:08,253 --> 00:28:14,159 +We'll use these APIs to give +this guest card some party pop! + +489 +00:28:14,159 --> 00:28:16,361 +Color has a new +gradient property + +490 +00:28:16,361 --> 00:28:21,099 +that adds a subtle gradient +derived from the color. + +491 +00:28:21,099 --> 00:28:23,769 +These look great +with the system colors. + +492 +00:28:25,837 --> 00:28:30,208 +ShapeStyle also got +a new shadow modifier. + +493 +00:28:30,208 --> 00:28:32,144 +Adding it to the white +foreground style + +494 +00:28:32,144 --> 00:28:36,315 +adds a shadow to the text +and to the symbol. + +495 +00:28:36,315 --> 00:28:39,418 +And the detail of this shadow +is remarkable. + +496 +00:28:39,418 --> 00:28:42,220 +The drop shadow has applied +to every element + +497 +00:28:42,220 --> 00:28:43,855 +of the Calendar symbol. + +498 +00:28:47,192 --> 00:28:49,828 +With the whole world +of SF Symbols + +499 +00:28:49,828 --> 00:28:52,564 +and the new SwiftUI +ShapeStyle extensions, + +500 +00:28:52,564 --> 00:28:55,567 +you can make some +absolutely gorgeous icons. + +501 +00:28:57,736 --> 00:29:03,208 +Now, it's time to bring that +grid of SF Symbols to the party. + +502 +00:29:03,208 --> 00:29:06,945 +We'll iterate quickly on it +using SwiftUI Previews, + +503 +00:29:06,945 --> 00:29:10,916 +which has some fantastic +improvements this year. + +504 +00:29:10,916 --> 00:29:13,318 +Previews have always been +a convenient way + +505 +00:29:13,318 --> 00:29:17,823 +to see a view in multiple +configurations at the same time. + +506 +00:29:17,823 --> 00:29:21,493 +With Xcode 14, we're making +this easier than ever + +507 +00:29:21,493 --> 00:29:23,929 +with preview variants. + +508 +00:29:23,929 --> 00:29:27,432 +These let you develop your view +in multiple appearances, + +509 +00:29:27,432 --> 00:29:30,669 +type sizes, or orientations +at the same time + +510 +00:29:30,669 --> 00:29:34,840 +without writing +any configuration code. + +511 +00:29:34,840 --> 00:29:37,009 +We can use that +same gradient again, + +512 +00:29:37,009 --> 00:29:39,344 +or we can style it +as an elliptical gradient + +513 +00:29:39,344 --> 00:29:41,213 +to give these images +a soft glow. + +514 +00:29:41,213 --> 00:29:43,815 +and preview it in dark +and light appearances. + +515 +00:29:46,985 --> 00:29:50,489 +Previews now runs +in live mode by default. + +516 +00:29:50,489 --> 00:29:52,224 +It can't be a great +birthday party + +517 +00:29:52,224 --> 00:29:54,259 +without a little dancing, + +518 +00:29:54,259 --> 00:29:57,696 +so let's get these +SF Symbols dancing. + +519 +00:29:57,696 --> 00:30:01,867 +♪ Electronic dance music ♪ + +520 +00:30:01,867 --> 00:30:10,442 +♪ + +521 +00:30:12,010 --> 00:30:16,682 +Those jovial icons +demonstrate something profound. + +522 +00:30:16,682 --> 00:30:22,054 +SwiftUI has taken text and image +animations to the next level. + +523 +00:30:22,054 --> 00:30:26,358 +Let's watch that text animate +again in slow motion. + +524 +00:30:26,358 --> 00:30:29,828 +Text can now be beautifully +animated between weights, + +525 +00:30:29,828 --> 00:30:32,164 +styles, and even layouts. + +526 +00:30:32,164 --> 00:30:34,900 +And the best part: +this takes advantage + +527 +00:30:34,900 --> 00:30:40,439 +of the same animation APIs used +throughout the rest of SwiftUI. + +528 +00:30:40,439 --> 00:30:43,675 +Let's close by talking about +my absolute favorite part + +529 +00:30:43,675 --> 00:30:46,912 +of UI programming, +applied geometry -- + +530 +00:30:46,912 --> 00:30:49,114 +or as we call it, Layout. + +531 +00:30:49,114 --> 00:30:53,085 +SwiftUI has added new ways +to lay out views. + +532 +00:30:53,085 --> 00:30:56,621 +Grid is a new container view +that arranges views + +533 +00:30:56,621 --> 00:30:59,024 +in a two-dimensional grid. + +534 +00:30:59,024 --> 00:31:01,626 +Grid will measure its subviews +up front to enable cells + +535 +00:31:01,626 --> 00:31:05,530 +that span multiple columns +and enable automatic alignments + +536 +00:31:05,530 --> 00:31:07,999 +across rows and columns. + +537 +00:31:07,999 --> 00:31:11,703 +In fact, you already +got a look at grid earlier. + +538 +00:31:14,973 --> 00:31:19,444 +Using Grid, GridRow, and +the gridCellColumns modifier, + +539 +00:31:19,444 --> 00:31:21,980 +you can build up +a grid piecemeal. + +540 +00:31:21,980 --> 00:31:24,883 +Of course, just like all +layouts in SwiftUI, + +541 +00:31:24,883 --> 00:31:27,919 +they're built for composition. + +542 +00:31:27,919 --> 00:31:31,490 +We introduced SwiftUI's layout +model with the first release, + +543 +00:31:31,490 --> 00:31:34,226 +providing a toolbox +of primitive layout types + +544 +00:31:34,226 --> 00:31:38,630 +to achieve some +of the most common layouts. + +545 +00:31:38,630 --> 00:31:41,666 +Most of the time, +you can get the job done + +546 +00:31:41,666 --> 00:31:45,604 +with these primitive layout +types, but sometimes, + +547 +00:31:45,604 --> 00:31:48,106 +sometimes, you want +that imperative layout code: + +548 +00:31:48,106 --> 00:31:50,108 +the size, the minX, + +549 +00:31:50,108 --> 00:31:56,848 +the frame.origin.x minus +frame.midX divided by 2 plus 3. + +550 +00:31:56,848 --> 00:31:58,483 +It's times like these + +551 +00:31:58,483 --> 00:32:03,155 +when you should reach +for the new Layout protocol. + +552 +00:32:03,155 --> 00:32:07,225 +With it, you have the full power +and flexibility we used + +553 +00:32:07,225 --> 00:32:10,395 +to implement SwiftUI's +stacks and grids + +554 +00:32:10,395 --> 00:32:15,200 +to build your own first-class +layout abstractions. + +555 +00:32:15,200 --> 00:32:18,403 +Using Layout, I built this +bespoke seating chart layout + +556 +00:32:18,403 --> 00:32:21,940 +for the guests +at our birthday party. + +557 +00:32:21,940 --> 00:32:25,076 +Should our party guests +sit in rows or pods? + +558 +00:32:25,076 --> 00:32:29,281 +With the power of Layout, +we don't have to choose. + +559 +00:32:29,281 --> 00:32:31,950 +Using the Layout protocol, +you can build + +560 +00:32:31,950 --> 00:32:36,221 +all kinds of efficient layouts, +tailored to the specific needs + +561 +00:32:36,221 --> 00:32:39,391 +of your view hierarchies. + +562 +00:32:39,391 --> 00:32:40,992 +To learn how to adopt Layout + +563 +00:32:40,992 --> 00:32:44,563 +and about other new, +great layout techniques, + +564 +00:32:44,563 --> 00:32:49,301 +check out the "Compose custom +layouts with SwiftUI" session. + +565 +00:32:49,301 --> 00:32:53,905 +I've prepared a taste of Layout +especially for you. + +566 +00:32:53,905 --> 00:32:56,074 +Using the new AnyLayout type, + +567 +00:32:56,074 --> 00:32:58,310 +I can switch +between the Grid layout + +568 +00:32:58,310 --> 00:33:01,513 +and a custom scattered +layout I've written. + +569 +00:33:01,513 --> 00:33:03,481 +As this session +draws to a close, + +570 +00:33:03,481 --> 00:33:06,017 +there's one surprise left: + +571 +00:33:06,017 --> 00:33:08,353 +You're invited! + +572 +00:33:08,353 --> 00:33:10,522 +♪ + +573 +00:33:10,522 --> 00:33:13,725 +You are invited to celebrate +SwiftUI's birthday + +574 +00:33:13,725 --> 00:33:17,896 +and all of the new APIs +with us this week. + +575 +00:33:17,896 --> 00:33:21,600 +There is a lot of detail left to +explore in the APIs we covered, + +576 +00:33:21,600 --> 00:33:25,804 +and even more APIs that +we didn't have time to include. + +577 +00:33:25,804 --> 00:33:29,741 +Enjoy the party, +and enjoy WWDC 2022. + +578 +00:33:29,741 --> 00:33:36,314 +And we are going +to enjoy some cake. + +579 +00:33:36,314 --> 00:33:38,550 +♪ instrumental hip hop music ♪ + +580 +00:33:38,550 --> 00:33:41,219 +♪ + diff --git a/eng/2022 Session 10053 Discover Sign in with Apple at Work & School en.srt b/eng/2022 Session 10053 Discover Sign in with Apple at Work & School en.srt new file mode 100644 index 0000000..7c0bba1 --- /dev/null +++ b/eng/2022 Session 10053 Discover Sign in with Apple at Work & School en.srt @@ -0,0 +1,1870 @@ +1 +00:00:00,000 --> 00:00:03,070 +♪ instrumental hip hop music ♪ + +2 +00:00:03,070 --> 00:00:09,543 +♪ + +3 +00:00:09,543 --> 00:00:11,612 +Hello. +My name is Ashwath + +4 +00:00:11,612 --> 00:00:14,548 +and I am an engineer +with the Education team. + +5 +00:00:14,548 --> 00:00:16,283 +Today I am excited +to talk to you + +6 +00:00:16,283 --> 00:00:19,419 +about Sign in with Apple +at Work & School. + +7 +00:00:19,419 --> 00:00:20,988 +In this presentation, +I will be covering + +8 +00:00:20,988 --> 00:00:24,825 +the key features that we are +introducing this fall. + +9 +00:00:24,825 --> 00:00:25,993 +I will first walk you through + +10 +00:00:25,993 --> 00:00:30,631 +what Sign in with Apple at Work +& School is and how it works. + +11 +00:00:30,631 --> 00:00:32,432 +Then I will talk +about the Roster API + +12 +00:00:32,432 --> 00:00:36,169 +and specifics about +how you can make use of it. + +13 +00:00:36,169 --> 00:00:39,139 +And finally, I will be talking +about access management, + +14 +00:00:39,139 --> 00:00:42,876 +where I discuss how IT admins +can manage access + +15 +00:00:42,876 --> 00:00:45,245 +to Sign in with Apple +at Work & School + +16 +00:00:45,245 --> 00:00:49,182 +and the Roster API +on behalf of their organization. + +17 +00:00:49,182 --> 00:00:51,785 +Let me begin +by covering two concepts + +18 +00:00:51,785 --> 00:00:54,488 +you may be familiar with +but are important to review + +19 +00:00:54,488 --> 00:00:56,156 +for this session. + +20 +00:00:56,156 --> 00:00:58,425 +The first is Sign in with Apple. + +21 +00:00:58,425 --> 00:01:00,827 +Sign in with Apple +is a fast and easy way + +22 +00:01:00,827 --> 00:01:04,731 +for users to sign in to apps +with their Apple ID. + +23 +00:01:04,731 --> 00:01:07,000 +Users are able to create +an account within your app + +24 +00:01:07,000 --> 00:01:10,037 +with a simple tap. + +25 +00:01:10,037 --> 00:01:12,739 +The second concept +is Managed Apple IDs. + +26 +00:01:12,739 --> 00:01:14,675 +Like Apple IDs, +Managed Apple IDs + +27 +00:01:14,675 --> 00:01:16,810 +are used to personalize +a device. + +28 +00:01:16,810 --> 00:01:20,881 +They're also used to access +Apple apps and services. + +29 +00:01:20,881 --> 00:01:23,183 +Unlike Apple IDs, +Managed Apple IDs + +30 +00:01:23,183 --> 00:01:26,453 +are owned by an organization +and managed through + +31 +00:01:26,453 --> 00:01:30,023 +Apple School Manager +or Apple Business Manager. + +32 +00:01:30,023 --> 00:01:31,525 +With those definitions in mind, + +33 +00:01:31,525 --> 00:01:34,061 +I am excited to share +that Sign in with Apple + +34 +00:01:34,061 --> 00:01:37,297 +has now been extended +to support Managed Apple IDs. + +35 +00:01:37,297 --> 00:01:39,999 +Now you can get the benefits +of a fast and easy + +36 +00:01:39,999 --> 00:01:42,269 +sign-in experience +of Sign in with Apple, + +37 +00:01:42,269 --> 00:01:44,237 +but with the management +capabilities + +38 +00:01:44,237 --> 00:01:46,006 +of Managed Apple IDs. + +39 +00:01:46,006 --> 00:01:47,407 +To showcase the user experience + +40 +00:01:47,407 --> 00:01:49,509 +of Sign in with Apple +at Work & School, + +41 +00:01:49,509 --> 00:01:52,379 +let us take a look +at an example. + +42 +00:01:52,379 --> 00:01:54,915 +Here is the login screen +for Slack. + +43 +00:01:54,915 --> 00:01:56,149 +To use Sign in with Apple, + +44 +00:01:56,149 --> 00:01:59,953 +a user can just tap +Continue with Apple. + +45 +00:01:59,953 --> 00:02:03,190 +When using an Apple ID, +they will then see + +46 +00:02:03,190 --> 00:02:06,193 +this welcome screen +for Sign in with Apple. + +47 +00:02:06,193 --> 00:02:09,663 +On the other hand, if they are +using a Managed Apple ID, + +48 +00:02:09,663 --> 00:02:11,198 +they will see +the welcome screen + +49 +00:02:11,198 --> 00:02:15,102 +for Sign in with Apple +at Work & School. + +50 +00:02:15,102 --> 00:02:18,038 +You will notice a difference +in the welcome screen. + +51 +00:02:18,038 --> 00:02:19,639 +What do we mean when we say + +52 +00:02:19,639 --> 00:02:23,610 +we make it easy for apps +to provide access control? + +53 +00:02:23,610 --> 00:02:26,747 +Let's take a look at an example. + +54 +00:02:26,747 --> 00:02:28,415 +When using their personal +Apple ID + +55 +00:02:28,415 --> 00:02:29,950 +with Sign in with Apple, + +56 +00:02:29,950 --> 00:02:32,619 +the user sees +the account creation screen + +57 +00:02:32,619 --> 00:02:34,855 +where they may +edit their name + +58 +00:02:34,855 --> 00:02:38,825 +and choose whether to share +or hide their email. + +59 +00:02:38,825 --> 00:02:41,094 +But in an organizational +context, + +60 +00:02:41,094 --> 00:02:44,965 +it is important for the app +to understand who is logging in + +61 +00:02:44,965 --> 00:02:47,968 +and what level of access +to provide them. + +62 +00:02:47,968 --> 00:02:50,437 +In the example +shown on this screen, + +63 +00:02:50,437 --> 00:02:53,907 +the user is signing in +to Slack at work. + +64 +00:02:53,907 --> 00:02:55,542 +In order to know +which Slack channels + +65 +00:02:55,542 --> 00:02:58,045 +this employee should be +allowed to join, + +66 +00:02:58,045 --> 00:03:01,448 +Slack needs to know +which user is signing in. + +67 +00:03:01,448 --> 00:03:05,685 +So this is an example of Slack +providing access control + +68 +00:03:05,685 --> 00:03:09,022 +using the name and email +shared with their app + +69 +00:03:09,022 --> 00:03:12,392 +when using Sign in with Apple +at Work & School. + +70 +00:03:12,392 --> 00:03:16,463 +So this is what the user sees, +but what does this mean for you? + +71 +00:03:16,463 --> 00:03:17,831 +It means when a user + +72 +00:03:17,831 --> 00:03:20,400 +uses Sign in with Apple +at Work & School, + +73 +00:03:20,400 --> 00:03:25,272 +the name and email fields +will always be accessible. + +74 +00:03:25,272 --> 00:03:29,076 +If you are a developer +working on a client-side app, + +75 +00:03:29,076 --> 00:03:31,244 +here's a look +at some example code + +76 +00:03:31,244 --> 00:03:34,281 +showing the authorization +controller. + +77 +00:03:34,281 --> 00:03:37,417 +On a successful authorization, +the highlighted statements + +78 +00:03:37,417 --> 00:03:42,355 +indicate the full name and email +received by your app. + +79 +00:03:42,355 --> 00:03:45,025 +This assumes that +the request for authorization + +80 +00:03:45,025 --> 00:03:49,696 +included the scopes +for the full name and email. + +81 +00:03:49,696 --> 00:03:51,164 +If you are a developer + +82 +00:03:51,164 --> 00:03:54,000 +supporting Sign in with Apple +on the web, + +83 +00:03:54,000 --> 00:03:56,236 +here's a look +at some example code + +84 +00:03:56,236 --> 00:04:01,241 +that uses the signIn method to +start the authorization process. + +85 +00:04:01,241 --> 00:04:03,310 +Upon successful authorization, + +86 +00:04:03,310 --> 00:04:07,114 +the server returns the response +to the data object. + +87 +00:04:07,114 --> 00:04:11,084 +That data object +looks like this. + +88 +00:04:11,084 --> 00:04:13,753 +The highlighted fields +contain the email + +89 +00:04:13,753 --> 00:04:18,258 +and full name +of the user signing in. + +90 +00:04:18,258 --> 00:04:21,461 +It is important to note +a few points here. + +91 +00:04:21,461 --> 00:04:24,564 +In this release, Sign in +with Apple at Work & School + +92 +00:04:24,564 --> 00:04:27,400 +uses the primary +Managed Apple ID + +93 +00:04:27,400 --> 00:04:30,770 +that is signed in on the device. + +94 +00:04:30,770 --> 00:04:33,373 +Even though the example screens +that we showed you + +95 +00:04:33,373 --> 00:04:36,243 +were for the login flow +on the device, + +96 +00:04:36,243 --> 00:04:39,212 +the web flow is supported +as well. + +97 +00:04:39,212 --> 00:04:42,716 +Some accounts may not have +email addresses provided. + +98 +00:04:42,716 --> 00:04:45,919 +For example, schools may not +assign email addresses + +99 +00:04:45,919 --> 00:04:47,787 +to younger students. + +100 +00:04:47,787 --> 00:04:50,790 +Let's see what the account +creation screen looks like + +101 +00:04:50,790 --> 00:04:54,895 +in this scenario where +an email address is not present. + +102 +00:04:54,895 --> 00:04:59,099 +This user, Fatima Silva, +does not have an email address, + +103 +00:04:59,099 --> 00:05:01,201 +so only their full name +is present + +104 +00:05:01,201 --> 00:05:04,004 +on the account creation screen. + +105 +00:05:04,004 --> 00:05:06,439 +If you have already implemented +Sign in with Apple, + +106 +00:05:06,439 --> 00:05:08,742 +there is no work +needed from your end + +107 +00:05:08,742 --> 00:05:11,378 +to add support +for Managed Apple IDs. + +108 +00:05:11,378 --> 00:05:13,480 +If you haven't implemented +support yet, + +109 +00:05:13,480 --> 00:05:15,949 +I would highly suggest +taking a look + +110 +00:05:15,949 --> 00:05:18,952 +at the "Get the most out +of Sign in with Apple" session + +111 +00:05:18,952 --> 00:05:21,021 +from WWDC2020 + +112 +00:05:21,021 --> 00:05:24,491 +and the "Enhance your Sign in +with Apple experience" session + +113 +00:05:24,491 --> 00:05:26,493 +from this year. + +114 +00:05:26,493 --> 00:05:29,196 +That was Sign in with Apple +at Work & School, + +115 +00:05:29,196 --> 00:05:31,998 +which brings the fast and easy +sign-in experience + +116 +00:05:31,998 --> 00:05:35,202 +of Sign in with Apple +to Managed Apple IDs. + +117 +00:05:35,202 --> 00:05:37,804 +Now let's dive into +the Roster API. + +118 +00:05:37,804 --> 00:05:39,706 +But before jumping into +any specifics, + +119 +00:05:39,706 --> 00:05:42,609 +let's consider +the following scenario. + +120 +00:05:42,609 --> 00:05:45,412 +You're a developer +in the education space. + +121 +00:05:45,412 --> 00:05:48,048 +Your app is being used +by school districts. + +122 +00:05:48,048 --> 00:05:51,851 +You have implemented support for +Sign in with Apple in your app. + +123 +00:05:51,851 --> 00:05:52,919 +That's great! + +124 +00:05:52,919 --> 00:05:56,590 +Students, teachers, and staff +can use their Managed Apple IDs + +125 +00:05:56,590 --> 00:05:58,625 +to log in to your app. + +126 +00:05:58,625 --> 00:06:01,161 +In this scenario, +your app allows teachers + +127 +00:06:01,161 --> 00:06:02,929 +to share assignments +and announcements + +128 +00:06:02,929 --> 00:06:04,297 +with their students. + +129 +00:06:04,297 --> 00:06:06,566 +To support this, +IT administrators + +130 +00:06:06,566 --> 00:06:10,570 +need to enter students, +teachers, and classes + +131 +00:06:10,570 --> 00:06:14,908 +into your app prior to the start +of the school year. + +132 +00:06:14,908 --> 00:06:18,178 +For a school with hundreds +or thousands of students, + +133 +00:06:18,178 --> 00:06:20,180 +creating these records by hand + +134 +00:06:20,180 --> 00:06:22,549 +would quickly become +a time-consuming + +135 +00:06:22,549 --> 00:06:24,718 +and tedious process. + +136 +00:06:24,718 --> 00:06:28,355 +We've created the Roster API +to address this problem. + +137 +00:06:28,355 --> 00:06:31,591 +The Roster API provides +programmatic access + +138 +00:06:31,591 --> 00:06:34,194 +to resources +like users and classes + +139 +00:06:34,194 --> 00:06:36,196 +in Apple School Manager. + +140 +00:06:36,196 --> 00:06:39,165 +This allows an IT admin +to automate the process + +141 +00:06:39,165 --> 00:06:42,669 +of importing this information +to your app. + +142 +00:06:42,669 --> 00:06:47,374 +These resources are defined +in the form of REST endpoints. + +143 +00:06:47,374 --> 00:06:50,377 +This data can be +accessed by your app + +144 +00:06:50,377 --> 00:06:53,346 +after the IT admin +has granted permission + +145 +00:06:53,346 --> 00:06:57,350 +through a successful OAuth 2.0 +authorization. + +146 +00:06:57,350 --> 00:07:00,120 +The Administrator +and Site Manager accounts + +147 +00:07:00,120 --> 00:07:01,521 +in Apple School Manager + +148 +00:07:01,521 --> 00:07:06,993 +can manage which apps +are able to access the data. + +149 +00:07:06,993 --> 00:07:08,328 +As part of this release, + +150 +00:07:08,328 --> 00:07:10,730 +we have created +a standardized way + +151 +00:07:10,730 --> 00:07:14,834 +for apps to request access +to organizational data. + +152 +00:07:14,834 --> 00:07:18,305 +We call this +Organizational Data Sharing. + +153 +00:07:18,305 --> 00:07:20,507 +We wanted to follow +industry standards + +154 +00:07:20,507 --> 00:07:23,476 +when it comes +to UI design patterns + +155 +00:07:23,476 --> 00:07:28,214 +and choice of technology +so developer adoption is easy. + +156 +00:07:28,214 --> 00:07:32,519 +So we created an authorization +flow using OAuth 2.0, + +157 +00:07:32,519 --> 00:07:37,023 +which includes using scopes +and asking the IT admin + +158 +00:07:37,023 --> 00:07:40,527 +to consent to sharing +their organizational data. + +159 +00:07:40,527 --> 00:07:43,330 +Here is what it looks like. + +160 +00:07:43,330 --> 00:07:46,999 +The consent screen provides +information on which app + +161 +00:07:46,999 --> 00:07:50,303 +is requesting access +to the organization's data + +162 +00:07:50,303 --> 00:07:54,107 +and what data the app +is requesting access to. + +163 +00:07:54,107 --> 00:07:57,110 +Looking more closely, +the consent screen + +164 +00:07:57,110 --> 00:08:00,213 +contains information +on the requestor -- + +165 +00:08:00,213 --> 00:08:04,050 +which represents the app +requesting access to the data + +166 +00:08:04,050 --> 00:08:06,486 +made available by +Apple School Manager; + +167 +00:08:06,486 --> 00:08:08,888 +in this case, the requesting app +is Quizzler -- + +168 +00:08:08,888 --> 00:08:11,624 +and the scopes, +which represent the data + +169 +00:08:11,624 --> 00:08:14,327 +that the app +will have access to. + +170 +00:08:14,327 --> 00:08:17,097 +In this case, +once the IT admin consents, + +171 +00:08:17,097 --> 00:08:19,165 +Quizzler will be able to access + +172 +00:08:19,165 --> 00:08:24,738 +all user and class information +for this organization. + +173 +00:08:24,738 --> 00:08:27,240 +What does your implementation +journey look like? + +174 +00:08:27,240 --> 00:08:30,643 +The first step is to register +in the developer portal, + +175 +00:08:30,643 --> 00:08:33,279 +which involves +requesting scopes. + +176 +00:08:33,279 --> 00:08:37,050 +The next step is to request +consent from the IT admin + +177 +00:08:37,050 --> 00:08:39,352 +to share the data with your app + +178 +00:08:39,352 --> 00:08:42,689 +through the OAuth 2.0 +authorization flow. + +179 +00:08:42,689 --> 00:08:46,259 +Finally, your app queries +the Roster API endpoints + +180 +00:08:46,259 --> 00:08:48,395 +to fetch the data. + +181 +00:08:48,395 --> 00:08:51,464 +Let us take a look +at each of these steps. + +182 +00:08:51,464 --> 00:08:55,034 +To request scopes, +visit the Developer portal. + +183 +00:08:55,034 --> 00:08:58,638 +Under the Certificates, +Identifiers & Profiles page, + +184 +00:08:58,638 --> 00:09:00,540 +click on Configure + +185 +00:09:00,540 --> 00:09:03,843 +under Account & Organizational +Data Sharing. + +186 +00:09:03,843 --> 00:09:08,815 +In the next step, click on +"Set up a configuration." + +187 +00:09:08,815 --> 00:09:12,218 +Choose the primary app ID +from the drop-down + +188 +00:09:12,218 --> 00:09:15,955 +and hit Continue. + +189 +00:09:15,955 --> 00:09:18,691 +In the additional configuration +details page, + +190 +00:09:18,691 --> 00:09:21,928 +under Organizational Data +Sharing Scopes, + +191 +00:09:21,928 --> 00:09:24,063 +choose the scopes desired. + +192 +00:09:24,063 --> 00:09:28,735 +Right now, we offer two: +user access and class access. + +193 +00:09:28,735 --> 00:09:32,405 +Ensure return URLs +are configured for your app. + +194 +00:09:32,405 --> 00:09:35,942 +This step is important +as the return URLs will be used + +195 +00:09:35,942 --> 00:09:39,045 +in the OAuth 2.0 +authorization flow. + +196 +00:09:39,045 --> 00:09:41,414 +Hit Continue. + +197 +00:09:41,414 --> 00:09:44,150 +Finally, confirm +your configuration. + +198 +00:09:44,150 --> 00:09:47,320 +Now we're ready +for the next step. + +199 +00:09:47,320 --> 00:09:49,823 +The second step +in the implementation journey + +200 +00:09:49,823 --> 00:09:53,193 +is getting the access token +through the OAuth 2.0 + +201 +00:09:53,193 --> 00:09:55,094 +authorization flow. + +202 +00:09:55,094 --> 00:09:57,797 +Let us look at this in detail. + +203 +00:09:57,797 --> 00:10:00,900 +The IT admin +first logs in to your app. + +204 +00:10:00,900 --> 00:10:02,969 +The IT admin then +interacts with your app + +205 +00:10:02,969 --> 00:10:05,371 +to initiate +the authorization flow -- + +206 +00:10:05,371 --> 00:10:07,941 +for example, +by clicking a button + +207 +00:10:07,941 --> 00:10:10,577 +named "Connect to Apple +School Manager." + +208 +00:10:10,577 --> 00:10:12,946 +This starts a sequence of events + +209 +00:10:12,946 --> 00:10:16,015 +following the OAuth 2.0 +standard. + +210 +00:10:16,015 --> 00:10:19,118 +The first step is +to execute a GET API call + +211 +00:10:19,118 --> 00:10:20,854 +to the authorize endpoint. + +212 +00:10:20,854 --> 00:10:24,824 +Let us take a look +at the details of this request. + +213 +00:10:24,824 --> 00:10:28,061 +The request takes in +a few query parameters: + +214 +00:10:28,061 --> 00:10:30,530 +the client_id, +the redirect_uri, + +215 +00:10:30,530 --> 00:10:34,167 +the state, response_type, +and scopes. + +216 +00:10:34,167 --> 00:10:36,102 +The response will be a redirect + +217 +00:10:36,102 --> 00:10:38,338 +to the Apple OAuth +consent screen + +218 +00:10:38,338 --> 00:10:41,875 +to request access +from the IT admin. + +219 +00:10:41,875 --> 00:10:45,078 +In this diagram, +we assume that the IT admin + +220 +00:10:45,078 --> 00:10:48,381 +has authenticated +with their Managed Apple ID. + +221 +00:10:48,381 --> 00:10:49,449 +If they have not, + +222 +00:10:49,449 --> 00:10:52,785 +they would be prompted +to authenticate first. + +223 +00:10:52,785 --> 00:10:56,022 +The IT admin is presented +with the consent screen + +224 +00:10:56,022 --> 00:10:58,224 +to confirm that they +would like to share + +225 +00:10:58,224 --> 00:11:01,427 +their organizational data +with your app. + +226 +00:11:01,427 --> 00:11:04,531 +This is the same consent screen +that we saw earlier. + +227 +00:11:04,531 --> 00:11:08,735 +As a reminder, +it looks like this. + +228 +00:11:08,735 --> 00:11:11,671 +Once they click Allow, +your app is provided + +229 +00:11:11,671 --> 00:11:14,774 +with an authorization code +at your return URL + +230 +00:11:14,774 --> 00:11:19,112 +that you registered +in the developer portal. + +231 +00:11:19,112 --> 00:11:23,016 +The final step involves a POST +request to the token endpoint + +232 +00:11:23,016 --> 00:11:26,252 +to retrieve your app's +access token. + +233 +00:11:26,252 --> 00:11:29,589 +Let us see what this request +looks like. + +234 +00:11:29,589 --> 00:11:33,660 +The request body is composed +of standard OAuth parameters + +235 +00:11:33,660 --> 00:11:38,998 +including the authorization code +received on your return URL. + +236 +00:11:38,998 --> 00:11:41,734 +The response would have +the access token, + +237 +00:11:41,734 --> 00:11:44,571 +its expiration, +and the refresh token + +238 +00:11:44,571 --> 00:11:49,275 +that is used to create a new +access token when it expires. + +239 +00:11:49,275 --> 00:11:51,644 +Let us take a look +at the request details + +240 +00:11:51,644 --> 00:11:55,949 +to generate a new access token +using a refresh token. + +241 +00:11:55,949 --> 00:11:59,652 +The request body is composed of +the standard OAuth parameters, + +242 +00:11:59,652 --> 00:12:02,655 +including the refresh token, +and the response would have + +243 +00:12:02,655 --> 00:12:06,326 +the access token +and its expiration. + +244 +00:12:06,326 --> 00:12:07,894 +After successfully completing + +245 +00:12:07,894 --> 00:12:10,296 +the OAuth 2.0 +authorization flow, + +246 +00:12:10,296 --> 00:12:12,632 +your app now has +the access token, + +247 +00:12:12,632 --> 00:12:16,169 +which can be used to fetch +user and class information + +248 +00:12:16,169 --> 00:12:18,705 +from the Roster API. + +249 +00:12:18,705 --> 00:12:20,673 +As part of our current release, + +250 +00:12:20,673 --> 00:12:22,542 +we are introducing +five endpoints + +251 +00:12:22,542 --> 00:12:24,477 +that the apps can query. + +252 +00:12:24,477 --> 00:12:27,814 +These include +fetching a list of classes, + +253 +00:12:27,814 --> 00:12:30,516 +a list of users, +a specific class, + +254 +00:12:30,516 --> 00:12:34,320 +a specific user, +users in a class. + +255 +00:12:34,320 --> 00:12:36,656 +Let's walk through +a simple example flow + +256 +00:12:36,656 --> 00:12:40,793 +to demonstrate how you can +fetch users and classes. + +257 +00:12:40,793 --> 00:12:42,929 +Your app server +can first retrieve + +258 +00:12:42,929 --> 00:12:46,466 +the list of students, +teachers, and staff + +259 +00:12:46,466 --> 00:12:49,369 +by querying the users endpoint. + +260 +00:12:49,369 --> 00:12:53,973 +Let us take a look at the +request and response in detail. + +261 +00:12:53,973 --> 00:12:57,477 +The users endpoint takes in +three query parameters. + +262 +00:12:57,477 --> 00:13:01,547 +The pageToken and limit +are pagination parameters, + +263 +00:13:01,547 --> 00:13:03,116 +and an optional role parameter + +264 +00:13:03,116 --> 00:13:07,520 +lets you query for students, +instructors, or staff. + +265 +00:13:07,520 --> 00:13:10,189 +The request requires +the authorization header + +266 +00:13:10,189 --> 00:13:11,724 +with the access token + +267 +00:13:11,724 --> 00:13:14,227 +generated from +the authorization flow. + +268 +00:13:14,227 --> 00:13:18,931 +The response is a JSON payload +that includes a "users" key, + +269 +00:13:18,931 --> 00:13:20,967 +which has the list of users; + +270 +00:13:20,967 --> 00:13:22,402 +the "nextPageToken" key + +271 +00:13:22,402 --> 00:13:25,004 +that is used to fetch +the next set of records, + +272 +00:13:25,004 --> 00:13:28,408 +and a "moreToFollow" +Boolean key that indicates + +273 +00:13:28,408 --> 00:13:32,712 +if there are any more records +to query. + +274 +00:13:32,712 --> 00:13:35,715 +Let's take a look +at an example request. + +275 +00:13:35,715 --> 00:13:39,252 +The API call here is retrieving +a list of students + +276 +00:13:39,252 --> 00:13:42,622 +with a maximum of 10 students +in the response. + +277 +00:13:42,622 --> 00:13:44,957 +This is the first API request, + +278 +00:13:44,957 --> 00:13:49,162 +so the pageToken query +parameter is not specified. + +279 +00:13:49,162 --> 00:13:51,831 +The response has +standard user information + +280 +00:13:51,831 --> 00:13:54,901 +such as givenName, +familyName, etcetera, + +281 +00:13:54,901 --> 00:13:57,103 +but I would like +to draw your attention + +282 +00:13:57,103 --> 00:14:00,239 +to the stable unique identifier +for the user. + +283 +00:14:00,239 --> 00:14:03,242 +This is the same identifier +your app receives + +284 +00:14:03,242 --> 00:14:06,913 +when the user authenticates +with Sign in with Apple. + +285 +00:14:06,913 --> 00:14:09,082 +The email address of the user + +286 +00:14:09,082 --> 00:14:12,952 +and their roles in the +organization are also included. + +287 +00:14:12,952 --> 00:14:15,088 +The nextPageToken +needs to be used + +288 +00:14:15,088 --> 00:14:18,291 +in the subsequent API request +as a query parameter + +289 +00:14:18,291 --> 00:14:20,093 +to fetch the next +set of students, + +290 +00:14:20,093 --> 00:14:24,330 +since the moreToFollow key +is set to true. + +291 +00:14:24,330 --> 00:14:26,332 +After querying +the users endpoint, + +292 +00:14:26,332 --> 00:14:30,002 +your app server can now query +the classes endpoint, + +293 +00:14:30,002 --> 00:14:32,371 +which gives you +the list of classes + +294 +00:14:32,371 --> 00:14:34,140 +for that particular +school district + +295 +00:14:34,140 --> 00:14:37,977 +and the relationship between +the class and the students + +296 +00:14:37,977 --> 00:14:40,780 +and teachers associated +with that class. + +297 +00:14:40,780 --> 00:14:45,985 +Let us take a look at the +request and response in detail. + +298 +00:14:45,985 --> 00:14:48,187 +As part of +the get classes request, + +299 +00:14:48,187 --> 00:14:51,157 +the request takes in +two query parameters: + +300 +00:14:51,157 --> 00:14:53,793 +the pageToken +and the limit parameter, + +301 +00:14:53,793 --> 00:14:56,095 +both of which we saw before. + +302 +00:14:56,095 --> 00:14:59,799 +The headers are the same +as when querying users. + +303 +00:14:59,799 --> 00:15:02,101 +The response is a JSON payload + +304 +00:15:02,101 --> 00:15:07,240 +in which the "classes" key +holds the list of classes. + +305 +00:15:07,240 --> 00:15:10,143 +Let us take a look +at an example request. + +306 +00:15:10,143 --> 00:15:12,845 +This request retrieves +a list of classes + +307 +00:15:12,845 --> 00:15:17,416 +with a maximum of 200 +to be included in the response. + +308 +00:15:17,416 --> 00:15:20,453 +The API request also has +the pageToken parameter, + +309 +00:15:20,453 --> 00:15:25,191 +which means it is in the middle +of fetching all classes. + +310 +00:15:25,191 --> 00:15:27,994 +The response has +standard class information + +311 +00:15:27,994 --> 00:15:31,130 +such as the name, +the class identifier, etcetera, + +312 +00:15:31,130 --> 00:15:33,232 +but I would like +to draw your attention + +313 +00:15:33,232 --> 00:15:36,702 +to the list of instructor IDs +and student IDs + +314 +00:15:36,702 --> 00:15:38,838 +that belong to the class. + +315 +00:15:38,838 --> 00:15:40,773 +That was the Roster API. + +316 +00:15:40,773 --> 00:15:42,742 +With these endpoints, +your app can now + +317 +00:15:42,742 --> 00:15:45,178 +programmatically query +users and classes + +318 +00:15:45,178 --> 00:15:47,280 +from Apple School Manager. + +319 +00:15:47,280 --> 00:15:51,484 +Finally, I would like to talk +to you about access management. + +320 +00:15:51,484 --> 00:15:54,120 +It is vital to provide +management capabilities + +321 +00:15:54,120 --> 00:15:57,523 +to organizations using +Organizational Data Sharing + +322 +00:15:57,523 --> 00:16:00,359 +and Sign in with Apple +at Work & School. + +323 +00:16:00,359 --> 00:16:04,163 +Let me explain what this means +in more detail. + +324 +00:16:04,163 --> 00:16:05,798 +When using Sign in with Apple, + +325 +00:16:05,798 --> 00:16:09,335 +users manage their own account +and it's solely up to them + +326 +00:16:09,335 --> 00:16:12,905 +to decide which apps +they choose to sign in to. + +327 +00:16:12,905 --> 00:16:15,741 +But when using Sign in +with Apple at Work & School, + +328 +00:16:15,741 --> 00:16:20,179 +the IT admin at the organization +centrally manages access + +329 +00:16:20,179 --> 00:16:24,317 +on behalf of all the users +at that organization. + +330 +00:16:24,317 --> 00:16:27,687 +This is because the IT admin +is responsible for ensuring + +331 +00:16:27,687 --> 00:16:31,490 +the safety and security +of the organization's data. + +332 +00:16:31,490 --> 00:16:34,894 +So, they must be empowered +to manage which apps + +333 +00:16:34,894 --> 00:16:38,998 +users are allowed to use Sign in +with Apple at Work & School, + +334 +00:16:38,998 --> 00:16:43,269 +and which apps are allowed to +use Organizational Data Sharing. + +335 +00:16:43,269 --> 00:16:46,772 +These access management +capabilities can be configured + +336 +00:16:46,772 --> 00:16:50,743 +in Apple School Manager +and Apple Business Manager. + +337 +00:16:50,743 --> 00:16:53,045 +For Sign in with Apple +at Work & School, + +338 +00:16:53,045 --> 00:16:54,947 +we offer two modes. + +339 +00:16:54,947 --> 00:16:58,551 +The first mode +is "Allow all apps." + +340 +00:16:58,551 --> 00:17:01,587 +Let us see what this mode +looks like. + +341 +00:17:01,587 --> 00:17:05,892 +Here, the IT admin is logged in +to Apple Business Manager. + +342 +00:17:05,892 --> 00:17:08,594 +On selecting Access Management +from the sidebar + +343 +00:17:08,594 --> 00:17:10,863 +and choosing Sign in with Apple, + +344 +00:17:10,863 --> 00:17:13,566 +they are presented +with the two modes. + +345 +00:17:13,566 --> 00:17:17,737 +The "Allow all apps" mode lets +all users of the organization + +346 +00:17:17,737 --> 00:17:21,207 +log in to all apps that support +Sign in with Apple. + +347 +00:17:21,207 --> 00:17:25,211 +The second mode +is "Allow only certain apps." + +348 +00:17:25,211 --> 00:17:28,214 +Let us see what this mode +looks like. + +349 +00:17:28,214 --> 00:17:31,350 +The "Allow only certain apps" +mode lets the IT admin + +350 +00:17:31,350 --> 00:17:34,353 +search for apps that support +Sign in with Apple + +351 +00:17:34,353 --> 00:17:36,355 +to add to the list. + +352 +00:17:36,355 --> 00:17:39,258 +Users will only be able +to use Sign in with Apple + +353 +00:17:39,258 --> 00:17:42,161 +with the apps that have been +added to the list. + +354 +00:17:42,161 --> 00:17:45,765 +If a user tries to log in to +an app that is not on the list, + +355 +00:17:45,765 --> 00:17:48,067 +they will see +an error message. + +356 +00:17:48,067 --> 00:17:50,336 +It is important to note +that this functionality + +357 +00:17:50,336 --> 00:17:52,772 +is the same +for Apple School Manager, + +358 +00:17:52,772 --> 00:17:54,140 +Apple Business Manager, + +359 +00:17:54,140 --> 00:17:56,709 +and Business Essentials +customers. + +360 +00:17:56,709 --> 00:17:59,278 +Similar to Sign in with Apple +at Work & School, + +361 +00:17:59,278 --> 00:18:02,848 +Organizational Data Sharing +also has the same two modes. + +362 +00:18:02,848 --> 00:18:04,917 +Let's take a look. + +363 +00:18:04,917 --> 00:18:08,220 +The "Allow only certain apps" +mode lets the IT admin + +364 +00:18:08,220 --> 00:18:11,390 +search for supported apps +to add to the list. + +365 +00:18:11,390 --> 00:18:15,361 +It is these apps that can access +user and class information + +366 +00:18:15,361 --> 00:18:17,196 +using the Roster API. + +367 +00:18:17,196 --> 00:18:19,165 +In addition +to the consent screen, + +368 +00:18:19,165 --> 00:18:22,468 +this is a second layer +of control for the IT admin + +369 +00:18:22,468 --> 00:18:26,806 +to manage which apps +can access organizational data. + +370 +00:18:26,806 --> 00:18:29,508 +With the release of Sign in +with Apple at Work & School, + +371 +00:18:29,508 --> 00:18:33,412 +now users in any environment +can use an Apple ID + +372 +00:18:33,412 --> 00:18:36,549 +or a Managed Apple ID +to log in to your app. + +373 +00:18:36,549 --> 00:18:39,518 +If you haven't already +implemented Sign in with Apple, + +374 +00:18:39,518 --> 00:18:42,355 +I encourage you to do so. + +375 +00:18:42,355 --> 00:18:45,257 +I would also highly encourage +you to adopt the Roster API + +376 +00:18:45,257 --> 00:18:47,626 +if you are +in the education space. + +377 +00:18:47,626 --> 00:18:49,628 +Implementing support +for Roster API + +378 +00:18:49,628 --> 00:18:52,298 +will provide a seamless +onboarding experience + +379 +00:18:52,298 --> 00:18:57,870 +for IT administrators, staff, +teachers, and students. + +380 +00:18:57,870 --> 00:19:00,072 +As always, +we love hearing from you. + +381 +00:19:00,072 --> 00:19:03,242 +So please submit +any feedback you may have. + +382 +00:19:03,242 --> 00:19:05,911 +If you'd like to learn more, +I would highly suggest + +383 +00:19:05,911 --> 00:19:09,582 +the "Get the most out of Sign in +with Apple" session from 2020 + +384 +00:19:09,582 --> 00:19:13,052 +and the "Enhance your Sign in +with Apple experience" session + +385 +00:19:13,052 --> 00:19:14,186 +this year. + +386 +00:19:14,186 --> 00:19:15,588 +Thank you so much for watching + +387 +00:19:15,588 --> 00:19:17,823 +and we hope you enjoy +this year's WWDC. + +388 +00:19:17,823 --> 00:19:24,597 +♪ + diff --git a/eng/2022 Session 10054 The SwiftUI cookbook for navigation en.srt b/eng/2022 Session 10054 The SwiftUI cookbook for navigation en.srt new file mode 100644 index 0000000..1cec9c3 --- /dev/null +++ b/eng/2022 Session 10054 The SwiftUI cookbook for navigation en.srt @@ -0,0 +1,2556 @@ +1 +00:00:00,000 --> 00:00:02,970 +♪ instrumental hip hop music ♪ + +2 +00:00:02,970 --> 00:00:09,409 +♪ + +3 +00:00:09,409 --> 00:00:13,247 +Hi. I'm Curt, an engineer +on the SwiftUI team. + +4 +00:00:13,247 --> 00:00:16,950 +There are some exciting new APIs +for navigation in SwiftUI. + +5 +00:00:16,950 --> 00:00:19,720 +I've been enjoying building apps +with these new APIs + +6 +00:00:19,720 --> 00:00:23,290 +and I'm thrilled to be able +to share them with you. + +7 +00:00:23,290 --> 00:00:25,893 +These APIs scale +from basic stacks -- + +8 +00:00:25,893 --> 00:00:28,929 +like on Apple TV, +iPhone, and Apple Watch -- + +9 +00:00:28,929 --> 00:00:31,565 +to powerful multicolumn +presentations. + +10 +00:00:31,565 --> 00:00:33,901 +The new APIs +bring robust support + +11 +00:00:33,901 --> 00:00:36,703 +for programmatic navigation +and deep linking, + +12 +00:00:36,703 --> 00:00:40,140 +letting you compose pieces +to build the perfect structure + +13 +00:00:40,140 --> 00:00:42,109 +for your app. + +14 +00:00:42,109 --> 00:00:45,412 +In this talk, I'll give you +some straightforward recipes + +15 +00:00:45,412 --> 00:00:48,782 +for cooking up an app +with navigation in SwiftUI. + +16 +00:00:48,782 --> 00:00:51,118 +And if you're already +using SwiftUI, + +17 +00:00:51,118 --> 00:00:54,554 +we hope these new APIs will +help you kick it up a notch. + +18 +00:00:54,554 --> 00:00:55,889 +I'll start with the ingredients + +19 +00:00:55,889 --> 00:00:59,559 +that go into the new +data-driven navigation APIs. + +20 +00:00:59,559 --> 00:01:01,695 +Then, we'll move +to our tasting menu: + +21 +00:01:01,695 --> 00:01:03,497 +several quick and easy recipes + +22 +00:01:03,497 --> 00:01:06,533 +for full programmatic +control of navigation. + +23 +00:01:06,533 --> 00:01:09,136 +For the dessert course, +I'll share some tips + +24 +00:01:09,136 --> 00:01:14,374 +on using the new APIs to persist +navigation state in your apps. + +25 +00:01:14,374 --> 00:01:17,344 +If you've used navigation +in SwiftUI before, + +26 +00:01:17,344 --> 00:01:20,514 +you might be wondering +how the new APIs are different. + +27 +00:01:20,514 --> 00:01:25,052 +So before digging in, let's +review some of the existing API. + +28 +00:01:25,052 --> 00:01:28,755 +The existing APIs are based +on links that send views + +29 +00:01:28,755 --> 00:01:31,625 +that are shown in other columns +or on a stack. + +30 +00:01:31,625 --> 00:01:33,026 +For example, I might have a list + +31 +00:01:33,026 --> 00:01:35,495 +of navigation links +in a root view. + +32 +00:01:35,495 --> 00:01:37,331 +When I tap one of these links, + +33 +00:01:37,331 --> 00:01:40,600 +the link pushes +its view on the stack. + +34 +00:01:40,600 --> 00:01:42,869 +This works great +for basic navigation, + +35 +00:01:42,869 --> 00:01:45,272 +and you can continue +using this pattern. + +36 +00:01:45,272 --> 00:01:48,809 +But let's pop back +to the root view. + +37 +00:01:48,809 --> 00:01:51,345 +With the existing +navigation API, + +38 +00:01:51,345 --> 00:01:53,246 +to present a link +programmatically, + +39 +00:01:53,246 --> 00:01:55,382 +I add a binding to the link. + +40 +00:01:55,382 --> 00:01:57,985 +For example, +I can present this link's view + +41 +00:01:57,985 --> 00:02:01,421 +by setting item.showDetail +to true. + +42 +00:02:01,421 --> 00:02:06,693 +But this means I need a separate +binding for each link. + +43 +00:02:06,693 --> 00:02:09,363 +With the new API, +we lift the binding up + +44 +00:02:09,363 --> 00:02:13,066 +to the entire container, +called a NavigationStack. + +45 +00:02:13,066 --> 00:02:14,601 +The path here +is a collection + +46 +00:02:14,601 --> 00:02:18,672 +that represents all the values +pushed on the stack. + +47 +00:02:18,672 --> 00:02:21,475 +NavigationLinks +append values to the path. + +48 +00:02:21,475 --> 00:02:23,810 +You can deep link +by mutating the path; + +49 +00:02:23,810 --> 00:02:26,513 +or pop to the root view +by removing all the items + +50 +00:02:26,513 --> 00:02:28,248 +from the path. + +51 +00:02:28,248 --> 00:02:31,651 +In this talk, I'll show you +how the new navigation API + +52 +00:02:31,651 --> 00:02:34,955 +enables data-driven +programmatic navigation. + +53 +00:02:34,955 --> 00:02:38,792 +I hope you find it +powerful and easy to use. + +54 +00:02:38,792 --> 00:02:42,629 +Before jumping into recipes for +using the new navigation APIs, + +55 +00:02:42,629 --> 00:02:46,066 +I thought it would be helpful +to share what's on the menu. + +56 +00:02:46,066 --> 00:02:48,135 +I've really gotten +into cooking lately + +57 +00:02:48,135 --> 00:02:51,304 +and I've been working on an app +to keep track of my recipes. + +58 +00:02:51,304 --> 00:02:52,506 +I have a lot of ideas + +59 +00:02:52,506 --> 00:02:55,075 +about different ways +to present this info. + +60 +00:02:55,075 --> 00:02:58,178 +For example, +here's a three-column approach. + +61 +00:02:58,178 --> 00:03:01,381 +The first column lets me +select a recipe category. + +62 +00:03:01,381 --> 00:03:02,883 +When I select a category, + +63 +00:03:02,883 --> 00:03:06,186 +the second column lists +the recipes I've collected. + +64 +00:03:06,186 --> 00:03:07,554 +And when I select a recipe, + +65 +00:03:07,554 --> 00:03:11,525 +the detail area shows +the ingredients for that recipe. + +66 +00:03:11,525 --> 00:03:13,326 +The detail area also has links + +67 +00:03:13,326 --> 00:03:15,829 +to a selection +of related recipes. + +68 +00:03:15,829 --> 00:03:19,599 +My grandma always said, +"The crust makes the pie." + +69 +00:03:19,599 --> 00:03:21,701 +So that's what +we're cooking up today. + +70 +00:03:21,701 --> 00:03:24,638 +Our ingredients are +the new navigation APIs. + +71 +00:03:24,638 --> 00:03:25,906 +Let's dig into those, + +72 +00:03:25,906 --> 00:03:28,675 +then we'll look at some +specific navigation recipes + +73 +00:03:28,675 --> 00:03:31,578 +that mix them together. + +74 +00:03:31,578 --> 00:03:34,081 +The new navigation APIs +introduce a couple + +75 +00:03:34,081 --> 00:03:36,416 +of new container types +that you can use + +76 +00:03:36,416 --> 00:03:38,885 +to describe +the structure of your app, + +77 +00:03:38,885 --> 00:03:41,521 +along with a fresh new varietal +of NavigationLink + +78 +00:03:41,521 --> 00:03:45,492 +for helping your guests +move around that structure. + +79 +00:03:45,492 --> 00:03:48,728 +The first new container +is NavigationStack. + +80 +00:03:48,728 --> 00:03:51,398 +NavigationStack represents +a push-pop interface + +81 +00:03:51,398 --> 00:03:53,867 +like you see +in Find My on Apple Watch, + +82 +00:03:53,867 --> 00:03:55,135 +Settings on iPhone, + +83 +00:03:55,135 --> 00:04:00,874 +and the new System Settings app +on macOS Ventura. + +84 +00:04:00,874 --> 00:04:04,945 +The second new container type +is NavigationSplitView. + +85 +00:04:04,945 --> 00:04:07,747 +NavigationSplitView +is perfect for multicolumn apps + +86 +00:04:07,747 --> 00:04:11,418 +like Mail or Notes +on Mac and iPad. + +87 +00:04:11,418 --> 00:04:13,820 +And NavigationSplitView +automatically adapts + +88 +00:04:13,820 --> 00:04:16,323 +to a single-column stack +on iPhone, + +89 +00:04:16,323 --> 00:04:17,891 +in Slide Over on iPad, + +90 +00:04:17,891 --> 00:04:20,927 +and even on Apple Watch +and Apple TV. + +91 +00:04:20,927 --> 00:04:24,097 +NavigationSplitView +has two sets of initializers. + +92 +00:04:24,097 --> 00:04:29,970 +One set, like shown here, +creates a two-column experience. + +93 +00:04:29,970 --> 00:04:31,338 +The other set of initializers + +94 +00:04:31,338 --> 00:04:33,840 +creates a three-column +experience. + +95 +00:04:33,840 --> 00:04:36,109 +NavigationSplitView +comes with a cartload + +96 +00:04:36,109 --> 00:04:39,446 +of configuration options that +let you customize column widths, + +97 +00:04:39,446 --> 00:04:41,281 +sidebar presentation, + +98 +00:04:41,281 --> 00:04:45,118 +and even programmatically +show and hide columns. + +99 +00:04:45,118 --> 00:04:46,887 +I won't dive into +the configuration options + +100 +00:04:46,887 --> 00:04:50,223 +in this talk, but please check +out my colleague Raj's talk, + +101 +00:04:50,223 --> 00:04:53,660 +"SwiftUI on iPad: +Organize your interface" + +102 +00:04:53,660 --> 00:04:55,996 +and the great documentation +on how to tune + +103 +00:04:55,996 --> 00:05:00,634 +NavigationSplitView +to be just right for your app. + +104 +00:05:00,634 --> 00:05:03,737 +Previously, NavigationLinks +always included + +105 +00:05:03,737 --> 00:05:06,106 +a title and view to present. + +106 +00:05:06,106 --> 00:05:08,542 +The new varieties +still include a title, + +107 +00:05:08,542 --> 00:05:10,510 +but instead +of a view to present, + +108 +00:05:10,510 --> 00:05:12,379 +they present a value. + +109 +00:05:12,379 --> 00:05:14,181 +For example, +this link is presenting + +110 +00:05:14,181 --> 00:05:16,349 +the recipe for apple pie. + +111 +00:05:16,349 --> 00:05:18,718 +As we'll see, +NavigationLink is smart. + +112 +00:05:18,718 --> 00:05:21,721 +A link's behavior depends on +the NavigationStack or list + +113 +00:05:21,721 --> 00:05:24,758 +that it appears in. + +114 +00:05:24,758 --> 00:05:28,295 +To see how these +tasty new APIs work together, + +115 +00:05:28,295 --> 00:05:31,097 +let's look at some specific +recipes for using them + +116 +00:05:31,097 --> 00:05:34,601 +in my cookbook app, +and in your apps. + +117 +00:05:34,601 --> 00:05:37,137 +Our first recipe +is a basic stack of views, + +118 +00:05:37,137 --> 00:05:39,306 +like you'd find in +Find My on Apple Watch + +119 +00:05:39,306 --> 00:05:41,374 +or Settings on iPhone. + +120 +00:05:41,374 --> 00:05:43,910 +I have a section +for each category. + +121 +00:05:43,910 --> 00:05:47,647 +Within a section, I can tap +on a recipe to see the details. + +122 +00:05:47,647 --> 00:05:51,218 +Within any recipe, I can tap +one of the related recipes + +123 +00:05:51,218 --> 00:05:53,753 +to push it onto the stack. + +124 +00:05:53,753 --> 00:05:56,690 +I can use the back button +to return to the original recipe + +125 +00:05:56,690 --> 00:06:01,728 +and then to the categories list. + +126 +00:06:01,728 --> 00:06:04,264 +This recipe combines +a NavigationStack + +127 +00:06:04,264 --> 00:06:06,499 +with the new variety +of NavigationLink, + +128 +00:06:06,499 --> 00:06:09,536 +and a navigation +destination modifier. + +129 +00:06:09,536 --> 00:06:11,805 +Let's see how. + +130 +00:06:11,805 --> 00:06:15,175 +I'll start with a basic +NavigationStack. + +131 +00:06:15,175 --> 00:06:19,713 +Inside, I have a List that +iterates over all my categories + +132 +00:06:19,713 --> 00:06:22,315 +and a navigationTitle. + +133 +00:06:22,315 --> 00:06:26,453 +Inside the List, I have +a section for each category. + +134 +00:06:26,453 --> 00:06:30,123 +Next, inside each section, +I'll add a NavigationLink + +135 +00:06:30,123 --> 00:06:33,560 +for each recipe in the category. + +136 +00:06:33,560 --> 00:06:36,896 +For now, I'll make the link +present my RecipeDetail view. + +137 +00:06:36,896 --> 00:06:38,231 +This is using the existing + +138 +00:06:38,231 --> 00:06:41,001 +view destination +NavigationLink. + +139 +00:06:41,001 --> 00:06:43,069 +And that's enough to get +this navigation experience + +140 +00:06:43,069 --> 00:06:45,338 +cooking along. + +141 +00:06:45,338 --> 00:06:48,275 +But what about +programmatic navigation? + +142 +00:06:48,275 --> 00:06:50,210 +To add programmatic navigation, + +143 +00:06:50,210 --> 00:06:53,680 +I need to tease apart two pieces +of this navigation link: + +144 +00:06:53,680 --> 00:06:55,582 +the value it presents + +145 +00:06:55,582 --> 00:06:57,951 +and the view +that goes with that value. + +146 +00:06:57,951 --> 00:06:59,853 +Let's see how. + +147 +00:06:59,853 --> 00:07:03,390 +First, I'll pull the destination +view out of the link + +148 +00:07:03,390 --> 00:07:08,028 +and into the new +navigationDestination modifier. + +149 +00:07:08,028 --> 00:07:10,997 +This modifier declares +the type of the presented data + +150 +00:07:10,997 --> 00:07:15,268 +that it's responsible for; +here, that's a Recipe. + +151 +00:07:15,268 --> 00:07:17,270 +The modifier takes +a view builder + +152 +00:07:17,270 --> 00:07:20,173 +that describes what view +to push onto the stack + +153 +00:07:20,173 --> 00:07:23,910 +when a recipe value +is presented. + +154 +00:07:23,910 --> 00:07:26,980 +Then, I'll switch to one +of the new NavigationLinks + +155 +00:07:26,980 --> 00:07:30,050 +and just present +the recipe value. + +156 +00:07:30,050 --> 00:07:31,117 +Let's peek under the hood + +157 +00:07:31,117 --> 00:07:35,555 +and see how NavigationStack +makes this work. + +158 +00:07:35,555 --> 00:07:38,391 +Every navigation stack +keeps track of a path + +159 +00:07:38,391 --> 00:07:41,795 +that represents all the data +that the stack is showing. + +160 +00:07:41,795 --> 00:07:43,830 +When the stack is +just showing its root view, + +161 +00:07:43,830 --> 00:07:47,267 +like shown here, +the path is empty. + +162 +00:07:47,267 --> 00:07:49,436 +Next, the stack also keeps track + +163 +00:07:49,436 --> 00:07:53,006 +of all the navigation +destinations declared inside it, + +164 +00:07:53,006 --> 00:07:55,842 +or inside any view +pushed onto the stack. + +165 +00:07:55,842 --> 00:07:58,378 +In general, this is a set, +though for this example, + +166 +00:07:58,378 --> 00:08:01,448 +we only have one destination. + +167 +00:08:01,448 --> 00:08:04,751 +Let's add the pushed views +to the diagram, too. + +168 +00:08:04,751 --> 00:08:09,255 +Now, because the path is empty, +so is the list of pushed views. + +169 +00:08:09,255 --> 00:08:10,690 +Now, like milk and cookies, + +170 +00:08:10,690 --> 00:08:13,893 +the magic happens +when we put these together. + +171 +00:08:13,893 --> 00:08:16,162 +When I tap +a value-presenting link, + +172 +00:08:16,162 --> 00:08:19,099 +it appends that value +to the path. + +173 +00:08:19,099 --> 00:08:23,169 +Then, the navigation stack +maps its destinations over + +174 +00:08:23,169 --> 00:08:29,209 +the path values to decide which +views to push on the stack. + +175 +00:08:29,209 --> 00:08:34,180 +Now, from my apple pie recipe, +if I tap Pie Crust, + +176 +00:08:34,180 --> 00:08:37,350 +the link appends +that to the path, too. + +177 +00:08:37,350 --> 00:08:42,021 +NavigationStack does its magic + +178 +00:08:42,021 --> 00:08:46,025 +and pushes another RecipeDetail +view onto the stack. + +179 +00:08:46,025 --> 00:08:48,194 +For every value +I add to the path, + +180 +00:08:48,194 --> 00:08:51,831 +NavigationStack +pushes another view. + +181 +00:08:51,831 --> 00:08:54,567 +When I tap the back button, + +182 +00:08:54,567 --> 00:08:57,470 +NavigationStack removes +the last item from the path + +183 +00:08:57,470 --> 00:08:59,672 +and from the pushed views. + +184 +00:08:59,672 --> 00:09:02,642 +And NavigationStack +has one more trick to offer. + +185 +00:09:02,642 --> 00:09:06,045 +It lets us connect to this path +using a binding. + +186 +00:09:06,045 --> 00:09:08,548 +Let's go back to our code. + +187 +00:09:08,548 --> 00:09:10,750 +Here's where we were. + +188 +00:09:10,750 --> 00:09:13,686 +To bind the path, +first I'll add some State. + +189 +00:09:13,686 --> 00:09:17,023 +Because every value pushed +on this stack is a recipe, + +190 +00:09:17,023 --> 00:09:20,293 +I can use an array +of recipes as my path. + +191 +00:09:20,293 --> 00:09:22,929 +If you need to present +a variety of data on a stack, + +192 +00:09:22,929 --> 00:09:23,830 +be sure to check out + +193 +00:09:23,830 --> 00:09:29,836 +the new type-erasing +NavigationPath collection. + +194 +00:09:29,836 --> 00:09:32,505 +Once I have my path state, +I add an argument + +195 +00:09:32,505 --> 00:09:36,209 +to my NavigationStack +and pass a binding to the path. + +196 +00:09:36,209 --> 00:09:40,280 +With that in place, +I can make my stack sizzle. + +197 +00:09:40,280 --> 00:09:42,182 +For example, +I could add a method + +198 +00:09:42,182 --> 00:09:44,751 +to jump to a particular recipe. + +199 +00:09:44,751 --> 00:09:46,152 +Or from anywhere on my stack, + +200 +00:09:46,152 --> 00:09:51,157 +I can pop back to the root +just by resetting the path. + +201 +00:09:51,157 --> 00:09:53,793 +That's how to prepare +a pushable stack using + +202 +00:09:53,793 --> 00:09:55,628 +the new NavigationStack, + +203 +00:09:55,628 --> 00:09:57,864 +value-presenting +NavigationLinks, + +204 +00:09:57,864 --> 00:10:02,068 +and navigationDestinations +in SwiftUI. + +205 +00:10:02,068 --> 00:10:05,338 +This recipe works on all +platforms, including the Mac, + +206 +00:10:05,338 --> 00:10:10,076 +but really shines on iPhone, +Apple TV, and Apple Watch. + +207 +00:10:10,076 --> 00:10:12,912 +To see NavigationStack in +action, be sure to check out + +208 +00:10:12,912 --> 00:10:18,251 +"Build a productivity app +for Apple Watch." + +209 +00:10:18,251 --> 00:10:21,387 +Our next recipe is for +multicolumn presentation + +210 +00:10:21,387 --> 00:10:25,792 +without any stacks, like you'd +find in Mail on Mac and iPad. + +211 +00:10:25,792 --> 00:10:28,661 +On iPad, +the sidebar is initially hidden. + +212 +00:10:28,661 --> 00:10:31,164 +I can reveal it +and choose a category. + +213 +00:10:31,164 --> 00:10:34,334 +Then, in the second column, +I can choose a recipe. + +214 +00:10:34,334 --> 00:10:38,471 +The third column +shows the recipe details. + +215 +00:10:38,471 --> 00:10:41,708 +This recipe combines +a NavigationSplitView + +216 +00:10:41,708 --> 00:10:43,877 +with the new variety +of NavigationLink, + +217 +00:10:43,877 --> 00:10:45,678 +and a List selection. + +218 +00:10:45,678 --> 00:10:47,914 +This recipe is great +on larger devices + +219 +00:10:47,914 --> 00:10:50,049 +because it helps avoid modality. + +220 +00:10:50,049 --> 00:10:53,286 +I can see all my information +without having to drill in. + +221 +00:10:53,286 --> 00:10:55,522 +Let's see how. + +222 +00:10:55,522 --> 00:10:59,092 +I'll start with a three-column +NavigationSplitView + +223 +00:10:59,092 --> 00:11:02,462 +with placeholder views +for the content and detail. + +224 +00:11:02,462 --> 00:11:04,664 +Then, I'll add a List +in the sidebar + +225 +00:11:04,664 --> 00:11:07,700 +that iterates over +all my categories, + +226 +00:11:07,700 --> 00:11:09,869 +and a navigationTitle. + +227 +00:11:09,869 --> 00:11:10,970 +Inside the List, + +228 +00:11:10,970 --> 00:11:14,541 +I have a NavigationLink +for each category. + +229 +00:11:14,541 --> 00:11:16,476 +Next, I'll introduce some State + +230 +00:11:16,476 --> 00:11:19,479 +to keep track of which category +is selected. + +231 +00:11:19,479 --> 00:11:21,014 +I'll tweak our list +in the sidebar + +232 +00:11:21,014 --> 00:11:23,283 +to use the selectedCategory. + +233 +00:11:23,283 --> 00:11:26,019 +Note that we're passing +a binding to the selection. + +234 +00:11:26,019 --> 00:11:27,987 +This lets the list +and its contents + +235 +00:11:27,987 --> 00:11:30,423 +manipulate the selection. + +236 +00:11:30,423 --> 00:11:32,792 +When you put a value-presenting +link inside a list + +237 +00:11:32,792 --> 00:11:35,028 +with a matching +selection type -- + +238 +00:11:35,028 --> 00:11:37,730 +category here -- +the link will automatically + +239 +00:11:37,730 --> 00:11:41,100 +update the selection +when tapped or clicked. + +240 +00:11:41,100 --> 00:11:43,670 +So now when I select +a category in the sidebar, + +241 +00:11:43,670 --> 00:11:47,140 +SwiftUI updates +the selectedCategory. + +242 +00:11:47,140 --> 00:11:49,208 +Check out Raj's +"Organize your interface" talk + +243 +00:11:49,208 --> 00:11:50,476 +that I mentioned earlier + +244 +00:11:50,476 --> 00:11:56,349 +for some great information +on selection and lists. + +245 +00:11:56,349 --> 00:11:58,484 +Next, +I'll replace my placeholder + +246 +00:11:58,484 --> 00:12:00,987 +in the content column +with a list of the recipes + +247 +00:12:00,987 --> 00:12:03,523 +for the selected category, + +248 +00:12:03,523 --> 00:12:06,893 +and add a navigationTitle +for this column too. + +249 +00:12:06,893 --> 00:12:09,028 +Just like +for the selected category, + +250 +00:12:09,028 --> 00:12:10,463 +I can use the same technique + +251 +00:12:10,463 --> 00:12:14,567 +to keep track of the selected +recipe in the content list. + +252 +00:12:14,567 --> 00:12:17,337 +I'll use State +for the selectedRecipe, + +253 +00:12:17,337 --> 00:12:20,206 +have my content list +use that state, + +254 +00:12:20,206 --> 00:12:24,444 +and use a value-presenting link +for each recipe. + +255 +00:12:24,444 --> 00:12:26,446 +Finally, +I'll update the detail column + +256 +00:12:26,446 --> 00:12:30,917 +to show, well, the details +for the selectedRecipe. + +257 +00:12:30,917 --> 00:12:32,051 +With this in place, + +258 +00:12:32,051 --> 00:12:37,790 +I again have full programmatic +control over navigation. + +259 +00:12:37,790 --> 00:12:40,727 +For example, to navigate +to my recipe of the day, + +260 +00:12:40,727 --> 00:12:45,665 +I just need to update +my selection state. + +261 +00:12:45,665 --> 00:12:47,433 +That's how to prepare +a multi-column + +262 +00:12:47,433 --> 00:12:51,504 +navigation experience using +the new NavigationSplitView, + +263 +00:12:51,504 --> 00:12:54,774 +value-presenting +NavigationLinks, + +264 +00:12:54,774 --> 00:13:00,446 +and Lists with selection +in SwiftUI. + +265 +00:13:00,446 --> 00:13:03,282 +One super cool thing +about combining List selection + +266 +00:13:03,282 --> 00:13:05,084 +and NavigationSplitView +like this, + +267 +00:13:05,084 --> 00:13:07,820 +is that SwiftUI +can automatically adapt + +268 +00:13:07,820 --> 00:13:10,723 +the split view +to a single stack on iPhone + +269 +00:13:10,723 --> 00:13:14,127 +or in Slide Over on iPad. + +270 +00:13:14,127 --> 00:13:16,763 +Changes to selection +automatically translate + +271 +00:13:16,763 --> 00:13:20,667 +into the appropriate +pushes and pops on iPhone. + +272 +00:13:20,667 --> 00:13:22,535 +Of course, +this multicolumn presentation + +273 +00:13:22,535 --> 00:13:24,671 +also works great on the Mac. + +274 +00:13:24,671 --> 00:13:26,539 +And although Apple TV +and Apple Watch + +275 +00:13:26,539 --> 00:13:29,842 +don't show multiple columns, +those platforms also get + +276 +00:13:29,842 --> 00:13:33,346 +the automatic translation +to a single stack. + +277 +00:13:33,346 --> 00:13:39,118 +NavigationSplitView in SwiftUI +works on all platforms. + +278 +00:13:39,118 --> 00:13:42,021 +Next, let's look at how we can +put all these ingredients + +279 +00:13:42,021 --> 00:13:45,525 +together by building a +two-column navigation experience + +280 +00:13:45,525 --> 00:13:48,628 +like that in Photos +on iPad and Mac. + +281 +00:13:48,628 --> 00:13:50,096 +When I select a category, + +282 +00:13:50,096 --> 00:13:55,234 +the detail area shows a grid of +all my recipes in that category. + +283 +00:13:55,234 --> 00:13:58,004 +When I tap a recipe, +it's pushed onto a stack + +284 +00:13:58,004 --> 00:13:59,806 +in the detail area. + +285 +00:13:59,806 --> 00:14:03,543 +When I tap a related recipe, +it's also pushed onto the stack. + +286 +00:14:03,543 --> 00:14:07,013 +And I can navigate back +to the grid of recipes. + +287 +00:14:10,016 --> 00:14:12,952 +This recipe is our +pièce de résistance, + +288 +00:14:12,952 --> 00:14:14,887 +combining navigation split view, + +289 +00:14:14,887 --> 00:14:17,857 +stack, link, destination, +and list. + +290 +00:14:17,857 --> 00:14:21,661 +Let's see how all these +ingredients go together. + +291 +00:14:21,661 --> 00:14:24,797 +I'll start with a two-column +NavigationSplitView. + +292 +00:14:24,797 --> 00:14:27,800 +The first column is exactly +like the previous recipe. + +293 +00:14:27,800 --> 00:14:30,770 +I have some State to track +the selectedCategory + +294 +00:14:30,770 --> 00:14:33,539 +and a List that uses +a binding to that state + +295 +00:14:33,539 --> 00:14:36,509 +and a value-presenting +NavigationLink, + +296 +00:14:36,509 --> 00:14:39,045 +and the requisite +navigationTitle. + +297 +00:14:39,045 --> 00:14:42,782 +The differences in this recipe +are in the detail area. + +298 +00:14:42,782 --> 00:14:46,419 +The new navigation APIs really +take advantage of composition. + +299 +00:14:46,419 --> 00:14:48,154 +Just like I can put a list + +300 +00:14:48,154 --> 00:14:51,190 +inside a column +of a NavigationSplitView, + +301 +00:14:51,190 --> 00:14:56,028 +I can also put a NavigationStack +inside a column. + +302 +00:14:56,028 --> 00:14:58,264 +The root view +of this Navigation Stack + +303 +00:14:58,264 --> 00:15:00,233 +is my RecipeGrid. + +304 +00:15:00,233 --> 00:15:04,337 +Notice that the RecipeGrid +is inside the NavigationStack. + +305 +00:15:04,337 --> 00:15:06,639 +That means I can put +stack-related modifiers + +306 +00:15:06,639 --> 00:15:09,075 +inside RecipeGrid. + +307 +00:15:09,075 --> 00:15:11,077 +Let's zoom in to +the body of RecipeGrid + +308 +00:15:11,077 --> 00:15:14,847 +to see what that means. + +309 +00:15:14,847 --> 00:15:18,584 +RecipeGrid is a view and takes +a category as a parameter. + +310 +00:15:18,584 --> 00:15:22,622 +Because category is optional +here, I'll start with an if-let. + +311 +00:15:22,622 --> 00:15:25,825 +The else case handles +an empty selection. + +312 +00:15:25,825 --> 00:15:30,263 +Inside my if, I'll add +a scroll view and a lazy grid. + +313 +00:15:30,263 --> 00:15:33,166 +Lazy grid layout +takes a sequence of views. + +314 +00:15:33,166 --> 00:15:36,569 +Here, I'm using ForEach +to iterate over my recipes. + +315 +00:15:36,569 --> 00:15:37,770 +For each recipe, + +316 +00:15:37,770 --> 00:15:41,107 +I have a value-presenting +NavigationLink. + +317 +00:15:41,107 --> 00:15:43,576 +The link presents +a recipe value. + +318 +00:15:43,576 --> 00:15:45,878 +The link's label, +in this trailing closure, + +319 +00:15:45,878 --> 00:15:50,449 +is my RecipeTile +with the thumbnail and title. + +320 +00:15:50,449 --> 00:15:52,852 +So what's left +to finish this grid? + +321 +00:15:52,852 --> 00:15:54,453 +Well, I haven't told +the NavigationStack + +322 +00:15:54,453 --> 00:15:57,723 +how to map +from recipes to detail views. + +323 +00:15:57,723 --> 00:15:59,659 +Like I mentioned +with the first recipe, + +324 +00:15:59,659 --> 00:16:01,127 +the new NavigationStack + +325 +00:16:01,127 --> 00:16:04,030 +uses the navigationDestination +modifier + +326 +00:16:04,030 --> 00:16:08,034 +to map from values on its path +to views shown on the stack. + +327 +00:16:08,034 --> 00:16:11,737 +So let's add a +navigationDestination modifier. + +328 +00:16:11,737 --> 00:16:14,440 +But where should I attach it? + +329 +00:16:14,440 --> 00:16:17,276 +I'm tempted to attach it +directly to the link, + +330 +00:16:17,276 --> 00:16:19,779 +but this is wrong +for two reasons. + +331 +00:16:19,779 --> 00:16:23,683 +Lazy containers, like List, +Table, or, here, LazyVGrid, + +332 +00:16:23,683 --> 00:16:26,485 +don't load all of their views +immediately. + +333 +00:16:26,485 --> 00:16:28,087 +If I put the modifier here, + +334 +00:16:28,087 --> 00:16:30,122 +the destination +might not be loaded, + +335 +00:16:30,122 --> 00:16:31,858 +so the surrounding +NavigationStack + +336 +00:16:31,858 --> 00:16:33,793 +might not see it. + +337 +00:16:33,793 --> 00:16:36,062 +Second, if I put +the modifier here, + +338 +00:16:36,062 --> 00:16:39,465 +it will be repeated +for every item in my grid. + +339 +00:16:39,465 --> 00:16:43,870 +Instead, I'll attach +the modifier to my ScrollView. + +340 +00:16:43,870 --> 00:16:47,039 +By attaching the modifier +outside the ScrollView, + +341 +00:16:47,039 --> 00:16:49,008 +I ensure that +the NavigationStack + +342 +00:16:49,008 --> 00:16:51,377 +can see +this navigationDestination + +343 +00:16:51,377 --> 00:16:54,280 +regardless +of the scroll position. + +344 +00:16:54,280 --> 00:16:57,083 +Another thing I like about +putting the modifier here + +345 +00:16:57,083 --> 00:17:00,152 +is that it's still close +to the links that target it. + +346 +00:17:00,152 --> 00:17:04,290 +Navigation destination gives me +flexibility to organize my code + +347 +00:17:04,290 --> 00:17:07,827 +the way that makes sense +to me or my team. + +348 +00:17:07,827 --> 00:17:10,363 +Popping back +to my NavigationSplitView, + +349 +00:17:10,363 --> 00:17:11,964 +there's just one more thing + +350 +00:17:11,964 --> 00:17:14,867 +to enable full programmatic +navigation here. + +351 +00:17:14,867 --> 00:17:17,937 +I need to add a navigation path. + +352 +00:17:17,937 --> 00:17:20,506 +I'll add State to hold the path + +353 +00:17:20,506 --> 00:17:24,043 +and bind the state +to my NavigationStack. + +354 +00:17:24,043 --> 00:17:27,380 +With full programmatic +navigation in place, + +355 +00:17:27,380 --> 00:17:29,849 +I can write a method +to show my recipe of the day + +356 +00:17:29,849 --> 00:17:33,052 +in this navigation experience. + +357 +00:17:33,052 --> 00:17:33,986 +That's how to prepare + +358 +00:17:33,986 --> 00:17:37,189 +a multicolumn navigation +experience with stacks + +359 +00:17:37,189 --> 00:17:40,459 +using the new +NavigationSplitView, + +360 +00:17:40,459 --> 00:17:42,061 +NavigationStack, + +361 +00:17:42,061 --> 00:17:44,096 +value-presenting +NavigationLinks, + +362 +00:17:44,096 --> 00:17:48,801 +and Lists with selection +in SwiftUI. + +363 +00:17:48,801 --> 00:17:51,904 +As with the previous recipe, +this one also automatically + +364 +00:17:51,904 --> 00:17:57,243 +adapts to narrow presentations +and works on all platforms. + +365 +00:17:57,243 --> 00:17:59,545 +It was fun exploring +these recipes for structuring + +366 +00:17:59,545 --> 00:18:04,417 +the navigation in my app, +but our navigation feast + +367 +00:18:04,417 --> 00:18:06,986 +wouldn't be complete +without dessert. + +368 +00:18:06,986 --> 00:18:11,457 +For that, let's look at how +to persist the navigation state. + +369 +00:18:11,457 --> 00:18:13,726 +To persist navigation state +in my app, + +370 +00:18:13,726 --> 00:18:15,661 +I just need +two more ingredients: + +371 +00:18:15,661 --> 00:18:18,764 +Codable and SceneStorage. + +372 +00:18:18,764 --> 00:18:21,367 +This recipe +has three basic steps. + +373 +00:18:21,367 --> 00:18:23,736 +First, I'll encapsulate +my navigation state + +374 +00:18:23,736 --> 00:18:26,238 +in a NavigationModel type. + +375 +00:18:26,238 --> 00:18:28,507 +That lets me save +and restore it as a unit + +376 +00:18:28,507 --> 00:18:30,843 +so it's always consistent. + +377 +00:18:30,843 --> 00:18:34,580 +Then, I'll make my +navigation model Codable. + +378 +00:18:34,580 --> 00:18:38,784 +Finally, I'll use SceneStorage +to save and restore my model. + +379 +00:18:38,784 --> 00:18:40,386 +I'll have to take care +along the way -- + +380 +00:18:40,386 --> 00:18:43,122 +I don't want my app to crash +like a fallen soufflé -- + +381 +00:18:43,122 --> 00:18:45,858 +but the steps +are straightforward. + +382 +00:18:45,858 --> 00:18:48,127 +Let's look at step one. + +383 +00:18:48,127 --> 00:18:51,030 +Here's the code +from the end of our last recipe. + +384 +00:18:51,030 --> 00:18:54,233 +My navigation state is stored +in the selectedCategory + +385 +00:18:54,233 --> 00:18:55,935 +and path properties. + +386 +00:18:55,935 --> 00:18:59,472 +The selectedCategory tracks +the selection in the sidebar. + +387 +00:18:59,472 --> 00:19:02,141 +The path tracks the views +pushed onto the stack + +388 +00:19:02,141 --> 00:19:04,310 +in the detail area. + +389 +00:19:04,310 --> 00:19:06,979 +I'll introduce +a new NavigationModel class + +390 +00:19:06,979 --> 00:19:12,718 +and make it conform +to ObservableObject. + +391 +00:19:12,718 --> 00:19:17,490 +Next, I'll move my navigation +state into my model object, + +392 +00:19:17,490 --> 00:19:22,161 +changing the property wrappers +from State to Published. + +393 +00:19:22,161 --> 00:19:24,497 +Then, +I'll introduce a StateObject + +394 +00:19:24,497 --> 00:19:27,900 +to hold an instance +of my NavigationModel + +395 +00:19:27,900 --> 00:19:33,072 +and change the parameters +to use the new model object. + +396 +00:19:33,072 --> 00:19:37,043 +Next, I'll make my +navigation model Codable. + +397 +00:19:37,043 --> 00:19:38,811 +I'll start by adding +the Codable conformance + +398 +00:19:38,811 --> 00:19:39,612 +to the class. + +399 +00:19:39,612 --> 00:19:41,113 +In many cases, + +400 +00:19:41,113 --> 00:19:43,816 +Swift can automatically generate +Codable conformance, + +401 +00:19:43,816 --> 00:19:46,919 +but I want to implement +my own conformance here. + +402 +00:19:46,919 --> 00:19:50,256 +The main reason is that +Recipe is a model value. + +403 +00:19:50,256 --> 00:19:52,425 +I don't want to store +the entire model value + +404 +00:19:52,425 --> 00:19:54,093 +for state restoration. + +405 +00:19:54,093 --> 00:19:55,928 +There are two reasons for this. + +406 +00:19:55,928 --> 00:19:57,897 +First, my recipe database + +407 +00:19:57,897 --> 00:20:00,766 +already contains all the details +for the recipe. + +408 +00:20:00,766 --> 00:20:03,369 +It's not a good use of storage +to repeat that information + +409 +00:20:03,369 --> 00:20:06,038 +in my saved navigation state. + +410 +00:20:06,038 --> 00:20:09,241 +Second, if my recipe database +can change independently + +411 +00:20:09,241 --> 00:20:11,410 +of my local navigation state -- + +412 +00:20:11,410 --> 00:20:14,747 +say, because I finally get +around to adding syncing -- + +413 +00:20:14,747 --> 00:20:19,819 +I don't want my local navigation +state to contain stale data. + +414 +00:20:19,819 --> 00:20:23,155 +For custom codability, +next I'll add CodingKeys. + +415 +00:20:23,155 --> 00:20:25,458 +One of the keys +is just selectedCategory. + +416 +00:20:25,458 --> 00:20:29,728 +But notice that I named +the other "recipePathIds” + +417 +00:20:29,728 --> 00:20:30,729 +I'm planning to just store + +418 +00:20:30,729 --> 00:20:34,233 +the identifiers of the recipes +on the path. + +419 +00:20:34,233 --> 00:20:37,002 +In my encode method, +I'll create a keyed container + +420 +00:20:37,002 --> 00:20:40,106 +using my coding keys +and add the selected category + +421 +00:20:40,106 --> 00:20:42,808 +to the container. + +422 +00:20:42,808 --> 00:20:44,677 +I'm using encodeIfPresent, + +423 +00:20:44,677 --> 00:20:48,881 +so I only write the value +if it's non-nil. + +424 +00:20:48,881 --> 00:20:52,418 +Then, I'll add +the recipe path identifiers. + +425 +00:20:52,418 --> 00:20:54,220 +Note that I'm +mapping over the path + +426 +00:20:54,220 --> 00:20:57,056 +to get the identifiers +to encode. + +427 +00:20:57,056 --> 00:20:59,458 +For example, +suppose my navigation state + +428 +00:20:59,458 --> 00:21:02,495 +included Dessert +as a selected category, + +429 +00:21:02,495 --> 00:21:05,331 +with Apple Pie +and Pie Crust on the path, + +430 +00:21:05,331 --> 00:21:07,933 +like shown +in the green box on top. + +431 +00:21:07,933 --> 00:21:14,273 +This might be encoded to JSON, +like shown in this other box. + +432 +00:21:14,273 --> 00:21:15,941 +To finish up Codability, + +433 +00:21:15,941 --> 00:21:18,277 +I'll add the required +initializer. + +434 +00:21:18,277 --> 00:21:22,381 +The interesting bit is here +where I decode the recipe IDs, + +435 +00:21:22,381 --> 00:21:23,983 +then use my shared data model + +436 +00:21:23,983 --> 00:21:28,354 +to convert the IDs +back into recipes. + +437 +00:21:28,354 --> 00:21:30,856 +I'm using compactMap +to discard any recipes + +438 +00:21:30,856 --> 00:21:32,324 +that couldn't be found. + +439 +00:21:32,324 --> 00:21:34,827 +For example, this might happen +if I delete a recipe + +440 +00:21:34,827 --> 00:21:37,830 +on another device +after I have sync working -- + +441 +00:21:37,830 --> 00:21:40,666 +something I'm definitely +going to do someday. + +442 +00:21:40,666 --> 00:21:42,868 +This is a place you'll need +to use discretion + +443 +00:21:42,868 --> 00:21:46,105 +in your own apps to make sure +any restored navigation state + +444 +00:21:46,105 --> 00:21:50,142 +still makes sense. + +445 +00:21:50,142 --> 00:21:52,111 +Finally, +I'll add a computed property + +446 +00:21:52,111 --> 00:21:56,749 +for reading and writing +my model as JSON data. + +447 +00:21:56,749 --> 00:21:58,284 +Now that I have +a navigation model + +448 +00:21:58,284 --> 00:22:00,586 +and it knows how to encode +and decode itself, + +449 +00:22:00,586 --> 00:22:03,856 +all that's left is to actually +save and restore it. + +450 +00:22:03,856 --> 00:22:07,092 +For that I'll use SceneStorage. + +451 +00:22:07,092 --> 00:22:09,161 +Here's where we left +our main view. + +452 +00:22:09,161 --> 00:22:12,231 +I was using a StateObject +to hold my NavigationModel. + +453 +00:22:12,231 --> 00:22:14,667 +Now, I'll introduce +some SceneStorage + +454 +00:22:14,667 --> 00:22:18,204 +to persist my NavigationModel. + +455 +00:22:18,204 --> 00:22:21,040 +SceneStorage properties +automatically save and restore + +456 +00:22:21,040 --> 00:22:22,942 +their associated values. + +457 +00:22:22,942 --> 00:22:26,312 +When the type of the storage +is optional, like my data here, + +458 +00:22:26,312 --> 00:22:30,049 +the value is nil when +a new scene is created. + +459 +00:22:30,049 --> 00:22:31,784 +When the system +restores a scene, + +460 +00:22:31,784 --> 00:22:35,187 +SwiftUI ensures that the value +of the SceneStorage property + +461 +00:22:35,187 --> 00:22:36,922 +is also restored. + +462 +00:22:36,922 --> 00:22:40,926 +I'll take advantage of this +to persist my NavigationModel. + +463 +00:22:40,926 --> 00:22:45,431 +To do that, I'll add +a task modifier to my view. + +464 +00:22:45,431 --> 00:22:48,534 +The task modifier +runs its closure asynchronously. + +465 +00:22:48,534 --> 00:22:50,202 +It starts when the view appears + +466 +00:22:50,202 --> 00:22:53,772 +and is cancelled +when the view goes away. + +467 +00:22:53,772 --> 00:22:56,508 +Whenever my view appears, +I'll first check whether I have + +468 +00:22:56,508 --> 00:23:00,913 +any existing data +from a previous run of the app. + +469 +00:23:00,913 --> 00:23:05,451 +If so, I'll update my +navigation model with that data. + +470 +00:23:05,451 --> 00:23:09,121 +Then, I'll start an asynchronous +for loop that will iterate + +471 +00:23:09,121 --> 00:23:11,657 +whenever my +navigation model changes. + +472 +00:23:11,657 --> 00:23:15,194 +The body of this loop +will run on each change, + +473 +00:23:15,194 --> 00:23:17,596 +so I can use that +to save my navigation state + +474 +00:23:17,596 --> 00:23:21,634 +back to my scene storage data. + +475 +00:23:21,634 --> 00:23:23,369 +And that's it! + +476 +00:23:23,369 --> 00:23:24,670 +When I leave my app +to go check out + +477 +00:23:24,670 --> 00:23:27,539 +some vintage Julia Child +cooking shows on the web, + +478 +00:23:27,539 --> 00:23:29,308 +it remembers where I was. + +479 +00:23:29,308 --> 00:23:30,643 +When I return to the app, + +480 +00:23:30,643 --> 00:23:34,146 +it takes me back +to where I left off. + +481 +00:23:34,146 --> 00:23:35,814 +Now, no cookbook +would be complete + +482 +00:23:35,814 --> 00:23:39,418 +without a weird section at +the end with handy kitchen tips. + +483 +00:23:39,418 --> 00:23:41,987 +I don't have three great +substitutes for cilantro, + +484 +00:23:41,987 --> 00:23:45,791 +but I do have some +navigation tips to share. + +485 +00:23:45,791 --> 00:23:47,860 +Switch to the new +NavigationStack + +486 +00:23:47,860 --> 00:23:50,929 +and NavigationSplitView +as soon as you can. + +487 +00:23:50,929 --> 00:23:53,932 +If you're using NavigationView +with the stack style, + +488 +00:23:53,932 --> 00:23:55,901 +switch to NavigationStack. + +489 +00:23:55,901 --> 00:23:59,438 +NavigationStack is also +a good first choice on Apple TV, + +490 +00:23:59,438 --> 00:24:03,008 +Apple Watch, +or in sheets on iPad and iPhone, + +491 +00:24:03,008 --> 00:24:06,211 +where the stack style +has always been the default. + +492 +00:24:06,211 --> 00:24:08,781 +If you're using +a multicolumn NavigationView, + +493 +00:24:08,781 --> 00:24:11,450 +switch to NavigationSplitView. + +494 +00:24:11,450 --> 00:24:14,520 +And if you've already adopted +programmatic navigation + +495 +00:24:14,520 --> 00:24:16,422 +using the links +that take bindings, + +496 +00:24:16,422 --> 00:24:18,190 +I strongly encourage you to move + +497 +00:24:18,190 --> 00:24:20,893 +to the new value-presenting +NavigationLink + +498 +00:24:20,893 --> 00:24:24,663 +along with navigation paths +and list selection. + +499 +00:24:24,663 --> 00:24:27,633 +The old-style programmatic links +are deprecated + +500 +00:24:27,633 --> 00:24:31,937 +beginning in iOS 16 +and aligned releases. + +501 +00:24:31,937 --> 00:24:35,240 +For details and examples +on migrating to the new APIs, + +502 +00:24:35,240 --> 00:24:36,542 +check out the article, + +503 +00:24:36,542 --> 00:24:38,644 +"Migrating to new +navigation types" + +504 +00:24:38,644 --> 00:24:41,814 +in the developer documentation. + +505 +00:24:41,814 --> 00:24:45,184 +Next, keep in mind that List +and the new NavigationSplitView + +506 +00:24:45,184 --> 00:24:48,520 +and NavigationStack +were made to mix together. + +507 +00:24:48,520 --> 00:24:51,357 +Compose them to create +navigation experiences + +508 +00:24:51,357 --> 00:24:54,059 +your guests will love. + +509 +00:24:54,059 --> 00:24:56,962 +When using navigation stacks, +navigation destinations + +510 +00:24:56,962 --> 00:25:00,632 +can be anywhere inside +the stack or its subviews. + +511 +00:25:00,632 --> 00:25:03,369 +Consider putting destinations +near the corresponding links + +512 +00:25:03,369 --> 00:25:05,104 +to make maintenance easier, + +513 +00:25:05,104 --> 00:25:08,907 +but remember not to put them +inside of lazy containers. + +514 +00:25:08,907 --> 00:25:11,176 +Finally, I'd encourage you +to start building + +515 +00:25:11,176 --> 00:25:14,213 +your navigation experiences +with NavigationSplitView + +516 +00:25:14,213 --> 00:25:15,681 +when it makes sense. + +517 +00:25:15,681 --> 00:25:17,783 +Even if you're initially +developing for iPhone, + +518 +00:25:17,783 --> 00:25:20,185 +NavigationSplitView +will automatically adapt + +519 +00:25:20,185 --> 00:25:22,087 +to the narrower device. + +520 +00:25:22,087 --> 00:25:25,424 +And when you're ready to support +iPhone Pro Max in landscape, + +521 +00:25:25,424 --> 00:25:27,793 +or to bring your app +to iPad or Mac, + +522 +00:25:27,793 --> 00:25:29,061 +NavigationSplitView + +523 +00:25:29,061 --> 00:25:32,498 +will take advantage +of all that additional space. + +524 +00:25:32,498 --> 00:25:33,532 +Thanks for the chance + +525 +00:25:33,532 --> 00:25:36,935 +to share the new SwiftUI +Navigation APIs with you! + +526 +00:25:36,935 --> 00:25:39,037 +Besides the talks +I mentioned earlier, + +527 +00:25:39,037 --> 00:25:40,139 +I invite you to check out + +528 +00:25:40,139 --> 00:25:42,975 +"Bring multiple windows +to your SwiftUI app" + +529 +00:25:42,975 --> 00:25:45,978 +for some great info on opening +new windows and scenes + +530 +00:25:45,978 --> 00:25:47,579 +in your apps. + +531 +00:25:47,579 --> 00:25:50,382 +I hope that these recipes for +navigation in our cookbook app + +532 +00:25:50,382 --> 00:25:51,717 +were palate-pleasing. + +533 +00:25:51,717 --> 00:25:54,119 +I'm looking forward to seeing +the great experiences + +534 +00:25:54,119 --> 00:25:55,954 +you cook up in your own apps. + +535 +00:25:55,954 --> 00:25:58,190 +Bon appétit! +[LIPS SMACK] + +536 +00:25:58,190 --> 00:26:03,796 +♪ + diff --git a/eng/2022 Session 10056 Compose custom layouts with SwiftUI en.srt b/eng/2022 Session 10056 Compose custom layouts with SwiftUI en.srt new file mode 100644 index 0000000..15b5e53 --- /dev/null +++ b/eng/2022 Session 10056 Compose custom layouts with SwiftUI en.srt @@ -0,0 +1,2273 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,843 --> 00:00:13,514 +Paul: Hello, and welcome to +Compose Custom Layouts with SwiftUI. + +3 +00:00:13,547 --> 00:00:16,183 +I'm Paul, and I work +on developer documentation. + +4 +00:00:16,216 --> 00:00:18,919 +SwiftUI provides +a rich set of building blocks + +5 +00:00:18,952 --> 00:00:22,489 +that you use to compose +your app's interface. + +6 +00:00:22,523 --> 00:00:27,761 +You can combine built-in views +that display elements like text, images, + +7 +00:00:27,794 --> 00:00:31,932 +and graphics to create +custom, composite views. + +8 +00:00:31,965 --> 00:00:35,769 +To arrange all these elements +in ever more sophisticated groupings + +9 +00:00:35,802 --> 00:00:37,804 +SwiftUI provides layout tools. + +10 +00:00:39,139 --> 00:00:42,709 +Containers like horizontal and +vertical stacks let you tell SwiftUI + +11 +00:00:42,743 --> 00:00:45,112 +where to put views +relative to one another, + +12 +00:00:45,145 --> 00:00:47,581 +while view modifiers give you +additional control + +13 +00:00:47,614 --> 00:00:50,484 +over things like spacing and alignment. + +14 +00:00:51,218 --> 00:00:53,487 +In this talk, +I'm going to introduce some new tools + +15 +00:00:53,520 --> 00:00:56,590 +that will make some common layouts +even easier to build, + +16 +00:00:56,623 --> 00:00:59,560 +and will make +more complicated layouts possible. + +17 +00:00:59,593 --> 00:01:04,364 +Along the way, I'll give you some tips +for working with layout in SwiftUI. + +18 +00:01:04,398 --> 00:01:06,967 +I'll start by showing you a new member +of the grid family + +19 +00:01:07,000 --> 00:01:09,269 +that's perfect for two-dimensional layouts + +20 +00:01:09,303 --> 00:01:12,372 +when you have a static set of views +to display. + +21 +00:01:12,406 --> 00:01:16,176 +Next I'll talk about how you can create +a custom view container type + +22 +00:01:16,210 --> 00:01:18,979 +that lets you interact directly +with the layout engine, + +23 +00:01:19,012 --> 00:01:21,949 +using the new layout protocol. + +24 +00:01:21,982 --> 00:01:24,318 +Then I'll talk about ViewThatFits, + +25 +00:01:24,351 --> 00:01:27,621 +a container type that automatically +selects from a collection of views + +26 +00:01:27,654 --> 00:01:31,792 +for the one that, +well, fits in the available space. + +27 +00:01:31,825 --> 00:01:34,862 +And finally, I'll show you how to add +seamless transitions + +28 +00:01:34,895 --> 00:01:38,232 +between layout types using AnyLayout. + +29 +00:01:38,265 --> 00:01:40,334 +To see all these new features in action, + +30 +00:01:40,367 --> 00:01:42,703 +let's take a look at an app +I've been working on. + +31 +00:01:44,204 --> 00:01:45,906 +In recent years, +there's been some debate + +32 +00:01:45,939 --> 00:01:50,410 +among some of my colleagues about +who makes the best furry companion. + +33 +00:01:50,444 --> 00:01:54,781 +I have my own opinion, but I was curious +to see if we could come to some consensus, + +34 +00:01:54,815 --> 00:01:58,452 +so I decided +to make an app to take a poll. + +35 +00:01:58,485 --> 00:02:00,754 +And I want to include folks +with fur allergies, too, + +36 +00:02:00,787 --> 00:02:03,357 +so I'm throwing in one extra option. + +37 +00:02:03,390 --> 00:02:07,160 +Now, I like to do most of +my interface design in SwiftUI, + +38 +00:02:07,194 --> 00:02:09,997 +because it's so easy to prototype +using previews, + +39 +00:02:10,030 --> 00:02:14,334 +but as a starting point, I drew +a quick sketch of what I'm aiming for. + +40 +00:02:14,368 --> 00:02:16,737 +I expect the voting to go on +over a period of time, + +41 +00:02:16,770 --> 00:02:20,774 +so I want a leaderboard in +the middle showing the current standings. + +42 +00:02:20,807 --> 00:02:22,676 +I'll put buttons for voting at the bottom. + +43 +00:02:22,709 --> 00:02:26,647 +And at the top, I'll display some images +of what folks are voting for. + +44 +00:02:28,482 --> 00:02:30,751 +Okay, the first thing I want to do +is to build the leaderboard. + +45 +00:02:30,784 --> 00:02:33,554 +So let's take a closer look at that. + +46 +00:02:33,587 --> 00:02:38,559 +The leaderboard is a two-dimensional grid +of elements with rows for each contender, + +47 +00:02:38,592 --> 00:02:43,197 +and columns that show names, +percentages, and a vote count. + +48 +00:02:43,230 --> 00:02:47,367 +I have a couple of specific things +I want to achieve here. + +49 +00:02:47,401 --> 00:02:51,104 +First, I want the two text columns +to be only as wide as they need to be + +50 +00:02:51,138 --> 00:02:54,074 +to accommodate the widest cell +in each case + +51 +00:02:54,107 --> 00:02:56,610 +because I want the progress views +that represent percentages + +52 +00:02:56,643 --> 00:02:58,812 +to get as much space as they can. + +53 +00:02:58,846 --> 00:03:02,349 +And this needs to be true +no matter how big the counts get + +54 +00:03:02,382 --> 00:03:05,018 +for my friends that speak other languages + +55 +00:03:05,052 --> 00:03:09,022 +or for anyone who uses +different text sizes on their devices. + +56 +00:03:09,056 --> 00:03:11,792 +Second, I want the names +to be leading edge aligned, + +57 +00:03:11,825 --> 00:03:14,728 +but the amounts to be +trailing edge aligned. + +58 +00:03:14,761 --> 00:03:20,167 +Now, SwiftUI already has lazy grids, +which are great for scrollable content. + +59 +00:03:20,200 --> 00:03:23,704 +These containers are very efficient +when you have a lot of views, + +60 +00:03:23,737 --> 00:03:27,774 +because they only load views +that are visible, or about to be visible. + +61 +00:03:27,808 --> 00:03:29,776 +On the other hand, +that means the container + +62 +00:03:29,810 --> 00:03:33,080 +can't automatically size +its cells in both dimensions. + +63 +00:03:34,515 --> 00:03:38,118 +For example, the LazyHGrid can figure out +how wide to make each column, + +64 +00:03:38,151 --> 00:03:42,823 +because it can measure all the views +in a column before drawing them. + +65 +00:03:42,856 --> 00:03:46,827 +But it can't measure every view in a row +to figure out the row's height. + +66 +00:03:46,860 --> 00:03:48,795 +To make this work, +the lazy grids need you + +67 +00:03:48,829 --> 00:03:52,366 +to provide information about one +of their dimensions at initialization time. + +68 +00:03:53,867 --> 00:03:58,305 +For a closer look at lazy grids and other +existing SwiftUI layout container types, + +69 +00:03:58,338 --> 00:04:02,176 +see the Stacks, grids, +and outlines talk from 2020. + +70 +00:04:02,209 --> 00:04:06,079 +But in my case, I don't need scrolling, +and I'd like to let SwiftUI + +71 +00:04:06,113 --> 00:04:09,616 +figure out both the height +and the width for each cell. + +72 +00:04:09,650 --> 00:04:14,421 +For this kind of layout, +SwiftUI now offers a Grid view. + +73 +00:04:14,454 --> 00:04:17,891 +Unlike the lazy grid, +the grid loads all of its views at once, + +74 +00:04:17,925 --> 00:04:21,161 +so it can automatically size +and align its cells + +75 +00:04:21,195 --> 00:04:25,265 +across both its columns and rows. + +76 +00:04:25,299 --> 00:04:27,201 +Let's take a look at the code for this. + +77 +00:04:28,135 --> 00:04:31,438 +Here's a basic version +of my leaderboard written as a Grid. + +78 +00:04:31,471 --> 00:04:35,142 +This particular grid view +contains three GridRow instances. + +79 +00:04:35,175 --> 00:04:38,512 +Within a row, +each view corresponds to a column. + +80 +00:04:38,545 --> 00:04:41,815 +So in this example, +the first text view in each row + +81 +00:04:41,849 --> 00:04:43,684 +corresponds to the first column, + +82 +00:04:43,717 --> 00:04:45,919 +the progress view is in the second column, + +83 +00:04:45,953 --> 00:04:49,623 +and the last text view +is the third column. + +84 +00:04:49,656 --> 00:04:53,393 +Notice that the grid allocates +as much space to each row and column + +85 +00:04:53,427 --> 00:04:55,696 +as it needs to hold its largest view. + +86 +00:04:55,729 --> 00:05:00,234 +So the first text column is wide enough +for the longest name, but no wider. + +87 +00:05:00,267 --> 00:05:04,104 +Flexible views like the progress indicator +take as much space as the grid offers, + +88 +00:05:04,137 --> 00:05:06,139 +which in this case +is whatever is left over + +89 +00:05:06,173 --> 00:05:09,343 +after allocating space +for the text columns. + +90 +00:05:09,376 --> 00:05:13,247 +I want to adjust this a bit, but first, +let me create a basic data model + +91 +00:05:13,280 --> 00:05:15,382 +to give me somewhere +to store vote counts. + +92 +00:05:16,683 --> 00:05:20,354 +I'll need more logic to manage +and share the data across the network, + +93 +00:05:20,387 --> 00:05:24,892 +but while I'm prototyping the interface, +I just need a simple structure like this. + +94 +00:05:24,925 --> 00:05:28,195 +I'll include Identifiable conformance, +because that'll make it easier + +95 +00:05:28,228 --> 00:05:30,497 +to use this type in a ForEach, + +96 +00:05:30,531 --> 00:05:34,368 +and Equatable conformance +to make it possible to animate changes. + +97 +00:05:35,869 --> 00:05:41,475 +And I'll create a set of example data +to use in my previews while I prototype. + +98 +00:05:41,508 --> 00:05:44,578 +Going back to my grid, +I can create a state variable + +99 +00:05:44,611 --> 00:05:46,947 +and initialize it with my example data. + +100 +00:05:46,980 --> 00:05:51,218 +And using that data, +I can now create rows with a ForEach. + +101 +00:05:51,251 --> 00:05:53,453 +Notice that the rendered output +hasn't changed + +102 +00:05:53,487 --> 00:05:56,757 +because it's still displaying +the same data. + +103 +00:05:56,790 --> 00:06:00,460 +That's already pretty close, +but I need to fix the cell alignment. + +104 +00:06:00,494 --> 00:06:02,529 +Right now, +all the cells are center aligned, + +105 +00:06:02,563 --> 00:06:05,399 +which is the default for a grid, +but if you remember, + +106 +00:06:05,432 --> 00:06:07,568 +I want the names +to be leading edge aligned, + +107 +00:06:07,601 --> 00:06:09,970 +and the values +to be trailing edge aligned. + +108 +00:06:10,804 --> 00:06:14,808 +To do that, I'll initialize the grid +with leading edge alignment. + +109 +00:06:14,842 --> 00:06:18,512 +The value I use here +applies to all the cells in the grid. + +110 +00:06:18,545 --> 00:06:23,183 +That works fine for my first two columns, +but what about the last? + +111 +00:06:23,217 --> 00:06:25,385 +To affect the alignment +of a single column, + +112 +00:06:25,419 --> 00:06:31,091 +I can apply the gridColumnAlignment view +modifier to any one cell in that column. + +113 +00:06:31,124 --> 00:06:34,862 +So I'll do that with the text view +in the last column. + +114 +00:06:34,895 --> 00:06:37,764 +Okay, it's getting there, +but now that I'm looking at it, + +115 +00:06:37,798 --> 00:06:41,835 +I feel like it would be better +with a divider between each row. + +116 +00:06:41,869 --> 00:06:45,272 +If I just add a new row +to the ForEach with a divider, + +117 +00:06:45,305 --> 00:06:47,407 +this isn't exactly what I want, + +118 +00:06:47,441 --> 00:06:50,677 +but notice that this shows +a couple of interesting things. + +119 +00:06:50,711 --> 00:06:53,814 +First, because +the divider is a flexible view, + +120 +00:06:53,847 --> 00:06:56,550 +it's causing the first column +to take more space. + +121 +00:06:56,583 --> 00:06:59,586 +Basically, the grid is now giving +the last column what it needs, + +122 +00:06:59,620 --> 00:07:03,824 +and dividing the remaining space +between the first two columns. + +123 +00:07:03,857 --> 00:07:08,028 +Second, for a grid row that doesn't +have as many views as other grid rows, + +124 +00:07:08,061 --> 00:07:11,965 +the missing views just create empty cells +in the later columns. + +125 +00:07:11,999 --> 00:07:16,670 +But what I really want is to have the +divider span all the columns of the grid, + +126 +00:07:16,703 --> 00:07:20,440 +and SwiftUI has a new view modifier +that lets me do that. + +127 +00:07:23,744 --> 00:07:27,014 +By adding the gridCellColumns +modifier to a view, + +128 +00:07:27,047 --> 00:07:30,017 +I can tell a single view to span +some number of columns; + +129 +00:07:30,050 --> 00:07:32,719 +in this case, all three. + +130 +00:07:32,753 --> 00:07:35,923 +And actually, for the case where +the view should span the entire grid, + +131 +00:07:35,956 --> 00:07:40,994 +I can simplify this by just writing +the view by itself, outside of a grid row. + +132 +00:07:41,028 --> 00:07:44,264 +Okay, my leaderboard is in +pretty good shape + +133 +00:07:44,298 --> 00:07:47,668 +so let me take a look at the buttons +used for voting next. + +134 +00:07:48,836 --> 00:07:51,338 +At first glance, +there's nothing too fancy here. + +135 +00:07:51,371 --> 00:07:54,641 +However, +I do have one special requirement. + +136 +00:07:54,675 --> 00:07:57,077 +On the one hand, +I don't want to bias my participants + +137 +00:07:57,110 --> 00:08:00,013 +with smaller buttons for certain choices. + +138 +00:08:00,047 --> 00:08:03,050 +But I also don't want the buttons +to grow as large as their container, + +139 +00:08:03,083 --> 00:08:06,386 +which could be very large on iPad or Mac. + +140 +00:08:06,420 --> 00:08:11,825 +Instead, the buttons should all have +widths equal to the widest button text. + +141 +00:08:11,859 --> 00:08:15,462 +So what happens +if I try to build this with an Hstack? + +142 +00:08:15,495 --> 00:08:19,666 +What I find is that each button +sizes itself to fit its text label, + +143 +00:08:19,700 --> 00:08:23,203 +and the HStack +packs these together horizontally. + +144 +00:08:23,237 --> 00:08:27,508 +This default stack behavior is exactly +what you want in a lot of cases, + +145 +00:08:27,541 --> 00:08:30,177 +but it doesn't quite meet +my spec for this project. + +146 +00:08:31,778 --> 00:08:34,781 +For a refresher +on layout fundamentals in SwiftUI, + +147 +00:08:34,815 --> 00:08:39,019 +see the Building custom views +with SwiftUI talk from 2019. + +148 +00:08:39,052 --> 00:08:42,789 +Using concepts from that talk, +let's take a look at this view hierarchy + +149 +00:08:42,823 --> 00:08:45,659 +to see what I can change +to get the behavior I want. + +150 +00:08:47,694 --> 00:08:52,132 +First, the stack's container +proposes a size to the stack. + +151 +00:08:52,165 --> 00:08:56,069 +Based on this, the stack proposes a size +to its three buttons, + +152 +00:08:56,103 --> 00:09:00,674 +and then each button passes that size +through to its text label. + +153 +00:09:00,707 --> 00:09:03,377 +The text views calculate the size +they actually want, + +154 +00:09:03,410 --> 00:09:05,746 +which depends on the string they contain + +155 +00:09:05,779 --> 00:09:08,115 +and report this to the button. + +156 +00:09:08,148 --> 00:09:10,884 +The button passes the information +back through. + +157 +00:09:10,918 --> 00:09:13,387 +The stack sizes itself +with this information, + +158 +00:09:13,420 --> 00:09:18,425 +places the buttons in its space, and then +reports its own size to its container. + +159 +00:09:18,458 --> 00:09:21,028 +Okay, so if the buttons +take the size of their text, + +160 +00:09:21,061 --> 00:09:26,133 +what if I wrap each text view in +a flexible frame and allow it to grow? + +161 +00:09:26,166 --> 00:09:29,770 +The text hasn't changed, +but the button sees a flexible subview, + +162 +00:09:29,803 --> 00:09:33,607 +which takes as much space +as the HStack offers. + +163 +00:09:33,640 --> 00:09:38,178 +The stack then distributes its space +equally among the views that it contains. + +164 +00:09:38,212 --> 00:09:40,714 +So the buttons are all the same size now, +which is great, + +165 +00:09:40,747 --> 00:09:44,117 +but their actual size depends +on the stack's container. + +166 +00:09:44,151 --> 00:09:47,921 +The stack will expand to fill +whatever space the container offers, + +167 +00:09:47,955 --> 00:09:49,590 +and that's not what I want. + +168 +00:09:49,623 --> 00:09:52,793 +What I really want is a custom stack type + +169 +00:09:52,826 --> 00:09:55,562 +that asks for the ideal size +of each button, + +170 +00:09:55,596 --> 00:09:59,800 +finds the widest, and then offers +that amount of space to each one. + +171 +00:09:59,833 --> 00:10:04,471 +Fortunately, SwiftUI has a new tool +that lets me do just that. + +172 +00:10:04,505 --> 00:10:07,841 +Using the Layout protocol, +I can define a custom layout container + +173 +00:10:07,875 --> 00:10:10,777 +that participates directly +in the layout process + +174 +00:10:10,811 --> 00:10:13,981 +with behavior +that's tailored to my use case. + +175 +00:10:14,014 --> 00:10:15,682 +Let's see how this works. + +176 +00:10:16,583 --> 00:10:21,788 +Looking at the HStack again, +let me change it to an EqualWidthHStack + +177 +00:10:21,822 --> 00:10:26,093 +a type that I'm going to define +to solve my specific problem. + +178 +00:10:26,126 --> 00:10:29,129 +This type is going to allocate width +to the buttons equally, + +179 +00:10:29,162 --> 00:10:33,500 +in an amount that's as wide +as the widest button's ideal width. + +180 +00:10:33,534 --> 00:10:36,970 +I'll keep the flexible frames +so that buttons with narrower text + +181 +00:10:37,004 --> 00:10:40,174 +can expand to fill the space +that the stack offers. + +182 +00:10:40,207 --> 00:10:43,310 +But the buttons will still have +an ideal size that I can measure, + +183 +00:10:43,343 --> 00:10:45,779 +which is the width of their text. + +184 +00:10:45,812 --> 00:10:49,316 +So let's see how I can implement +MyEqualWidthHStack. + +185 +00:10:52,052 --> 00:10:56,290 +I start by creating a type +that conforms to the Layout protocol. + +186 +00:10:56,323 --> 00:10:59,693 +For a basic layout, +all I need are the two required methods. + +187 +00:10:59,726 --> 00:11:02,462 +Let's add stubs for those. + +188 +00:11:02,496 --> 00:11:04,831 +The first method is sizeThatFits, + +189 +00:11:04,865 --> 00:11:08,669 +where I'll calculate and report +how large my layout container is. + +190 +00:11:10,571 --> 00:11:12,739 +I get a proposed view size input, + +191 +00:11:12,773 --> 00:11:16,677 +which is a size proposal from +my layout's own container view. + +192 +00:11:16,710 --> 00:11:21,648 +And I can propose sizes to my layout's +subviews using the Subviews parameter. + +193 +00:11:22,883 --> 00:11:25,953 +Notice that I can't access +the subviews directly. + +194 +00:11:25,986 --> 00:11:28,789 +Instead, the subviews input +is a collection of proxies + +195 +00:11:28,822 --> 00:11:32,059 +that let me interact in specific ways +with the subviews, + +196 +00:11:32,092 --> 00:11:34,761 +like proposing a size. + +197 +00:11:34,795 --> 00:11:39,132 +Each proxy returns a concrete size +based on the proposal that I’ve made. + +198 +00:11:39,166 --> 00:11:43,437 +I'll collect all those responses +and use them to do some calculations + +199 +00:11:43,470 --> 00:11:48,041 +and then return a concrete size for +the EqualWidthHStack to its container. + +200 +00:11:49,209 --> 00:11:52,846 +The second method that I have to +implement is placeSubviews. + +201 +00:11:52,880 --> 00:11:56,850 +I'll use this to tell my layout's subviews +where to appear. + +202 +00:11:56,884 --> 00:12:01,054 +This method takes the same size proposal +and subviews inputs, + +203 +00:12:01,088 --> 00:12:04,157 +and it also takes a bounds input +that represents the region + +204 +00:12:04,191 --> 00:12:07,060 +that I need to place my subviews into. + +205 +00:12:07,094 --> 00:12:10,097 +Bounds is a rectangle +that has the size that I asked for + +206 +00:12:10,130 --> 00:12:12,666 +in my sizeThatFits implementation. + +207 +00:12:12,699 --> 00:12:15,202 +Remember, +views pick their own size in SwiftUI, + +208 +00:12:15,235 --> 00:12:19,673 +so my layout container +will get the size that it asks for. + +209 +00:12:19,706 --> 00:12:22,309 +The origin of the region +is at the top left, + +210 +00:12:22,342 --> 00:12:25,812 +with positive X to the right, +and positive Y down. + +211 +00:12:25,846 --> 00:12:28,515 +You can assume this for all +your placement calculations, + +212 +00:12:28,549 --> 00:12:30,851 +even in right to left +language environments, + +213 +00:12:30,884 --> 00:12:34,655 +because the framework automatically flips +the x position of each view + +214 +00:12:34,688 --> 00:12:37,457 +when laying out views in that direction. + +215 +00:12:37,491 --> 00:12:42,596 +However, don't assume that the rectangle's +origin has the value (0,0). + +216 +00:12:42,629 --> 00:12:45,299 +Among other things, +allowing for a non-zero origin + +217 +00:12:45,332 --> 00:12:47,334 +enables layout composition, + +218 +00:12:47,367 --> 00:12:49,536 +where the placeSubviews method +of one layout + +219 +00:12:49,570 --> 00:12:52,206 +calls into the same method of another. + +220 +00:12:52,239 --> 00:12:55,442 +To make it a little easier to work with, +the rectangle provides properties + +221 +00:12:55,475 --> 00:12:57,945 +for accessing important parts +of the region, + +222 +00:12:57,978 --> 00:12:59,980 +like the minimum, + +223 +00:13:00,013 --> 00:13:03,884 +center, +and maximum points in each dimension. + +224 +00:13:05,152 --> 00:13:07,421 +Now, before I move on, +notice one other parameter + +225 +00:13:07,454 --> 00:13:09,356 +that these methods both have: + +226 +00:13:09,389 --> 00:13:11,458 +a bidirectional cache that I could use + +227 +00:13:11,491 --> 00:13:16,363 +to share the results of intermediate +calculations across method calls. + +228 +00:13:16,396 --> 00:13:18,899 +For many simple layouts, +you won't need this, + +229 +00:13:18,932 --> 00:13:22,035 +and I'm just going to +ignore the cache for now. + +230 +00:13:22,069 --> 00:13:25,772 +However, if profiling your app with +Instruments shows that you need to improve + +231 +00:13:25,806 --> 00:13:27,908 +the efficiency of your layout code, + +232 +00:13:27,941 --> 00:13:29,877 +you can look into adding one. + +233 +00:13:29,910 --> 00:13:32,513 +Check out the documentation +for more information about that. + +234 +00:13:34,715 --> 00:13:37,084 +Okay, let's implement sizeThatFits. + +235 +00:13:37,117 --> 00:13:39,419 +Remember, I want to return +a size for my container + +236 +00:13:39,453 --> 00:13:44,825 +that fits all of the buttons arranged +horizontally, all at the same width. + +237 +00:13:44,858 --> 00:13:47,761 +So first, +I'll ask each button for its size, + +238 +00:13:47,794 --> 00:13:51,565 +which I do by proposing a size +and seeing what comes back. + +239 +00:13:51,598 --> 00:13:53,567 +To measure the flexibility of a subview, + +240 +00:13:53,600 --> 00:13:57,137 +I can make multiple measurements +using special proposals for minimum, + +241 +00:13:57,171 --> 00:13:59,406 +maximum, and ideal sizes, + +242 +00:13:59,439 --> 00:14:01,875 +or I can propose a specific size. + +243 +00:14:01,909 --> 00:14:07,347 +In this case, I use the unspecified +size proposal to ask for the ideal size. + +244 +00:14:08,582 --> 00:14:11,318 +Then I'll find the largest value +in each dimension + +245 +00:14:11,351 --> 00:14:13,487 +for all the sizes that I get back. + +246 +00:14:13,520 --> 00:14:15,789 +In this case, +the goldfish button sets the width, + +247 +00:14:15,822 --> 00:14:18,625 +and the heights are all the same. + +248 +00:14:18,659 --> 00:14:20,360 +Now let me refactor that into a method, + +249 +00:14:20,394 --> 00:14:23,964 +because I'll need it again +when I place my subviews. + +250 +00:14:23,997 --> 00:14:27,401 +Next, I need to account +for the spacing between views. + +251 +00:14:27,434 --> 00:14:30,604 +I could just use a constant spacing, +like 10 points, + +252 +00:14:30,637 --> 00:14:33,440 +but the layout protocol lets me do better. + +253 +00:14:33,473 --> 00:14:37,377 +In SwiftUI, all views have spacing +preferences that indicate + +254 +00:14:37,411 --> 00:14:42,015 +the amount of space the view prefers +to have between itself and the next view. + +255 +00:14:42,049 --> 00:14:45,219 +These preferences are stored +in a ViewSpacing instance + +256 +00:14:45,252 --> 00:14:48,488 +that's available to layout containers. + +257 +00:14:48,522 --> 00:14:51,024 +The view might prefer different values +on different edges, + +258 +00:14:51,058 --> 00:14:54,928 +and even different values for +different kinds of adjacent views. + +259 +00:14:54,962 --> 00:14:57,497 +For example, +a view might want more or less space + +260 +00:14:57,531 --> 00:15:02,803 +between itself and a text view +than it wants between itself and an image. + +261 +00:15:02,836 --> 00:15:06,206 +And the values can vary +by platform as well. + +262 +00:15:06,240 --> 00:15:09,743 +You can ignore these preferences +if it makes sense for your layout, + +263 +00:15:09,776 --> 00:15:13,380 +which is essentially what's happening +when you initialize a built-in stack + +264 +00:15:13,413 --> 00:15:15,415 +with a custom spacing, + +265 +00:15:15,449 --> 00:15:17,584 +but respecting these preferences +in your own layouts + +266 +00:15:17,618 --> 00:15:20,487 +is a good way to get results +that automatically follow + +267 +00:15:20,521 --> 00:15:22,489 +Apple's interface guidelines, + +268 +00:15:22,523 --> 00:15:26,527 +and as a result, +match the look of the rest of the system. + +269 +00:15:26,560 --> 00:15:29,796 +Now, every view +has preferences on all edges, + +270 +00:15:29,830 --> 00:15:31,532 +and when I bring two views together, + +271 +00:15:31,565 --> 00:15:35,068 +the preferences on a common edge +might not match. + +272 +00:15:35,102 --> 00:15:37,204 +To resolve this, +a built-in layout container + +273 +00:15:37,237 --> 00:15:39,740 +uses the larger of the two preferences. + +274 +00:15:39,773 --> 00:15:41,942 +And I can do the same thing +in my own layout. + +275 +00:15:43,443 --> 00:15:47,314 +The subview proxies give me a way +to ask for each button's preferred spacing + +276 +00:15:47,347 --> 00:15:50,651 +to some other button along a given axis. + +277 +00:15:50,684 --> 00:15:54,688 +So let me create an array of values by +scanning through the subviews + +278 +00:15:54,721 --> 00:15:59,493 +and calling the distance method +on each proxy's spacing instance + +279 +00:15:59,526 --> 00:16:02,930 +to get the spacing +to the next view's spacing instance + +280 +00:16:02,963 --> 00:16:05,599 +along the horizontal axis. + +281 +00:16:05,632 --> 00:16:07,601 +This call takes into account +the preferences + +282 +00:16:07,634 --> 00:16:11,004 +of both views on their common edge. + +283 +00:16:11,038 --> 00:16:13,407 +The first element in this array +tells me how much space + +284 +00:16:13,440 --> 00:16:17,611 +the cat button wants horizontally +to the goldfish button, + +285 +00:16:17,644 --> 00:16:22,216 +and the next tells me how much the +goldfish button wants to the dog button. + +286 +00:16:22,249 --> 00:16:24,952 +I'll force the last element in the array +to be zero + +287 +00:16:24,985 --> 00:16:28,622 +because there aren't any more buttons +to compare against. + +288 +00:16:28,655 --> 00:16:32,626 +Okay, let me refactor that +into a method for later too. + +289 +00:16:32,659 --> 00:16:36,597 +Now I can combine the spacing values +to find the total spacing + +290 +00:16:36,630 --> 00:16:39,132 +and use that that with the width +and height measurements + +291 +00:16:39,166 --> 00:16:41,168 +to return a size value. + +292 +00:16:41,201 --> 00:16:43,070 +This is the size that my layout needs, + +293 +00:16:43,103 --> 00:16:45,439 +given the ideal sizes of its subviews + +294 +00:16:45,472 --> 00:16:49,376 +and each subview's preferred spacing. + +295 +00:16:49,409 --> 00:16:52,946 +The other method that I need +to implement is placeSubviews. + +296 +00:16:52,980 --> 00:16:55,983 +As I mentioned before, +I get both the bounds of the container, + +297 +00:16:56,016 --> 00:17:01,021 +and the collection of subview proxies +that I can use to direct the buttons. + +298 +00:17:01,054 --> 00:17:04,291 +First, I calculate maxSize +and the spacing array + +299 +00:17:04,324 --> 00:17:06,593 +just like I did in sizeThatFits method, + +300 +00:17:06,627 --> 00:17:10,030 +because I'll need those values here too. + +301 +00:17:10,063 --> 00:17:13,967 +Then I'll create a size proposal +that I can use for each of my subviews, + +302 +00:17:14,001 --> 00:17:16,537 +this time based on the size +that I want them to have, + +303 +00:17:16,570 --> 00:17:18,639 +rather than their ideal size. + +304 +00:17:18,672 --> 00:17:23,443 +I only need one proposal, because I want +all the buttons to the be the same size. + +305 +00:17:23,477 --> 00:17:26,313 +And I'll find a starting position +in the horizontal dimension + +306 +00:17:26,346 --> 00:17:30,250 +for my first subview, calculated as +the leading edge of my bounds, + +307 +00:17:30,284 --> 00:17:32,553 +plus half the width of a button. + +308 +00:17:32,586 --> 00:17:34,955 +Notice I'm not relying +on the origin to be zero, + +309 +00:17:34,988 --> 00:17:39,159 +but instead starting +with the minX value instead. + +310 +00:17:39,193 --> 00:17:42,296 +Finally, I can go through +each of the subview proxies + +311 +00:17:42,329 --> 00:17:45,532 +and call its place method with a point, + +312 +00:17:45,566 --> 00:17:49,036 +a statement of what that point represents +in terms of the button, + +313 +00:17:49,069 --> 00:17:52,105 +and the size proposal. + +314 +00:17:52,139 --> 00:17:55,075 +Each time through the loop, +I update the horizontal position + +315 +00:17:55,108 --> 00:17:58,545 +by the width of a view, +plus the spacing for the next view pair, + +316 +00:17:58,579 --> 00:18:01,114 +to get ready for the next iteration. + +317 +00:18:01,148 --> 00:18:02,549 +And that's it. + +318 +00:18:02,583 --> 00:18:06,053 +Now let's see what happens +when I use this new view layout type. + +319 +00:18:07,454 --> 00:18:08,655 +And there it is. + +320 +00:18:08,689 --> 00:18:11,058 +I instantiate my own +custom layout container + +321 +00:18:11,091 --> 00:18:13,527 +just like I would a built-in HStack, + +322 +00:18:13,560 --> 00:18:18,532 +and the buttons are arranged horizontally, +all at the same width. + +323 +00:18:18,565 --> 00:18:20,467 +Now, I want to pause here for a moment + +324 +00:18:20,501 --> 00:18:22,970 +and talk about how the Layout protocol +solves a problem + +325 +00:18:23,003 --> 00:18:27,040 +that you might have tried to use +geometry reader for in the past. + +326 +00:18:27,074 --> 00:18:31,378 +Geometry reader is, after all, +a tool for measuring view sizes. + +327 +00:18:31,411 --> 00:18:35,015 +However, +it's not the best choice in this case. + +328 +00:18:35,048 --> 00:18:38,919 +And that's because a geometry reader +is designed to measure its container view, + +329 +00:18:38,952 --> 00:18:41,288 +and report the that size to its subview. + +330 +00:18:41,321 --> 00:18:45,792 +The subview then uses the information +to draw its own content. + +331 +00:18:45,826 --> 00:18:48,428 +Notice that for the intended use +of a geometry reader, + +332 +00:18:48,462 --> 00:18:50,330 +the information flows downward. + +333 +00:18:50,364 --> 00:18:52,432 +The measurement that the reader makes +has no effect + +334 +00:18:52,466 --> 00:18:54,701 +on the layout of its own container. + +335 +00:18:55,836 --> 00:18:59,439 +This is great for things like drawing +a path that scales with its container. + +336 +00:18:59,473 --> 00:19:03,143 +The geometry reader tells the path logic +how much space it has to work with, + +337 +00:19:03,177 --> 00:19:07,714 +and the path logic inside the subview +adjusts accordingly. + +338 +00:19:07,748 --> 00:19:10,984 +If the container changes size, +so does the path, + +339 +00:19:11,018 --> 00:19:14,755 +because the geometry reader +passes along the new size. + +340 +00:19:14,788 --> 00:19:17,591 +However, for my buttons, +and I'll just focus on one here + +341 +00:19:17,624 --> 00:19:21,528 +to make it easier to see, +I need to measure the text view, + +342 +00:19:21,562 --> 00:19:26,967 +and then use that to decide how to set +a frame that's the text view's container. + +343 +00:19:27,000 --> 00:19:30,938 +So I could add a geometry reader +in an overlay to the text view– + +344 +00:19:30,971 --> 00:19:33,173 +remember, it measures its container– + +345 +00:19:33,207 --> 00:19:36,343 +and then somehow send the measurement data +back up to the frame, + +346 +00:19:36,376 --> 00:19:38,645 +outside of the normal flow. + +347 +00:19:38,679 --> 00:19:41,548 +But notice that if I do this, +I'm bypassing the layout engine, + +348 +00:19:41,582 --> 00:19:43,784 +which might result in a loop. + +349 +00:19:43,817 --> 00:19:46,887 +The reader measures the layout +and changes the frame, + +350 +00:19:46,920 --> 00:19:51,925 +which might change the layout, which could +require another measurement, and so on. + +351 +00:19:51,959 --> 00:19:54,394 +Now it is possible to make this work, + +352 +00:19:54,428 --> 00:19:57,931 +but if I'm not careful, +I could end up crashing my app. + +353 +00:19:57,965 --> 00:20:00,868 +As a result, +this strategy isn't recommended. + +354 +00:20:00,901 --> 00:20:04,238 +Fortunately, the layout protocol gives you +a better way to solve this problem, + +355 +00:20:04,271 --> 00:20:07,140 +by letting you work +within the layout engine. + +356 +00:20:08,108 --> 00:20:10,711 +Okay, let's look at the buttons again. + +357 +00:20:10,744 --> 00:20:13,013 +There's something else I want to do here. + +358 +00:20:13,046 --> 00:20:14,882 +First, to make this +a little easier to read, + +359 +00:20:14,915 --> 00:20:18,519 +I'll refactor the buttons +into their own subview. + +360 +00:20:18,552 --> 00:20:21,321 +Now, I happen to know that one +of my colleagues uses larger type + +361 +00:20:21,355 --> 00:20:22,990 +on their device. + +362 +00:20:23,023 --> 00:20:27,461 +My app automatically supports Dynamic Type +because I've used default fonts, + +363 +00:20:27,494 --> 00:20:30,898 +so I should mostly get +the right behavior for free. + +364 +00:20:30,931 --> 00:20:35,235 +Let's see what happens +if I increase the type size. + +365 +00:20:35,269 --> 00:20:37,437 +Uh-oh, the buttons don't fit anymore. + +366 +00:20:37,471 --> 00:20:41,041 +Remember that my custom stack +doesn't constrain the button widths, + +367 +00:20:41,074 --> 00:20:43,644 +but just lets them have their ideal size, + +368 +00:20:43,677 --> 00:20:47,080 +which in this case +exceeds the width of the display. + +369 +00:20:47,114 --> 00:20:48,582 +So what can I do? + +370 +00:20:48,615 --> 00:20:51,318 +Well, I could modify the layout +to do something more complicated + +371 +00:20:51,351 --> 00:20:54,421 +when the views don't fit, +taking into account the size proposal + +372 +00:20:54,454 --> 00:20:56,323 +from the layout's container. + +373 +00:20:56,356 --> 00:20:59,626 +But for this case, +I can use the new ViewThatFits container + +374 +00:20:59,660 --> 00:21:01,828 +to do most of the work for me. + +375 +00:21:01,862 --> 00:21:05,499 +This new type picks the first view +that fits in the available space + +376 +00:21:05,532 --> 00:21:07,901 +from a list of views that I give it. + +377 +00:21:09,336 --> 00:21:13,006 +By wrapping my custom stack +in a ViewThatFits structure, + +378 +00:21:13,040 --> 00:21:16,076 +and then adding a vertical stack version +of the same content, + +379 +00:21:16,109 --> 00:21:20,747 +I can let SwiftUI figure out when the +buttons need to be arranged differently. + +380 +00:21:20,781 --> 00:21:24,284 +Of course, the built-in VStack +doesn't have the equal width property + +381 +00:21:24,318 --> 00:21:27,521 +that my custom horizontal stack does, + +382 +00:21:27,554 --> 00:21:31,725 +so I've gone ahead and implemented a +vertical version of the custom stack too. + +383 +00:21:31,758 --> 00:21:34,061 +It's very similar to the one +I already described, + +384 +00:21:34,094 --> 00:21:37,564 +except that it places equal width items +along the vertical axis + +385 +00:21:37,598 --> 00:21:40,067 +instead of the horizontal axis. + +386 +00:21:41,668 --> 00:21:44,771 +And of course, when I remove +the dynamic type size override, + +387 +00:21:44,805 --> 00:21:47,574 +it goes back to the horizontal layout. + +388 +00:21:47,608 --> 00:21:50,010 +Now, there's one last piece of the app +I need to build, + +389 +00:21:50,043 --> 00:21:52,846 +and that's the images at the top. + +390 +00:21:52,880 --> 00:21:56,483 +I could do something simple, like +just show a group of profile pictures, + +391 +00:21:56,517 --> 00:21:59,186 +but I thought I'd have a little fun +with it. + +392 +00:21:59,219 --> 00:22:01,321 +So I made another custom layout type + +393 +00:22:01,355 --> 00:22:04,458 +that draws views in a circular arrangement + +394 +00:22:04,491 --> 00:22:08,028 +and then rotates the arrangement +according to rankings. + +395 +00:22:08,061 --> 00:22:11,031 +So this configuration shows +goldfish in first place, + +396 +00:22:11,064 --> 00:22:13,500 +and the other two tied for second. + +397 +00:22:13,534 --> 00:22:17,070 +And then if dog pulls ahead of cat, +I can rotate a bit to show that. + +398 +00:22:17,104 --> 00:22:19,740 +Or I can show +a slightly more realistic result, + +399 +00:22:19,773 --> 00:22:23,110 +all by rotating a radial layout. + +400 +00:22:23,143 --> 00:22:27,014 +Creating this layout is actually quite +straightforward with the layout protocol. + +401 +00:22:27,047 --> 00:22:30,317 +Like before, I just need two methods. + +402 +00:22:30,350 --> 00:22:33,687 +For size that fits, I want my view +to fill the available space, + +403 +00:22:33,720 --> 00:22:37,724 +so I'll return whatever size +the container view proposes. + +404 +00:22:37,758 --> 00:22:40,360 +I'll convert the proposal +into a concrete size + +405 +00:22:40,394 --> 00:22:44,031 +using the replacing-unspecified-dimensions +method. + +406 +00:22:44,064 --> 00:22:47,234 +That method automatically handles +nil values that could be present + +407 +00:22:47,267 --> 00:22:50,737 +if the container asks for an ideal size. + +408 +00:22:50,771 --> 00:22:53,340 +Then inside place subviews method, + +409 +00:22:53,373 --> 00:22:55,609 +I'll offset each subview from the middle + +410 +00:22:55,642 --> 00:22:59,213 +by some radius that's based +on the size of the layout region, + +411 +00:22:59,246 --> 00:23:03,350 +and apply a rotation that depends +on the index of the view. + +412 +00:23:03,383 --> 00:23:07,588 +As a baseline, +this places the views at 0, 1, + +413 +00:23:07,621 --> 00:23:12,459 +and two-thirds of the way around a circle. + +414 +00:23:12,492 --> 00:23:14,161 +To reflect the current rankings, + +415 +00:23:14,194 --> 00:23:18,365 +I'll also apply an offset +that affects all the views equally. + +416 +00:23:18,398 --> 00:23:20,033 +But where do I get the rankings? + +417 +00:23:20,067 --> 00:23:23,537 +Remember, my layout can only access +the subview proxies, + +418 +00:23:23,570 --> 00:23:27,107 +and not the views, +let alone my data model. + +419 +00:23:27,140 --> 00:23:31,044 +Well, it turns out that the layout +protocol has another trick up its sleeve. + +420 +00:23:31,078 --> 00:23:33,380 +It lets you store values on each subview, + +421 +00:23:33,413 --> 00:23:37,384 +and read the values from inside +the layout protocol methods. + +422 +00:23:37,417 --> 00:23:41,822 +Let's see how I can use that +to communicate the rank information. + +423 +00:23:41,855 --> 00:23:46,593 +First, I declare a new type that conforms +to the LayoutValueKey protocol, + +424 +00:23:46,627 --> 00:23:48,829 +and give it a default value. + +425 +00:23:48,862 --> 00:23:53,166 +In addition to providing a value for +a view when you don't explicitly set one, + +426 +00:23:53,200 --> 00:23:56,303 +the default value establishes +the associated value's type, + +427 +00:23:56,336 --> 00:23:59,339 +which is an integer in this case. + +428 +00:23:59,373 --> 00:24:03,410 +Then, I create a convenience method +on View to set the value + +429 +00:24:03,443 --> 00:24:07,114 +using the layoutValue view modifier. + +430 +00:24:07,147 --> 00:24:08,882 +Now in my view hierarchy, + +431 +00:24:08,916 --> 00:24:13,187 +I can apply my convenience rank modifier +to the views in my layout. + +432 +00:24:13,220 --> 00:24:15,322 +Here, I calculate the rank of each pet + +433 +00:24:15,355 --> 00:24:20,894 +and add it to the pet's corresponding +avatar view inside my radial layout. + +434 +00:24:20,928 --> 00:24:23,597 +Finally, back in my place subviews method, + +435 +00:24:23,630 --> 00:24:27,167 +I can add some code to read the values +from each subview + +436 +00:24:27,201 --> 00:24:31,371 +by using the layout value key as an index. + +437 +00:24:31,405 --> 00:24:34,675 +And I can use the ranks +to calculate an offset. + +438 +00:24:34,708 --> 00:24:37,511 +I won't go through that logic here, +but it basically produces + +439 +00:24:37,544 --> 00:24:40,914 +an appropriate angle +for any possible set of rankings. + +440 +00:24:40,948 --> 00:24:43,283 +Well, all except one. + +441 +00:24:43,317 --> 00:24:45,752 +What happens if there's a three-way tie? + +442 +00:24:45,786 --> 00:24:48,856 +There's no way to rotate +the layout to get all the views in a line, + +443 +00:24:48,889 --> 00:24:53,393 +so I'd have to substitute completely +different layout logic for that case. + +444 +00:24:53,427 --> 00:24:56,330 +However, there is already +a layout type that does this, + +445 +00:24:56,363 --> 00:24:58,498 +and that's the built-in HStack. + +446 +00:24:58,532 --> 00:25:01,168 +So what I'd really like +is to transition to an HStack + +447 +00:25:01,201 --> 00:25:03,604 +when I detect a three-way tie. + +448 +00:25:03,637 --> 00:25:07,107 +And it turns out that +there's a new tool for that, too. + +449 +00:25:07,140 --> 00:25:09,476 +The AnyLayout type lets you +apply different layouts + +450 +00:25:09,510 --> 00:25:12,513 +to a single view hierarchy, +so that you maintain the identity + +451 +00:25:12,546 --> 00:25:16,517 +of the views as you transition +from one layout type to another. + +452 +00:25:17,451 --> 00:25:20,988 +So here I have the radial layout +that we saw before, + +453 +00:25:21,021 --> 00:25:24,057 +and all I have to do is replace that +with a new layout type + +454 +00:25:24,091 --> 00:25:27,528 +that depends on whether +there's a three-way tie. + +455 +00:25:27,561 --> 00:25:31,465 +Because the isThreeWayTie property +is derived from state, + +456 +00:25:31,498 --> 00:25:33,333 +SwiftUI notices when it changes + +457 +00:25:33,367 --> 00:25:36,370 +and recognizes +that it needs to redraw this view. + +458 +00:25:36,403 --> 00:25:39,540 +But because the structural identity +of the view hierarchy + +459 +00:25:39,573 --> 00:25:43,844 +always remains the same, +SwiftUI sees this as a view that changes, + +460 +00:25:43,877 --> 00:25:46,213 +rather than as a new view. + +461 +00:25:46,246 --> 00:25:48,949 +As a result, +with only one more line, + +462 +00:25:48,982 --> 00:25:52,553 +I can create smooth transitions +between layout types. + +463 +00:25:52,586 --> 00:25:55,155 +And in fact, +by adding the animation view modifier, + +464 +00:25:55,189 --> 00:25:59,726 +I also get animations between all +the different states of the radial layout, + +465 +00:25:59,760 --> 00:26:03,931 +because the configuration of the radial +layout depends on the same data. + +466 +00:26:03,964 --> 00:26:06,733 +And here's what +all that looks like in action. + +467 +00:26:06,767 --> 00:26:09,403 +As I tap on different buttons +to change the vote counts, + +468 +00:26:09,436 --> 00:26:13,540 +you can see how the avatars move around +smoothly to reflect the current standings. + +469 +00:26:17,845 --> 00:26:20,447 +So those are some of the new tools +that SwiftUI has + +470 +00:26:20,480 --> 00:26:23,083 +for composing your app's view layouts. + +471 +00:26:23,116 --> 00:26:25,719 +You can use the Grid type +to build highly customizable, + +472 +00:26:25,752 --> 00:26:28,589 +two-dimensional layouts +of static information. + +473 +00:26:28,622 --> 00:26:31,859 +You can use the Layout protocol +to define your own general purpose, + +474 +00:26:31,892 --> 00:26:36,730 +reusable layouts, or layouts that are +highly targeted to a particular use case. + +475 +00:26:36,763 --> 00:26:40,734 +You can use ViewThatFits when you want +to let SwiftUI pick from a group of views + +476 +00:26:40,767 --> 00:26:43,303 +to best fit in the available space. + +477 +00:26:43,337 --> 00:26:48,442 +And you can seamlessly transition +between layout types using AnyLayout. + +478 +00:26:48,475 --> 00:26:51,411 +Thanks for joining me today, +and I hope you have as much fun + +479 +00:26:51,445 --> 00:26:54,214 +playing with these new layout tools +as I have. + diff --git a/eng/2022 Session 10058 SwiftUI on iPad - Organize your interface en.srt b/eng/2022 Session 10058 SwiftUI on iPad - Organize your interface en.srt new file mode 100644 index 0000000..35e3bc4 --- /dev/null +++ b/eng/2022 Session 10058 SwiftUI on iPad - Organize your interface en.srt @@ -0,0 +1,1599 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,676 --> 00:00:12,546 +Raj Ramamurthy: Hello, +and welcome to "SwiftUI on iPad: + +3 +00:00:12,579 --> 00:00:14,147 +Organize your interface." + +4 +00:00:14,181 --> 00:00:17,751 +I'm Raj, and I work on SwiftUI. + +5 +00:00:17,784 --> 00:00:22,055 +iPadOS 16 has a number of updates +to allow building more productive, + +6 +00:00:22,089 --> 00:00:25,592 +professional-grade apps +with rich features. + +7 +00:00:25,626 --> 00:00:28,562 +In this session, +I'm going to discuss a few of them + +8 +00:00:28,595 --> 00:00:34,268 +and talk about organizing the interface +of your SwiftUI apps to shine on iPad. + +9 +00:00:34,301 --> 00:00:38,305 +To start, I'll take you on a tour +of lists and tables. + +10 +00:00:38,338 --> 00:00:41,041 +Then, I'll talk about +the SwiftUI selection model + +11 +00:00:41,074 --> 00:00:44,378 +and how to integrate selection with menus. + +12 +00:00:44,411 --> 00:00:47,581 +Finally, I'll discuss how to structure +your app's navigation + +13 +00:00:47,614 --> 00:00:49,983 +for iPad by using split views. + +14 +00:00:50,017 --> 00:00:52,352 +But wait, there's more. + +15 +00:00:52,386 --> 00:00:55,689 +This is actually the first session +of a two-part series. + +16 +00:00:55,722 --> 00:00:58,492 +In the second part, +my colleague Harry takes a tour + +17 +00:00:58,525 --> 00:01:01,762 +through toolbars, titles, and more. + +18 +00:01:01,795 --> 00:01:03,797 +Harry covers +some really important additions + +19 +00:01:03,830 --> 00:01:06,733 +that take SwiftUI iPad apps +to the next level, + +20 +00:01:06,767 --> 00:01:09,970 +so please make sure +to watch both sessions. + +21 +00:01:10,003 --> 00:01:14,041 +Let's get started with lists and tables. + +22 +00:01:14,074 --> 00:01:18,011 +I recently joined a few book clubs, +but I'm running pretty behind. + +23 +00:01:18,045 --> 00:01:20,848 +It's so hard to find +a quiet place to read. + +24 +00:01:20,881 --> 00:01:24,051 +So to help me focus on my reading, +I've started working on an app + +25 +00:01:24,084 --> 00:01:26,620 +to find these mythical quiet places. + +26 +00:01:26,653 --> 00:01:30,858 +A quiet place is like a reading oasis, +where the pages just fly. + +27 +00:01:30,891 --> 00:01:35,062 +And this app helps me track +all the quiet places that I've found. + +28 +00:01:35,095 --> 00:01:38,131 +I've built the app for iPhone, +but I think it'd be a fun exercise + +29 +00:01:38,165 --> 00:01:42,035 +to update it for iPad to really +take advantage of the larger display. + +30 +00:01:42,069 --> 00:01:45,305 +By making the app better for iPad, +I'll also be further along + +31 +00:01:45,339 --> 00:01:48,175 +when I eventually bring it +to the Mac as well. + +32 +00:01:48,208 --> 00:01:50,811 +I won't be covering the Mac +explicitly in this session, + +33 +00:01:50,844 --> 00:01:54,815 +but many of the APIs shown +also apply on macOS. + +34 +00:01:54,848 --> 00:01:58,318 +Here is a list of all the quiet places +that I've found so far. + +35 +00:01:58,352 --> 00:02:01,989 +This list is a great place +to start with updating the app. + +36 +00:02:02,022 --> 00:02:04,424 +I've started working on the iPad version. + +37 +00:02:04,458 --> 00:02:08,395 +It's not terrible, but it doesn't +take advantage of the bigger screen. + +38 +00:02:08,428 --> 00:02:12,833 +There's quite a bit of wasted space, +and the information density is low. + +39 +00:02:12,866 --> 00:02:16,336 +Thankfully, in iPadOS 16, +there's a great solution + +40 +00:02:16,370 --> 00:02:19,072 +for these sorts +of information dense scenarios: + +41 +00:02:19,106 --> 00:02:21,441 +multi-column tables. + +42 +00:02:21,475 --> 00:02:24,178 +Let me show you what they look like. + +43 +00:02:24,211 --> 00:02:27,915 +Here's the “All Places" view +after adopting the SwiftUI API + +44 +00:02:27,948 --> 00:02:29,816 +for multi-column tables. + +45 +00:02:29,850 --> 00:02:33,420 +I'll work up towards this screenshot +over the next few minutes. + +46 +00:02:33,453 --> 00:02:37,291 +Multi-column tables in SwiftUI +were first introduced in macOS Monterey, + +47 +00:02:37,324 --> 00:02:42,663 +and starting in iPadOS 16, the same +table API is now available for iPad. + +48 +00:02:42,696 --> 00:02:47,968 +Just like on the Mac, tables on iPad +support multiple columns and sorting. + +49 +00:02:48,001 --> 00:02:50,337 +Along with the introduction +of tables on iPad, + +50 +00:02:50,370 --> 00:02:54,374 +SwiftUI now supports sections +in tables on iPad and the Mac. + +51 +00:02:55,976 --> 00:02:58,879 +The general guidance around tables +from the previous session + +52 +00:02:58,912 --> 00:03:03,016 +"SwiftUI on the Mac: Build +the fundamentals" still applies on iPad, + +53 +00:03:03,050 --> 00:03:06,320 +so I invite you to watch that session +if you haven't already. + +54 +00:03:07,354 --> 00:03:10,924 +Let's build up the table shown earlier, +starting from the iPhone list. + +55 +00:03:10,958 --> 00:03:13,794 +Here's the code +for the places list from earlier. + +56 +00:03:15,495 --> 00:03:18,665 +First, I'll switch from a list to a table. + +57 +00:03:18,699 --> 00:03:21,502 +Tables have +a different construction from lists. + +58 +00:03:21,535 --> 00:03:24,872 +Instead of a view builder, +tables accept a column builder. + +59 +00:03:26,406 --> 00:03:29,443 +The first column I'll add +is for the place's name. + +60 +00:03:29,476 --> 00:03:32,546 +The column requires a name +for its header and a view builder + +61 +00:03:32,579 --> 00:03:35,616 +to produce the view +for each element in the collection. + +62 +00:03:35,649 --> 00:03:39,119 +I've also specified a value key path, +which will be important later + +63 +00:03:39,152 --> 00:03:41,889 +when I add sorting to the table. + +64 +00:03:41,922 --> 00:03:45,559 +Notice how similar the view builder is +to the list-based construction. + +65 +00:03:45,592 --> 00:03:49,229 +In fact, I can even reuse +the PlaceCell type from before. + +66 +00:03:50,797 --> 00:03:55,068 +In compact size classes, +tables only show their first column, + +67 +00:03:55,102 --> 00:03:58,939 +which means my table still looks great +on iPhone and in slide over on iPad. + +68 +00:04:00,340 --> 00:04:03,610 +You might notice that this is similar +to a list in appearance. + +69 +00:04:03,644 --> 00:04:05,979 +But I didn't just replace +the table with a list, + +70 +00:04:06,013 --> 00:04:09,416 +because reusing the table allows +for scroll position and selection + +71 +00:04:09,449 --> 00:04:12,920 +to be preserved when transitioning +between size classes. + +72 +00:04:12,953 --> 00:04:15,322 +In general, +make sure to use the first column + +73 +00:04:15,355 --> 00:04:18,592 +for compact-specific appearance, +and always make sure you're testing + +74 +00:04:18,625 --> 00:04:21,862 +your iPad apps in a variety +of environments, like slide over. + +75 +00:04:23,163 --> 00:04:25,165 +Okay, moving on. + +76 +00:04:25,199 --> 00:04:28,135 +I'll add columns +for the comfort and noise levels. + +77 +00:04:28,168 --> 00:04:32,506 +For columns with just textual content, +TableColumn offers a convenience API + +78 +00:04:32,539 --> 00:04:36,376 +that lets me omit the view builder +when my value points to a string. + +79 +00:04:36,410 --> 00:04:38,645 +And in this case, +I know the comfort level isn't + +80 +00:04:38,679 --> 00:04:41,515 +going to need much room, +so I've applied a fixed width. + +81 +00:04:42,683 --> 00:04:45,686 +I can also add sorting to the table +by using comparators. + +82 +00:04:45,719 --> 00:04:48,689 +I'll create some state +to store the comparators in. + +83 +00:04:48,722 --> 00:04:51,225 +The state here is an array +because it represents + +84 +00:04:51,258 --> 00:04:53,460 +all of the comparators for the table. + +85 +00:04:53,493 --> 00:04:56,096 +And setting the initial value +to the name comparator + +86 +00:04:56,129 --> 00:04:59,333 +allows the table to appear sorted +when it's first displayed. + +87 +00:05:00,534 --> 00:05:04,872 +Next, I'll pass a binding to my state +into the table to wire everything up. + +88 +00:05:06,073 --> 00:05:09,176 +Because the columns each specify +their value as a key path + +89 +00:05:09,209 --> 00:05:12,613 +to a comparable field, +they are sortable by default. + +90 +00:05:12,646 --> 00:05:18,051 +And now the table is fully sortable +by name, comfort level, and noise. + +91 +00:05:18,085 --> 00:05:20,587 +Note that table doesn't handle +the sorting on its own. + +92 +00:05:20,621 --> 00:05:21,989 +That's up to me. + +93 +00:05:22,022 --> 00:05:26,059 +I can use the onChange modifier to sort +the data when the sort order changes. + +94 +00:05:27,361 --> 00:05:29,796 +All right, let's take it for a spin. + +95 +00:05:30,797 --> 00:05:33,600 +The table looks great, +showing all the places data + +96 +00:05:33,634 --> 00:05:37,137 +and really taking advantage +of the larger screen. + +97 +00:05:37,171 --> 00:05:40,607 +Unlike on the Mac, +tables on iPad don't scroll horizontally, + +98 +00:05:40,641 --> 00:05:43,310 +so it's important to limit +the number of columns. + +99 +00:05:43,343 --> 00:05:47,181 +This ensures that all of the columns +can be shown at once. + +100 +00:05:47,214 --> 00:05:49,816 +Each column shows its label in the header. + +101 +00:05:49,850 --> 00:05:52,286 +And tapping on the label +sorts that column. + +102 +00:05:52,319 --> 00:05:54,154 +I can even sort by noise level. + +103 +00:05:57,090 --> 00:06:00,127 +And in slide over, +the table collapses into a single column + +104 +00:06:00,160 --> 00:06:03,897 +that represents all of the information +in a more condensed format. + +105 +00:06:03,931 --> 00:06:07,868 +Now that I've updated the list +into a table, let's dive into selection. + +106 +00:06:07,901 --> 00:06:11,071 +In this section, +I'll review the SwiftUI selection model + +107 +00:06:11,104 --> 00:06:14,041 +and discuss +integrating selection with menus. + +108 +00:06:14,074 --> 00:06:18,612 +Along the way, I'm going to supercharge +the places table with rich functionality. + +109 +00:06:18,645 --> 00:06:22,149 +But first, I'll talk about +how selection works in SwiftUI. + +110 +00:06:22,182 --> 00:06:27,287 +SwiftUI includes a robust API +for managing list and table selection. + +111 +00:06:27,321 --> 00:06:30,624 +Here's a diagram with a list +containing a few rows. + +112 +00:06:30,657 --> 00:06:32,426 +Each row has a tag. + +113 +00:06:32,459 --> 00:06:37,331 +These tags are unique values for each row +that help the list manage selection. + +114 +00:06:37,364 --> 00:06:40,234 +In this diagram, +the tags are shown in green circles. + +115 +00:06:41,602 --> 00:06:45,706 +Along with the tag, there's also +some state that holds the selection. + +116 +00:06:45,739 --> 00:06:48,242 +This is the type +that holds the tag values. + +117 +00:06:48,275 --> 00:06:50,344 +For example, with multiple selection, + +118 +00:06:50,377 --> 00:06:54,448 +this is a set that holds the tags +for each selected row. + +119 +00:06:54,481 --> 00:06:57,518 +The job of the list is to coordinate +between the tag in each row + +120 +00:06:57,551 --> 00:06:58,886 +and the selection state. + +121 +00:06:58,919 --> 00:07:02,289 +And it does this +via its selection binding. + +122 +00:07:02,322 --> 00:07:05,926 +So when a row is selected, +such as row number two here, + +123 +00:07:05,959 --> 00:07:09,763 +the list adds it to the set +via the selection binding. + +124 +00:07:09,796 --> 00:07:12,032 +And similarly, +if some other part of the app + +125 +00:07:12,065 --> 00:07:17,437 +programmatically changes the set, +say to add three like shown here, + +126 +00:07:17,471 --> 00:07:20,941 +the list selects it +because the selection binding changes. + +127 +00:07:20,974 --> 00:07:24,611 +This general model is the same +across iOS and macOS. + +128 +00:07:24,645 --> 00:07:29,383 +So there are two parts to selection: +a tag and some state. + +129 +00:07:29,416 --> 00:07:32,719 +Next, I'd like to talk +about where tags come from. + +130 +00:07:32,753 --> 00:07:36,957 +A tag is just a value for a view +in a selectable container + +131 +00:07:36,990 --> 00:07:40,260 +that is used to track +whether that view is selected. + +132 +00:07:40,294 --> 00:07:44,998 +In many cases, SwiftUI can automatically +synthesize tags on your behalf. + +133 +00:07:45,032 --> 00:07:49,203 +Tags are similar to identifiers, +but not quite the same. + +134 +00:07:49,236 --> 00:07:52,673 +When using ForEach, +SwiftUI will automatically derive the tag + +135 +00:07:52,706 --> 00:07:55,242 +for a view from its explicit identity. + +136 +00:07:55,275 --> 00:08:00,013 +And tables will use their row value's +identifier as the selection tag. + +137 +00:08:00,047 --> 00:08:04,885 +In the places app, that means the place +struct's identifier type will be used. + +138 +00:08:04,918 --> 00:08:07,120 +For more information +about explicit identity, + +139 +00:08:07,154 --> 00:08:09,323 +check out "Demystify SwiftUI." + +140 +00:08:11,325 --> 00:08:14,595 +To manually tag views, +use the tag modifier. + +141 +00:08:14,628 --> 00:08:16,763 +This is what ForEach does under the hood. + +142 +00:08:16,797 --> 00:08:19,099 +The tag modifier takes a Hashable value. + +143 +00:08:19,132 --> 00:08:22,336 +However, when using the tag modifier, +be careful– + +144 +00:08:22,369 --> 00:08:25,405 +it's important that all of the views +in a selectable container + +145 +00:08:25,439 --> 00:08:27,708 +share the same tag type. + +146 +00:08:27,741 --> 00:08:31,612 +Otherwise, SwiftUI might not know +how to select the view. + +147 +00:08:31,645 --> 00:08:35,582 +Note that if you use the ID modifier, +it doesn't set the tag. + +148 +00:08:35,616 --> 00:08:39,219 +So that's an overview of tags. +Let's bring back the diagram from earlier. + +149 +00:08:40,487 --> 00:08:42,789 +Now that I've explained the tag part +of this diagram, + +150 +00:08:42,823 --> 00:08:45,893 +I'd like to focus on the other half +of the selection equation: + +151 +00:08:45,926 --> 00:08:47,561 +the selection state. + +152 +00:08:47,594 --> 00:08:51,498 +In the previous example, I used a set, +but there are other options too. + +153 +00:08:52,900 --> 00:08:56,370 +You can use these data structures +to represent selection. + +154 +00:08:56,403 --> 00:08:59,806 +SwiftUI supports single selection, +new in macOS Ventura, + +155 +00:08:59,840 --> 00:09:03,810 +required selection for macOS sidebars, +and multiple selection. + +156 +00:09:06,680 --> 00:09:10,984 +iPadOS 16 also introduces +lightweight multiple selection. + +157 +00:09:11,018 --> 00:09:14,221 +Now, with a keyboard attached, +you don't need to enter edit mode + +158 +00:09:14,254 --> 00:09:18,025 +to select multiple rows, +which helps avoid modality. + +159 +00:09:18,058 --> 00:09:20,527 +When using a keyboard, +you can use common shortcuts + +160 +00:09:20,561 --> 00:09:24,164 +like shift and command +to extend and modify the selection. + +161 +00:09:24,198 --> 00:09:26,633 +And this works great with the pointer. + +162 +00:09:26,667 --> 00:09:29,803 +Here's what the places table looks like +after adopting selection. + +163 +00:09:29,837 --> 00:09:32,639 +In this example, +I have an attached keyboard and trackpad, + +164 +00:09:32,673 --> 00:09:35,976 +so the rows aren't indented, +but they're still selected. + +165 +00:09:36,009 --> 00:09:39,413 +However, when using touch, +I'll still need to enter edit mode, + +166 +00:09:39,446 --> 00:09:42,850 +which is accelerated +by using a two-finger pan. + +167 +00:09:42,883 --> 00:09:45,619 +SwiftUI supports +this gesture automatically. + +168 +00:09:45,652 --> 00:09:47,454 +Speaking of edit mode, +there are some updates + +169 +00:09:47,487 --> 00:09:50,257 +with single selection +and edit mode as well. + +170 +00:09:50,290 --> 00:09:53,327 +In iOS 16, +list selection on iPhone and iPad + +171 +00:09:53,360 --> 00:09:56,563 +no longer requires edit mode +when selecting a single row. + +172 +00:09:56,597 --> 00:10:00,334 +And this is super useful for coordinating +with the updated navigation APIs. + +173 +00:10:01,502 --> 00:10:04,238 +With all of these updates, +that brings us to this table, + +174 +00:10:04,271 --> 00:10:08,242 +which builds upon the previous table +by adding a new column for edit mode. + +175 +00:10:08,275 --> 00:10:12,446 +Edit mode is only required when using +multiple selection without a keyboard. + +176 +00:10:12,479 --> 00:10:16,416 +So with that, I'll update the places table +to support selection. + +177 +00:10:16,450 --> 00:10:19,019 +I can easily add selection +to the places table + +178 +00:10:19,052 --> 00:10:22,589 +by adding some state +to store the selection. + +179 +00:10:22,623 --> 00:10:24,992 +After creating the state, +I'll pass a binding + +180 +00:10:25,025 --> 00:10:27,628 +to the table's initializer. + +181 +00:10:27,661 --> 00:10:31,365 +Table enforces that its selection type +matches its row identifier, + +182 +00:10:31,398 --> 00:10:34,668 +so I've used the place ID type +as the selection type. + +183 +00:10:34,701 --> 00:10:38,872 +Since I want multiple selection, +I've used a set for the selection state. + +184 +00:10:38,906 --> 00:10:42,943 +Table tags its rows automatically, +so I don't need to tag anything myself. + +185 +00:10:44,144 --> 00:10:46,346 +And now I can select rows in the table. + +186 +00:10:46,380 --> 00:10:49,216 +But I haven't really done +anything with the selection. + +187 +00:10:49,249 --> 00:10:53,120 +I think it'd be great to add a button +that allows me to add the selected places + +188 +00:10:53,153 --> 00:10:56,523 +to a guide that I can share +with others in my book club. + +189 +00:10:56,557 --> 00:10:58,992 +This is the code +to add the toolbar button. + +190 +00:10:59,026 --> 00:11:03,096 +If there is a non-empty selection, +the button will now show up. + +191 +00:11:03,130 --> 00:11:04,765 +I've also added an edit button, + +192 +00:11:04,798 --> 00:11:07,634 +which complements the existing +lightweight selection support, + +193 +00:11:07,668 --> 00:11:10,704 +but it provides an affordance +to enter and exit edit mode + +194 +00:11:10,737 --> 00:11:12,606 +when there is no keyboard. + +195 +00:11:12,639 --> 00:11:15,976 +A good iPad app shines both with +and without the keyboard, + +196 +00:11:16,009 --> 00:11:19,780 +so it's important to offer controls +to enter and exit edit mode. + +197 +00:11:21,348 --> 00:11:22,416 +We're getting there. + +198 +00:11:22,449 --> 00:11:24,852 +Now we have a button that shows up +when we select rows + +199 +00:11:24,885 --> 00:11:28,021 +as well as a button +to enter and exit edit mode. + +200 +00:11:28,055 --> 00:11:30,390 +Please make sure to watch +the second session in this series + +201 +00:11:30,424 --> 00:11:33,460 +for more information about toolbars. + +202 +00:11:33,493 --> 00:11:35,162 +I'm pretty happy +with the toolbar button here, + +203 +00:11:35,195 --> 00:11:36,663 +but we can do more. + +204 +00:11:36,697 --> 00:11:39,333 +For actions on selection, +it's a great practice to keep them + +205 +00:11:39,366 --> 00:11:41,702 +as easy to access as possible. + +206 +00:11:41,735 --> 00:11:46,240 +That's why in iOS 16, +iPadOS 16, and macOS Ventura, + +207 +00:11:46,273 --> 00:11:50,544 +SwiftUI adds support +for multi-select context menus. + +208 +00:11:50,577 --> 00:11:53,981 +Multi-select context menus allow +for a context menu to be presented + +209 +00:11:54,014 --> 00:11:57,417 +that operates on a set +of selected identifiers. + +210 +00:11:57,451 --> 00:12:00,621 +Let's examine the anatomy of this table +to understand more. + +211 +00:12:02,122 --> 00:12:05,325 +Item-based context menus +have three variations. + +212 +00:12:05,359 --> 00:12:07,728 +First, you can show a menu +on multiple items, + +213 +00:12:07,761 --> 00:12:09,596 +such as the selection at the top. + +214 +00:12:10,764 --> 00:12:13,667 +You can also show a context menu +on an individual item. + +215 +00:12:15,102 --> 00:12:18,372 +And lastly, you can show a context menu +on an empty area, + +216 +00:12:18,405 --> 00:12:19,840 +where there is no content. + +217 +00:12:21,108 --> 00:12:24,077 +Let's add support for this +into our places table. + +218 +00:12:25,712 --> 00:12:28,282 +I've omitted some details +from the previous code examples + +219 +00:12:28,315 --> 00:12:30,784 +so we can focus on the context menu. + +220 +00:12:30,817 --> 00:12:34,888 +I've added the new contextMenu modifier +that takes a selection type. + +221 +00:12:34,922 --> 00:12:38,292 +This needs to match the selection type +of the list or table, + +222 +00:12:38,325 --> 00:12:41,328 +so since I'm using a table, +I'll use the PlaceID type. + +223 +00:12:43,030 --> 00:12:45,699 +The closure is passed +a set of the items to act on, + +224 +00:12:45,732 --> 00:12:49,136 +so if it's empty, +I know the menu is for the empty area. + +225 +00:12:49,169 --> 00:12:52,806 +I think a button to add a new place +would work great for the empty area. + +226 +00:12:52,840 --> 00:12:56,109 +That way, when I'm on the go +and find a new quiet place to read, + +227 +00:12:56,143 --> 00:12:58,145 +I can add it quickly. + +228 +00:12:58,178 --> 00:13:00,848 +Note that if the view builder +for the empty set of items + +229 +00:13:00,881 --> 00:13:02,549 +doesn't resolve to a view, + +230 +00:13:02,583 --> 00:13:05,586 +SwiftUI won't show a menu +on the empty area. + +231 +00:13:05,619 --> 00:13:08,188 +Next, let's handle a single selection. + +232 +00:13:08,222 --> 00:13:09,923 +If the set has only a single item, + +233 +00:13:09,957 --> 00:13:13,694 +I know the menu is being shown +for a single place. + +234 +00:13:13,727 --> 00:13:16,063 +And for both single +and multiple selections, + +235 +00:13:16,096 --> 00:13:18,732 +I want to be able to add +these places to a guide, + +236 +00:13:18,765 --> 00:13:21,068 +so I'll add another view to the menu. + +237 +00:13:21,101 --> 00:13:23,203 +Let's check out our progress. + +238 +00:13:23,237 --> 00:13:25,873 +Here's the new +context menu support in action. + +239 +00:13:25,906 --> 00:13:29,476 +Clicking on the empty area shows +a menu item to add a new place. + +240 +00:13:29,510 --> 00:13:33,881 +Selecting a single row shows +a context menu for just that row. + +241 +00:13:33,914 --> 00:13:36,183 +And I can extend the selection +with the keyboard, + +242 +00:13:36,216 --> 00:13:38,085 +creating this blue highlight. + +243 +00:13:38,118 --> 00:13:41,388 +I can then activate a context menu +over multiple rows, + +244 +00:13:41,421 --> 00:13:44,091 +allowing me to easily add places +to a guide. + +245 +00:13:45,459 --> 00:13:47,494 +This table is looking pretty snazzy now, + +246 +00:13:47,528 --> 00:13:49,997 +so I think it's time to add +some structure around it. + +247 +00:13:50,030 --> 00:13:52,833 +To do that, I'll need a split view. + +248 +00:13:52,866 --> 00:13:55,702 +Navigation is a fundamental part +of the iPad experience. + +249 +00:13:55,736 --> 00:14:00,174 +And split views are a great way +to avoid modality on iPad's larger display + +250 +00:14:00,207 --> 00:14:04,111 +by showing more information at once +without the need to drill in. + +251 +00:14:04,144 --> 00:14:06,847 +In this section, I'm going to cover +some updates to SwiftUI + +252 +00:14:06,880 --> 00:14:08,982 +around navigation and split views. + +253 +00:14:09,016 --> 00:14:12,386 +In the previous sections, +I created the places table + +254 +00:14:12,419 --> 00:14:16,089 +and added rich features +like selection and edit mode. + +255 +00:14:16,123 --> 00:14:18,959 +But I think the places app +is lacking some structure. + +256 +00:14:18,992 --> 00:14:22,062 +So in this section, I'll build +the foundation of our app's structure + +257 +00:14:22,095 --> 00:14:25,098 +by leveraging a navigation split view. + +258 +00:14:25,132 --> 00:14:27,868 +New in iPadOS 16 and macOS Ventura, + +259 +00:14:27,901 --> 00:14:30,370 +SwiftUI has improved support +for split views + +260 +00:14:30,404 --> 00:14:32,906 +with the NavigationSplitView type. + +261 +00:14:32,940 --> 00:14:35,909 +SwiftUI supports two +or three column split views + +262 +00:14:35,943 --> 00:14:38,545 +and has multiple styles +for complex control + +263 +00:14:38,579 --> 00:14:41,215 +over how the columns are displayed. + +264 +00:14:41,248 --> 00:14:42,416 +I'm not going to fully cover + +265 +00:14:42,449 --> 00:14:44,985 +how to present navigation content +in this session, + +266 +00:14:45,018 --> 00:14:48,889 +so for that, I invite you to check out +the SwiftUI cookbook for navigation. + +267 +00:14:48,922 --> 00:14:50,657 +Curt has quite a few recipes + +268 +00:14:50,691 --> 00:14:53,827 +for cooking up some really tasty +navigation experiences. + +269 +00:14:53,861 --> 00:14:56,897 +Instead, I'm going to focus +more on split views. + +270 +00:14:56,930 --> 00:15:00,534 +Here's a diagram showing +a two column split view on iPad. + +271 +00:15:00,567 --> 00:15:03,737 +In SwiftUI, the leading column +is called the sidebar column + +272 +00:15:03,770 --> 00:15:07,207 +and the trailing column is called +the detail column. + +273 +00:15:07,241 --> 00:15:10,177 +Notice how the columns are balanced +next to each other here. + +274 +00:15:10,210 --> 00:15:13,413 +In landscape, +SwiftUI offers this by default. + +275 +00:15:13,447 --> 00:15:16,650 +In portrait, however, +the sidebar hides out of the way, + +276 +00:15:16,683 --> 00:15:19,186 +showing only the detail column. + +277 +00:15:19,219 --> 00:15:20,654 +Tapping on the sidebar button + +278 +00:15:20,687 --> 00:15:22,656 +shows the sidebar, + +279 +00:15:22,689 --> 00:15:25,926 +which appears over the detail column, +dimming it underneath. + +280 +00:15:27,294 --> 00:15:30,230 +Generally, a two column split view +will prefer to show + +281 +00:15:30,264 --> 00:15:32,833 +only the detail column +when space constrained, + +282 +00:15:32,866 --> 00:15:35,002 +because the detail column often shows + +283 +00:15:35,035 --> 00:15:37,871 +more important information +than the sidebar column. + +284 +00:15:37,905 --> 00:15:39,806 +If you'd like to customize this behavior, + +285 +00:15:39,840 --> 00:15:42,042 +you can either always prefer +the detail column + +286 +00:15:42,075 --> 00:15:44,578 +with the prominentDetail navigation +split view style + +287 +00:15:44,611 --> 00:15:46,113 +or balance the weighting + +288 +00:15:46,146 --> 00:15:48,649 +with the balanced +NavigationSplitView style. + +289 +00:15:48,682 --> 00:15:53,020 +NavigationSplitView also supports +three column layouts. + +290 +00:15:53,053 --> 00:15:55,422 +With three columns, +there is an additional column + +291 +00:15:55,455 --> 00:15:59,059 +between the sidebar and detail +called the content column. + +292 +00:15:59,092 --> 00:16:02,829 +If you're coming from UIKit, you may +know this as the supplementary column. + +293 +00:16:02,863 --> 00:16:06,133 +In landscape, +the content and detail column are shown, + +294 +00:16:06,166 --> 00:16:09,269 +and the sidebar can be toggled. + +295 +00:16:09,303 --> 00:16:13,307 +After tapping on the toolbar button, +the detail column slides out of the way, + +296 +00:16:13,340 --> 00:16:16,810 +making room for the sidebar and content. + +297 +00:16:16,844 --> 00:16:19,813 +In portrait, +only the detail column is shown, + +298 +00:16:19,847 --> 00:16:23,083 +and tapping on the toolbar button +shows the content. + +299 +00:16:23,116 --> 00:16:26,520 +From there, +tapping again shows the sidebar. + +300 +00:16:26,553 --> 00:16:29,556 +The sidebar and content +both overlay the detail. + +301 +00:16:31,124 --> 00:16:33,861 +In general, I recommend +sticking with the automatic style + +302 +00:16:33,894 --> 00:16:36,663 +for three column split views +because it makes the best use + +303 +00:16:36,697 --> 00:16:40,467 +of the available space +and is specialized for larger displays. + +304 +00:16:40,501 --> 00:16:43,804 +Just like the two column split view, +the three column split view collapses + +305 +00:16:43,837 --> 00:16:46,540 +to a stack in compact size classes. + +306 +00:16:46,573 --> 00:16:48,575 +Now that I've covered +the basics of split views, + +307 +00:16:48,609 --> 00:16:51,411 +it's time to add one to the places app. + +308 +00:16:51,445 --> 00:16:52,980 +Here is the content view. + +309 +00:16:53,013 --> 00:16:56,116 +I've created a NavigationSplitView +with two columns here. + +310 +00:16:56,149 --> 00:16:57,951 +The first column is the sidebar column + +311 +00:16:57,985 --> 00:17:00,354 +and the second column +is the detail column. + +312 +00:17:00,387 --> 00:17:03,657 +The detail column is populated +by links from the sidebar column, + +313 +00:17:03,690 --> 00:17:04,958 +but if nothing is presented, + +314 +00:17:04,992 --> 00:17:08,629 +the placeholder with "select a place" +will be shown instead. + +315 +00:17:10,597 --> 00:17:13,700 +Here's a screenshot of the placeholder. +It's pretty great. + +316 +00:17:13,734 --> 00:17:17,204 +This is using the automatic style, +which shows the sidebar in landscape, + +317 +00:17:17,237 --> 00:17:20,174 +hiding it out of the way in portrait. + +318 +00:17:20,207 --> 00:17:24,678 +Tapping on a row in the sidebar +presents that row in the detail column. + +319 +00:17:24,711 --> 00:17:28,182 +And when using slide over, +the columns collapse automatically. + +320 +00:17:28,215 --> 00:17:30,217 +This is just the tip of the iceberg– + +321 +00:17:30,250 --> 00:17:32,753 +there are so many +exciting navigation additions, + +322 +00:17:32,786 --> 00:17:35,455 +including better support +for state restoration, + +323 +00:17:35,489 --> 00:17:38,859 +deep linking, +and even richer programmatic control. + +324 +00:17:38,892 --> 00:17:42,896 +Again, I encourage you to check out +the navigation cookbook session for more. + +325 +00:17:44,364 --> 00:17:47,034 +I've built some awesome iPad features +into the app, + +326 +00:17:47,067 --> 00:17:50,671 +and I'm excited to go find +some peaceful places to read. + +327 +00:17:50,704 --> 00:17:53,640 +Hopefully I'll be caught up +with my book club soon. + +328 +00:17:53,674 --> 00:17:56,410 +In this session, +I've covered how to leverage tables + +329 +00:17:56,443 --> 00:17:58,345 +for rich display of data, + +330 +00:17:58,378 --> 00:18:01,281 +how to manage +sophisticated selection interactions, + +331 +00:18:01,315 --> 00:18:03,984 +and how to avoid modality +with split views. + +332 +00:18:05,485 --> 00:18:07,221 +Make sure to check out +the related sessions + +333 +00:18:07,254 --> 00:18:10,724 +and refine your SwiftUI apps +to leverage the power of iPad. + +334 +00:18:11,992 --> 00:18:13,727 +Thank you. + +335 +00:18:13,760 --> 00:18:17,764 +♪ ♪ + diff --git a/eng/2022 Session 10059 The craft of SwiftUI API design - Progressive disclosure en.srt b/eng/2022 Session 10059 The craft of SwiftUI API design - Progressive disclosure en.srt new file mode 100644 index 0000000..2d132c2 --- /dev/null +++ b/eng/2022 Session 10059 The craft of SwiftUI API design - Progressive disclosure en.srt @@ -0,0 +1,1131 @@ +1 +00:00:09,309 --> 00:00:15,082 +Sam Lazarus: Hi my name is Sam, +and I'm an engineer on the SwiftUI team. + +2 +00:00:15,115 --> 00:00:18,852 +When designing SwiftUI, +we've always strived to make decisions + +3 +00:00:18,886 --> 00:00:21,421 +based on clearly defined principles, + +4 +00:00:21,455 --> 00:00:26,260 +and today, we're going to highlight +one of them: progressive disclosure. + +5 +00:00:26,293 --> 00:00:30,430 +On the SwiftUI team, +we spend a lot of time thinking about + +6 +00:00:30,464 --> 00:00:34,501 +and building new API, +but what you might not have realized + +7 +00:00:34,535 --> 00:00:37,037 +is that the moment +you build a reusable component + +8 +00:00:37,070 --> 00:00:38,572 +or abstraction, + +9 +00:00:38,605 --> 00:00:41,808 +you, too, are an API designer. + +10 +00:00:41,842 --> 00:00:46,413 +In this talk, we wanted to peel back +the curtains on our design process + +11 +00:00:46,446 --> 00:00:49,683 +and share what we've learned +about progressive disclosure, + +12 +00:00:49,716 --> 00:00:54,021 +so the next time you're building +a reusable component, or abstraction, + +13 +00:00:54,054 --> 00:00:56,056 +you have a new tool in your tool belt. + +14 +00:00:57,457 --> 00:01:00,661 +Let's start by talking +about what progressive disclosure + +15 +00:01:00,694 --> 00:01:02,462 +actually means. + +16 +00:01:02,496 --> 00:01:06,934 +As it turns out, +it's not unique to the design of APIs! + +17 +00:01:06,967 --> 00:01:12,406 +In fact, you can see it in action +in one of the most common macOS UIs: + +18 +00:01:12,439 --> 00:01:13,974 +the save dialog. + +19 +00:01:14,875 --> 00:01:16,810 +When you're first shown a save dialog, + +20 +00:01:16,844 --> 00:01:20,647 +there's a default location +already populated for you. + +21 +00:01:20,681 --> 00:01:25,786 +Additionally, the dialog shows you +a drop-down with some common locations, + +22 +00:01:25,819 --> 00:01:29,957 +so locations you're likely to select +are easy to reach. + +23 +00:01:29,990 --> 00:01:34,728 +And finally, if you need to browse +the filesystem to find the right path, + +24 +00:01:34,761 --> 00:01:40,667 +you can expand the dialog to reveal +a more complex but more powerful UI. + +25 +00:01:40,701 --> 00:01:45,572 +There are different layers of complexity +here that can be revealed when needed. + +26 +00:01:45,606 --> 00:01:51,345 +This is the same experience that we want +to provide with our APIs. + +27 +00:01:51,378 --> 00:01:55,349 +The code equivalent +to providing a nice UI experience + +28 +00:01:55,382 --> 00:01:58,418 +is making your APIs feel great to use. + +29 +00:02:00,053 --> 00:02:03,090 +As developers, +we're used to looking at our code + +30 +00:02:03,123 --> 00:02:07,160 +from the perspective of where +we write it: the declaration site. + +31 +00:02:07,194 --> 00:02:09,997 +But to make code feel great to use, + +32 +00:02:10,030 --> 00:02:13,033 +we have to look at it +from a different perspective: + +33 +00:02:13,066 --> 00:02:15,068 +where the code actually gets used, + +34 +00:02:15,102 --> 00:02:17,337 +or what we refer to as the call site. + +35 +00:02:19,173 --> 00:02:22,509 +Progressive disclosure, then, +is designing APIs + +36 +00:02:22,543 --> 00:02:27,314 +so that the complexity of the call site +grows with the complexity of the use case. + +37 +00:02:28,515 --> 00:02:32,486 +An ideal API is both simple +and approachable + +38 +00:02:32,519 --> 00:02:35,923 +but also be able to accommodate +powerful use cases. + +39 +00:02:37,257 --> 00:02:40,961 +This has real benefits for developers. + +40 +00:02:40,994 --> 00:02:45,799 +First, it minimizes the time +to the the first build and run, + +41 +00:02:45,832 --> 00:02:49,336 +letting you make use of your API quickly. + +42 +00:02:49,369 --> 00:02:53,907 +It also lowers the learning curve +of your code, preventing APIs + +43 +00:02:53,941 --> 00:02:58,145 +from getting bogged down by concepts +that aren't relevant in all use cases. + +44 +00:02:59,479 --> 00:03:02,950 +Finally, it creates a tight feedback loop. + +45 +00:03:02,983 --> 00:03:05,853 +With APIs that embrace +progressive disclosure, + +46 +00:03:05,886 --> 00:03:10,357 +you can add pieces bit by bit, +seeing what you've created at each step. + +47 +00:03:11,391 --> 00:03:16,263 +All of these things together make +building apps a cycle of quick refinements + +48 +00:03:16,296 --> 00:03:19,066 +rather than a single, large, +up-front investment. + +49 +00:03:20,634 --> 00:03:24,104 +So progressive disclosure +is a useful guiding light, + +50 +00:03:24,137 --> 00:03:28,809 +but how can we design specific API +so they embrace that principle? + +51 +00:03:28,842 --> 00:03:34,481 +On the SwiftUI team, we start +by considering common use cases. + +52 +00:03:34,515 --> 00:03:37,184 +In order to progressively disclose +functionality, + +53 +00:03:37,217 --> 00:03:40,087 +we need to identify +what the simple cases should be. + +54 +00:03:41,088 --> 00:03:44,291 +We also strive +to provide intelligent defaults, + +55 +00:03:44,324 --> 00:03:49,096 +so those common cases +can specify only what they need to. + +56 +00:03:49,129 --> 00:03:52,199 +Next, we aim to optimize the call site, + +57 +00:03:52,232 --> 00:03:56,537 +ensuring every character +of your call site has a purpose. + +58 +00:03:56,570 --> 00:03:59,540 +And finally, we design our APIs + +59 +00:03:59,573 --> 00:04:03,477 +so they compose pieces +rather than enumerating possibilities. + +60 +00:04:04,344 --> 00:04:08,982 +Let's dive right in and take a look +at some examples from SwiftUI, + +61 +00:04:09,016 --> 00:04:12,920 +starting with how we consider +common use cases. + +62 +00:04:12,953 --> 00:04:17,291 +One place where SwiftUI does this +particularly well is with labels. + +63 +00:04:18,492 --> 00:04:22,162 +When you create a button, for example, +we require that you provide + +64 +00:04:22,196 --> 00:04:24,131 +a label for the button. + +65 +00:04:24,164 --> 00:04:27,768 +Most of the time, +that label will just be some text, + +66 +00:04:27,801 --> 00:04:31,238 +describing the purpose of the button, +and SwiftUI provides you + +67 +00:04:31,271 --> 00:04:33,540 +a concise way to spell that. + +68 +00:04:33,574 --> 00:04:35,876 +But if you want to customize +the button further, + +69 +00:04:35,909 --> 00:04:38,145 +SwiftUI provides another overload. + +70 +00:04:38,178 --> 00:04:40,614 +which takes an arbitrary view as a label. + +71 +00:04:41,815 --> 00:04:44,618 +This allows you to build +complex functionality + +72 +00:04:44,651 --> 00:04:47,221 +out of this simple control. + +73 +00:04:47,254 --> 00:04:51,225 +But because this API carefully considers +its common use cases, + +74 +00:04:51,258 --> 00:04:55,262 +99% percent of the time, +you only need the simple version. + +75 +00:04:56,930 --> 00:05:00,767 +This label pattern +shows up everywhere in SwiftUI. + +76 +00:05:00,801 --> 00:05:03,704 +And when I say everywhere, +I really mean it. + +77 +00:05:04,671 --> 00:05:07,708 +So considering common use cases +is something we do + +78 +00:05:07,741 --> 00:05:10,010 +across the entire framework. + +79 +00:05:10,043 --> 00:05:14,081 +Next, let's look +at providing intelligent defaults. + +80 +00:05:14,114 --> 00:05:16,850 +In order to streamline +our common use cases, + +81 +00:05:16,884 --> 00:05:19,052 +we have to provide intelligent defaults + +82 +00:05:19,086 --> 00:05:22,189 +for all the things +we don't specify explicitly. + +83 +00:05:22,222 --> 00:05:26,627 +And there's no better example of this +than one of the most commonly used APIs + +84 +00:05:26,660 --> 00:05:28,595 +in all of SwiftUI: + +85 +00:05:28,629 --> 00:05:29,730 +Text. + +86 +00:05:30,464 --> 00:05:34,034 +Text is such a great example +of intelligent defaults + +87 +00:05:34,067 --> 00:05:37,804 +that you've probably written code +like this hundreds of times + +88 +00:05:37,838 --> 00:05:41,308 +without thinking about everything +you don't have to specify. + +89 +00:05:42,309 --> 00:05:45,913 +Just with this code, +SwiftUI will localize your text + +90 +00:05:45,946 --> 00:05:48,849 +by looking up the localized string +in your app bundle + +91 +00:05:48,882 --> 00:05:50,784 +with the environment locale. + +92 +00:05:50,817 --> 00:05:53,720 +It will automatically adapt +to the current color scheme, + +93 +00:05:53,754 --> 00:05:56,857 +supporting dark mode right out of the box. + +94 +00:05:56,890 --> 00:06:00,761 +And it will automatically scale +the text up or down + +95 +00:06:00,794 --> 00:06:03,964 +depending on the current +accessibility dynamic type size. + +96 +00:06:04,831 --> 00:06:07,100 +We've talked about these behaviors before, + +97 +00:06:07,134 --> 00:06:10,337 +but text is doing even more +behind the scenes than that. + +98 +00:06:11,905 --> 00:06:14,308 +When you put two texts +next to each other into a stack, + +99 +00:06:14,341 --> 00:06:19,179 +for example, the space between the texts +is automatically adjusted + +100 +00:06:19,213 --> 00:06:22,583 +to the correct line spacing +for text in the current context. + +101 +00:06:23,383 --> 00:06:26,787 +All that behavior +can be manually specified, + +102 +00:06:26,820 --> 00:06:30,424 +but SwiftUI's intelligent defaults +mean that when they aren't relevant + +103 +00:06:30,457 --> 00:06:33,427 +to your use case, +they don't appear at the call site. + +104 +00:06:34,695 --> 00:06:39,533 +Text is an example of an API where +the simplest case is extremely minimal, + +105 +00:06:39,566 --> 00:06:43,337 +but intelligent defaults apply +to all sorts of call sites. + +106 +00:06:43,370 --> 00:06:45,305 +Take toolbar, for example. + +107 +00:06:45,339 --> 00:06:49,643 +Here, we have a toolbar +with a bunch of buttons. + +108 +00:06:49,676 --> 00:06:52,846 +Without having to explicitly specify +their position, + +109 +00:06:52,880 --> 00:06:57,184 +the toolbar buttons are placed +according to platform convention. + +110 +00:06:57,217 --> 00:07:01,388 +On macOS, they'll appear +in the leading edge of the toolbar, + +111 +00:07:01,421 --> 00:07:04,958 +but on iOS, they'll appear +in the navigation bar, + +112 +00:07:04,992 --> 00:07:07,261 +starting from the trailing edge. + +113 +00:07:07,294 --> 00:07:11,565 +And finally, on watchOS, +only the first item appears, + +114 +00:07:11,598 --> 00:07:14,201 +pinned under the navigation bar. + +115 +00:07:14,234 --> 00:07:17,704 +This works great for the large majority +of cases, + +116 +00:07:17,738 --> 00:07:20,040 +but if you do need more control, + +117 +00:07:20,073 --> 00:07:23,744 +we provide additional API +to explicitly specify + +118 +00:07:23,777 --> 00:07:25,712 +the placement of items. + +119 +00:07:25,746 --> 00:07:28,749 +Again, the customization +is there if you need it, + +120 +00:07:28,782 --> 00:07:32,085 +but intelligent defaults +handle the majority of cases. + +121 +00:07:33,420 --> 00:07:37,357 +Considering common use cases +and providing intelligent defaults + +122 +00:07:37,391 --> 00:07:42,529 +create some really great experiences, +but if using those APIs feels clunky, + +123 +00:07:42,563 --> 00:07:46,200 +or unrefined, +it can ruin the whole effect. + +124 +00:07:46,233 --> 00:07:50,604 +That brings us to our last strategy: +optimize the call site. + +125 +00:07:50,637 --> 00:07:54,808 +And for that, let's look +at another API: Table. + +126 +00:07:55,909 --> 00:08:00,013 +Multi-column tables +are a very feature-rich control. + +127 +00:08:00,047 --> 00:08:03,684 +There's lots to configure +and lots of functionality. + +128 +00:08:03,717 --> 00:08:06,920 +But the large majority +of tables are much simpler + +129 +00:08:06,954 --> 00:08:09,056 +and don't need all those features. + +130 +00:08:09,089 --> 00:08:13,594 +We want table to be capable +of this more complex behavior, + +131 +00:08:13,627 --> 00:08:16,897 +and in its most verbose format, +it has that. + +132 +00:08:16,930 --> 00:08:19,099 +It has support for sorting, + +133 +00:08:19,132 --> 00:08:24,838 +multiple columns with rich cell content, +sectioned rows, and much, much more. + +134 +00:08:25,906 --> 00:08:28,342 +But we also want to provide +a great experience + +135 +00:08:28,375 --> 00:08:30,777 +in the more common cases, + +136 +00:08:30,811 --> 00:08:33,614 +so let's take a look +at the fully specified code + +137 +00:08:33,647 --> 00:08:35,382 +for this simpler table + +138 +00:08:35,415 --> 00:08:38,819 +and see how we can optimize its call site. + +139 +00:08:38,852 --> 00:08:42,389 +First, let's break down this example. + +140 +00:08:42,422 --> 00:08:46,527 +Table starts by specifying +how it generates the data for each row. + +141 +00:08:48,128 --> 00:08:52,099 +Here, I'm iterating over each book +we're currently reading + +142 +00:08:52,132 --> 00:08:56,136 +and creating a table row +for each of those books. + +143 +00:08:56,170 --> 00:09:01,775 +Next, it specifies how to populate +the columns from the data for each row. + +144 +00:09:01,808 --> 00:09:06,180 +Here, I create a Title column +and an Author column. + +145 +00:09:08,448 --> 00:09:13,587 +It also takes a binding to the sort order +to allow table to change the sort + +146 +00:09:13,620 --> 00:09:16,323 +when users click +on the table column headers. + +147 +00:09:18,158 --> 00:09:22,529 +Finally, I've added some code +which re-sorts the table's data + +148 +00:09:22,563 --> 00:09:24,998 +whenever the sort order changes. + +149 +00:09:25,032 --> 00:09:27,734 +That's a lot of information, +so let's take a look + +150 +00:09:27,768 --> 00:09:32,072 +at how to optimize this call site +to really embrace progressive disclosure. + +151 +00:09:33,240 --> 00:09:38,145 +One common use case that stands out +right away has to do with rows. + +152 +00:09:38,178 --> 00:09:43,083 +Most of the time, the rows field will look +just like it does in this example: + +153 +00:09:43,116 --> 00:09:47,120 +a ForEach over a collection, +providing a table row for each item. + +154 +00:09:48,655 --> 00:09:51,859 +The developer doesn't need to loop +through all of this themselves, + +155 +00:09:51,892 --> 00:09:56,964 +so SwiftUI provides a convenience +that handles this under the hood. + +156 +00:09:56,997 --> 00:09:59,900 +By passing the collection +directly to table, + +157 +00:09:59,933 --> 00:10:03,504 +the ForEach behavior can be provided +behind the scenes, + +158 +00:10:03,537 --> 00:10:10,010 +drastically simplifying our call site, +but this can still be simplified further. + +159 +00:10:10,043 --> 00:10:12,279 +What are other common use cases? + +160 +00:10:12,312 --> 00:10:13,947 +Well, most of the time, + +161 +00:10:13,981 --> 00:10:15,582 +when one of the values +I want to show in a table + +162 +00:10:15,616 --> 00:10:19,753 +is a string, I'll just use +a text to display it in the column. + +163 +00:10:20,721 --> 00:10:23,156 +We optimize the call site +for this case too. + +164 +00:10:25,058 --> 00:10:27,961 +Whenever the value keypath +points to a string, + +165 +00:10:27,995 --> 00:10:31,832 +we allow the view associated +with the TableColumn to be omitted. + +166 +00:10:32,933 --> 00:10:38,172 +That's another significant simplification, +but there's still more to optimize! + +167 +00:10:38,205 --> 00:10:41,875 +There's information in the call site +which not all tables need + +168 +00:10:41,909 --> 00:10:46,079 +to concern themselves with: +the sort order. + +169 +00:10:46,113 --> 00:10:50,350 +The simplest use case for table +doesn't care about sorting at all! + +170 +00:10:50,384 --> 00:10:53,320 +So we provide a version of table +which doesn't concern itself + +171 +00:10:53,353 --> 00:10:55,355 +with sorting either. + +172 +00:10:55,389 --> 00:10:59,660 +And this brings us to our final iteration. +Much simpler! + +173 +00:10:59,693 --> 00:11:03,764 +Every character of this call site +serves a clear purpose, + +174 +00:11:03,797 --> 00:11:08,702 +and we got here by asking ourselves +two key questions at every step: + +175 +00:11:08,735 --> 00:11:14,074 +"What are the most common use cases +that we should build conveniences for?" + +176 +00:11:14,107 --> 00:11:19,680 +and "What is the essential information +that should always be required?" + +177 +00:11:19,713 --> 00:11:22,916 +These guiding questions are +great for helping you optimize + +178 +00:11:22,950 --> 00:11:26,587 +your call sites, but they need +to be applied carefully. + +179 +00:11:26,620 --> 00:11:29,857 +If you don't think through +their implications for your API, + +180 +00:11:29,890 --> 00:11:31,758 +they can lead you astray. + +181 +00:11:31,792 --> 00:11:37,464 +That brings us to our final strategy: +Compose, don't enumerate. + +182 +00:11:37,497 --> 00:11:40,501 +And to illustrate this, +let's talk about the design + +183 +00:11:40,534 --> 00:11:44,471 +of a part of SwiftUI's layout system: +stacks, + +184 +00:11:44,505 --> 00:11:46,907 +in particular, HStack. + +185 +00:11:46,940 --> 00:11:51,144 +First, let's think about what +the essential information is + +186 +00:11:51,178 --> 00:11:52,746 +for an HStack. + +187 +00:11:52,779 --> 00:11:57,451 +Well, it needs to know +what content should be in the stack + +188 +00:11:57,484 --> 00:12:01,788 +and how that content +should be arranged within the stack. + +189 +00:12:01,822 --> 00:12:05,692 +We already have view builders +to specify the content of the HStack, + +190 +00:12:05,726 --> 00:12:08,529 +so let's focus on the arrangement. + +191 +00:12:08,562 --> 00:12:10,964 +Going back to the guiding questions +we highlighted, + +192 +00:12:10,998 --> 00:12:15,402 +what are the most common use cases +when arranging elements in an Hstack? + +193 +00:12:15,435 --> 00:12:19,606 +Well, sometimes I want to show a stack +like this one + +194 +00:12:19,640 --> 00:12:23,944 +that shows the boxes one after another, +starting from the leading edge. + +195 +00:12:25,045 --> 00:12:28,715 +Another common case is +wanting to center the elements. + +196 +00:12:28,749 --> 00:12:31,451 +And finally, I might want +to align the elements + +197 +00:12:31,485 --> 00:12:32,886 +against the trailing edge. + +198 +00:12:34,388 --> 00:12:39,526 +VStack already has an API +with similar cases to this, alignment, + +199 +00:12:39,560 --> 00:12:42,396 +so it might seem tempting +to create a similar enum + +200 +00:12:42,429 --> 00:12:45,532 +for the arrangement +of elements within the stack. + +201 +00:12:45,566 --> 00:12:48,135 +This supports all the cases we mentioned! + +202 +00:12:48,168 --> 00:12:52,706 +By specifying the arrangement +of an HStack, I could select a leading, + +203 +00:12:52,739 --> 00:12:57,277 +trailing, or centered arrangement, +depending on which I wanted. + +204 +00:12:57,311 --> 00:13:01,114 +But now what if I want to space +the elements out evenly + +205 +00:13:01,148 --> 00:13:04,284 +or put spacing only between the elements + +206 +00:13:04,318 --> 00:13:08,322 +or put space +only before the last element? + +207 +00:13:08,355 --> 00:13:10,190 +This is getting really messy! + +208 +00:13:10,224 --> 00:13:13,293 +But more importantly, it's unsustainable. + +209 +00:13:13,327 --> 00:13:16,997 +I have to add an enum case +for every behavior we want, + +210 +00:13:17,030 --> 00:13:20,534 +and we might not think +of all the useful cases! + +211 +00:13:20,567 --> 00:13:23,203 +When you find yourself +enumerating common cases + +212 +00:13:23,237 --> 00:13:25,973 +rather than providing conveniences +for them, + +213 +00:13:26,006 --> 00:13:29,843 +try breaking your API apart into +composable pieces + +214 +00:13:29,877 --> 00:13:34,014 +that can build a solution: +Compose, don't enumerate. + +215 +00:13:35,449 --> 00:13:41,154 +In the case of stacks, SwiftUI +provides Spacer and lets you compose it + +216 +00:13:41,188 --> 00:13:45,626 +with the elements of your stack to build +all of the spacing schemes we enumerated, + +217 +00:13:45,659 --> 00:13:50,397 +and many, many more, which is +how we arrived at the API we have today. + +218 +00:13:51,798 --> 00:13:55,068 +Designing the best experience +for progressive disclosure here + +219 +00:13:55,102 --> 00:13:57,604 +wasn't just +about minimizing the call site, + +220 +00:13:57,638 --> 00:14:02,109 +but also involved careful thought +about how that call site should scale + +221 +00:14:02,142 --> 00:14:05,946 +to handle all its cases: in this case, +through composition. + +222 +00:14:07,614 --> 00:14:11,451 +When writing code yourself, +it can be incredibly helpful to apply + +223 +00:14:11,485 --> 00:14:15,789 +the same kind of careful consideration +for the components you create. + +224 +00:14:15,822 --> 00:14:20,627 +And to recap, that starts +by considering common use cases. + +225 +00:14:20,661 --> 00:14:23,997 +By applying progressive disclosure, +the code you write + +226 +00:14:24,031 --> 00:14:27,601 +will save you time +in the most common use cases. + +227 +00:14:27,634 --> 00:14:31,371 +Intelligent defaults will mean +you won't have to think about the details + +228 +00:14:31,405 --> 00:14:33,740 +in those common cases. + +229 +00:14:33,774 --> 00:14:36,410 +Working to optimize the call sites +you build + +230 +00:14:36,443 --> 00:14:37,945 +will allow you to iterate quickly. + +231 +00:14:38,645 --> 00:14:41,415 +And finally, utilizing composition + +232 +00:14:41,448 --> 00:14:46,386 +will let you build APIs that are flexible +enough to accommodate all their use cases. + +233 +00:14:47,521 --> 00:14:51,558 +And because you are an API designer, +you can apply these lessons + +234 +00:14:51,592 --> 00:14:53,694 +to the code you write every day, + +235 +00:14:53,727 --> 00:14:56,063 +whether it's +being designed for someone else, + +236 +00:14:56,096 --> 00:14:57,664 +or just for you to use. + +237 +00:14:58,465 --> 00:14:59,967 +Thank you for watching. + +238 +00:15:00,000 --> 00:15:02,102 +[spacey music] + diff --git a/eng/2022 Session 10061 Bring multiple windows to your SwiftUI app en.srt b/eng/2022 Session 10061 Bring multiple windows to your SwiftUI app en.srt new file mode 100644 index 0000000..f0d236a --- /dev/null +++ b/eng/2022 Session 10061 Bring multiple windows to your SwiftUI app en.srt @@ -0,0 +1,1437 @@ +1 +00:00:00,267 --> 00:00:03,270 +♪ Mellow instrumental +hip-hip music ♪ + +2 +00:00:03,270 --> 00:00:09,843 +♪ + +3 +00:00:09,843 --> 00:00:10,944 +Hi, everyone. + +4 +00:00:10,944 --> 00:00:14,414 +I'm Jeff, an engineer +on the SwiftUI team. + +5 +00:00:14,414 --> 00:00:16,149 +Today, I'm excited +to talk to you + +6 +00:00:16,149 --> 00:00:19,519 +about bringing multiple windows +to your SwiftUI app + +7 +00:00:19,519 --> 00:00:22,756 +on iPadOS and macOS. + +8 +00:00:22,756 --> 00:00:25,659 +In this session, +we'll open with an overview + +9 +00:00:25,659 --> 00:00:29,162 +of the various scene types +in the SwiftUI lifecycle, + +10 +00:00:29,162 --> 00:00:32,799 +including a few new types +we're introducing. + +11 +00:00:32,799 --> 00:00:35,135 +Followed by showing +how these scene types + +12 +00:00:35,135 --> 00:00:39,573 +can be composed together, +by adding auxiliary scenes. + +13 +00:00:39,573 --> 00:00:42,643 +Then we'll cover some +new APIs for opening windows + +14 +00:00:42,643 --> 00:00:45,512 +for a particular scene +in your app. + +15 +00:00:45,512 --> 00:00:47,447 +And we'll wrap things up +with a few ways + +16 +00:00:47,447 --> 00:00:51,018 +for customizing an app's scenes. + +17 +00:00:51,018 --> 00:00:54,388 +Let's start with an overview +of the existing scene types + +18 +00:00:54,388 --> 00:00:57,057 +before digging in +to some new ones. + +19 +00:00:57,057 --> 00:01:00,894 +You'll recall from previous +sessions that apps in SwiftUI + +20 +00:01:00,894 --> 00:01:03,730 +are composed of scenes +and views. + +21 +00:01:03,730 --> 00:01:08,001 +Scenes commonly represent their +contents with a window onscreen. + +22 +00:01:08,001 --> 00:01:10,170 +For example, +here is an app I've built + +23 +00:01:10,170 --> 00:01:12,205 +to keep track of the books +I'm reading. + +24 +00:01:12,205 --> 00:01:14,274 +It's defined as +a single window group + +25 +00:01:14,274 --> 00:01:17,811 +which shows my reading list +in a platform-appropriate way. + +26 +00:01:17,811 --> 00:01:20,580 +On platforms which support +multiple windows, + +27 +00:01:20,580 --> 00:01:22,983 +such as iPadOS and macOS, + +28 +00:01:22,983 --> 00:01:26,753 +a scene can represent itself +with several such windows. + +29 +00:01:26,753 --> 00:01:30,123 +The behaviors and +representation of a scene vary + +30 +00:01:30,123 --> 00:01:32,359 +based on the type used. + +31 +00:01:32,359 --> 00:01:35,262 +For example, a scene +may only represent itself + +32 +00:01:35,262 --> 00:01:36,930 +with a single instance, + +33 +00:01:36,930 --> 00:01:39,433 +regardless of platform +capabilities. + +34 +00:01:39,433 --> 00:01:41,868 +Let's take a look at +the current list of scene types + +35 +00:01:41,868 --> 00:01:43,136 +in SwiftUI. + +36 +00:01:43,136 --> 00:01:47,007 +WindowGroup provides a way to +build data-driven applications + +37 +00:01:47,007 --> 00:01:49,776 +across all of Apple's platforms. + +38 +00:01:49,776 --> 00:01:52,512 +DocumentGroup lets you build +document-based apps + +39 +00:01:52,512 --> 00:01:54,848 +on iOS and macOS. + +40 +00:01:54,848 --> 00:01:57,050 +And Settings +defines an interface + +41 +00:01:57,050 --> 00:02:01,288 +for representing in-app settings +values on macOS. + +42 +00:02:01,288 --> 00:02:03,290 +These scene types +can be composed together + +43 +00:02:03,290 --> 00:02:05,492 +to extend your app's +functionality. + +44 +00:02:05,492 --> 00:02:08,996 +We're extending the list of +scenes with two new additions. + +45 +00:02:08,996 --> 00:02:12,432 +The first of which is Window, +a scene which represents + +46 +00:02:12,432 --> 00:02:16,403 +a single, unique window +on all platforms; + +47 +00:02:16,403 --> 00:02:20,374 +as well as a new scene type +for macOS: MenuBarExtra, + +48 +00:02:20,374 --> 00:02:24,578 +which renders as a persistent +control in the system menu bar. + +49 +00:02:24,578 --> 00:02:26,179 +As with the other +scene types, + +50 +00:02:26,179 --> 00:02:27,914 +you can use +Window and MenuBarExtra + +51 +00:02:27,914 --> 00:02:30,450 +both as a standalone scene, + +52 +00:02:30,450 --> 00:02:33,253 +or composed with other scenes +in your app. + +53 +00:02:33,253 --> 00:02:36,523 +Unlike WindowGroup, the Window +scene will only ever represent + +54 +00:02:36,523 --> 00:02:41,261 +its contents in a single, +unique window instance. + +55 +00:02:41,261 --> 00:02:43,063 +This characteristic +can be useful + +56 +00:02:43,063 --> 00:02:46,500 +when the contents of your scene +represents some global app state + +57 +00:02:46,500 --> 00:02:48,969 +that would not +necessarily fit well + +58 +00:02:48,969 --> 00:02:52,806 +with WindowGroups' multi-window +presentation style + +59 +00:02:52,806 --> 00:02:54,908 +on macOS and iPadOS. + +60 +00:02:54,908 --> 00:02:58,011 +For example, +a game may wish to only allow + +61 +00:02:58,011 --> 00:03:01,314 +for a single main window +to render its contents. + +62 +00:03:01,314 --> 00:03:04,751 +MenuBarExtra is a new +macOS-only scene type + +63 +00:03:04,751 --> 00:03:06,286 +which behaves +a little differently + +64 +00:03:06,286 --> 00:03:08,088 +from our other scenes. + +65 +00:03:08,088 --> 00:03:10,891 +Rather than rendering +its contents in a window, + +66 +00:03:10,891 --> 00:03:13,560 +it will place its label +in the menu bar + +67 +00:03:13,560 --> 00:03:17,431 +and show its contents +in either a menu or window + +68 +00:03:17,431 --> 00:03:19,332 +which is anchored to the label. + +69 +00:03:19,332 --> 00:03:21,435 +Additionally, +it will be useable + +70 +00:03:21,435 --> 00:03:24,671 +as long as its associated app +is running, + +71 +00:03:24,671 --> 00:03:27,474 +regardless of whether +that app is frontmost. + +72 +00:03:27,474 --> 00:03:31,511 +MenuBarExtra is great for +creating standalone utility apps + +73 +00:03:31,511 --> 00:03:34,481 +that provide easy access +to their functionality. + +74 +00:03:34,481 --> 00:03:36,450 +Or it can be composed +with other scenes + +75 +00:03:36,450 --> 00:03:41,321 +to provide an alternate way to +access your app's functionality. + +76 +00:03:41,321 --> 00:03:45,225 +It also supports two rendering +styles: the default style, + +77 +00:03:45,225 --> 00:03:47,594 +which shows the contents +in a menu + +78 +00:03:47,594 --> 00:03:50,363 +which pulls down +from the menu bar, + +79 +00:03:50,363 --> 00:03:53,500 +as well as a style +that presents its contents + +80 +00:03:53,500 --> 00:03:56,903 +in a chromeless window +anchored to the menu bar. + +81 +00:03:56,903 --> 00:03:59,873 +With the addition of +these two new scene types, + +82 +00:03:59,873 --> 00:04:04,077 +SwiftUI apps can represent an +even richer set of functionality + +83 +00:04:04,077 --> 00:04:07,047 +across all of our platforms. + +84 +00:04:07,047 --> 00:04:09,816 +Let's see how these new APIs +can be used + +85 +00:04:09,816 --> 00:04:13,320 +in conjunction with our +existing scene types. + +86 +00:04:13,320 --> 00:04:15,288 +Here's the definition +of my BookClub app + +87 +00:04:15,288 --> 00:04:17,023 +that I showed earlier. + +88 +00:04:17,023 --> 00:04:19,826 +It currently consists +of a single window group. + +89 +00:04:19,826 --> 00:04:22,696 +On macOS, my BookClub app +could benefit + +90 +00:04:22,696 --> 00:04:24,431 +from an additional window + +91 +00:04:24,431 --> 00:04:27,200 +to display our reading activity +over time. + +92 +00:04:27,200 --> 00:04:31,271 +This is a great example +of how macOS apps can make use + +93 +00:04:31,271 --> 00:04:33,240 +of the additional +screen real estate + +94 +00:04:33,240 --> 00:04:35,208 +and flexible windowing +arrangements + +95 +00:04:35,208 --> 00:04:37,177 +present on that platform. + +96 +00:04:37,177 --> 00:04:39,146 +We'll add an auxiliary scene +to our app + +97 +00:04:39,146 --> 00:04:41,214 +for representing this interface. + +98 +00:04:41,214 --> 00:04:43,583 +Our Activity window's data +is derived + +99 +00:04:43,583 --> 00:04:45,719 +from our overall app state, + +100 +00:04:45,719 --> 00:04:48,855 +so a window scene +is the ideal choice for it. + +101 +00:04:48,855 --> 00:04:50,657 +Opening multiple windows +with the same state + +102 +00:04:50,657 --> 00:04:52,726 +would not fit well +with our design. + +103 +00:04:52,726 --> 00:04:55,962 +The title provided to our scene +will be used as the label + +104 +00:04:55,962 --> 00:05:00,834 +for a menu item which is added +to a section of the Window menu. + +105 +00:05:00,834 --> 00:05:04,137 +When selecting this item, the +scene's window will be opened + +106 +00:05:04,137 --> 00:05:05,872 +if not already so. + +107 +00:05:05,872 --> 00:05:08,408 +Otherwise, it will be +brought to the front. + +108 +00:05:08,408 --> 00:05:10,544 +Now that we've covered +adding an auxiliary scene + +109 +00:05:10,544 --> 00:05:12,179 +to our BookClub app, + +110 +00:05:12,179 --> 00:05:15,248 +I'd like to discuss some of +the new scene presentation APIs + +111 +00:05:15,248 --> 00:05:18,318 +we're adding and how you can +integrate them into your app + +112 +00:05:18,318 --> 00:05:21,221 +to provide richer experiences. + +113 +00:05:21,221 --> 00:05:24,357 +Our BookClub app has a context +menu that can be invoked + +114 +00:05:24,357 --> 00:05:26,693 +for any book +in our Content List pane. + +115 +00:05:26,693 --> 00:05:28,828 +This context menu +will include a button + +116 +00:05:28,828 --> 00:05:31,464 +for triggering +our window presentation. + +117 +00:05:31,464 --> 00:05:33,500 +We'll fill in the details +shortly. + +118 +00:05:33,500 --> 00:05:36,169 +SwiftUI provides several +new callable types + +119 +00:05:36,169 --> 00:05:39,039 +via the environment +for presenting windows + +120 +00:05:39,039 --> 00:05:41,908 +tied to the scenes +your app defines. + +121 +00:05:41,908 --> 00:05:44,578 +The first of these +is openWindow action, + +122 +00:05:44,578 --> 00:05:45,712 +which can present windows + +123 +00:05:45,712 --> 00:05:49,216 +for either a WindowGroup +or window scene. + +124 +00:05:49,216 --> 00:05:52,652 +The identifier passed +to the action must match + +125 +00:05:52,652 --> 00:05:56,056 +an identifier for a scene +defined in your app. + +126 +00:05:56,056 --> 00:06:00,060 +openWindow action can also take +a presentation value, + +127 +00:06:00,060 --> 00:06:03,330 +which the presented scene will +use to display its contents. + +128 +00:06:03,330 --> 00:06:06,999 +This form of the action is only +supported by WindowGroup, + +129 +00:06:06,999 --> 00:06:10,203 +using a new initializer that +we'll take a look at shortly. + +130 +00:06:10,203 --> 00:06:12,339 +The type of the value +must match + +131 +00:06:12,339 --> 00:06:15,775 +against the type provided +to the scene's initializer. + +132 +00:06:15,775 --> 00:06:17,944 +There are also two callable +types in the environment + +133 +00:06:17,944 --> 00:06:22,349 +for presenting document windows: +newDocument action, + +134 +00:06:22,349 --> 00:06:24,384 +which supports opening +new document windows + +135 +00:06:24,384 --> 00:06:28,255 +for both FileDocuments +and ReferenceFileDocuments. + +136 +00:06:28,255 --> 00:06:31,358 +This action requires that the +corresponding DocumentGroup + +137 +00:06:31,358 --> 00:06:34,894 +in your app is defined +with an editor role. + +138 +00:06:34,894 --> 00:06:37,397 +The document provided +to this action will be created + +139 +00:06:37,397 --> 00:06:39,933 +each time the window +is presented. + +140 +00:06:39,933 --> 00:06:43,236 +For presenting document windows +where the contents are provided + +141 +00:06:43,236 --> 00:06:45,772 +by an existing file on disk, + +142 +00:06:45,772 --> 00:06:48,008 +there is the openDocument +action. + +143 +00:06:48,008 --> 00:06:51,711 +This action takes a URL +to the file you wish to open. + +144 +00:06:51,711 --> 00:06:53,747 +Your app must define +a DocumentGroup + +145 +00:06:53,747 --> 00:06:55,282 +for presenting the window, + +146 +00:06:55,282 --> 00:06:57,917 +and the document type +for that group must allow + +147 +00:06:57,917 --> 00:07:01,588 +for reading the type of the file +at the provided URL. + +148 +00:07:01,588 --> 00:07:03,857 +Revisiting our button, we'll add + +149 +00:07:03,857 --> 00:07:06,860 +the openWindow environment +property to our view. + +150 +00:07:06,860 --> 00:07:10,096 +Since this type is a callable, +we can just call it directly + +151 +00:07:10,096 --> 00:07:11,965 +from our button's action. + +152 +00:07:11,965 --> 00:07:15,101 +Our Book type conforms +to identifiable, + +153 +00:07:15,101 --> 00:07:18,872 +so we'll pass its identifier +as the value to present. + +154 +00:07:18,872 --> 00:07:22,242 +Now, before we move on, I'd like +to discuss the values passed + +155 +00:07:22,242 --> 00:07:23,977 +to the openWindow action. + +156 +00:07:23,977 --> 00:07:26,980 +I noted that I'm passing +the book's identifier, + +157 +00:07:26,980 --> 00:07:30,183 +which is a value +of the UUID type. + +158 +00:07:30,183 --> 00:07:32,986 +In general, +you'll want to prefer to use + +159 +00:07:32,986 --> 00:07:35,422 +your model's identifier +like this, + +160 +00:07:35,422 --> 00:07:37,424 +rather than the value itself. + +161 +00:07:37,424 --> 00:07:39,826 +Note that our Book type +is a value type. + +162 +00:07:39,826 --> 00:07:43,530 +As such, if we were to use it +as the presented value, + +163 +00:07:43,530 --> 00:07:45,665 +our new window would get +a copy of the one + +164 +00:07:45,665 --> 00:07:47,967 +which originated +the presentation. + +165 +00:07:47,967 --> 00:07:50,904 +Any edits to either one +will not affect the other. + +166 +00:07:50,904 --> 00:07:53,640 +Using the book's identifier +lets our model store + +167 +00:07:53,640 --> 00:07:56,309 +be the source of truth +for these values instead + +168 +00:07:56,309 --> 00:07:59,379 +by providing multiple +bindings to a single value. + +169 +00:07:59,379 --> 00:08:01,548 +For more info +on value type semantics, + +170 +00:08:01,548 --> 00:08:03,750 +please see +the developer documentation. + +171 +00:08:03,750 --> 00:08:06,219 +The type being presented +must also conform + +172 +00:08:06,219 --> 00:08:10,123 +to both the Hashable +and Codable protocols. + +173 +00:08:10,123 --> 00:08:13,693 +Hashable conformance is needed +to associate the presented value + +174 +00:08:13,693 --> 00:08:15,195 +with an open window; + +175 +00:08:15,195 --> 00:08:17,664 +while Codable +conformance is required + +176 +00:08:17,664 --> 00:08:20,200 +in order to persist +the presented value + +177 +00:08:20,200 --> 00:08:21,501 +for state restoration. + +178 +00:08:21,501 --> 00:08:23,036 +I'll discuss both +of these behaviors + +179 +00:08:23,036 --> 00:08:24,871 +in more detail shortly. + +180 +00:08:24,871 --> 00:08:29,109 +Lastly, when possible, prefer +passing lightweight values. + +181 +00:08:29,109 --> 00:08:32,445 +Our book's identifier is +another great example of this. + +182 +00:08:32,445 --> 00:08:35,181 +Since the value will be +persisted by SwiftUI + +183 +00:08:35,181 --> 00:08:38,118 +for state restoration, +using smaller values + +184 +00:08:38,118 --> 00:08:41,688 +will result in greater +responsiveness of your app. + +185 +00:08:41,688 --> 00:08:44,090 +Now, our button now has +the necessary pieces + +186 +00:08:44,090 --> 00:08:45,959 +to present our detail windows, + +187 +00:08:45,959 --> 00:08:48,128 +but nothing will show +when it is selected. + +188 +00:08:48,128 --> 00:08:51,498 +This is because we've told +SwiftUI to present a window + +189 +00:08:51,498 --> 00:08:53,333 +for a certain data type, + +190 +00:08:53,333 --> 00:08:56,169 +but haven't defined a scene +in our app that reflects that. + +191 +00:08:56,169 --> 00:08:58,505 +Let's head back to our app +and make that change now. + +192 +00:08:58,505 --> 00:09:00,540 +Alongside our primary +WindowGroup + +193 +00:09:00,540 --> 00:09:02,242 +and auxiliary window, + +194 +00:09:02,242 --> 00:09:04,177 +we'll add +an additional WindowGroup + +195 +00:09:04,177 --> 00:09:05,912 +for handling our book details. + +196 +00:09:05,912 --> 00:09:09,582 +Our book details WindowGroup +uses a new initializer. + +197 +00:09:09,582 --> 00:09:12,118 +In addition to the title, +we're noting that this group + +198 +00:09:12,118 --> 00:09:15,021 +presents data +for the Book.ID type -- + +199 +00:09:15,021 --> 00:09:16,956 +in our case, UUIDs. + +200 +00:09:16,956 --> 00:09:19,592 +This type should match +the value that we are passing + +201 +00:09:19,592 --> 00:09:22,595 +to the openWindow action +we added earlier. + +202 +00:09:22,595 --> 00:09:24,164 +When a given value is provided + +203 +00:09:24,164 --> 00:09:26,533 +to the WindowGroup +for presentation, + +204 +00:09:26,533 --> 00:09:30,103 +SwiftUI will create a new +child scene for that value, + +205 +00:09:30,103 --> 00:09:32,405 +and the root content +of that scene's window + +206 +00:09:32,405 --> 00:09:36,943 +will be defined by that value, +using the group's view builder. + +207 +00:09:36,943 --> 00:09:40,680 +Each unique presented value +will create a new scene. + +208 +00:09:40,680 --> 00:09:43,583 +The value's equality +will be used to determine + +209 +00:09:43,583 --> 00:09:45,685 +if a new window +should be created + +210 +00:09:45,685 --> 00:09:48,054 +or if an existing window +can be reused. + +211 +00:09:48,054 --> 00:09:50,190 +When openWindow +presents a value + +212 +00:09:50,190 --> 00:09:53,092 +for which a window +already exists, + +213 +00:09:53,092 --> 00:09:56,463 +the group will use that window +rather than creating a new one. + +214 +00:09:56,463 --> 00:09:58,865 +Using our BookClub app +as an example, + +215 +00:09:58,865 --> 00:10:01,501 +selecting the context menu +action for a book + +216 +00:10:01,501 --> 00:10:04,404 +which has already +been presented in a window + +217 +00:10:04,404 --> 00:10:07,040 +will result in that window +being ordered front, + +218 +00:10:07,040 --> 00:10:09,542 +rather than a second window +showing the same book. + +219 +00:10:09,542 --> 00:10:12,712 +The presented value will also +be automatically persisted + +220 +00:10:12,712 --> 00:10:16,216 +by SwiftUI for the purposes +of state restoration. + +221 +00:10:16,216 --> 00:10:18,284 +Your view will be +given a binding + +222 +00:10:18,284 --> 00:10:20,687 +to the initial presented value. + +223 +00:10:20,687 --> 00:10:22,722 +This binding can be modified +at any time + +224 +00:10:22,722 --> 00:10:24,057 +while the window is open. + +225 +00:10:24,057 --> 00:10:26,926 +When the scene is recreated +for state restoration, + +226 +00:10:26,926 --> 00:10:29,429 +SwiftUI will pass +the most recent value + +227 +00:10:29,429 --> 00:10:31,931 +to the window's Content view. + +228 +00:10:31,931 --> 00:10:36,336 +Here, we're giving the Book.ID +binding to our detail view, + +229 +00:10:36,336 --> 00:10:38,571 +which can look up +the specified item + +230 +00:10:38,571 --> 00:10:40,707 +in our model store +for display. + +231 +00:10:40,707 --> 00:10:42,342 +With all our pieces in place, + +232 +00:10:42,342 --> 00:10:45,211 +we can now select +our context menu item + +233 +00:10:45,211 --> 00:10:48,114 +and view our book details +in its own window. + +234 +00:10:48,114 --> 00:10:50,683 +Lastly, I'd like to go over +some of the ways + +235 +00:10:50,683 --> 00:10:54,320 +in which you can customize +the scenes in your app. + +236 +00:10:54,320 --> 00:10:57,590 +Because we've defined our app +with two WindowGroup scenes -- + +237 +00:10:57,590 --> 00:10:59,192 +one for the main viewer window + +238 +00:10:59,192 --> 00:11:01,160 +and one for our +detail windows -- + +239 +00:11:01,160 --> 00:11:04,297 +SwiftUI by default +will add a menu item + +240 +00:11:04,297 --> 00:11:06,633 +for each group in the File menu. + +241 +00:11:06,633 --> 00:11:08,234 +The menu item +for our detail window + +242 +00:11:08,234 --> 00:11:10,670 +doesn't quite fit +our use case, however. + +243 +00:11:10,670 --> 00:11:13,406 +I'd prefer that the windows +can only be opened + +244 +00:11:13,406 --> 00:11:16,376 +via the context menu +that was added earlier. + +245 +00:11:16,376 --> 00:11:19,612 +A new scene modifier, +commandsRemoved, + +246 +00:11:19,612 --> 00:11:21,781 +allows you to modify a scene + +247 +00:11:21,781 --> 00:11:25,251 +such that it will no longer +provide its default commands, + +248 +00:11:25,251 --> 00:11:26,953 +like the one in the File menu. + +249 +00:11:26,953 --> 00:11:30,890 +After applying this modifier, +our File menu now only contains + +250 +00:11:30,890 --> 00:11:34,994 +an item for opening windows +for the primary WindowGroup. + +251 +00:11:34,994 --> 00:11:37,063 +I'm not quite happy +with the current presentation + +252 +00:11:37,063 --> 00:11:40,967 +of the auxiliary window scene +for showing my reading activity, + +253 +00:11:40,967 --> 00:11:43,136 +so let's focus on that next. + +254 +00:11:43,136 --> 00:11:46,272 +Since I'm going to apply +a few modifiers to it, + +255 +00:11:46,272 --> 00:11:48,575 +I'll extract it out +into a custom scene, + +256 +00:11:48,575 --> 00:11:50,843 +which will keep +my app definition cleaner. + +257 +00:11:50,843 --> 00:11:53,546 +Absent any previous state +for a window, + +258 +00:11:53,546 --> 00:11:57,784 +SwiftUI will by default place it +in the center of the screen. + +259 +00:11:57,784 --> 00:11:59,786 +I'd prefer it +if the Reading Activity + +260 +00:11:59,786 --> 00:12:03,089 +was placed in a different +location by default, however. + +261 +00:12:03,089 --> 00:12:05,625 +By adding the new +defaultPosition modifier, + +262 +00:12:05,625 --> 00:12:08,227 +I can specify +a position to be used + +263 +00:12:08,227 --> 00:12:11,030 +when no previous state +is available. + +264 +00:12:11,030 --> 00:12:14,200 +This position is relative +to the screen size + +265 +00:12:14,200 --> 00:12:17,136 +and will place the window +in the appropriate location + +266 +00:12:17,136 --> 00:12:19,772 +taking into account +the current locale. + +267 +00:12:19,772 --> 00:12:22,742 +This new position helps +differentiate my Activity window + +268 +00:12:22,742 --> 00:12:25,578 +from the other viewing windows +on the screen. + +269 +00:12:25,578 --> 00:12:27,146 +I'd also like my Activity window + +270 +00:12:27,146 --> 00:12:31,417 +to show at a certain size by +default, but still be resizable. + +271 +00:12:31,417 --> 00:12:33,553 +Alongside the defaultPosition, + +272 +00:12:33,553 --> 00:12:36,089 +I'll add the defaultSize +modifier. + +273 +00:12:36,089 --> 00:12:39,826 +The value provided to it will +be given to the layout system + +274 +00:12:39,826 --> 00:12:43,162 +to derive an initial size +for the window. + +275 +00:12:43,162 --> 00:12:46,299 +Now that I've customized +the presentation of my window, + +276 +00:12:46,299 --> 00:12:49,469 +let's add one more modifier +to customize its behavior. + +277 +00:12:49,469 --> 00:12:52,538 +The keyboardShortcut modifier +has been expanded + +278 +00:12:52,538 --> 00:12:54,674 +to work on scene types as well. + +279 +00:12:54,674 --> 00:12:56,376 +When used at the scene level, + +280 +00:12:56,376 --> 00:12:58,611 +this modifier affects +the command + +281 +00:12:58,611 --> 00:12:59,812 +which creates a new window. + +282 +00:12:59,812 --> 00:13:03,483 +Here, I've modified my Activity +window so that it can be opened + +283 +00:13:03,483 --> 00:13:06,786 +with the shortcut +Option-Command-0. + +284 +00:13:06,786 --> 00:13:09,756 +This can be a great way +to customize your app + +285 +00:13:09,756 --> 00:13:13,226 +by providing shortcuts +to commonly used scenes + +286 +00:13:13,226 --> 00:13:16,629 +and can also be used to +customize the default shortcut + +287 +00:13:16,629 --> 00:13:18,031 +of Command-N, + +288 +00:13:18,031 --> 00:13:21,134 +which is added to the primary +WindowGroup in your app. + +289 +00:13:21,134 --> 00:13:23,202 +This closes our tour +of the new scene + +290 +00:13:23,202 --> 00:13:25,638 +and windowing functionality +in SwiftUI. + +291 +00:13:25,638 --> 00:13:28,641 +We're really excited about +the potential of these new APIs + +292 +00:13:28,641 --> 00:13:29,809 +and hope you are too! + +293 +00:13:29,809 --> 00:13:32,311 +For more great info on +how to add functionality + +294 +00:13:32,311 --> 00:13:35,114 +to your iPadOS and macOS apps, + +295 +00:13:35,114 --> 00:13:37,450 +please check out +these other sessions: + +296 +00:13:37,450 --> 00:13:40,353 +"SwiftUI on iPad: +Organize your interface" + +297 +00:13:40,353 --> 00:13:43,823 +and "SwiftUI on iPad: +Add toolbars, titles, and more." + +298 +00:13:44,924 --> 00:13:46,526 +Thanks for watching. + +299 +00:13:46,526 --> 00:13:50,997 +♪ + diff --git a/eng/2022 Session 10062 Meet Transferable en.srt b/eng/2022 Session 10062 Meet Transferable en.srt new file mode 100644 index 0000000..f231fad --- /dev/null +++ b/eng/2022 Session 10062 Meet Transferable en.srt @@ -0,0 +1,1173 @@ +1 +00:00:00,200 --> 00:00:03,370 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,370 --> 00:00:09,643 +♪ + +3 +00:00:09,643 --> 00:00:13,347 +Hello and welcome to the session +"Meet Transferable." + +4 +00:00:13,347 --> 00:00:16,717 +My name is Julia. +I am a SwiftUI engineer, + +5 +00:00:16,717 --> 00:00:19,152 +and I am excited to introduce +Transferable, + +6 +00:00:19,152 --> 00:00:21,889 +a declarative way +to support drag and drop, + +7 +00:00:21,889 --> 00:00:26,226 +copy/paste, and other +functionality in your app. + +8 +00:00:26,226 --> 00:00:29,997 +Apart from SwiftUI and +developing applications for Mac, + +9 +00:00:29,997 --> 00:00:32,566 +I'm also interested +in the story of women + +10 +00:00:32,566 --> 00:00:34,601 +in computer science. + +11 +00:00:34,601 --> 00:00:38,538 +I think it's important +that we know our heroes. + +12 +00:00:38,538 --> 00:00:41,208 +So I decided to create +a catalog application + +13 +00:00:41,208 --> 00:00:46,580 +where I can view, add, and edit +a list of the female inventors', + +14 +00:00:46,580 --> 00:00:50,484 +engineers', +and scientists' profiles. + +15 +00:00:50,484 --> 00:00:54,187 +I want this application to +seamlessly support drag and drop + +16 +00:00:54,187 --> 00:00:58,659 +of the scientists' portraits +to and from the app, + +17 +00:00:58,659 --> 00:01:01,528 +being able to use +the clipboard content + +18 +00:01:01,528 --> 00:01:04,798 +to paste interesting facts. + +19 +00:01:04,798 --> 00:01:06,366 +And for the first time, + +20 +00:01:06,366 --> 00:01:10,003 +my app can support sharing +on watchOS! + +21 +00:01:10,003 --> 00:01:12,606 +My potential users say +that they would love + +22 +00:01:12,606 --> 00:01:15,208 +to be able to share +a personality profile + +23 +00:01:15,208 --> 00:01:17,577 +from their watch. + +24 +00:01:17,577 --> 00:01:23,884 +Also, via SwiftUI, sharing is +now available on iOS and Mac, + +25 +00:01:23,884 --> 00:01:29,523 +which also got this brand-new +design for ShareSheet this year. + +26 +00:01:29,523 --> 00:01:32,726 +Under the hood, +enabling all these features + +27 +00:01:32,726 --> 00:01:35,829 +require the models +that we already have + +28 +00:01:35,829 --> 00:01:39,599 +to support being sent over +to a receiver inside our app + +29 +00:01:39,599 --> 00:01:42,336 +or even in other applications. + +30 +00:01:42,336 --> 00:01:46,039 +The profile structure +contains all the information + +31 +00:01:46,039 --> 00:01:49,476 +that we have +about a single personality. + +32 +00:01:49,476 --> 00:01:52,212 +All the profiles +packed in an archive + +33 +00:01:52,212 --> 00:01:54,982 +can be shared among friends. + +34 +00:01:54,982 --> 00:01:58,685 +We store fun facts +about the personality in strings + +35 +00:01:58,685 --> 00:02:01,455 +and even can attach videos. + +36 +00:02:01,455 --> 00:02:06,059 +And there's a great new easy way +to make all these model types + +37 +00:02:06,059 --> 00:02:07,828 +to be shared. + +38 +00:02:07,828 --> 00:02:09,863 +Meet Transferable! + +39 +00:02:09,863 --> 00:02:14,401 +It is a Swift-first declarative +way to describe how your models + +40 +00:02:14,401 --> 00:02:17,571 +can be serialized +and deserialized + +41 +00:02:17,571 --> 00:02:20,240 +for sharing and data transfer. + +42 +00:02:20,240 --> 00:02:22,809 +Today, we are going +to be talking about + +43 +00:02:22,809 --> 00:02:24,611 +what Transferable is + +44 +00:02:24,611 --> 00:02:28,448 +and what is going on behind +the scenes when we use it; + +45 +00:02:28,448 --> 00:02:32,786 +how to conform custom types; +and at the end, + +46 +00:02:32,786 --> 00:02:35,188 +I'll share some +advanced tips and tricks + +47 +00:02:35,188 --> 00:02:37,657 +that can help +to customize Transferable + +48 +00:02:37,657 --> 00:02:40,827 +to do exactly what you need. + +49 +00:02:40,827 --> 00:02:43,196 +Let's start! + +50 +00:02:43,196 --> 00:02:46,633 +Say there are +two applications running, + +51 +00:02:46,633 --> 00:02:50,337 +and the user wants to pass +some information from one app + +52 +00:02:50,337 --> 00:02:54,975 +to another via copy/paste, +ShareSheet, just drag, + +53 +00:02:54,975 --> 00:02:58,578 +or use some other app feature. + +54 +00:02:58,578 --> 00:03:02,149 +When you send something +between two different apps, + +55 +00:03:02,149 --> 00:03:05,986 +there's all this binary data +that goes across. + +56 +00:03:05,986 --> 00:03:08,755 +An important part +of sending this data + +57 +00:03:08,755 --> 00:03:12,225 +is determining +what it corresponds to. + +58 +00:03:12,225 --> 00:03:15,662 +It could be text, a video, + +59 +00:03:15,662 --> 00:03:21,234 +my favorite female engineer +profile, or whole archive. + +60 +00:03:21,234 --> 00:03:26,073 +And there's the UTType that +describes what the data is for. + +61 +00:03:26,073 --> 00:03:31,144 +Let's take a closer look at how +apps generate this binary data. + +62 +00:03:31,144 --> 00:03:34,448 +All the types that can be +shared with other apps, + +63 +00:03:34,448 --> 00:03:36,750 +or even within +a single application, + +64 +00:03:36,750 --> 00:03:40,253 +have to provide +two pieces of information: + +65 +00:03:40,253 --> 00:03:45,525 +ways to convert them +to and from binary data, + +66 +00:03:45,525 --> 00:03:48,562 +and the content type that +corresponds to the structure + +67 +00:03:48,562 --> 00:03:55,001 +of the binary data and tells the +receiver what they actually got. + +68 +00:03:55,001 --> 00:03:56,536 +The content type -- + +69 +00:03:56,536 --> 00:03:59,606 +otherwise known as +uniform type identifiers -- + +70 +00:03:59,606 --> 00:04:03,276 +is an Apple-specific technology +that describes identifiers + +71 +00:04:03,276 --> 00:04:07,814 +for different binary structures +as well as abstract concepts. + +72 +00:04:07,814 --> 00:04:09,983 +The identifiers form a tree, + +73 +00:04:09,983 --> 00:04:13,453 +and we can also define +custom identifiers. + +74 +00:04:13,453 --> 00:04:18,959 +For example, one for the binary +structure used by our profiles. + +75 +00:04:18,959 --> 00:04:21,828 +In order to declare +a custom identifier, + +76 +00:04:21,828 --> 00:04:26,366 +first, add its declaration +to the Info.plist file. + +77 +00:04:26,366 --> 00:04:30,303 +It is also a good idea +to add a file extension. + +78 +00:04:30,303 --> 00:04:32,739 +If the data is saved on disk, + +79 +00:04:32,739 --> 00:04:37,110 +the system will know that +your app can open that file. + +80 +00:04:37,110 --> 00:04:40,981 +Secondly, declare it in code. + +81 +00:04:40,981 --> 00:04:43,483 +To learn more about +content types, + +82 +00:04:43,483 --> 00:04:45,585 +I invite you to watch a video: + +83 +00:04:45,585 --> 00:04:49,422 +"Uniform Type Identifiers +-- A reintroduction." + +84 +00:04:49,422 --> 00:04:53,593 +Personally, I love it, +and it gives a clear idea + +85 +00:04:53,593 --> 00:04:58,899 +what are uniform type +identifiers and how to use them. + +86 +00:04:58,899 --> 00:05:01,835 +Good news is that +many standard types + +87 +00:05:01,835 --> 00:05:04,237 +already conform to Transferable; + +88 +00:05:04,237 --> 00:05:10,744 +for example, string, data, URL, +attributed string, image. + +89 +00:05:10,744 --> 00:05:14,981 +You need only a couple of lines +of code to paste fun facts + +90 +00:05:14,981 --> 00:05:20,053 +to a profile with the new +SwiftUI paste button interface, + +91 +00:05:20,053 --> 00:05:23,089 +support dragging images +from a view, + +92 +00:05:23,089 --> 00:05:27,794 +or receiving a dropped image +from Finder or other apps. + +93 +00:05:27,794 --> 00:05:29,963 +Using the br and-new ShareLink, + +94 +00:05:29,963 --> 00:05:34,100 +we can now implement sharing +experience from the watch. + +95 +00:05:34,100 --> 00:05:37,871 +We covered the basics, +and now you have an idea + +96 +00:05:37,871 --> 00:05:40,941 +how to use Transferable +and what it is. + +97 +00:05:40,941 --> 00:05:43,476 +Let's see how to add +Transferable conformance + +98 +00:05:43,476 --> 00:05:47,514 +to the models +in our application. + +99 +00:05:47,514 --> 00:05:51,885 +As I mentioned earlier, there +are four model types in our app + +100 +00:05:51,885 --> 00:05:54,821 +that are going to be shared. + +101 +00:05:54,821 --> 00:05:58,959 +One of them -- string -- already +conforms to Transferable; + +102 +00:05:58,959 --> 00:06:01,728 +we don't need to do +anything more. + +103 +00:06:01,728 --> 00:06:05,165 +But what about the single +profile, ProfilesArchive, + +104 +00:06:05,165 --> 00:06:09,135 +and the video that +I want to share as well? + +105 +00:06:09,135 --> 00:06:11,938 +To conform a type +to Transferable, + +106 +00:06:11,938 --> 00:06:15,141 +there's only one property +to implement: + +107 +00:06:15,141 --> 00:06:17,510 +TransferRepresentation. + +108 +00:06:17,510 --> 00:06:22,048 +It describes how the model +is going to get transferred. + +109 +00:06:22,048 --> 00:06:25,785 +There are three important +representations to be aware of: + +110 +00:06:25,785 --> 00:06:27,520 +CodableRepresentation, + +111 +00:06:27,520 --> 00:06:29,256 +DataRepresentation, + +112 +00:06:29,256 --> 00:06:31,358 +and FileRepresentation. + +113 +00:06:31,358 --> 00:06:34,094 +We'll talk about each of them. + +114 +00:06:34,094 --> 00:06:39,065 +But first, meet our central +model, Profile structure. + +115 +00:06:39,065 --> 00:06:43,069 +It has an id, a name, a bio, +maybe some fun facts, + +116 +00:06:43,069 --> 00:06:46,106 +a portrait, and a video. + +117 +00:06:46,106 --> 00:06:48,742 +It already conforms to Codable. + +118 +00:06:48,742 --> 00:06:52,112 +Because of that, we can include +CodableRepresentation + +119 +00:06:52,112 --> 00:06:56,049 +in our Transferable conformance. + +120 +00:06:56,049 --> 00:06:59,019 +Codable representation +uses an encoder + +121 +00:06:59,019 --> 00:07:02,022 +to convert the profile +into binary data, + +122 +00:07:02,022 --> 00:07:05,158 +and a decoder +to convert it back. + +123 +00:07:05,158 --> 00:07:09,162 +By default, it uses JSON, +but you can also provide + +124 +00:07:09,162 --> 00:07:12,565 +your own encoder/decoder pair. + +125 +00:07:12,565 --> 00:07:15,168 +To learn more +about the Codable protocol + +126 +00:07:15,168 --> 00:07:18,004 +and how encoders +and decoders work, + +127 +00:07:18,004 --> 00:07:22,275 +I invite you to watch a WWDC +session where this protocol + +128 +00:07:22,275 --> 00:07:26,413 +was first introduced: +"Data you can trust." + +129 +00:07:26,413 --> 00:07:28,348 +Back to our profile. + +130 +00:07:28,348 --> 00:07:31,017 +The only thing Codable requires + +131 +00:07:31,017 --> 00:07:34,254 +is knowing +the desired content type. + +132 +00:07:34,254 --> 00:07:37,123 +Since this is going to be +a custom format, + +133 +00:07:37,123 --> 00:07:41,561 +we will use a custom declared +uniform type identifier. + +134 +00:07:41,561 --> 00:07:45,665 +After adding the profile +content type, we're good to go. + +135 +00:07:45,665 --> 00:07:49,602 +Profile now conforms +to Transferable! + +136 +00:07:49,602 --> 00:07:54,441 +Now, let's take look at another +case: ProfilesArchive. + +137 +00:07:54,441 --> 00:07:58,078 +It already supports +converting to CSV data. + +138 +00:07:58,078 --> 00:08:02,749 +I can export the list of +the women profiles in CSV files + +139 +00:08:02,749 --> 00:08:08,521 +and then share with friends or +import it on another computer. + +140 +00:08:08,521 --> 00:08:13,760 +The archive can be converted +to and from data, + +141 +00:08:13,760 --> 00:08:18,431 +and it means that we can use +the DataRepresentation. + +142 +00:08:18,431 --> 00:08:20,433 +If we peek inside, we'll see + +143 +00:08:20,433 --> 00:08:23,737 +that DataRepresentation uses +the conversion functions + +144 +00:08:23,737 --> 00:08:26,740 +to directly create +binary representation + +145 +00:08:26,740 --> 00:08:30,677 +and to reconstruct +the value for the receiver. + +146 +00:08:30,677 --> 00:08:34,114 +This is how easy it is +to conform to Transferable + +147 +00:08:34,114 --> 00:08:36,583 +using the DataRepresentation. + +148 +00:08:36,583 --> 00:08:41,388 +All it takes is calling the two +functions that we already have: + +149 +00:08:41,388 --> 00:08:46,559 +the initializer +and the converter to CSV. + +150 +00:08:46,559 --> 00:08:50,163 +If a personality profile +has a video attached, + +151 +00:08:50,163 --> 00:08:53,867 +I want to be able to drag +or share it as well. + +152 +00:08:53,867 --> 00:08:55,568 +But videos can be large; + +153 +00:08:55,568 --> 00:08:58,471 +I don't want to load it +into memory. + +154 +00:08:58,471 --> 00:09:02,108 +This is where +FileRepresentation comes in. + +155 +00:09:02,108 --> 00:09:04,310 +And again, +if we lift the curtain, + +156 +00:09:04,310 --> 00:09:06,780 +we'll see that +FileRepresentation + +157 +00:09:06,780 --> 00:09:09,983 +passes the provided URL +to the receiver + +158 +00:09:09,983 --> 00:09:14,821 +and uses it to reconstruct +the Transferable item for them. + +159 +00:09:14,821 --> 00:09:18,024 +FileRepresentation +allows us to share items + +160 +00:09:18,024 --> 00:09:21,861 +backed by a binary +representation written to disk: + +161 +00:09:21,861 --> 00:09:23,596 +file. + +162 +00:09:23,596 --> 00:09:25,131 +Let's summarize. + +163 +00:09:25,131 --> 00:09:27,734 +If you want to pick just +a single representation + +164 +00:09:27,734 --> 00:09:29,569 +for a simple use case, + +165 +00:09:29,569 --> 00:09:32,839 +first check if the model +has the Codable conformance + +166 +00:09:32,839 --> 00:09:37,076 +and doesn't have any specific +binary format requirements. + +167 +00:09:37,076 --> 00:09:40,580 +Use CodableRepresentation +if it is the case. + +168 +00:09:40,580 --> 00:09:45,051 +If not, check if it is stored +in memory or on disk. + +169 +00:09:45,051 --> 00:09:48,188 +DataRepresentation +works best for the former, + +170 +00:09:48,188 --> 00:09:51,558 +and FileRepresetnation +for the latter. + +171 +00:09:51,558 --> 00:09:54,961 +Transferable is meant to cover +not only simple use cases, + +172 +00:09:54,961 --> 00:09:57,397 +but also complex ones. + +173 +00:09:57,397 --> 00:10:01,401 +And most of the time, +with just a few lines of code. + +174 +00:10:01,401 --> 00:10:04,337 +See it for yourself! + +175 +00:10:04,337 --> 00:10:07,040 +Previously, we have added +Transferable conformance + +176 +00:10:07,040 --> 00:10:10,710 +to the profile, +but let's go further. + +177 +00:10:10,710 --> 00:10:13,379 +When the profile is copied +to the pasteboard + +178 +00:10:13,379 --> 00:10:16,483 +and pasted into any text field, + +179 +00:10:16,483 --> 00:10:19,686 +I want to paste +the profile's name. + +180 +00:10:19,686 --> 00:10:23,523 +This means we need to add +another representation. + +181 +00:10:23,523 --> 00:10:27,360 +ProxyRepresentation allows +other Transferable types + +182 +00:10:27,360 --> 00:10:29,929 +to represent our model. + +183 +00:10:29,929 --> 00:10:34,968 +One line, and Profile can be +pasted as text. + +184 +00:10:34,968 --> 00:10:37,770 +Notice that I added +the ProxyRepresentation + +185 +00:10:37,770 --> 00:10:41,474 +after Codable; +the order is important. + +186 +00:10:41,474 --> 00:10:44,143 +The receiver will use +the first representation + +187 +00:10:44,143 --> 00:10:47,013 +with the content type +they support. + +188 +00:10:47,013 --> 00:10:50,984 +If the receiver is aware of +our custom content type Profile, + +189 +00:10:50,984 --> 00:10:52,552 +they should use it. + +190 +00:10:52,552 --> 00:10:55,555 +If not, but they support text, + +191 +00:10:55,555 --> 00:11:00,760 +let them use the +ProxyRepresentation instead. + +192 +00:11:00,760 --> 00:11:04,697 +Now, Profile supports both +encoder/decoder conversions + +193 +00:11:04,697 --> 00:11:07,500 +and a conversion to text. + +194 +00:11:07,500 --> 00:11:09,602 +The ProxyRepresentation +in this case + +195 +00:11:09,602 --> 00:11:12,038 +describes only +exporting to text, + +196 +00:11:12,038 --> 00:11:15,441 +but not reconstructing +the profile from it. + +197 +00:11:15,441 --> 00:11:20,713 +Any representation can describe +both conversions, or just one. + +198 +00:11:20,713 --> 00:11:24,551 +Now, when we know about +ProxyRepresentations, + +199 +00:11:24,551 --> 00:11:26,953 +do we really need +the FileRepresentation + +200 +00:11:26,953 --> 00:11:28,621 +for the video? + +201 +00:11:28,621 --> 00:11:31,491 +We could have proxy with a URL. + +202 +00:11:31,491 --> 00:11:33,760 +The difference is subtle. + +203 +00:11:33,760 --> 00:11:37,063 +FileRepresentation is intended +to work with the URLs + +204 +00:11:37,063 --> 00:11:38,431 +written to disk, + +205 +00:11:38,431 --> 00:11:42,569 +and ensure receivers' +access to this file or its copy + +206 +00:11:42,569 --> 00:11:46,105 +by granting a temporary +sandbox extension. + +207 +00:11:46,105 --> 00:11:49,375 +ProxyRepresentation +treats URLs the same way + +208 +00:11:49,375 --> 00:11:53,146 +as any other Transferable items, +like strings. + +209 +00:11:53,146 --> 00:11:55,982 +It doesn't have any +of these additional capabilities + +210 +00:11:55,982 --> 00:11:58,484 +that we need for files. + +211 +00:11:58,484 --> 00:12:01,087 +It means that we can have both. + +212 +00:12:01,087 --> 00:12:04,190 +The first one, +FileRepresentation, + +213 +00:12:04,190 --> 00:12:06,926 +allows the receiver +to access the movie file + +214 +00:12:06,926 --> 00:12:08,928 +with its contents. + +215 +00:12:08,928 --> 00:12:11,664 +The second one will work +when I paste the copied video + +216 +00:12:11,664 --> 00:12:15,268 +in a text field. + +217 +00:12:15,268 --> 00:12:18,304 +So the URL is treated +very differently + +218 +00:12:18,304 --> 00:12:21,107 +by file and proxy +representations. + +219 +00:12:21,107 --> 00:12:26,713 +In the first case, the actual +payload is the asset on disk, + +220 +00:12:26,713 --> 00:12:31,217 +and in the second, the payload +is the URL structure itself + +221 +00:12:31,217 --> 00:12:35,421 +that can point +to a remote website. + +222 +00:12:35,421 --> 00:12:39,993 +Another model that I'd like to +upgrade is the ProfilesArchive. + +223 +00:12:39,993 --> 00:12:43,830 +There are cases when it doesn't +support converting to CSV, + +224 +00:12:43,830 --> 00:12:46,933 +and I'd like to reflect that +in code. + +225 +00:12:46,933 --> 00:12:48,167 +Let's see. + +226 +00:12:48,167 --> 00:12:52,972 +We add a Boolean property that +tells us if we can export to CSV + +227 +00:12:52,972 --> 00:12:57,877 +and conversion functions +to and from data. + +228 +00:12:57,877 --> 00:13:03,016 +To express this idea in code, +we can use .exportingCondition. + +229 +00:13:03,016 --> 00:13:05,785 +If given archive +doesn't support CSV, + +230 +00:13:05,785 --> 00:13:09,489 +it won't be exported +in this format. + +231 +00:13:09,489 --> 00:13:11,991 +With this API, +you can even build + +232 +00:13:11,991 --> 00:13:14,227 +custom TransferRepresentation, + +233 +00:13:14,227 --> 00:13:16,929 +just like custom Views +in SwiftUI. + +234 +00:13:16,929 --> 00:13:20,299 +The only requirement is +to provide the body property + +235 +00:13:20,299 --> 00:13:22,502 +where you can have +other representations + +236 +00:13:22,502 --> 00:13:24,904 +configured the way you need. + +237 +00:13:24,904 --> 00:13:27,106 +It is useful +if you want to reuse + +238 +00:13:27,106 --> 00:13:29,475 +a combination of +representations, + +239 +00:13:29,475 --> 00:13:32,045 +or you have some +private data representation + +240 +00:13:32,045 --> 00:13:35,248 +that you don't want +to expose publicly. + +241 +00:13:35,248 --> 00:13:38,685 +Transferable helped me to +quickly build this application + +242 +00:13:38,685 --> 00:13:42,722 +with all the functionality +that I wanted to have. + +243 +00:13:42,722 --> 00:13:46,459 +I hope it is going to help you +building feature-rich apps + +244 +00:13:46,459 --> 00:13:49,462 +in less time than ever before. + +245 +00:13:49,462 --> 00:13:51,664 +Thank you for joining me +for this session + +246 +00:13:51,664 --> 00:13:54,200 +and keep building amazing apps! + +247 +00:13:54,200 --> 00:13:58,471 +♪ + diff --git a/eng/2022 Session 10063 Accelerate machine learning with Metal en.srt b/eng/2022 Session 10063 Accelerate machine learning with Metal en.srt new file mode 100644 index 0000000..ebc661e --- /dev/null +++ b/eng/2022 Session 10063 Accelerate machine learning with Metal en.srt @@ -0,0 +1,2155 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,510 --> 00:00:12,913 +Dhruva: Welcome to WWDC 2022. + +3 +00:00:12,946 --> 00:00:16,517 +My name is Dhruva, +and I am a GPUSW Engineer. + +4 +00:00:16,550 --> 00:00:21,522 +Today, Matteo and I will explore +all the new features and enhancements + +5 +00:00:21,555 --> 00:00:25,726 +introduced for machine learning +this year in Metal. + +6 +00:00:25,759 --> 00:00:29,730 +Machine learning training is the most +computationally intensive process + +7 +00:00:29,763 --> 00:00:31,665 +of the ML pipeline. + +8 +00:00:31,698 --> 00:00:37,371 +Due to their parallel nature, +GPUs excel at ML workloads. + +9 +00:00:37,404 --> 00:00:41,074 +The Metal machine learning APIs +are exposed through a framework + +10 +00:00:41,108 --> 00:00:44,811 +called Metal Performance Shaders, +or MPS. + +11 +00:00:44,845 --> 00:00:49,082 +MPS is a collection +of high performance GPU primitives + +12 +00:00:49,116 --> 00:00:53,387 +for various fields like Image Processing, +Linear Algebra, + +13 +00:00:53,420 --> 00:00:56,290 +Ray Tracing, and machine learning. + +14 +00:00:56,323 --> 00:01:00,460 +These Metal kernels are optimized +to provide the best performance + +15 +00:01:00,494 --> 00:01:02,362 +on all of our platforms. + +16 +00:01:02,396 --> 00:01:05,699 +For example the MPSImageCanny filter + +17 +00:01:05,732 --> 00:01:08,702 +returns an edge-map for an input-image. + +18 +00:01:08,735 --> 00:01:12,773 +This is a common operation +in image segmentation applications. + +19 +00:01:12,806 --> 00:01:16,143 +This year, +the Canny filter is able to process + +20 +00:01:16,176 --> 00:01:21,181 +4K, high-resolution images +up to eight times faster. + +21 +00:01:21,215 --> 00:01:24,618 +MPS Graph, +is a general purpose compute graph + +22 +00:01:24,651 --> 00:01:28,188 +for the GPU which sits on top +of the MPS framework + +23 +00:01:28,222 --> 00:01:32,025 +and extends support +to multidimensional tensors. + +24 +00:01:32,059 --> 00:01:35,429 +I recommend watching the previous session +to get more details + +25 +00:01:35,462 --> 00:01:38,532 +on how to use MPS Graph. + +26 +00:01:38,565 --> 00:01:42,769 +High level ML frameworks +like CoreML and Tensorflow + +27 +00:01:42,803 --> 00:01:44,972 +sit on top of MPS Graph. + +28 +00:01:45,005 --> 00:01:48,809 +You can accelerate your TensorFlow +networks on the GPU + +29 +00:01:48,842 --> 00:01:51,411 +with the TensorFlow Metal plug-in. + +30 +00:01:51,445 --> 00:01:54,581 +For more on how to make the most +of TensorFlow, + +31 +00:01:54,615 --> 00:01:57,417 +check out last year's session. + +32 +00:01:57,451 --> 00:02:01,188 +Matteo and I have three topics to cover +in this session. + +33 +00:02:01,221 --> 00:02:06,727 +First, I'll introduce the newest ML +framework coming to Apple GPUs, + +34 +00:02:06,760 --> 00:02:07,828 +PyTorch. + +35 +00:02:07,861 --> 00:02:14,034 +Next, I'll dive into the enhancements +made to TensorFlow over this year. + +36 +00:02:14,067 --> 00:02:19,339 +Finally, Matteo will talk about what's new +in the MPS Graph framework. + +37 +00:02:20,674 --> 00:02:25,913 +We are really excited that you will now be +able to accelerate your PyTorch networks + +38 +00:02:25,946 --> 00:02:27,948 +on your Mac GPUs. + +39 +00:02:27,981 --> 00:02:32,653 +PyTorch, is a popular open source +machine learning framework. + +40 +00:02:32,686 --> 00:02:37,057 +The number one most requested feature, +in the PyTorch community + +41 +00:02:37,090 --> 00:02:41,361 +was support for GPU acceleration +on Apple silicon. + +42 +00:02:41,395 --> 00:02:44,364 +We are bringing the power of Metal +to PyTorch + +43 +00:02:44,398 --> 00:02:50,037 +by introducing a new MPS backend +to the PyTorch ecosystem. + +44 +00:02:50,070 --> 00:02:56,210 +This backend will be part +of the official PyTorch 1.12 release. + +45 +00:02:56,243 --> 00:03:00,380 +The MPS backend implements +the PyTorch operation kernels, + +46 +00:03:00,414 --> 00:03:02,749 +as well as a Runtime framework. + +47 +00:03:02,783 --> 00:03:06,987 +The operations call into MPS Graph and MPS + +48 +00:03:07,020 --> 00:03:10,424 +and the Runtime component uses Metal. + +49 +00:03:10,457 --> 00:03:14,995 +This enables PyTorch to use +the highly efficient kernels from MPS + +50 +00:03:15,028 --> 00:03:17,364 +along with Metal's Command queues, + +51 +00:03:17,397 --> 00:03:20,901 +Command buffers, +and synchronization primitives. + +52 +00:03:22,336 --> 00:03:26,974 +The operation kernels +and PyTorch MPS Runtime components + +53 +00:03:27,007 --> 00:03:29,109 +are part of the open source code + +54 +00:03:29,142 --> 00:03:32,579 +and merged into the official +PyTorch GitHub repo. + +55 +00:03:32,613 --> 00:03:37,484 +Using the MPS PyTorch backend +is a simple three-step process. + +56 +00:03:37,518 --> 00:03:40,854 +First, starting with PyTorch 1.12, + +57 +00:03:40,888 --> 00:03:44,958 +you can install the base package +using ‘pip install torch'. + +58 +00:03:44,992 --> 00:03:49,329 +This package is available in +the official python package repository. + +59 +00:03:49,363 --> 00:03:53,033 +For more details +on environment setup and installation, + +60 +00:03:53,066 --> 00:03:57,237 +please refer to +the Metal Developer Resources web page. + +61 +00:03:57,271 --> 00:04:01,608 +Second, import PyTorch +and create the MPS device. + +62 +00:04:01,642 --> 00:04:04,945 +This code snippet +uses the MPS device backend + +63 +00:04:04,978 --> 00:04:09,016 +if it is available, +otherwise it'll fall back to the CPU. + +64 +00:04:09,049 --> 00:04:14,621 +The last step is to convert your models +and inputs to use the MPS device. + +65 +00:04:14,655 --> 00:04:16,590 +To demonstrate how to do this, + +66 +00:04:16,623 --> 00:04:19,493 +I will use an example which runs inference + +67 +00:04:19,526 --> 00:04:23,664 +on a pre-trained ResNet50 model +from torchvision. + +68 +00:04:23,697 --> 00:04:27,301 +By default, the model will run on the CPU. + +69 +00:04:27,334 --> 00:04:32,306 +You can use the "to" method to convert +the model to use the MPS device. + +70 +00:04:32,339 --> 00:04:36,443 +This ensures that intermediate tensors +inside the model + +71 +00:04:36,476 --> 00:04:39,813 +will also use the accelerated MPS backend. + +72 +00:04:39,847 --> 00:04:42,249 +Finally, you can run the model. + +73 +00:04:42,282 --> 00:04:47,521 +This example passes a random +input tensor to the MPS model. + +74 +00:04:47,554 --> 00:04:51,592 +By default, +all tensors are allocated on the CPU. + +75 +00:04:51,625 --> 00:04:53,694 +In order to use the MPS backend, + +76 +00:04:53,727 --> 00:04:58,031 +you will also need to provide +the mpsDevice here as well. + +77 +00:04:58,065 --> 00:05:03,770 +All subsequent operations on this tensor +will be accelerated on the GPU. + +78 +00:05:03,804 --> 00:05:09,009 +Finally, pass the sample input +to the MPS model to get a prediction. + +79 +00:05:09,042 --> 00:05:12,112 +Now that you know +how to use the MPS device, + +80 +00:05:12,145 --> 00:05:15,516 +I'll show you an example +of PyTorch in action. + +81 +00:05:15,549 --> 00:05:17,951 +I've always wanted to be a famous artist. + +82 +00:05:17,985 --> 00:05:21,889 +So I decided to use machine learning +and my GPU + +83 +00:05:21,922 --> 00:05:25,826 +to help create my artwork +using the StyleTransfer network. + +84 +00:05:25,859 --> 00:05:30,564 +This network allows you to apply +a different artistic style to an image. + +85 +00:05:30,597 --> 00:05:34,735 +In this case, the goal is to learn +how to apply Van Gogh's style + +86 +00:05:34,768 --> 00:05:37,437 +in Starry Night to this picture of a cat. + +87 +00:05:37,471 --> 00:05:41,575 +With the new MPS device, +you will be able to use the GPU + +88 +00:05:41,608 --> 00:05:45,312 +to train your PyTorch networks +significantly faster. + +89 +00:05:45,345 --> 00:05:48,615 +To demonstrate this, +I'll start training this network + +90 +00:05:48,649 --> 00:05:53,320 +on both the CPU and GPU simultaneously +on an M1 Max. + +91 +00:05:53,353 --> 00:05:56,857 +It takes thousands of iterations +to learn this style, + +92 +00:05:56,890 --> 00:06:01,929 +but the GPU is able to converge +to a reasonable model in much less time. + +93 +00:06:03,897 --> 00:06:07,568 +In addition to StyleTransfer, +we have seen amazing speedups + +94 +00:06:07,601 --> 00:06:10,137 +on all these PyTorch benchmarks. + +95 +00:06:10,170 --> 00:06:15,075 +On the M1 Ultra, +we saw speedups of up to 20 times faster + +96 +00:06:15,108 --> 00:06:18,612 +with an average of 8.3 times faster. + +97 +00:06:18,645 --> 00:06:22,583 +PyTorch makes it easy +to develop machine learning models, + +98 +00:06:22,616 --> 00:06:27,688 +and you'll be able to save a lot of time +by using Apple GPUs to train them. + +99 +00:06:27,721 --> 00:06:33,727 +Next, I'll dive into all the enhancements +we've made this year to TensorFlow. + +100 +00:06:33,760 --> 00:06:37,331 +TensorFlow Metal acceleration +has been available + +101 +00:06:37,364 --> 00:06:40,200 +since TensorFlow version 2.5 + +102 +00:06:40,234 --> 00:06:42,970 +through the TensorFlow Metal plug-in. + +103 +00:06:43,003 --> 00:06:47,741 +Since then, several additional features +and improvements have been added. + +104 +00:06:47,774 --> 00:06:51,879 +These include improved training +with bigger batches, + +105 +00:06:51,912 --> 00:06:54,948 +new operations and custom op support, + +106 +00:06:54,982 --> 00:06:58,452 +RNN improvements, +and distributed training. + +107 +00:06:58,485 --> 00:07:00,854 +The TensorFlow Metal plug-in releases + +108 +00:07:00,888 --> 00:07:04,091 +are aligned with major TensorFlow +releases, + +109 +00:07:04,124 --> 00:07:06,827 +so make sure you update +your TensorFlow packages + +110 +00:07:06,860 --> 00:07:10,397 +to get the latest features +and improvements. + +111 +00:07:10,430 --> 00:07:13,367 +Let's start with bigger batch sizes. + +112 +00:07:13,400 --> 00:07:17,938 +This year software improvements +in TensorFlow Metal allow you + +113 +00:07:17,971 --> 00:07:21,875 +to leverage the unique benefits +of the Apple silicon architecture. + +114 +00:07:21,909 --> 00:07:26,513 +This graph shows speedups +training a ResNet50 model + +115 +00:07:26,547 --> 00:07:28,782 +with various batch sizes. + +116 +00:07:28,815 --> 00:07:33,587 +The data shows that performance improves +with bigger batch sizes + +117 +00:07:33,620 --> 00:07:38,659 +because each gradient update corresponds +more closely to the true gradient. + +118 +00:07:38,692 --> 00:07:41,461 +Apple silicon's +unified memory architecture + +119 +00:07:41,495 --> 00:07:45,999 +allows you to run larger networks +or larger batch sizes. + +120 +00:07:46,033 --> 00:07:49,670 +Now you can run your workload +on a single Mac Studio + +121 +00:07:49,703 --> 00:07:53,407 +instead of splitting it +across a cloud cluster, which is awesome! + +122 +00:07:53,440 --> 00:07:57,311 +The Apple Silicon architecture +also has high performance per watt, + +123 +00:07:57,344 --> 00:08:01,381 +meaning your networks run +more efficiently than ever. + +124 +00:08:01,415 --> 00:08:06,353 +Next l'll talk about the new operations +and custom operations. + +125 +00:08:06,386 --> 00:08:10,357 +The Tensorflow Metal plug-in now has +GPU acceleration + +126 +00:08:10,390 --> 00:08:12,960 +for a variety of new operations, + +127 +00:08:12,993 --> 00:08:18,899 +including argMin, all, pack, +adaDelta, and many more. + +128 +00:08:18,932 --> 00:08:22,569 +But what if you want GPU acceleration +for an operation + +129 +00:08:22,603 --> 00:08:26,073 +that's currently not supported +in the TensorFlow API? + +130 +00:08:26,106 --> 00:08:30,844 +To do this, you will need to create +a custom operation. + +131 +00:08:30,878 --> 00:08:33,914 +Here's an example +of a simple convolutional network + +132 +00:08:33,947 --> 00:08:36,149 +running for two iterations. + +133 +00:08:36,183 --> 00:08:40,387 +The timeline represents the work done +on the GPU and CPU, + +134 +00:08:40,420 --> 00:08:42,756 +above and below respectively. + +135 +00:08:42,789 --> 00:08:46,460 +The network does a convolution +followed by maxpool-ing + +136 +00:08:46,493 --> 00:08:50,230 +and then a softmax cross entropy loss. + +137 +00:08:50,264 --> 00:08:53,033 +All of these operations +are GPU accelerated + +138 +00:08:53,066 --> 00:08:56,937 +in the TensorFlow Metal plug-in +through MPS Graph + +139 +00:08:56,970 --> 00:09:00,507 +But you might want to use +a custom loss function. + +140 +00:09:00,541 --> 00:09:04,511 +Without MPS GPU acceleration +for this custom loss, + +141 +00:09:04,545 --> 00:09:08,782 +that work will need to be performed +on the CPU timeline + +142 +00:09:08,815 --> 00:09:13,687 +which introduces synchronization overhead +and starves the GPU. + +143 +00:09:13,720 --> 00:09:18,926 +You can achieve far better performance +by doing this custom loss on the GPU. + +144 +00:09:18,959 --> 00:09:21,628 +In order to implement a custom operation, + +145 +00:09:21,662 --> 00:09:26,500 +you will need to understand +the TensorFlow Metal Stream protocol. + +146 +00:09:26,533 --> 00:09:31,405 +This is a protocol which you use +to encode GPU operations. + +147 +00:09:31,438 --> 00:09:35,576 +The Metal stream holds a reference +to the MTLCommandBuffer you use + +148 +00:09:35,609 --> 00:09:37,411 +to encode your GPU kernel. + +149 +00:09:37,444 --> 00:09:41,615 +It also exposes the dispatch_queue +to use for CPU side synchronization + +150 +00:09:41,648 --> 00:09:45,786 +while encoding as there may be +multiple threads submitting work. + +151 +00:09:45,819 --> 00:09:51,158 +Use the commit or commitAndWait methods +to submit the work to the GPU. + +152 +00:09:51,191 --> 00:09:55,963 +CommitAndWait is a debugging tool that +will wait until the current command buffer + +153 +00:09:55,996 --> 00:09:59,666 +is done so you can observe +serialized submissions. + +154 +00:09:59,700 --> 00:10:04,471 +Now let's see how these concepts +can be used to implement a custom op. + +155 +00:10:04,505 --> 00:10:07,274 +There are three steps +to write a custom operation. + +156 +00:10:07,307 --> 00:10:09,877 +First, register the operation. + +157 +00:10:09,910 --> 00:10:13,514 +Next, implement the operation +using a MetalStream. + +158 +00:10:13,547 --> 00:10:19,219 +And finally, import the operation into +your training scripts and begin using it. + +159 +00:10:19,253 --> 00:10:22,189 +I'll start with registering the operation. + +160 +00:10:22,222 --> 00:10:25,826 +Use the REGISTER_OP macro +exposed by TensorFlow core + +161 +00:10:25,859 --> 00:10:28,095 +to specify the semantics of the op + +162 +00:10:28,128 --> 00:10:32,165 +and how it should be defined +in the TensorFlow Metal plug-in. + +163 +00:10:32,199 --> 00:10:36,603 +Next, implement the op using +the TensorFlow_MetalStream. + +164 +00:10:36,637 --> 00:10:40,507 +Start by defining the "compute" function. + +165 +00:10:40,541 --> 00:10:44,978 +Now, inside this function, +get the TensorFlow_Tensor objects + +166 +00:10:45,012 --> 00:10:50,417 +for the input and define the output, +which might require an allocation. + +167 +00:10:50,450 --> 00:10:55,022 +Then create an encoder +using the Metal stream's command buffer. + +168 +00:10:55,055 --> 00:10:57,991 +Next, define the custom GPU kernel. + +169 +00:10:58,025 --> 00:11:00,928 +Your op should be encoded inside +the dispatch_queue + +170 +00:11:00,961 --> 00:11:02,763 +provided by the Metal stream. + +171 +00:11:02,796 --> 00:11:07,000 +This ensures submissions +from multiple threads are serialized. + +172 +00:11:08,802 --> 00:11:12,005 +Then commit the kernel +by using the method provided + +173 +00:11:12,039 --> 00:11:14,374 +in the TensorFlow_MetalStream protocol. + +174 +00:11:16,143 --> 00:11:19,947 +Finally, delete the references +to the allocated tensors. + +175 +00:11:21,315 --> 00:11:27,955 +Last, import the operation into +your training script to begin using it. + +176 +00:11:27,988 --> 00:11:35,062 +In this step, build the custom op's shared +dynamic library file called zero_out.so. + +177 +00:11:35,095 --> 00:11:37,130 +Refer to Metal Developer Resources + +178 +00:11:37,164 --> 00:11:41,235 +for info on how to build and import +.so files. + +179 +00:11:41,268 --> 00:11:45,172 +This example imports the operation +into the training script + +180 +00:11:45,205 --> 00:11:48,141 +by using the TensorFlow load_op_library, + +181 +00:11:48,175 --> 00:11:50,043 +which is an optional step. + +182 +00:11:50,077 --> 00:11:52,746 +Now, this works like a python wrapper + +183 +00:11:52,779 --> 00:11:56,884 +and our custom op can be invoked +in the training script. + +184 +00:11:56,917 --> 00:12:00,687 +Next, I'd like to show you an example +of an interesting application + +185 +00:12:00,721 --> 00:12:04,024 +called Neural Radiance Fields, or NeRF. + +186 +00:12:04,057 --> 00:12:08,328 +We wrote a custom operation +that elevated the network's performance + +187 +00:12:08,362 --> 00:12:12,766 +by enabling GPU acceleration +for a better algorithm. + +188 +00:12:13,901 --> 00:12:18,472 +NeRF is a network used +to synthesize 3D views of a model. + +189 +00:12:18,505 --> 00:12:23,710 +For training, NeRF takes as input, +images of an object from different angles. + +190 +00:12:23,744 --> 00:12:28,382 +The NeRF network consists of +two stacked Multi-layer perceptrons, + +191 +00:12:28,415 --> 00:12:32,819 +and the output is a volumetric +representation of the model. + +192 +00:12:32,853 --> 00:12:35,956 +A key performance optimization +for real time training + +193 +00:12:35,989 --> 00:12:38,592 +uses a hash table implementation. + +194 +00:12:38,625 --> 00:12:43,564 +This updated network allows +a much smaller multi-layer perceptron. + +195 +00:12:43,597 --> 00:12:46,967 +TensorFlow does not support +hash tables natively + +196 +00:12:47,000 --> 00:12:49,236 +so we use the custom op feature + +197 +00:12:49,269 --> 00:12:52,005 +to implement them in the Metal plug-in. + +198 +00:12:52,039 --> 00:12:58,011 +The GPU acceleration for hash tables makes +it possible to train NeRF much faster. + +199 +00:12:58,045 --> 00:12:59,947 +I'll start on this MacBook + +200 +00:12:59,980 --> 00:13:03,817 +and run original multi-layer perceptron +implementation. + +201 +00:13:06,186 --> 00:13:10,724 +In order to render anything reasonable, +we need at least 20 epochs + +202 +00:13:10,757 --> 00:13:13,727 +but each epoch takes about 100 seconds. + +203 +00:13:13,760 --> 00:13:17,798 +That means it will take about 30 minutes +before anything is seen. + +204 +00:13:17,831 --> 00:13:22,636 +So now I will restart training +from a pre-trained checkpoint file, + +205 +00:13:22,669 --> 00:13:26,073 +which was left to train +for 30 minutes beforehand. + +206 +00:13:26,106 --> 00:13:28,675 +This starts at epoch 20. + +207 +00:13:28,709 --> 00:13:33,780 +The 3D model is blurred and unclear +even after 30 minutes of training. + +208 +00:13:33,814 --> 00:13:37,017 +It would require a much longer +training time for the network + +209 +00:13:37,050 --> 00:13:38,719 +to learn a clearer model. + +210 +00:13:38,752 --> 00:13:42,256 +The original two stacked multi-layer +perceptron approach + +211 +00:13:42,289 --> 00:13:45,559 +without custom hash tables is too slow. + +212 +00:13:45,592 --> 00:13:49,363 +Now on this MacBook +I'll kick off the optimized version + +213 +00:13:49,396 --> 00:13:52,299 +that uses custom hash tables. + +214 +00:13:52,332 --> 00:13:57,037 +This implementation is already able +to render a much clearer model + +215 +00:13:57,070 --> 00:14:00,641 +and each epoch takes only 10 seconds +to learn. + +216 +00:14:00,674 --> 00:14:02,910 +For more information on this project, + +217 +00:14:02,943 --> 00:14:07,981 +check out the sample code which we have +uploaded to Metal Developer Resources. + +218 +00:14:09,683 --> 00:14:13,253 +NeRF is just one of the many networks +which demonstrates + +219 +00:14:13,287 --> 00:14:17,958 +how you can implement GPU acceleration +for your own custom operations + +220 +00:14:17,991 --> 00:14:20,727 +to make your networks run blazing fast. + +221 +00:14:20,761 --> 00:14:24,331 +I look forward to learning +about all the creative customizations + +222 +00:14:24,364 --> 00:14:26,400 +you make, going forward. + +223 +00:14:26,433 --> 00:14:30,270 +Now I want to show you how to use +Apple GPUs + +224 +00:14:30,304 --> 00:14:33,207 +to distribute training of ML workloads. + +225 +00:14:33,240 --> 00:14:35,976 +In order to distribute +training of workloads, + +226 +00:14:36,009 --> 00:14:40,848 +you can run multiple instances of the +training script in separate processes + +227 +00:14:40,881 --> 00:14:45,018 +where each process evaluates +a single iteration of the model. + +228 +00:14:46,286 --> 00:14:50,090 +Each process will read data +from a central data store. + +229 +00:14:50,123 --> 00:14:55,662 +After which, it will run through the model +and calculate the model gradients. + +230 +00:14:55,696 --> 00:15:00,167 +Next, the processes will average +the gradients and communicate this + +231 +00:15:00,200 --> 00:15:06,073 +to each other so each process has the same +gradients before the next iteration. + +232 +00:15:06,106 --> 00:15:10,043 +Finally, the model is updated +and you can repeat this process + +233 +00:15:10,077 --> 00:15:13,180 +until all the iterations are exhausted. + +234 +00:15:13,213 --> 00:15:15,415 +To demonstrate this on TensorFlow, + +235 +00:15:15,449 --> 00:15:18,118 +I will use an example +of distributed training + +236 +00:15:18,151 --> 00:15:22,155 +using a popular open source +framework called Horovod. + +237 +00:15:23,724 --> 00:15:27,194 +Horovod uses a ring all-reduce approach. + +238 +00:15:27,227 --> 00:15:31,064 +In this algorithm, +each of N nodes communicates + +239 +00:15:31,098 --> 00:15:34,101 +with two of its peers multiple times. + +240 +00:15:34,134 --> 00:15:37,938 +Using this communication, +the worker processes synchronize + +241 +00:15:37,971 --> 00:15:40,741 +gradients before each iteration. + +242 +00:15:40,774 --> 00:15:44,211 +I'll show this in action +using four Mac Studios + +243 +00:15:44,244 --> 00:15:47,414 +connected to each other +with Thunderbolt cables. + +244 +00:15:47,447 --> 00:15:53,120 +For this example, I will train ResNet, +a classifier for images. + +245 +00:15:53,153 --> 00:15:58,025 +The bar to the side of each Mac Studio +shows the GPU utilization + +246 +00:15:58,058 --> 00:15:59,793 +while training this network. + +247 +00:15:59,826 --> 00:16:04,665 +For a single Mac Studio, the performance +is about 200 images per second. + +248 +00:16:04,698 --> 00:16:08,468 +When I add another Mac Studio +connected via Thunderbolt, + +249 +00:16:08,502 --> 00:16:12,706 +the performance almost doubles +to 400 images per second + +250 +00:16:12,739 --> 00:16:16,710 +since both GPUs are utilized +to the fullest. + +251 +00:16:16,743 --> 00:16:19,713 +Finally, +when I connect two more Mac Studios, + +252 +00:16:19,746 --> 00:16:24,051 +the performance is elevated +to 800 images per second. + +253 +00:16:24,084 --> 00:16:28,322 +This is almost linear scaling on +your compute bound training workloads. + +254 +00:16:30,090 --> 00:16:34,995 +Now here's a look at the Distributed +training performance of TensorFlow. + +255 +00:16:35,028 --> 00:16:41,034 +This chart shows the relative speedup +for one, two, and four Mac Studios. + +256 +00:16:41,068 --> 00:16:45,939 +They are connected in a ring topology and +run compute bound TensorFlow networks + +257 +00:16:45,973 --> 00:16:48,442 +such as resNet and DistilBERT + +258 +00:16:48,475 --> 00:16:52,813 +with the latest TensorFlow Metal plug-in +and Horovod. + +259 +00:16:52,846 --> 00:16:57,050 +The base is the performance +on a single Mac Studio. + +260 +00:16:57,084 --> 00:17:02,422 +The graph show that network performance +scales with the addition of each GPU + +261 +00:17:02,456 --> 00:17:05,859 +so you can now leverage GPUs +on multiple devices, + +262 +00:17:05,893 --> 00:17:07,394 +to speed up your training time + +263 +00:17:07,427 --> 00:17:10,697 +and make the most +out of all your Apple devices. + +264 +00:17:12,032 --> 00:17:15,636 +All the improvements and features +unlocked for TensorFlow this year + +265 +00:17:15,669 --> 00:17:19,373 +culminate into this chart +showing the relative performance + +266 +00:17:19,406 --> 00:17:21,542 +against the CPU implementation + +267 +00:17:21,575 --> 00:17:24,344 +with more improvements to come +in the future. + +268 +00:17:24,378 --> 00:17:29,449 +Now Matteo will share what's new +in the MPSGraph framework. + +269 +00:17:30,184 --> 00:17:31,318 +Matteo: Thanks, Dhruva. + +270 +00:17:31,351 --> 00:17:35,756 +Hi, my name is Matteo, +and I'm a GPU software engineer. + +271 +00:17:35,789 --> 00:17:41,028 +PyTorch and TensorFlow sit on top +of the MPSGraph framework. + +272 +00:17:41,061 --> 00:17:45,199 +In turn, MPSGraph uses +the parallel primitives + +273 +00:17:45,232 --> 00:17:50,103 +exposed by the MPS framework +to accelerate work on the GPU. + +274 +00:17:50,137 --> 00:17:54,241 +Today I am going to talk about +two features that you can use + +275 +00:17:54,274 --> 00:17:58,979 +to accelerate your compute workloads +even further with MPSGraph. + +276 +00:17:59,012 --> 00:18:02,216 +First, I will show +the new shared events API + +277 +00:18:02,249 --> 00:18:05,953 +which allows you to synchronize work +between two graphs. + +278 +00:18:05,986 --> 00:18:08,922 +Second, I will go over new operations, + +279 +00:18:08,956 --> 00:18:13,060 +which you can use to do even more +with MPSGraph. + +280 +00:18:13,093 --> 00:18:15,996 +I'll begin with the Shared Events API. + +281 +00:18:16,029 --> 00:18:19,132 +Running applications on +the same command queue + +282 +00:18:19,166 --> 00:18:22,736 +ensures synchronization between workloads. + +283 +00:18:22,769 --> 00:18:26,440 +In this example, +the compute workload is guaranteed + +284 +00:18:26,473 --> 00:18:29,209 +to always terminate +before other workloads, + +285 +00:18:29,243 --> 00:18:33,380 +such as post processing and display, +are dispatched. + +286 +00:18:33,413 --> 00:18:37,150 +In cases like this, +you will leverage the GPU parallelism + +287 +00:18:37,184 --> 00:18:39,753 +within each single dispatch. + +288 +00:18:39,786 --> 00:18:44,358 +However, some applications could benefit +from more parallelism, + +289 +00:18:44,391 --> 00:18:47,995 +where a first portion of the GPU +is used for the compute, + +290 +00:18:48,028 --> 00:18:52,666 +and a second portion is used +for the post processing and display. + +291 +00:18:52,699 --> 00:18:58,605 +This could be achieved by submitting work +to the GPU on multiple command queues. + +292 +00:18:58,639 --> 00:19:01,975 +Unfortunately, in this case, +the post processing pipeline + +293 +00:19:02,009 --> 00:19:06,146 +may be dispatched before +the compute has produced the results, + +294 +00:19:06,180 --> 00:19:09,449 +introducing a data race. + +295 +00:19:09,483 --> 00:19:13,554 +The Shared Events API can be used +to solve this problem + +296 +00:19:13,587 --> 00:19:16,723 +and introduce synchronization +across command queues + +297 +00:19:16,757 --> 00:19:21,428 +to make sure that workflow dependencies +can be satisfied. + +298 +00:19:21,461 --> 00:19:25,566 +Using shared events within +your code is very simple. + +299 +00:19:25,599 --> 00:19:29,169 +Let's assume +you are working with two graphs. + +300 +00:19:29,203 --> 00:19:32,873 +The first is responsible +for the compute workload. + +301 +00:19:32,906 --> 00:19:37,277 +The second, is responsible +for the post processing workload. + +302 +00:19:37,311 --> 00:19:41,815 +Let's also assume that the result +of the compute graph is used as input + +303 +00:19:41,849 --> 00:19:43,550 +for the post processing graph, + +304 +00:19:43,584 --> 00:19:47,187 +and that they run +on different command queues. + +305 +00:19:47,221 --> 00:19:51,258 +The new MPSGraph track +in the Metal System Trace + +306 +00:19:51,291 --> 00:19:55,329 +indicates that the command queues +are overlapping with each other. + +307 +00:19:55,362 --> 00:19:58,398 +This produces a data race. + +308 +00:19:58,432 --> 00:20:01,902 +You can solve this problem +using a shared event. + +309 +00:20:01,935 --> 00:20:05,906 +First, create the event +using the Metal device. + +310 +00:20:05,939 --> 00:20:10,477 +Next, invoke the signal method +in the execution descriptor, + +311 +00:20:10,511 --> 00:20:14,248 +providing the event, the action, +and the value. + +312 +00:20:14,281 --> 00:20:18,552 +Then all you have to do is to call +the wait method + +313 +00:20:18,585 --> 00:20:20,087 +on the second descriptor, + +314 +00:20:20,120 --> 00:20:23,156 +providing event variable and the value. + +315 +00:20:24,658 --> 00:20:29,596 +Now, the Metal system trace +indicates that the two command queues + +316 +00:20:29,630 --> 00:20:32,866 +are run sequentially, +and the dependency between + +317 +00:20:32,900 --> 00:20:37,137 +compute and post processing graph +has been resolved. + +318 +00:20:37,171 --> 00:20:41,141 +That's how you can use shared events +to solve synchronization problems + +319 +00:20:41,175 --> 00:20:43,043 +in your applications. + +320 +00:20:43,076 --> 00:20:48,348 +Second, I'll talk about the new operations +supported by MPSGraph. + +321 +00:20:48,382 --> 00:20:52,619 +These operations allow you +to do even more with the framework. + +322 +00:20:52,653 --> 00:20:58,458 +I'll go through some of the details of +each of these new ops, starting with RNNs. + +323 +00:20:59,760 --> 00:21:03,797 +MPSGraph now exposes three operations +commonly used + +324 +00:21:03,830 --> 00:21:07,334 +within Recurrent Neural Network +applications. + +325 +00:21:07,367 --> 00:21:12,172 +These are the RNN, LSTM, +and GRU layers. + +326 +00:21:12,206 --> 00:21:14,808 +These operations all work similarly, + +327 +00:21:14,842 --> 00:21:18,979 +so I'll just focus on LSTMs today. + +328 +00:21:19,012 --> 00:21:23,217 +The LSTM operation is commonly used +for natural language processing + +329 +00:21:23,250 --> 00:21:25,552 +and other applications. + +330 +00:21:25,586 --> 00:21:29,790 +This diagram shows +how the LSTM operation works. + +331 +00:21:29,823 --> 00:21:35,529 +To learn more about it, +check out our previous WWDC session. + +332 +00:21:35,562 --> 00:21:39,600 +You could implement the LSTM unit +yourself, but to do so, + +333 +00:21:39,633 --> 00:21:43,637 +you would have to build this +rather complicated custom subgraph. + +334 +00:21:43,670 --> 00:21:47,508 +Instead, you can use +the new LSTM operation, + +335 +00:21:47,541 --> 00:21:53,647 +which efficiently encodes all the GPU work +required by the recurrent unit. + +336 +00:21:53,680 --> 00:21:59,586 +This new operation makes LSTM-based CoreML +inference models significantly faster. + +337 +00:22:01,388 --> 00:22:03,557 +To use the new LSTM operation, + +338 +00:22:03,590 --> 00:22:08,529 +first create an MPSGraphLSTMDescriptor. + +339 +00:22:08,562 --> 00:22:11,798 +You can modify the descriptor properties +as needed, + +340 +00:22:11,832 --> 00:22:15,636 +for example +selecting the activation functions. + +341 +00:22:15,669 --> 00:22:18,705 +Next, add the LSTM unit to the graph, + +342 +00:22:18,739 --> 00:22:21,275 +providing the input tensors. + +343 +00:22:21,308 --> 00:22:23,911 +You can also provide a bias vector, + +344 +00:22:23,944 --> 00:22:27,714 +as well as the initial state and cell +for the operation. + +345 +00:22:27,748 --> 00:22:30,450 +Finally, provide the descriptor. + +346 +00:22:30,484 --> 00:22:34,154 +That's all you need to do +to set up an LSTM. + +347 +00:22:34,188 --> 00:22:38,058 +The other RNN operations work similarly. + +348 +00:22:38,091 --> 00:22:40,694 +I encourage you +to try these operations out + +349 +00:22:40,727 --> 00:22:44,431 +and see what kind of speedups +you can get in your application. + +350 +00:22:44,464 --> 00:22:48,869 +Next, I'll show you +the improved support for Max Pooling. + +351 +00:22:48,902 --> 00:22:53,540 +The Max Pooling operation takes +an input tensor and a window size + +352 +00:22:53,574 --> 00:22:56,543 +and computes, +for each application of the window, + +353 +00:22:56,577 --> 00:23:00,314 +the maximum value of the input +within the window. + +354 +00:23:00,347 --> 00:23:05,686 +It is commonly used in computer vision +to reduce the dimensionality of an image. + +355 +00:23:05,719 --> 00:23:10,891 +The API has been extended to return +the indices of the maximum value location + +356 +00:23:10,924 --> 00:23:13,093 +extracted by the pooling operator. + +357 +00:23:13,126 --> 00:23:15,729 +You can use indices in the gradient pass, + +358 +00:23:15,762 --> 00:23:19,433 +where the gradients must be propagated +through the locations + +359 +00:23:19,466 --> 00:23:23,170 +where the maximum values were extracted. + +360 +00:23:23,203 --> 00:23:26,573 +The new API works for training too. + +361 +00:23:26,607 --> 00:23:30,844 +Reusing the indices during training +can be up to six times faster + +362 +00:23:30,878 --> 00:23:33,380 +for PyTorch and TensorFlow. + +363 +00:23:34,515 --> 00:23:40,254 +To set this up in code, first, +create the GraphPooling descriptor. + +364 +00:23:40,287 --> 00:23:42,723 +You can specify the returnIndicesMode, + +365 +00:23:42,756 --> 00:23:46,593 +for example, globalFlatten4D. + +366 +00:23:46,627 --> 00:23:53,233 +Then you can call the pooling operation +on the graph with the Return Indices API. + +367 +00:23:53,267 --> 00:23:56,203 +The result of the operation is twofold. + +368 +00:23:56,236 --> 00:24:01,475 +First, the poolingTensor, +and second, the indicesTensor. + +369 +00:24:01,508 --> 00:24:04,711 +You can cache the indicesTensor +for later use, + +370 +00:24:04,745 --> 00:24:07,748 +for example, on a training pipeline. + +371 +00:24:09,316 --> 00:24:13,654 +MPS Graph now exposes +a new parallel random number generator, + +372 +00:24:13,687 --> 00:24:15,722 +which can be used, for example, + +373 +00:24:15,756 --> 00:24:19,226 +to initialize the weights +of a training graph. + +374 +00:24:19,259 --> 00:24:22,863 +The new random operation +uses the Philox algorithm + +375 +00:24:22,896 --> 00:24:28,135 +and returns the same results +as TensorFlow for a given seed. + +376 +00:24:28,168 --> 00:24:31,772 +The new operation takes, +as input, a state tensor; + +377 +00:24:31,805 --> 00:24:34,608 +it returns as output a random tensor + +378 +00:24:34,641 --> 00:24:38,078 +and a new state tensor that can be used, +for example, + +379 +00:24:38,111 --> 00:24:41,148 +as input for a second random operation. + +380 +00:24:41,181 --> 00:24:43,517 +To use the new random operation, + +381 +00:24:43,550 --> 00:24:46,787 +call the randomPhiloxStateTensor method. + +382 +00:24:46,820 --> 00:24:52,192 +This method initializes +an input stateTensor with the given seed. + +383 +00:24:52,226 --> 00:24:55,329 +Then declare the RandomOp descriptor, + +384 +00:24:55,362 --> 00:24:59,466 +which takes as input +the distribution and the data type. + +385 +00:24:59,499 --> 00:25:02,102 +In the example, +the descriptor specifies + +386 +00:25:02,135 --> 00:25:07,574 +a truncatedNormal distribution +of 32bit floating point values. + +387 +00:25:07,608 --> 00:25:11,745 +You can also use +Normal and Uniform distributions. + +388 +00:25:12,846 --> 00:25:15,782 +You can further define +the distribution characteristics + +389 +00:25:15,816 --> 00:25:18,719 +by specifying the mean, +standard deviation, + +390 +00:25:18,752 --> 00:25:21,889 +minimum, and maximum values. + +391 +00:25:21,922 --> 00:25:26,326 +Finally, you can create the random +operation, providing a shapeTensor, + +392 +00:25:26,360 --> 00:25:29,663 +the descriptor, +and the stateTensor just created. + +393 +00:25:32,199 --> 00:25:34,067 +In addition to Random, + +394 +00:25:34,101 --> 00:25:37,905 +MPSGraph now supports +a new GPU accelerated operation + +395 +00:25:37,938 --> 00:25:42,109 +to compute the Hamming distance +between two bit vectors. + +396 +00:25:42,142 --> 00:25:45,279 +The hamming distance, +defined as the number of bits + +397 +00:25:45,312 --> 00:25:48,215 +that differ between two inputs +with same length, + +398 +00:25:48,248 --> 00:25:51,718 +is a measure of the edit distance +between two sequences, + +399 +00:25:51,752 --> 00:25:58,192 +and it is used on several applications, +from bioinformatics to cryptography. + +400 +00:25:58,225 --> 00:26:01,728 +To use HammingDistance, +call the API on the graph, + +401 +00:26:01,762 --> 00:26:06,867 +providing primaryTensor, +secondaryTensor, and the resultDataType. + +402 +00:26:06,900 --> 00:26:11,138 +Note that the new kernel supports +broadcasting over batch dimensions + +403 +00:26:11,171 --> 00:26:13,941 +on the GPU. + +404 +00:26:13,974 --> 00:26:18,612 +Now, I'll show you all about +the new tensor manipulation operations, + +405 +00:26:18,645 --> 00:26:20,347 +which are very easy to use. + +406 +00:26:20,380 --> 00:26:24,084 +You can now expand the dimensionality +of the tensor, for example, + +407 +00:26:24,117 --> 00:26:26,720 +from two to three dimensions. + +408 +00:26:26,753 --> 00:26:28,889 +And you can squeeze the dimensions back. + +409 +00:26:30,524 --> 00:26:36,129 +You can also split a tensor evenly +providing a number of slices and an axis. + +410 +00:26:36,163 --> 00:26:39,233 +or stack tensors along a given axis. + +411 +00:26:40,834 --> 00:26:44,638 +You can also generate coordinate values +along tensor dimensions + +412 +00:26:44,671 --> 00:26:46,740 +for a given input shape. + +413 +00:26:46,773 --> 00:26:51,111 +For example, you can populate +a tensor of shape two by four + +414 +00:26:51,144 --> 00:26:54,348 +with coordinates along the 0 axis. + +415 +00:26:54,381 --> 00:26:59,720 +This can be also used +to implement a range1D operation. + +416 +00:26:59,753 --> 00:27:03,490 +For example, assume you want to generate +the range of numbers + +417 +00:27:03,524 --> 00:27:07,928 +between 3 and 27 with increments of 4. + +418 +00:27:07,961 --> 00:27:10,731 +You can do so +by first creating the coordinates + +419 +00:27:10,764 --> 00:27:15,002 +along the dimension 0 +of a tensor of shape 6. + +420 +00:27:15,035 --> 00:27:21,008 +Then, all you have to do is to multiply +by the increment, and add the offset. + +421 +00:27:21,041 --> 00:27:25,179 +Those are all of the new operations +added this year. + +422 +00:27:25,212 --> 00:27:29,016 +With all these new operations, +you will be able to do even more + +423 +00:27:29,049 --> 00:27:34,288 +and get higher performance across +the Apple ecosystem using MPSGraph. + +424 +00:27:34,321 --> 00:27:37,691 +Now, I am going to show you +the performance improvements you can get + +425 +00:27:37,724 --> 00:27:40,861 +on Apple silicon out of MPSGraph. + +426 +00:27:41,728 --> 00:27:45,766 +Blackmagic has just released +DaVinci Resolve version 18, + +427 +00:27:45,799 --> 00:27:50,470 +which uses MPS Graph to accelerate +machine learning workloads. + +428 +00:27:50,504 --> 00:27:54,808 +Magic Mask is a feature of Resolve +that uses machine learning + +429 +00:27:54,842 --> 00:28:00,814 +to identify a moving object on screen and +selectively apply filters on top of it. + +430 +00:28:00,848 --> 00:28:05,185 +First I'll demonstrate how this works +in the previous version of Resolve, + +431 +00:28:05,219 --> 00:28:08,889 +and then I'll compare it +to the current version. + +432 +00:28:08,922 --> 00:28:13,060 +To create the mask, +you just need to select the target object. + +433 +00:28:13,093 --> 00:28:16,730 +You can view the mask +by toggling the overlay. + +434 +00:28:16,763 --> 00:28:19,399 +The mask is identified by the red area, + +435 +00:28:19,433 --> 00:28:22,402 +which correctly marks +the shape of the subject. + +436 +00:28:22,436 --> 00:28:28,709 +Now, if I play the video, the mask will +track the object as it moves on screen. + +437 +00:28:28,742 --> 00:28:32,212 +This looks great, but it's running +at a pretty low frame rate, + +438 +00:28:32,246 --> 00:28:35,916 +as the machine learning pipeline +runs under the hood. + +439 +00:28:35,949 --> 00:28:39,419 +Now I'll switch to the newest version +of Resolve, + +440 +00:28:39,453 --> 00:28:44,424 +which uses MPSGraph to accelerate +the Magic Mask network. + +441 +00:28:44,458 --> 00:28:49,763 +Running the same timeline again, +the frame rate is way faster than before. + +442 +00:28:49,796 --> 00:28:54,334 +This results in a much better +editing experience on Apple silicon. + +443 +00:28:55,402 --> 00:29:00,073 +These are the kind of speedups you can get +just by adopting MPS Graph. + +444 +00:29:00,107 --> 00:29:04,645 +I encourage you to explore what kind +of performance it can bring to your app. + +445 +00:29:04,678 --> 00:29:07,981 +To wrap up, +you will now be able to leverage + +446 +00:29:08,015 --> 00:29:10,184 +GPU acceleration for PyTorch, + +447 +00:29:10,217 --> 00:29:13,420 +and the project is now open source. + +448 +00:29:13,453 --> 00:29:16,523 +You will find new ways to accelerate +training workloads + +449 +00:29:16,557 --> 00:29:19,126 +using the TensorFlow Metal plug-in, + +450 +00:29:19,159 --> 00:29:23,463 +for example, using custom operations +and distributed training. + +451 +00:29:23,497 --> 00:29:28,202 +Finally, you will be able to optimize the +most demanding machine learning tasks + +452 +00:29:28,235 --> 00:29:29,903 +with the MPSGraph framework + +453 +00:29:29,937 --> 00:29:32,206 +to make the best out of Apple silicon, + +454 +00:29:32,239 --> 00:29:34,842 +using shared events and new operations. + +455 +00:29:34,875 --> 00:29:38,345 +Dhruva and I can't wait to see +how you will use these new features + +456 +00:29:38,378 --> 00:29:39,880 +in your applications. + +457 +00:29:39,913 --> 00:29:43,851 +Thank you for watching the session, +and have a great WWDC. + diff --git a/eng/2022 Session 10064 Reach new players with Game Center dashboard en.srt b/eng/2022 Session 10064 Reach new players with Game Center dashboard en.srt new file mode 100644 index 0000000..14e0152 --- /dev/null +++ b/eng/2022 Session 10064 Reach new players with Game Center dashboard en.srt @@ -0,0 +1,741 @@ +1 +00:00:00,400 --> 00:00:06,406 +♪ instrumental hip hop music ♪ + +2 +00:00:11,712 --> 00:00:16,617 +Hello. My name is Knott. +I'm from Game Center Engineering. + +3 +00:00:16,650 --> 00:00:20,387 +Today, I'm going to walk you +through improvements we're making + +4 +00:00:20,420 --> 00:00:22,656 +in Game Center this year. + +5 +00:00:22,689 --> 00:00:26,260 +Game Center is +Apple's social gaming platform. + +6 +00:00:26,293 --> 00:00:30,931 +With Game Center, players set up +a profile and connect with friends. + +7 +00:00:31,965 --> 00:00:36,003 +Using GameKit, +you can easily integrate with Game Center. + +8 +00:00:36,036 --> 00:00:40,307 +Players will be automatically signed in, +can compare scores with their friends + +9 +00:00:40,340 --> 00:00:45,512 +and other players on leaderboards, and +can track progress through achievements. + +10 +00:00:45,546 --> 00:00:49,716 +You can also integrate support for +multiplayer matchmaking and realtime play. + +11 +00:00:50,717 --> 00:00:54,488 +Over the past couple of years, +we've introduced the Access Point, + +12 +00:00:54,521 --> 00:00:58,425 +redesigned the achievements +and leaderboards experiences — + +13 +00:00:58,458 --> 00:01:04,031 +introduced the friends API, +and improved the multiplayer experience. + +14 +00:01:04,064 --> 00:01:08,168 +Additionally, we've been bringing +Game Center activity to more places + +15 +00:01:08,202 --> 00:01:11,839 +across the device including surfacing +what you and friends are playing + +16 +00:01:11,872 --> 00:01:13,640 +in the App Store, + +17 +00:01:13,674 --> 00:01:17,477 +and creating new widgets like +Continue Playing and Friends are Playing. + +18 +00:01:18,946 --> 00:01:21,748 +This year, +we're going even further to make + +19 +00:01:21,782 --> 00:01:25,719 +the experience better for all players, +provide more exposure + +20 +00:01:25,752 --> 00:01:28,755 +for all of the great things +happening in your games, + +21 +00:01:28,789 --> 00:01:33,961 +and give you some new tools +to integrate more easily with Game Center. + +22 +00:01:33,994 --> 00:01:37,297 +I'll walk you through Activity +which brings together + +23 +00:01:37,331 --> 00:01:41,435 +all of the Game Center events +from your games into one place. + +24 +00:01:41,468 --> 00:01:45,672 +But before we get to that, +I have an announcement to make. + +25 +00:01:45,706 --> 00:01:50,010 +We know a lot of game developers +are leveraging Unity + +26 +00:01:50,043 --> 00:01:53,747 +to help build gaming experiences +on Apple platforms. + +27 +00:01:53,780 --> 00:01:56,884 +This year, +we are releasing a Unity plug-in + +28 +00:01:56,917 --> 00:02:00,220 +we've developed specifically for GameKit! + +29 +00:02:01,522 --> 00:02:06,293 +The plug-in provides +an entire GameKit API in C#. + +30 +00:02:06,326 --> 00:02:10,330 +So, you don't have to choose +between building your game with Unity, + +31 +00:02:10,364 --> 00:02:14,902 +and taking full advantage of the first +class gaming features in Game Center. + +32 +00:02:16,370 --> 00:02:19,406 +Throughout this session, +you'll see code examples + +33 +00:02:19,439 --> 00:02:23,210 +provided not only in Swift, +but also in C#. + +34 +00:02:23,243 --> 00:02:27,948 +Now, let's dive right into one +of the biggest changes to Game Center + +35 +00:02:27,981 --> 00:02:30,417 +this year: Activity. + +36 +00:02:30,450 --> 00:02:33,654 +There are so many exciting +things happening in your games + +37 +00:02:33,687 --> 00:02:36,924 +that friends might want to +tell each other about. + +38 +00:02:36,957 --> 00:02:42,029 +For example, when they get a +new achievement, or jump up a leaderboard. + +39 +00:02:42,062 --> 00:02:47,067 +We've redesigned the Game Center dashboard +this year so that it will now include + +40 +00:02:47,100 --> 00:02:51,505 +activity from a player's friends in your +game and across games, all in one place. + +41 +00:02:52,973 --> 00:02:57,511 +When a player opens the dashboard, +they will see recent activity in your game + +42 +00:02:57,544 --> 00:03:02,583 +like achievements earned, when friends +have made huge jumps on the leaderboard, + +43 +00:03:02,616 --> 00:03:06,353 +or when one friend has beaten +another player's high score. + +44 +00:03:06,386 --> 00:03:12,559 +And, of course we are designing this +to work on iPad, Mac and Apple TV as well. + +45 +00:03:12,593 --> 00:03:15,896 +I am really excited about what this means +for players to see + +46 +00:03:15,929 --> 00:03:18,899 +and engage more +with what their friends are doing, + +47 +00:03:18,932 --> 00:03:22,202 +and to have your game and all of +the great activity from your game + +48 +00:03:22,236 --> 00:03:24,137 +discovered in more places. + +49 +00:03:24,171 --> 00:03:29,409 +If you are already using Game Center for +your games, you don't have to do anything! + +50 +00:03:29,443 --> 00:03:33,046 +Your games +will already appear in Activity. + +51 +00:03:33,080 --> 00:03:36,617 +But if not, +it's really easy to get started. + +52 +00:03:36,650 --> 00:03:40,053 +All you need to do is enable +the Game Center capability + +53 +00:03:40,087 --> 00:03:45,392 +and write just a few lines of code +to make sure authentication goes smoothly. + +54 +00:03:45,425 --> 00:03:48,128 +First, go to your game's Xcode project, + +55 +00:03:48,161 --> 00:03:51,798 +then head over +to the Signing and Capabilities tab. + +56 +00:03:51,832 --> 00:03:56,837 +Click on the add capability button, +then select Game Center. + +57 +00:03:56,870 --> 00:03:59,573 +Then, go to your app record +in App Store Connect + +58 +00:03:59,606 --> 00:04:02,676 +in order to enable Game Center +for your game. + +59 +00:04:02,709 --> 00:04:06,847 +This is also the place where you +can configure Game Center features, + +60 +00:04:06,880 --> 00:04:10,017 +such as leaderboards and achievements. + +61 +00:04:10,050 --> 00:04:14,655 +And finally, import GameKit, +then authenticate the local player + +62 +00:04:14,688 --> 00:04:17,424 +by setting the authenticateHandler. + +63 +00:04:17,457 --> 00:04:21,495 +You'll want to place this code +as early as possible in your game, + +64 +00:04:21,528 --> 00:04:23,564 +even on the title screen. + +65 +00:04:23,597 --> 00:04:27,034 +Then, if there is a view controller +available from the callback, + +66 +00:04:27,067 --> 00:04:29,102 +present it here. + +67 +00:04:29,136 --> 00:04:34,074 +For Unity developers, +just call "authenticate" on GKLocalPlayer. + +68 +00:04:34,107 --> 00:04:39,046 +This static method returns the +local player object once authenticated. + +69 +00:04:39,079 --> 00:04:43,584 +Congratulations! Your game +is now Game Center enabled. + +70 +00:04:43,617 --> 00:04:46,954 +Players will see this welcome banner +when they launch your game, + +71 +00:04:46,987 --> 00:04:52,092 +and gameplay activity for your game will +start appearing in the players' feeds. + +72 +00:04:52,125 --> 00:04:58,332 +Now, all that's left is for you to provide +players easy access to the Dashboard. + +73 +00:04:58,365 --> 00:05:02,402 +The best way to do so is through +the Game Center Access Point. + +74 +00:05:02,436 --> 00:05:06,273 +Access Point provides your players +a convenient way to launch + +75 +00:05:06,306 --> 00:05:08,542 +the Game Center dashboard. + +76 +00:05:08,575 --> 00:05:12,713 +Let's jump back to some code. +Showing the access point is easy. + +77 +00:05:12,746 --> 00:05:16,717 +First, you'll want to decide on +the most appropriate time to display it. + +78 +00:05:16,750 --> 00:05:20,220 +You should consider what makes +the most sense for your game, + +79 +00:05:20,254 --> 00:05:24,658 +but for many games, the ideal presentation +occurs on the game menu page. + +80 +00:05:24,691 --> 00:05:27,561 +Once you've determined that, +all you need to do + +81 +00:05:27,594 --> 00:05:30,030 +is set a location for its appearance, + +82 +00:05:30,063 --> 00:05:35,235 +and then set the GKAccessPoint's +'isActive' property to 'true.' + +83 +00:05:35,269 --> 00:05:39,573 +As a result, the Access Point +will show up in your game. + +84 +00:05:39,606 --> 00:05:43,877 +And here is how Unity developers +can use GKAccessPoint in C#. + +85 +00:05:43,911 --> 00:05:48,549 +Set the Access Point's location +and set IsActive to true. + +86 +00:05:48,582 --> 00:05:51,351 +When players interact +with the Access Point, + +87 +00:05:51,385 --> 00:05:53,954 +the system brings up the Dashboard. + +88 +00:05:53,987 --> 00:05:58,091 +This provides a familiar place for +players to learn more about your game + +89 +00:05:58,125 --> 00:06:00,994 +as well as check in on recent activity. + +90 +00:06:01,028 --> 00:06:05,032 +Players can explore your game's +own achievements and leaderboards. + +91 +00:06:05,065 --> 00:06:10,470 +Next, players will see gameplay events +for your game, featured first in the feed. + +92 +00:06:10,504 --> 00:06:14,708 +Here, Cloeax, +Jeezzzy and I are playing The Coast. + +93 +00:06:14,741 --> 00:06:16,543 +With just those few lines of code, + +94 +00:06:16,577 --> 00:06:19,713 +your game will get increased reach +and distribution + +95 +00:06:19,746 --> 00:06:24,451 +while tying in to the broader gaming +experience on Apple platforms. + +96 +00:06:24,484 --> 00:06:29,056 +There are a lot of things you can add +in your game that will generate Activity. + +97 +00:06:29,089 --> 00:06:30,924 +Let's start with Leaderboards. + +98 +00:06:30,958 --> 00:06:35,896 +Leaderboards are a powerful way to +increase your game's exposure in Activity. + +99 +00:06:35,929 --> 00:06:37,998 +They encourage friendly competition, + +100 +00:06:38,031 --> 00:06:41,969 +giving players more reasons +to jump back into your game. + +101 +00:06:42,002 --> 00:06:44,371 +After you have set up +a leaderboard in your game, + +102 +00:06:44,404 --> 00:06:48,876 +players will see new activity when their +friends are doing well on a leaderboard. + +103 +00:06:48,909 --> 00:06:53,614 +Here, my friend placed +in the top 25% of a leaderboard. + +104 +00:06:53,647 --> 00:06:58,385 +Activity also highlights when a player's +friend beats their leaderboard score. + +105 +00:06:58,418 --> 00:07:01,989 +Here, Simundane just beat my score +in a game we've been competing on + +106 +00:07:02,022 --> 00:07:03,957 +back and forth. + +107 +00:07:03,991 --> 00:07:07,794 +For this activity, +players will also get a notification. + +108 +00:07:07,828 --> 00:07:10,831 +This notification is sent from +Game Center, + +109 +00:07:10,864 --> 00:07:12,900 +and you don't need to worry about + +110 +00:07:12,933 --> 00:07:16,970 +asking the user to opt in +to notifications for your game. + +111 +00:07:17,004 --> 00:07:20,073 +If your game +already provides leaderboards, + +112 +00:07:20,107 --> 00:07:23,544 +these activities +will appear automatically. + +113 +00:07:23,577 --> 00:07:26,680 +Even if you're already taking +advantage of leaderboards, + +114 +00:07:26,713 --> 00:07:29,416 +consider expanding your leaderboard sets + +115 +00:07:29,449 --> 00:07:33,554 +to provide more moments of competition +for players and their friends. + +116 +00:07:33,587 --> 00:07:38,158 +Recurring leaderboards in particular +provide a sense of timeliness + +117 +00:07:38,192 --> 00:07:42,196 +and a reason to re-engage +with your game on an ongoing basis. + +118 +00:07:42,229 --> 00:07:45,666 +Next, I'd like to talk about +how Game Center Achievements + +119 +00:07:45,699 --> 00:07:48,068 +are featured in Activity. + +120 +00:07:48,101 --> 00:07:52,372 +Achievements provide players an +additional way of engaging with your game, + +121 +00:07:52,406 --> 00:07:56,944 +tracking gameplay progress, +and sharing that progress with friends. + +122 +00:07:56,977 --> 00:08:00,047 +When a player completes +an achievement in your game, + +123 +00:08:00,080 --> 00:08:04,918 +this will be reflected in their activity +and their friends' activity as well. + +124 +00:08:04,952 --> 00:08:07,754 +When a player completes +every achievement in your game, + +125 +00:08:07,788 --> 00:08:13,327 +we take a moment to recognize that +with a special celebratory activity. + +126 +00:08:13,360 --> 00:08:17,364 +Achievements provide players with +a sense of progress and accomplishment, + +127 +00:08:17,397 --> 00:08:22,369 +and can help tell a story of how far +a player has made it through your game. + +128 +00:08:22,402 --> 00:08:27,207 +With prominent placement in Activity, +achievements instantly receive + +129 +00:08:27,241 --> 00:08:31,144 +wider visibility throughout +the Game Center social network. + +130 +00:08:31,178 --> 00:08:33,881 +Players can see +how their friends are doing, + +131 +00:08:33,914 --> 00:08:38,185 +and they'll have more reasons to +jump into your game and play together. + +132 +00:08:38,218 --> 00:08:42,189 +You just saw what players will see +when they land on your game's Dashboard. + +133 +00:08:42,222 --> 00:08:44,558 +Players can also visit a friend's profile + +134 +00:08:44,591 --> 00:08:47,794 +to see all of their +recent activity in one place. + +135 +00:08:47,828 --> 00:08:50,964 +And of course, what you see or don't see +on someone's profile + +136 +00:08:50,998 --> 00:08:53,400 +is still based on +the profile privacy option + +137 +00:08:53,433 --> 00:08:55,636 +they choose in Game Center settings. + +138 +00:08:55,669 --> 00:08:59,506 +And that is Activity: a place +where players can discover + +139 +00:08:59,540 --> 00:09:01,808 +their friends' activity in your game, + +140 +00:09:01,842 --> 00:09:05,546 +as well as check in +on their friends' activity across games. + +141 +00:09:05,579 --> 00:09:09,883 +In summary, Game Center +is helping you help your players + +142 +00:09:09,917 --> 00:09:13,720 +have more fun playing with +and competing against their friends. + +143 +00:09:13,754 --> 00:09:18,825 +Activity is a great destination for +players to check in on their progress + +144 +00:09:18,859 --> 00:09:23,564 +as well as their friends' progress +and provide you more distribution. + +145 +00:09:23,597 --> 00:09:28,268 +And for Unity developers, +it is now possible to take advantage + +146 +00:09:28,302 --> 00:09:33,106 +of the full extent of GameKit +with our brand new Unity plug-in. + +147 +00:09:33,140 --> 00:09:37,044 +You can learn more about how to bring +the best out of Game Center features + +148 +00:09:37,077 --> 00:09:40,180 +from your game, +how to use recurring leaderboards, + +149 +00:09:40,214 --> 00:09:42,616 +and how to use GameKit +in your Unity games + +150 +00:09:42,649 --> 00:09:45,485 +in the sessions linked to this video. + +151 +00:09:45,519 --> 00:09:48,322 +I have covered a lot today. + +152 +00:09:48,355 --> 00:09:53,126 +And we trust that this year's updates will +bring more joy to players of your games. + +153 +00:09:53,160 --> 00:09:55,462 +Thank you for watching. + +154 +00:09:55,495 --> 00:09:57,731 +[speaking language] + diff --git a/eng/2022 Session 10065 Plug-in and play - Add Apple frameworks to your Unity game projects en.srt b/eng/2022 Session 10065 Plug-in and play - Add Apple frameworks to your Unity game projects en.srt new file mode 100644 index 0000000..3dbc93c --- /dev/null +++ b/eng/2022 Session 10065 Plug-in and play - Add Apple frameworks to your Unity game projects en.srt @@ -0,0 +1,2514 @@ +1 +00:00:00,033 --> 00:00:03,036 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,036 --> 00:00:09,443 +♪ + +3 +00:00:09,443 --> 00:00:13,981 +Hi, I'm Jared Marsau and I work +on Game Technologies at Apple. + +4 +00:00:13,981 --> 00:00:16,416 +Today, I'll be talking to you +about how you can use + +5 +00:00:16,416 --> 00:00:19,186 +select Apple frameworks +to add new features + +6 +00:00:19,186 --> 00:00:21,655 +to your Unity-based games. + +7 +00:00:21,655 --> 00:00:25,525 +We know that many of you +are using tools, such as Unity, + +8 +00:00:25,525 --> 00:00:27,427 +to build your games. + +9 +00:00:27,427 --> 00:00:29,696 +Our goal is to bring you +the latest features, + +10 +00:00:29,696 --> 00:00:32,165 +no matter what tool +you're using. + +11 +00:00:32,165 --> 00:00:34,334 +Starting today, +enhance your games + +12 +00:00:34,334 --> 00:00:37,237 +with a new set +of Unity plug-ins. + +13 +00:00:37,237 --> 00:00:39,940 +Use the Game Center plug-in +to add GameKit features like + +14 +00:00:39,940 --> 00:00:43,710 +player authentication, +leaderboards, and matchmaking. + +15 +00:00:43,710 --> 00:00:46,613 +Use the Game Controller plug-in +for input customizations and + +16 +00:00:46,613 --> 00:00:51,385 +glyphs, along with support for +MFi and third-party controllers. + +17 +00:00:51,385 --> 00:00:54,321 +Use the Accessibility plug-in +to improve accessibility + +18 +00:00:54,321 --> 00:00:58,659 +through system features, +like VoiceOver and Dynamic Type. + +19 +00:00:58,659 --> 00:01:02,429 +Integrate Apple's data-driven +rich haptic feedback system + +20 +00:01:02,429 --> 00:01:04,965 +with the Core Haptics plug-in. + +21 +00:01:04,965 --> 00:01:07,634 +Add advanced geometry-aware +spatial audio + +22 +00:01:07,634 --> 00:01:09,202 +with the PHASE plug-in. + +23 +00:01:09,202 --> 00:01:12,873 +Finally, use the Apple.Core +plug-in to manage build settings + +24 +00:01:12,873 --> 00:01:16,576 +and simplify the build process. + +25 +00:01:16,576 --> 00:01:18,745 +This initial set of plug-ins +will let you add + +26 +00:01:18,745 --> 00:01:22,282 +new gameplay mechanics, +make your games more accessible, + +27 +00:01:22,282 --> 00:01:24,151 +and help you more rapidly tap + +28 +00:01:24,151 --> 00:01:27,054 +into the latest features +and services. + +29 +00:01:27,054 --> 00:01:29,423 +I am extremely excited +to see the amazing games + +30 +00:01:29,423 --> 00:01:32,592 +you create with this new set +of Unity plug-ins. + +31 +00:01:32,592 --> 00:01:35,662 +Now, I'll get into the details +of the project. + +32 +00:01:35,662 --> 00:01:38,966 +First, I'll discuss the design +principles of the project. + +33 +00:01:38,966 --> 00:01:42,169 +Then, I'll cover project +concepts and organization. + +34 +00:01:42,169 --> 00:01:44,504 +Next, I'll offer +some key pointers + +35 +00:01:44,504 --> 00:01:47,140 +on interacting +with the project repository. + +36 +00:01:47,140 --> 00:01:50,544 +And finally, I'll go into detail +for each plug-in. + +37 +00:01:50,544 --> 00:01:52,279 +As you may know, + +38 +00:01:52,279 --> 00:01:56,450 +Apple frameworks encapsulate +functionality in a modular way. + +39 +00:01:56,450 --> 00:01:58,819 +This lets you pick and choose +the right technologies + +40 +00:01:58,819 --> 00:02:03,290 +for your apps while maintaining +compact, efficient code. + +41 +00:02:03,290 --> 00:02:06,893 +With the Unity plug-ins, +a similar pattern is followed; + +42 +00:02:06,893 --> 00:02:10,163 +each plug-in maps to a single +underlying framework. + +43 +00:02:10,163 --> 00:02:12,332 +This lets you pick and choose +the set of plug-ins + +44 +00:02:12,332 --> 00:02:15,669 +you want to use based upon +your game's needs. + +45 +00:02:15,669 --> 00:02:19,373 +Each plug-in exposes +C#-based Unity script, + +46 +00:02:19,373 --> 00:02:21,274 +which maps as directly +as possible + +47 +00:02:21,274 --> 00:02:23,443 +to the underlying framework. + +48 +00:02:23,443 --> 00:02:26,580 +Doing this means that any +familiarity that you might have + +49 +00:02:26,580 --> 00:02:29,950 +with the underlying framework +carries over to the plug-in. + +50 +00:02:29,950 --> 00:02:33,186 +Concepts, and in many cases +the framework API, + +51 +00:02:33,186 --> 00:02:35,022 +carry directly over. + +52 +00:02:35,022 --> 00:02:38,525 +Conversely, this also means +that by learning the plug-in, + +53 +00:02:38,525 --> 00:02:42,162 +you'll be implicitly learning +the underlying framework. + +54 +00:02:42,162 --> 00:02:44,598 +Another important detail +is that these plug-ins + +55 +00:02:44,598 --> 00:02:48,201 +are built as Apple platform +native libraries. + +56 +00:02:48,201 --> 00:02:51,638 +These libraries act as +the glue between the C# script + +57 +00:02:51,638 --> 00:02:55,242 +and the underlying +framework API. + +58 +00:02:55,242 --> 00:02:59,346 +Apple Unity plug-ins are +organized as Unity packages, + +59 +00:02:59,346 --> 00:03:02,082 +so managing their integration +to your project can be done + +60 +00:03:02,082 --> 00:03:05,952 +with the Unity Editor's +built-in Package Manager. + +61 +00:03:05,952 --> 00:03:09,356 +In some cases, plug-ins also +include additional Editor + +62 +00:03:09,356 --> 00:03:12,793 +functionality that makes working +with the plug-in even easier + +63 +00:03:12,793 --> 00:03:15,629 +and aligns with the Unity +inspector-driven workflows + +64 +00:03:15,629 --> 00:03:18,265 +that you're already +familiar with. + +65 +00:03:18,265 --> 00:03:21,701 +Of course, each plug-in +is paired with detailed readmes, + +66 +00:03:21,701 --> 00:03:25,038 +samples, and links +to additional resources, + +67 +00:03:25,038 --> 00:03:28,842 +such as associated Apple +Developer documentation. + +68 +00:03:28,842 --> 00:03:32,412 +Next, I'll briefly examine +some key workflow concepts + +69 +00:03:32,412 --> 00:03:35,315 +to help you get started. + +70 +00:03:35,315 --> 00:03:38,652 +The first step is to clone +the source from GitHub. + +71 +00:03:38,652 --> 00:03:42,456 +You can find all of the project +source and documentation there. + +72 +00:03:42,456 --> 00:03:44,758 +This is the starting point +for everyone who will be using + +73 +00:03:44,758 --> 00:03:47,260 +the Apple Unity plug-ins. + +74 +00:03:47,260 --> 00:03:48,962 +Once you've cloned +the repository, + +75 +00:03:48,962 --> 00:03:52,299 +building the plug-ins will be +one of your first tasks. + +76 +00:03:52,299 --> 00:03:55,135 +To simplify this process, +the repository includes + +77 +00:03:55,135 --> 00:03:59,806 +a Python script at +the repository root: build.py. + +78 +00:03:59,806 --> 00:04:02,175 +This script handles +building native libraries, + +79 +00:04:02,175 --> 00:04:04,511 +copying them +to the correct locations, + +80 +00:04:04,511 --> 00:04:08,081 +updating Unity meta files, +packing the plug-ins, + +81 +00:04:08,081 --> 00:04:10,250 +and building plug-in tests. + +82 +00:04:10,250 --> 00:04:13,353 +The script is organized such +that the simplest invocation + +83 +00:04:13,353 --> 00:04:17,090 +builds all of the plug-ins, +packages them into tarballs, + +84 +00:04:17,090 --> 00:04:18,959 +and saves them +to a build folder + +85 +00:04:18,959 --> 00:04:22,729 +ready for integration +into your Unity projects. + +86 +00:04:22,729 --> 00:04:25,665 +It's important to note +that fully building and packing + +87 +00:04:25,665 --> 00:04:32,239 +the plug-ins will require Xcode, +Python3, npm, and Unity. + +88 +00:04:32,239 --> 00:04:34,241 +Finally, +we have detailed documentation + +89 +00:04:34,241 --> 00:04:38,879 +for using build.py +in the project repository. + +90 +00:04:38,879 --> 00:04:40,614 +Now it's time +to dive into details + +91 +00:04:40,614 --> 00:04:42,549 +with each of the plug-ins. + +92 +00:04:42,549 --> 00:04:44,718 +For each plug-in, +I'll cover how to add them + +93 +00:04:44,718 --> 00:04:49,055 +to your Unity projects, a brief +overview of included features, + +94 +00:04:49,055 --> 00:04:51,825 +key scripting concepts, +and some code snippets + +95 +00:04:51,825 --> 00:04:54,060 +or examples +in the Unity Editor + +96 +00:04:54,060 --> 00:04:57,797 +highlighting how to integrate +them into your projects. + +97 +00:04:57,797 --> 00:04:59,999 +Let's start +with the foundational plug-in, + +98 +00:04:59,999 --> 00:05:01,768 +Apple.Core. + +99 +00:05:01,768 --> 00:05:04,838 +Apple.Core unifies build +settings for each plug-in into + +100 +00:05:04,838 --> 00:05:09,276 +a single preference pane within +Unity's Project Settings window. + +101 +00:05:09,276 --> 00:05:12,412 +Because you compile each +plug-in's native libraries, + +102 +00:05:12,412 --> 00:05:15,215 +Apple.Core also includes +an asset processor, + +103 +00:05:15,215 --> 00:05:17,884 +which ensures that each +plug-in library is configured + +104 +00:05:17,884 --> 00:05:21,021 +for the appropriate platform +on import. + +105 +00:05:21,021 --> 00:05:24,024 +When building your projects, +Apple.Core also contains + +106 +00:05:24,024 --> 00:05:26,660 +scripts that run as +a post-process to your build + +107 +00:05:26,660 --> 00:05:29,696 +to ensure that native libraries +are referenced correctly + +108 +00:05:29,696 --> 00:05:32,399 +in your intermediate +Xcode projects. + +109 +00:05:32,399 --> 00:05:35,669 +Because each plug-in interacts +with an underlying framework, + +110 +00:05:35,669 --> 00:05:37,103 +Apple.Core also defines + +111 +00:05:37,103 --> 00:05:40,707 +a handful of runtime inter-op +types, which ease data passing + +112 +00:05:40,707 --> 00:05:44,311 +between the C# script +and native code layers. + +113 +00:05:44,311 --> 00:05:46,646 +Finally, +Apple.Core is a dependency + +114 +00:05:46,646 --> 00:05:49,516 +for all other +Apple Unity plug-ins. + +115 +00:05:49,516 --> 00:05:51,585 +This means that Apple.Core +should be imported + +116 +00:05:51,585 --> 00:05:55,889 +into your projects before +any of the other plug-ins. + +117 +00:05:55,889 --> 00:05:57,924 +In this demo, +I'll show you how to import + +118 +00:05:57,924 --> 00:06:00,460 +the Apple.Core plug-in +into a new project + +119 +00:06:00,460 --> 00:06:04,965 +and briefly explore the Apple +Build Settings preferences. + +120 +00:06:04,965 --> 00:06:07,467 +Once the plug-ins +are built and packed, + +121 +00:06:07,467 --> 00:06:11,738 +Apple.Core can be imported +with the Unity Package Manager. + +122 +00:06:11,738 --> 00:06:14,708 +Just choose the option +to add package from tarball + +123 +00:06:14,708 --> 00:06:17,978 +and browse +to the packaged plug-in. + +124 +00:06:23,183 --> 00:06:26,920 +The Editor will then load the +package and compile the scripts. + +125 +00:06:26,920 --> 00:06:31,391 +Once complete, +Apple.Core is ready to use. + +126 +00:06:34,761 --> 00:06:37,264 +Apple.Core's primary +user-facing feature + +127 +00:06:37,264 --> 00:06:39,633 +is the addition +of the Apple Build Settings tab + +128 +00:06:39,633 --> 00:06:43,536 +in the Editor's +Project Settings window. + +129 +00:06:46,539 --> 00:06:49,009 +When you import +an Apple Unity plug-in, + +130 +00:06:49,009 --> 00:06:52,612 +all of it's available build +options will be visible here. + +131 +00:06:52,612 --> 00:06:53,780 +Out of the box, + +132 +00:06:53,780 --> 00:06:56,783 +Apple.Core comes with some +default configuration options, + +133 +00:06:56,783 --> 00:07:00,754 +such as minimum +supported OS version. + +134 +00:07:00,754 --> 00:07:03,156 +It's also useful to note +that you can disable + +135 +00:07:03,156 --> 00:07:07,127 +the post-process +build steps for any plug-in. + +136 +00:07:10,530 --> 00:07:13,466 +Finally, you can configure +common security settings, + +137 +00:07:13,466 --> 00:07:18,438 +which will propagate to your +intermediate Xcode projects. + +138 +00:07:18,438 --> 00:07:21,107 +Use the Game Center plug-in +to bring even more fun + +139 +00:07:21,107 --> 00:07:23,843 +and connection to your games +with Game Center, + +140 +00:07:23,843 --> 00:07:26,413 +Apple's social gaming network. + +141 +00:07:26,413 --> 00:07:29,015 +Game Center lets players +build an identity across + +142 +00:07:29,015 --> 00:07:32,085 +Apple platforms and enables +features like safe, + +143 +00:07:32,085 --> 00:07:35,822 +secure player authentication, +in-game achievements, + +144 +00:07:35,822 --> 00:07:39,559 +shared leaderboards, +challenges sent between players, + +145 +00:07:39,559 --> 00:07:41,995 +and multiplayer matchmaking. + +146 +00:07:41,995 --> 00:07:44,331 +You can pick and choose +which Game Center features + +147 +00:07:44,331 --> 00:07:45,999 +to integrate into your games, + +148 +00:07:45,999 --> 00:07:49,202 +but everything starts +with player authentication. + +149 +00:07:49,202 --> 00:07:51,671 +Add Game Center player +authentication to your game + +150 +00:07:51,671 --> 00:07:53,840 +and the Game Center widget +can feature your game + +151 +00:07:53,840 --> 00:07:57,977 +on the player's Home Screen +or within the App Store. + +152 +00:07:57,977 --> 00:07:59,813 +It also requires +very little code + +153 +00:07:59,813 --> 00:08:02,182 +to add player authentication. + +154 +00:08:02,182 --> 00:08:04,617 +The first step +is to add the Apple.Core + +155 +00:08:04,617 --> 00:08:06,786 +and GameKit plug-ins +to your project. + +156 +00:08:06,786 --> 00:08:10,457 +The GameKit plug-in connects +the Game Center service. + +157 +00:08:10,457 --> 00:08:13,593 +When authenticating, connect +with the Game Center service + +158 +00:08:13,593 --> 00:08:16,963 +and initialize +a GKLocalPlayer object. + +159 +00:08:16,963 --> 00:08:19,733 +Once initialized, +query for player restrictions + +160 +00:08:19,733 --> 00:08:22,335 +based upon +the local player's profile. + +161 +00:08:22,335 --> 00:08:26,139 +These restrictions include +limiting access to adult + +162 +00:08:26,139 --> 00:08:29,342 +or explicit content +for underage players, + +163 +00:08:29,342 --> 00:08:32,078 +limiting access +to multiplayer features, + +164 +00:08:32,078 --> 00:08:34,814 +or disabling +in-game communication. + +165 +00:08:34,814 --> 00:08:37,684 +A simple way to manage +a GKLocalPlayer + +166 +00:08:37,684 --> 00:08:40,153 +and it's interactions +with the Game Center service + +167 +00:08:40,153 --> 00:08:43,189 +is to define a component +within Unity. + +168 +00:08:43,189 --> 00:08:44,391 +Here, for example, + +169 +00:08:44,391 --> 00:08:47,660 +is a simple Game Manager +component definition. + +170 +00:08:47,660 --> 00:08:50,864 +This component holds +a reference to a GKLocalPlayer. + +171 +00:08:50,864 --> 00:08:53,566 +It also handles both +authentication and query + +172 +00:08:53,566 --> 00:08:56,002 +for player restrictions +in its start method; + +173 +00:08:56,002 --> 00:08:58,605 +player authentication +only needs to happen once + +174 +00:08:58,605 --> 00:09:01,074 +during the lifetime of the game. + +175 +00:09:01,074 --> 00:09:04,210 +This is the GameManager +component's script. + +176 +00:09:04,210 --> 00:09:07,213 +Here's the field for caching +a GKLocalPlayer. + +177 +00:09:07,213 --> 00:09:09,315 +Within the component's +start method + +178 +00:09:09,315 --> 00:09:12,986 +is the one-time call to +GKLocalPlayer.Authenticate, + +179 +00:09:12,986 --> 00:09:17,123 +a static method that returns +the GKLocalPlayer instance. + +180 +00:09:17,123 --> 00:09:19,926 +Once the local player +is successfully authenticated, + +181 +00:09:19,926 --> 00:09:22,962 +it's time to check +their player restrictions. + +182 +00:09:22,962 --> 00:09:24,798 +Checking player restrictions +in code + +183 +00:09:24,798 --> 00:09:27,734 +resolves to a series of +Boolean checks and can be added + +184 +00:09:27,734 --> 00:09:31,571 +to the try block in the +GameManager component's script. + +185 +00:09:31,571 --> 00:09:33,773 +True here means +your local player should have + +186 +00:09:33,773 --> 00:09:36,276 +limited access +to explicit content. + +187 +00:09:36,276 --> 00:09:38,044 +True here means +that your local player + +188 +00:09:38,044 --> 00:09:40,847 +should have a restricted +multiplayer experience. + +189 +00:09:40,847 --> 00:09:43,950 +And finally, true here means +that in-game communication + +190 +00:09:43,950 --> 00:09:46,319 +should be disabled. + +191 +00:09:46,319 --> 00:09:48,455 +And that's all +the code changes necessary + +192 +00:09:48,455 --> 00:09:51,324 +to add player authentication +to your game. + +193 +00:09:51,324 --> 00:09:54,327 +From here, there are two +additional steps necessary + +194 +00:09:54,327 --> 00:09:58,631 +to fully prepare your game to +take advantage of Game Center. + +195 +00:09:58,631 --> 00:10:01,534 +First, you'll need to add +the Game Center capability + +196 +00:10:01,534 --> 00:10:04,304 +to your intermediate +Xcode projects. + +197 +00:10:04,304 --> 00:10:07,440 +This is done from within +the Xcode project UI. + +198 +00:10:07,440 --> 00:10:09,175 +More information can be found + +199 +00:10:09,175 --> 00:10:11,644 +in the Apple Developer +documentation article + +200 +00:10:11,644 --> 00:10:14,881 +"Enabling and Configuring +Game Center." + +201 +00:10:14,881 --> 00:10:17,817 +Next, you'll need to add +Game Center features to your app + +202 +00:10:17,817 --> 00:10:20,019 +using App Store Connect. + +203 +00:10:20,019 --> 00:10:23,923 +Check out the App Store Connect +portal for more information. + +204 +00:10:23,923 --> 00:10:26,626 +With these steps complete, +you're ready to authenticate + +205 +00:10:26,626 --> 00:10:30,196 +players and ensure +a safe gaming environment. + +206 +00:10:30,196 --> 00:10:33,299 +Player authentication +only scratches the surface + +207 +00:10:33,299 --> 00:10:34,901 +of the features made available + +208 +00:10:34,901 --> 00:10:37,537 +by the Game Center Unity +plug-in. + +209 +00:10:37,537 --> 00:10:40,640 +To learn more about improving +discoverability of your game, + +210 +00:10:40,640 --> 00:10:42,141 +check out the session + +211 +00:10:42,141 --> 00:10:45,345 +"Reach new players +with Game Center dashboard." + +212 +00:10:45,345 --> 00:10:47,981 +To learn more about multiplayer +and matchmaking, + +213 +00:10:47,981 --> 00:10:52,485 +watch the "What's new in Game +Center" session from last year. + +214 +00:10:52,485 --> 00:10:56,122 +Controllers are the primary way +for players to interact + +215 +00:10:56,122 --> 00:10:58,424 +with the worlds +that you'll create. + +216 +00:10:58,424 --> 00:11:02,061 +Easily bring reliable and +flexible game controller support + +217 +00:11:02,061 --> 00:11:04,998 +to your games with +the Game Controller plug-in. + +218 +00:11:04,998 --> 00:11:08,067 +The Game Controller plug-in +brings a handful of features, + +219 +00:11:08,067 --> 00:11:11,137 +like support for game controller +customizations, + +220 +00:11:11,137 --> 00:11:13,106 +which allow players +to remap buttons + +221 +00:11:13,106 --> 00:11:15,842 +in one place for all games. + +222 +00:11:15,842 --> 00:11:19,712 +Button glyphs, to ensure +consistent user experience. + +223 +00:11:19,712 --> 00:11:22,115 +And support for all +MFi controllers, + +224 +00:11:22,115 --> 00:11:23,783 +as well as +third-party controllers + +225 +00:11:23,783 --> 00:11:27,353 +like select Sony +and Microsoft controllers. + +226 +00:11:27,353 --> 00:11:29,022 +Just as with the other plug-ins, + +227 +00:11:29,022 --> 00:11:31,324 +use the Package Manager +to add the Apple.Core + +228 +00:11:31,324 --> 00:11:34,227 +and Game Controller plug-ins +to your project. + +229 +00:11:34,227 --> 00:11:36,195 +With the Game Controller +plug-in loaded, + +230 +00:11:36,195 --> 00:11:40,500 +the first step is to initialize +the GCControllerService. + +231 +00:11:40,500 --> 00:11:43,403 +As we'll see shortly, +this service is how controllers + +232 +00:11:43,403 --> 00:11:46,472 +and their connection events +are accessed. + +233 +00:11:46,472 --> 00:11:49,208 +Once initialized, +query GCControllerService + +234 +00:11:49,208 --> 00:11:52,545 +for all controllers currently +connected to the system. + +235 +00:11:52,545 --> 00:11:54,581 +Connected controllers +are represented + +236 +00:11:54,581 --> 00:11:58,918 +by GCController objects +in the Game Controller plug-in. + +237 +00:11:58,918 --> 00:12:01,387 +For each GCController +that's connected, + +238 +00:12:01,387 --> 00:12:03,756 +poll for updated +controller state. + +239 +00:12:03,756 --> 00:12:06,659 +Polling can happen as little +or as often as is needed + +240 +00:12:06,659 --> 00:12:08,861 +by your game, +but a good place to start + +241 +00:12:08,861 --> 00:12:11,631 +is in Unity's +regular update loop. + +242 +00:12:11,631 --> 00:12:13,666 +Once controller state +is updated, + +243 +00:12:13,666 --> 00:12:15,969 +test for input +on each of the controller's + +244 +00:12:15,969 --> 00:12:19,405 +individual elements, +such as buttons, thumb sticks, + +245 +00:12:19,405 --> 00:12:20,907 +and so on. + +246 +00:12:20,907 --> 00:12:23,409 +Not to be forgotten, +controllers may come and go + +247 +00:12:23,409 --> 00:12:26,512 +during your game's lifecycle -- +register callbacks + +248 +00:12:26,512 --> 00:12:30,049 +to handle controller connect +and disconnect events. + +249 +00:12:30,049 --> 00:12:32,619 +A quick way to get +the Game Controller plug-in + +250 +00:12:32,619 --> 00:12:36,556 +integrated is to create a simple +input manager component. + +251 +00:12:36,556 --> 00:12:39,292 +This component +has three key elements: + +252 +00:12:39,292 --> 00:12:40,793 +a container which holds + +253 +00:12:40,793 --> 00:12:43,696 +all of the currently +connected controllers, + +254 +00:12:43,696 --> 00:12:46,065 +a start method +for initialization, + +255 +00:12:46,065 --> 00:12:48,267 +and an update method +for handling polling + +256 +00:12:48,267 --> 00:12:50,637 +and testing for input. + +257 +00:12:50,637 --> 00:12:53,940 +First, let's take a closer look +at the start method. + +258 +00:12:53,940 --> 00:12:56,476 +This is a great place +to do all of the necessary + +259 +00:12:56,476 --> 00:12:58,911 +one-time setup tasks. + +260 +00:12:58,911 --> 00:13:01,314 +Initialization of +the game controller service + +261 +00:13:01,314 --> 00:13:02,749 +should happen here, + +262 +00:13:02,749 --> 00:13:05,985 +along with the initial check +for connected controllers + +263 +00:13:05,985 --> 00:13:07,887 +and registration of callbacks + +264 +00:13:07,887 --> 00:13:11,124 +for connection +and disconnection events. + +265 +00:13:11,124 --> 00:13:14,494 +Here's the input manager +component's script. + +266 +00:13:14,494 --> 00:13:16,062 +All of the one-time setup code + +267 +00:13:16,062 --> 00:13:19,699 +goes in the component's +start method, including a call + +268 +00:13:19,699 --> 00:13:23,369 +to GCControllerService. +Initialize(). + +269 +00:13:23,369 --> 00:13:26,639 +Calling GetConnectedControllers +gets an enumerable container + +270 +00:13:26,639 --> 00:13:30,176 +of all the currently connected +controllers. + +271 +00:13:30,176 --> 00:13:33,446 +The final initialization step +is to register callbacks + +272 +00:13:33,446 --> 00:13:37,550 +for controller connect +and disconnect events. + +273 +00:13:37,550 --> 00:13:39,886 +Now that initialization +is complete, + +274 +00:13:39,886 --> 00:13:42,722 +the input manager also needs +an update method + +275 +00:13:42,722 --> 00:13:45,158 +in order to poll +each connected controller + +276 +00:13:45,158 --> 00:13:48,094 +to update input state, +and to handle input state + +277 +00:13:48,094 --> 00:13:50,830 +for each of the controller's +inputs. + +278 +00:13:50,830 --> 00:13:53,066 +To poll for input, +start by iterating + +279 +00:13:53,066 --> 00:13:55,601 +through the set +of connected controllers. + +280 +00:13:55,601 --> 00:13:57,770 +Call the +GCController's Poll method + +281 +00:13:57,770 --> 00:13:59,739 +to gather the latest state. + +282 +00:13:59,739 --> 00:14:05,011 +Then check each button state +and respond accordingly. + +283 +00:14:05,011 --> 00:14:08,347 +And that's a quick look at how to +use the Game Controller plug-in + +284 +00:14:08,347 --> 00:14:12,785 +to access connected controllers +and get controller input. + +285 +00:14:12,785 --> 00:14:15,988 +To get into more detail about +the Game Controller framework + +286 +00:14:15,988 --> 00:14:18,725 +and learn about topics +like third-party controllers + +287 +00:14:18,725 --> 00:14:22,795 +and nonstandard inputs, +check out prior years' sessions: + +288 +00:14:22,795 --> 00:14:24,530 +"Supporting New +Game Controllers" + +289 +00:14:24,530 --> 00:14:27,333 +and "Advancements +in Game Controllers." + +290 +00:14:27,333 --> 00:14:30,837 +Accessibility is about +making technologies available + +291 +00:14:30,837 --> 00:14:32,638 +for everyone. + +292 +00:14:32,638 --> 00:14:36,175 +Use the Accessibility plug-in +to integrate a wide range + +293 +00:14:36,175 --> 00:14:38,478 +of Apple's assistive +technologies + +294 +00:14:38,478 --> 00:14:41,247 +into your Unity-based games. + +295 +00:14:41,247 --> 00:14:43,750 +The Accessibility plug-in +gives you the ability + +296 +00:14:43,750 --> 00:14:46,519 +to add key features, +such as VoiceOver, + +297 +00:14:46,519 --> 00:14:50,189 +which can read programmatically +tagged content to your users; + +298 +00:14:50,189 --> 00:14:51,357 +Switch Control, + +299 +00:14:51,357 --> 00:14:54,794 +allowing for a wide range +of assistive input devices; + +300 +00:14:54,794 --> 00:14:57,797 +Dynamic Type, +to easily scale in-game text + +301 +00:14:57,797 --> 00:15:00,867 +and UI based upon +user preferences; + +302 +00:15:00,867 --> 00:15:03,736 +and UI accommodation +settings in order to adhere + +303 +00:15:03,736 --> 00:15:07,273 +to system-wide +accessibility preferences. + +304 +00:15:07,273 --> 00:15:09,876 +There's a lot to cover +with the Accessibility plug-in, + +305 +00:15:09,876 --> 00:15:11,878 +so I encourage you to check out +the session + +306 +00:15:11,878 --> 00:15:14,781 +"Add accessibility +to Unity games" + +307 +00:15:14,781 --> 00:15:18,684 +for a deep dive into the +Accessibility Unity plug-in. + +308 +00:15:18,684 --> 00:15:21,921 +In that session you'll not only +get examples and use cases, + +309 +00:15:21,921 --> 00:15:23,756 +but you'll also build +an understanding + +310 +00:15:23,756 --> 00:15:26,159 +of what's possible +with accessibility + +311 +00:15:26,159 --> 00:15:28,261 +on Apple platforms. + +312 +00:15:28,261 --> 00:15:32,331 +Be sure to check it out as soon +as you have the opportunity. + +313 +00:15:32,331 --> 00:15:34,801 +Adding haptic feedback +to your games is a great way + +314 +00:15:34,801 --> 00:15:38,671 +to increase immersion and +enhance the gameplay experience. + +315 +00:15:38,671 --> 00:15:41,407 +Integrate Apple's advanced +haptic capabilities + +316 +00:15:41,407 --> 00:15:43,810 +with the Core Haptics plug-in. + +317 +00:15:43,810 --> 00:15:47,079 +Use the Core Haptics plug-in +to build custom haptic patterns + +318 +00:15:47,079 --> 00:15:49,916 +from a set of haptic +and audio events. + +319 +00:15:49,916 --> 00:15:52,885 +Play back synchronized +custom audio and haptics. + +320 +00:15:52,885 --> 00:15:55,955 +Programmatically define +or update haptic feedback + +321 +00:15:55,955 --> 00:15:58,624 +by adjusting parameters +in real time. + +322 +00:15:58,624 --> 00:16:02,495 +Use the Apple Haptic and Audio +Pattern file format, or AHAP, + +323 +00:16:02,495 --> 00:16:04,764 +for a file-based approach +to designing and storing + +324 +00:16:04,764 --> 00:16:07,133 +your patterns as assets. + +325 +00:16:07,133 --> 00:16:09,702 +Tune your Core Haptics patterns +in the Unity Editor + +326 +00:16:09,702 --> 00:16:12,471 +with inspector support. + +327 +00:16:12,471 --> 00:16:14,841 +To get the most out of +the Core Haptics plug-in, + +328 +00:16:14,841 --> 00:16:17,210 +you'll need to understand +four fundamental elements + +329 +00:16:17,210 --> 00:16:20,680 +of Core Haptics and their +relationship to one another. + +330 +00:16:20,680 --> 00:16:24,517 +The highest-level element +is the CHHapticEngine. + +331 +00:16:24,517 --> 00:16:27,320 +The haptic engine represents +the link to the haptic server + +332 +00:16:27,320 --> 00:16:31,858 +on the device and is required +to play any haptic patterns. + +333 +00:16:31,858 --> 00:16:35,895 +The CHHapticEngine creates +CHHapticPatternPlayers. + +334 +00:16:35,895 --> 00:16:39,065 +Pattern players are used +for playback of CHHapticPatterns + +335 +00:16:39,065 --> 00:16:44,136 +with controls like start, +stop, pause, and resume. + +336 +00:16:44,136 --> 00:16:47,273 +A CHHapticPattern +is a logical grouping + +337 +00:16:47,273 --> 00:16:50,509 +of one or more haptic +and audio events. + +338 +00:16:50,509 --> 00:16:54,881 +The CHHapticEngine uses patterns +to create players. + +339 +00:16:54,881 --> 00:16:57,250 +CHHapticEvents +are the building blocks + +340 +00:16:57,250 --> 00:17:00,553 +used to define +a haptic experience. + +341 +00:17:00,553 --> 00:17:02,989 +Core Haptics +is a data-driven API, + +342 +00:17:02,989 --> 00:17:06,626 +which allows for haptic patterns +to be defined programmatically, + +343 +00:17:06,626 --> 00:17:11,330 +directly in your scripts, +or by leveraging AHAP files. + +344 +00:17:11,330 --> 00:17:13,833 +One easy way +to add Core Haptics support + +345 +00:17:13,833 --> 00:17:16,636 +to your projects is to create +a Haptics component + +346 +00:17:16,636 --> 00:17:20,706 +that manages each of the +necessary Core Haptics objects. + +347 +00:17:20,706 --> 00:17:22,909 +Here is an example +Haptics component + +348 +00:17:22,909 --> 00:17:25,011 +that contains a CHHapticEngine, + +349 +00:17:25,011 --> 00:17:29,282 +a CHHapticPatternPlayer, +and an AHAP Asset. + +350 +00:17:29,282 --> 00:17:31,851 +The AHAP asset +is a custom Unity asset + +351 +00:17:31,851 --> 00:17:34,654 +defined by +the Core Haptics plug-in. + +352 +00:17:34,654 --> 00:17:38,024 +This allows for easy import +and export to AHAP files, + +353 +00:17:38,024 --> 00:17:40,059 +as well as +a custom editor extension + +354 +00:17:40,059 --> 00:17:43,229 +to manage pattern creation +and customization. + +355 +00:17:43,229 --> 00:17:46,365 +Let's take a closer look. + +356 +00:17:46,365 --> 00:17:48,801 +I'll begin by ensuring +that both Apple.Core + +357 +00:17:48,801 --> 00:17:52,605 +and the Core Haptics plug-ins +are installed in my project. + +358 +00:17:52,605 --> 00:17:53,739 +With those added, + +359 +00:17:53,739 --> 00:17:59,345 +I can start enhancing my game +with haptics. + +360 +00:17:59,345 --> 00:18:01,547 +Here's the haptics component +that I've created + +361 +00:18:01,547 --> 00:18:03,783 +based upon the previous diagram. + +362 +00:18:03,783 --> 00:18:06,152 +We'll check out the +implementation in just a moment, + +363 +00:18:06,152 --> 00:18:09,655 +but for now, +I'll attach it to my airplane. + +364 +00:18:12,358 --> 00:18:13,559 +Once attached, + +365 +00:18:13,559 --> 00:18:16,762 +I now see that the component +requires an AHAP asset, + +366 +00:18:16,762 --> 00:18:20,166 +but my AHAP Assets folder +is empty. + +367 +00:18:20,166 --> 00:18:21,701 +I'll create a new one by going + +368 +00:18:21,701 --> 00:18:29,709 +to Assets Create Apple +CoreHaptics AHAP. + +369 +00:18:29,709 --> 00:18:31,677 +Once created, +I'll give it a fantastic + +370 +00:18:31,677 --> 00:18:39,218 +and original name: +MyHapticPattern. + +371 +00:18:39,218 --> 00:18:41,954 +The Core Haptics plug-in +comes with editor extensions + +372 +00:18:41,954 --> 00:18:46,726 +that let me tune my new pattern +right in the inspector window. + +373 +00:18:46,726 --> 00:18:49,095 +This is where I define +the CHHapticEvents + +374 +00:18:49,095 --> 00:18:50,930 +that are part of +the CHHapticPattern + +375 +00:18:50,930 --> 00:18:53,099 +that can be played. + +376 +00:18:53,099 --> 00:18:55,701 +By default, +there's a transient event, + +377 +00:18:55,701 --> 00:19:01,774 +but I can easily add +a continuous event as well. + +378 +00:19:01,774 --> 00:19:06,779 +There are also Import, Export, +and Reset buttons in the UI. + +379 +00:19:06,779 --> 00:19:08,881 +Reset clears any events +that I've added + +380 +00:19:08,881 --> 00:19:13,452 +and returns the pattern +to its default state. + +381 +00:19:13,452 --> 00:19:16,088 +Import and Export +are great features. + +382 +00:19:16,088 --> 00:19:23,095 +These allow your project to load +and save AHAP files. + +383 +00:19:23,095 --> 00:19:25,998 +Here I've imported a predefined +AHAP called Rumble, + +384 +00:19:25,998 --> 00:19:28,567 +which triggers +a nice vibration effect, + +385 +00:19:28,567 --> 00:19:34,874 +but I think it needs +to be tweaked just a little. + +386 +00:19:34,874 --> 00:19:36,475 +Now that I've updated +the pattern, + +387 +00:19:36,475 --> 00:19:38,644 +I can export it +to a new AHAP file + +388 +00:19:38,644 --> 00:19:40,813 +in order to share +this improved haptic pattern + +389 +00:19:40,813 --> 00:19:45,151 +with the rest of my team. + +390 +00:19:51,157 --> 00:19:53,225 +Now that my asset +is created and tuned, + +391 +00:19:53,225 --> 00:19:59,231 +I'll go back to my airplane +and point it to MyHapticPattern. + +392 +00:19:59,231 --> 00:20:03,335 +Great! Everything is wired up. + +393 +00:20:03,335 --> 00:20:06,205 +With the haptic pattern defined +and properly referenced, + +394 +00:20:06,205 --> 00:20:08,240 +all that remains +is to add some logic + +395 +00:20:08,240 --> 00:20:11,811 +to the Haptics component +so it can play a haptic pattern. + +396 +00:20:11,811 --> 00:20:14,413 +This can be divided +into two methods: + +397 +00:20:14,413 --> 00:20:17,616 +PrepareHaptics and Play. + +398 +00:20:17,616 --> 00:20:21,253 +PrepareHaptics is where the +haptic engine is initialized, + +399 +00:20:21,253 --> 00:20:24,390 +and the haptic pattern +player is created. + +400 +00:20:24,390 --> 00:20:25,925 +Play will simply call + +401 +00:20:25,925 --> 00:20:30,830 +the CHHapticPatternPlayer's +start method to begin playback. + +402 +00:20:30,830 --> 00:20:34,100 +And here's +the Haptics component script. + +403 +00:20:34,100 --> 00:20:36,869 +Fields are defined +for a haptic engine + +404 +00:20:36,869 --> 00:20:39,105 +and a haptic player. + +405 +00:20:39,105 --> 00:20:41,941 +Importantly, +add a serializeField attribute + +406 +00:20:41,941 --> 00:20:46,145 +to allow the AHAP asset +to be set in the editor UI. + +407 +00:20:46,145 --> 00:20:50,883 +Next, add the code to create +a CHHapticEngine, start it, + +408 +00:20:50,883 --> 00:20:52,852 +and create +a haptic pattern player + +409 +00:20:52,852 --> 00:20:56,922 +by accessing the AHAP directly +from the referenced asset. + +410 +00:20:56,922 --> 00:20:59,225 +Of course, +calling Start on the player + +411 +00:20:59,225 --> 00:21:02,294 +will play the haptic pattern. + +412 +00:21:02,294 --> 00:21:05,097 +The Core Haptics Unity plug-in +gives you the tools you need + +413 +00:21:05,097 --> 00:21:08,934 +to add an entirely new level +of immersion into your games. + +414 +00:21:08,934 --> 00:21:11,670 +Use the Core Haptics plug-in +to create magical game moments + +415 +00:21:11,670 --> 00:21:15,741 +that look, sound, and feel real. + +416 +00:21:15,741 --> 00:21:17,643 +For a deep dive +into Core Haptics, + +417 +00:21:17,643 --> 00:21:21,547 +check out the session +"Introducing Core Haptics." + +418 +00:21:21,547 --> 00:21:25,151 +For details on designing +engaging haptics experiences + +419 +00:21:25,151 --> 00:21:28,621 +be sure to watch "Designing +Audio-Haptic Experiences" + +420 +00:21:28,621 --> 00:21:32,057 +and "Practice audio +haptic design." + +421 +00:21:32,057 --> 00:21:34,994 +Immersive audio is an incredibly +important aspect + +422 +00:21:34,994 --> 00:21:37,196 +of great game experiences. + +423 +00:21:37,196 --> 00:21:41,167 +Use the PHASE Unity plug-in to +unlock your creative potential + +424 +00:21:41,167 --> 00:21:45,571 +and build lush soundscapes +into your game worlds. + +425 +00:21:45,571 --> 00:21:47,907 +With PHASE, +you can provide complex + +426 +00:21:47,907 --> 00:21:51,277 +and dynamic audio experiences +to your games. + +427 +00:21:51,277 --> 00:21:54,480 +Geometry-aware audio means +that sounds emanate from + +428 +00:21:54,480 --> 00:21:57,249 +and interact with +meshes in the scene. + +429 +00:21:57,249 --> 00:21:59,685 +Environments in your game +will sound more realistic + +430 +00:21:59,685 --> 00:22:02,788 +through reverberation +and reflection. + +431 +00:22:02,788 --> 00:22:05,291 +You can build hierarchical +audio graphs + +432 +00:22:05,291 --> 00:22:09,762 +that allow for dynamic audio +control during gameplay + +433 +00:22:09,762 --> 00:22:13,132 +The PHASE plug-in includes +a set of predefined components + +434 +00:22:13,132 --> 00:22:14,934 +that are game-ready. + +435 +00:22:14,934 --> 00:22:17,102 +Simply attach them +to your game objects + +436 +00:22:17,102 --> 00:22:18,370 +and you can start using PHASE + +437 +00:22:18,370 --> 00:22:21,207 +without writing +a single line of code. + +438 +00:22:21,207 --> 00:22:24,677 +The first component is the +PHASEListener component. + +439 +00:22:24,677 --> 00:22:27,246 +It acts as the "ears" +of your game scene + +440 +00:22:27,246 --> 00:22:31,483 +and processes audio based +upon its position, orientation, + +441 +00:22:31,483 --> 00:22:33,786 +and reverb preset. + +442 +00:22:33,786 --> 00:22:36,789 +Next is the +PHASEOccluder component. + +443 +00:22:36,789 --> 00:22:38,958 +PHASEOccluders +attach to game objects + +444 +00:22:38,958 --> 00:22:41,594 +with geometry data +and dampen audio + +445 +00:22:41,594 --> 00:22:46,098 +when they come between sources +and the listener in the scene. + +446 +00:22:46,098 --> 00:22:48,834 +Next is the +PHASESource component. + +447 +00:22:48,834 --> 00:22:50,369 +These are attached +to game objects + +448 +00:22:50,369 --> 00:22:53,005 +and use the object's transform +to position sounds + +449 +00:22:53,005 --> 00:22:55,040 +in your game world. + +450 +00:22:55,040 --> 00:22:57,376 +In addition to +the built-in components, + +451 +00:22:57,376 --> 00:23:00,579 +the PHASE plug-in also defines +a custom asset, + +452 +00:23:00,579 --> 00:23:02,748 +the SoundEvent asset. + +453 +00:23:02,748 --> 00:23:06,151 +Sound events are objects which +describe audio playback events + +454 +00:23:06,151 --> 00:23:09,955 +and define the audio played +by sources in the scene. + +455 +00:23:09,955 --> 00:23:11,824 +To start using +the PHASE plug-in, + +456 +00:23:11,824 --> 00:23:14,260 +the first step is to make sure +that both the Apple.Core + +457 +00:23:14,260 --> 00:23:17,896 +and PHASE plug-ins +are added to the project. + +458 +00:23:17,896 --> 00:23:21,133 +Once installed, I can start +adding the included components + +459 +00:23:21,133 --> 00:23:22,635 +to the scene. + +460 +00:23:22,635 --> 00:23:23,936 +In this example project, + +461 +00:23:23,936 --> 00:23:26,739 +I have three game objects +of interest: + +462 +00:23:26,739 --> 00:23:31,744 +an airplane, a building, +and then the camera. + +463 +00:23:31,744 --> 00:23:34,146 +First, I'll attach +the PHASEListener component + +464 +00:23:34,146 --> 00:23:36,282 +to the camera. + +465 +00:23:36,282 --> 00:23:40,085 +By doing that, I've added +the "ears" to the scene. + +466 +00:23:40,085 --> 00:23:42,121 +Next, I'll make +the building an occluder + +467 +00:23:42,121 --> 00:23:45,824 +by attaching +the PHASEOccluder component. + +468 +00:23:48,827 --> 00:23:51,630 +Finally, I'll add a source +to the scene by adding + +469 +00:23:51,630 --> 00:23:55,834 +the PHASESource component +to the airplane. + +470 +00:23:55,834 --> 00:23:59,104 +Now that I've added a source +it needs some audio to play, + +471 +00:23:59,104 --> 00:24:01,440 +so I need to attach +a sound event, + +472 +00:24:01,440 --> 00:24:04,743 +but the Sound Events folder +is empty. + +473 +00:24:04,743 --> 00:24:06,545 +I can create one by going to + +474 +00:24:06,545 --> 00:24:14,286 +Assets Create Apple +PHASE SoundEvent. + +475 +00:24:14,286 --> 00:24:15,921 +After creating a sound event, + +476 +00:24:15,921 --> 00:24:17,790 +the PHASE plug-in +will immediately open + +477 +00:24:17,790 --> 00:24:20,859 +the PHASE sound event +composer window. + +478 +00:24:20,859 --> 00:24:24,596 +This is the canvas +used to build sound events. + +479 +00:24:24,596 --> 00:24:27,533 +I start by right-clicking +anywhere in the window. + +480 +00:24:27,533 --> 00:24:31,370 +This shows a pop-up that allows +me to add a node to the event. + +481 +00:24:31,370 --> 00:24:36,108 +Because I want to play back +a clip I’ll create a sampler node. + +482 +00:24:36,108 --> 00:24:39,144 +I've already added an audio clip +of an idling airplane + +483 +00:24:39,144 --> 00:24:45,617 +to the project, +so I can reference that here. + +484 +00:24:45,617 --> 00:24:47,086 +I'll keep looping enabled + +485 +00:24:47,086 --> 00:24:50,155 +so that the airplane +keeps humming along. + +486 +00:24:50,155 --> 00:24:53,792 +To hear the airplane, +I need to route it to a mixer. + +487 +00:24:53,792 --> 00:24:55,060 +I can create a mixer + +488 +00:24:55,060 --> 00:24:58,964 +by dragging the output line onto +the event composer's canvas, + +489 +00:24:58,964 --> 00:25:02,468 +where it will show me +the option to create a mixer. + +490 +00:25:05,471 --> 00:25:10,943 +My sound event is now +complete and ready to use. + +491 +00:25:10,943 --> 00:25:13,212 +By clicking on the sound event, + +492 +00:25:13,212 --> 00:25:16,582 +I can see its settings +directly in the inspector. + +493 +00:25:16,582 --> 00:25:19,785 +This allows me to adjust values +without having to go back + +494 +00:25:19,785 --> 00:25:22,287 +into the sound event composer. + +495 +00:25:22,287 --> 00:25:23,789 +With the sound event created, + +496 +00:25:23,789 --> 00:25:26,291 +I can now reference it +in the PHASESource component + +497 +00:25:26,291 --> 00:25:30,963 +I attached +to the airplane earlier. + +498 +00:25:30,963 --> 00:25:33,365 +And with that, +audio in the scene is routed + +499 +00:25:33,365 --> 00:25:35,968 +and configured for playback. + +500 +00:25:35,968 --> 00:25:39,104 +The PHASE Unity plug-in +opens totally new possibilities + +501 +00:25:39,104 --> 00:25:41,440 +for in-game audio design. + +502 +00:25:41,440 --> 00:25:43,742 +To learn more about PHASE +and to dive deeper + +503 +00:25:43,742 --> 00:25:45,944 +into the concepts +I've introduced today, + +504 +00:25:45,944 --> 00:25:49,281 +be sure to check out the Apple +Developer documentation site + +505 +00:25:49,281 --> 00:25:54,887 +and last year's introductory +WWDC session video. + +506 +00:25:54,887 --> 00:25:59,558 +And that concludes our overview +of the new Apple Unity plug-ins. + +507 +00:25:59,558 --> 00:26:02,161 +I've covered a lot today, +but if you would like + +508 +00:26:02,161 --> 00:26:05,397 +to know more about any +of the Apple Unity plug-ins, + +509 +00:26:05,397 --> 00:26:09,635 +the repository on GitHub +is the best place to start. + +510 +00:26:09,635 --> 00:26:12,237 +That's where you'll find +the source, + +511 +00:26:12,237 --> 00:26:17,309 +detailed documentation, +and samples for each of the plug-ins. + +512 +00:26:17,309 --> 00:26:19,978 +Find out more +about integrating accessibility + +513 +00:26:19,978 --> 00:26:21,847 +into your Unity games with the + +514 +00:26:21,847 --> 00:26:24,950 +"Add accessibility +to Unity games" session + +515 +00:26:24,950 --> 00:26:26,351 +and be sure to check out + +516 +00:26:26,351 --> 00:26:29,254 +"Reach new players +with Game Center dashboard" + +517 +00:26:29,254 --> 00:26:32,324 +to learn how to boost +your game's visibility. + +518 +00:26:32,324 --> 00:26:34,026 +Thank you for watching. + +519 +00:26:34,026 --> 00:26:38,597 +♪ + diff --git a/eng/2022 Session 10066 Discover Metal 3 en.srt b/eng/2022 Session 10066 Discover Metal 3 en.srt new file mode 100644 index 0000000..cd60d58 --- /dev/null +++ b/eng/2022 Session 10066 Discover Metal 3 en.srt @@ -0,0 +1,1211 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,643 --> 00:00:11,812 +Tarun Belagodu: +Hello and welcome to Metal 3. + +3 +00:00:11,845 --> 00:00:13,514 +My name is Tarun Belagodu + +4 +00:00:13,547 --> 00:00:17,050 +and I'll be sharing the latest +in Metal's evolution. + +5 +00:00:17,084 --> 00:00:19,520 +First, let's start with the basics. + +6 +00:00:19,553 --> 00:00:23,991 +Metal is Apple's low-overhead +graphics and compute API. + +7 +00:00:24,024 --> 00:00:27,160 +It's designed to be the fastest +and most efficient way + +8 +00:00:27,194 --> 00:00:31,565 +to drive the incredibly powerful GPUs +behind Apple products. + +9 +00:00:31,598 --> 00:00:34,101 +It offers multi-threaded +and direct control + +10 +00:00:34,134 --> 00:00:36,270 +over the commands sent to the GPU, + +11 +00:00:36,303 --> 00:00:40,874 +a rich shading language that supports +explicit shader compilation, + +12 +00:00:40,908 --> 00:00:42,876 +and deeply integrated tools + +13 +00:00:42,910 --> 00:00:47,214 +to help debug and profile +complex applications and games. + +14 +00:00:48,916 --> 00:00:51,318 +Since its introduction, +Metal has added + +15 +00:00:51,351 --> 00:00:53,921 +many advanced graphics +and compute features, + +16 +00:00:53,954 --> 00:00:56,156 +with a focus on GPU-driven rendering, + +17 +00:00:56,190 --> 00:00:58,926 +machine learning, and ray tracing. + +18 +00:00:58,959 --> 00:01:02,329 +Apple silicon paves the way +for incredible graphics performance + +19 +00:01:02,362 --> 00:01:05,132 +and efficiency on every new Mac. + +20 +00:01:05,165 --> 00:01:08,035 +And Metal unlocks these capabilities. + +21 +00:01:08,068 --> 00:01:13,273 +This year, Metal is making a leap +to the next level with Metal 3. + +22 +00:01:15,342 --> 00:01:19,847 +Metal 3 is a powerful set of new features +that enable even higher performance + +23 +00:01:19,880 --> 00:01:23,784 +and rendering quality to help your apps +and games run faster + +24 +00:01:23,817 --> 00:01:25,586 +and look amazing. + +25 +00:01:25,619 --> 00:01:27,921 +Let's start with fast resource loading. + +26 +00:01:28,655 --> 00:01:32,359 +Modern games and apps have demanding +asset loading requirements, + +27 +00:01:32,392 --> 00:01:37,297 +and streaming many small asset requests +quickly from files to your Metal resources + +28 +00:01:37,331 --> 00:01:40,834 +is often the key to high quality visuals. + +29 +00:01:40,868 --> 00:01:45,272 +But existing storage APIs +are designed for large, bulk requests. + +30 +00:01:47,074 --> 00:01:51,478 +Metal 3's fast resource loading +lets you request many small loads + +31 +00:01:51,512 --> 00:01:56,917 +using the same explicit, multi-threaded +command model as graphics and compute. + +32 +00:01:56,950 --> 00:02:00,287 +Each request is a command, +and many commands can be queued + +33 +00:02:00,320 --> 00:02:03,056 +for asynchronous submission. + +34 +00:02:03,090 --> 00:02:07,895 +It loads directly into your Metal buffers +and textures without additional steps, + +35 +00:02:07,928 --> 00:02:11,565 +saving you both development effort +and transfer time. + +36 +00:02:11,598 --> 00:02:14,735 +Fast resource loading also makes it easy +to coordinate + +37 +00:02:14,768 --> 00:02:18,005 +between GPU operations +and loading operations, + +38 +00:02:18,038 --> 00:02:22,276 +using the Metal synchronization primitives +that you already know. + +39 +00:02:22,309 --> 00:02:26,380 +Texture streaming systems +really benefit from fast resource loading. + +40 +00:02:26,413 --> 00:02:27,915 +Let's look at an example. + +41 +00:02:29,082 --> 00:02:34,488 +Metal Sparse Textures allow applications +to stream textures at a tile granularity. + +42 +00:02:34,521 --> 00:02:37,624 +The texture streaming system +built on Metal sparse textures + +43 +00:02:37,658 --> 00:02:39,526 +consists of four steps: + +44 +00:02:39,560 --> 00:02:44,464 +First, decide what to load +based on feedback from the previous frame. + +45 +00:02:44,498 --> 00:02:48,202 +Second, load tiles from file storage. + +46 +00:02:48,235 --> 00:02:52,840 +Third, copy from your staging area +to your sparse textures. + +47 +00:02:52,873 --> 00:02:55,275 +And finally, draw your frame. + +48 +00:02:56,977 --> 00:03:00,113 +The longer it takes to load and copy +means the more time + +49 +00:03:00,147 --> 00:03:02,649 +your app draws with lower quality. + +50 +00:03:04,451 --> 00:03:07,321 +Fast resource loading +minimizes loading overhead + +51 +00:03:07,354 --> 00:03:10,991 +and ensures the storage hardware has +enough requests in its queues + +52 +00:03:11,024 --> 00:03:12,960 +to maximize throughput. + +53 +00:03:12,993 --> 00:03:16,263 +This provides faster +and more consistent performance + +54 +00:03:16,296 --> 00:03:19,833 +so that more time is spent +drawing at high quality. + +55 +00:03:22,269 --> 00:03:25,672 +Fast resource loading will greatly +simplify the code you need to write + +56 +00:03:25,706 --> 00:03:28,876 +to achieve high quality asset streaming. + +57 +00:03:28,909 --> 00:03:31,512 +To learn more about fast resource loading, + +58 +00:03:31,545 --> 00:03:35,382 +check out the "Load resources faster +with Metal 3" session. + +59 +00:03:36,350 --> 00:03:40,454 +Next, let me tell you how +the new offline compilation workflow + +60 +00:03:40,487 --> 00:03:44,691 +will help you reduce load times +and stutters in your apps. + +61 +00:03:44,725 --> 00:03:47,661 +Shader binaries +are GPU-specific machine code + +62 +00:03:47,694 --> 00:03:50,597 +that are traditionally generated +while the app is running + +63 +00:03:50,631 --> 00:03:53,734 +as part of the Metal pipeline +creation process. + +64 +00:03:53,767 --> 00:03:56,603 +Generating these binaries +is an expensive operation + +65 +00:03:56,637 --> 00:04:00,974 +that is usually hidden behind +a loading screen during app launch. + +66 +00:04:01,008 --> 00:04:03,644 +However, sometimes +they need to happen in-frame, + +67 +00:04:03,677 --> 00:04:06,847 +which in turn causes frame rate stutters. + +68 +00:04:06,880 --> 00:04:09,116 +These binaries are cached by Metal + +69 +00:04:09,149 --> 00:04:11,552 +so that you don't pay the cost often, + +70 +00:04:11,585 --> 00:04:14,922 +but their cost is still observed +on the app's first launch + +71 +00:04:14,955 --> 00:04:18,091 +or whenever the binary is first needed. + +72 +00:04:18,125 --> 00:04:21,929 +With offline compilation, you can +eliminate shader binary generation + +73 +00:04:21,962 --> 00:04:23,497 +at run time. + +74 +00:04:25,299 --> 00:04:28,268 +By moving binary generation +to project build time, + +75 +00:04:28,302 --> 00:04:33,006 +you can dramatically reduce the time spent +creating Metal pipelines at load time, + +76 +00:04:33,040 --> 00:04:37,678 +and reduce stutters in your app when +those pipelines are created just-in-time. + +77 +00:04:37,711 --> 00:04:41,048 +Let's take a closer look at +what it means to reduce stutters. + +78 +00:04:42,049 --> 00:04:44,384 +Here's an example of a game +that needs to create + +79 +00:04:44,418 --> 00:04:47,921 +a Metal pipeline state object +during encoding. + +80 +00:04:47,955 --> 00:04:51,225 +Since this is a pipeline +that Metal hasn't seen before, + +81 +00:04:51,258 --> 00:04:54,528 +it generates the needed shader binary. + +82 +00:04:54,561 --> 00:04:58,265 +This is a long operation that interrupts +encoding the rest of the frame, + +83 +00:04:58,298 --> 00:05:01,368 +and causes the app +to miss its frame rate target. + +84 +00:05:01,401 --> 00:05:03,437 +This only happens once, + +85 +00:05:03,470 --> 00:05:07,107 +but it's enough for your users +to notice a frame stutter. + +86 +00:05:07,140 --> 00:05:11,545 +In contrast, offline compilation +means the shader binary can be generated + +87 +00:05:11,578 --> 00:05:17,985 +at build-time so that every pipeline state +creation is fast, and execution is smooth. + +88 +00:05:18,018 --> 00:05:22,656 +Offline compilation can have a dramatic +effect on your app loading times too. + +89 +00:05:22,689 --> 00:05:24,424 +Let's look at an example. + +90 +00:05:25,726 --> 00:05:28,829 +Most apps create the majority +of Metal pipeline state objects + +91 +00:05:28,862 --> 00:05:31,098 +in a dedicated loading phase. + +92 +00:05:31,131 --> 00:05:34,101 +And shader binaries +are generated on first load. + +93 +00:05:34,134 --> 00:05:39,273 +This can be a long wait for your users +if your app creates many such pipelines. + +94 +00:05:39,306 --> 00:05:42,309 +With offline compilation, +shader binary generation can again + +95 +00:05:42,342 --> 00:05:46,213 +be moved to project build time, +resulting in smaller load times + +96 +00:05:46,246 --> 00:05:49,550 +and getting users +into your app more quickly. + +97 +00:05:50,184 --> 00:05:55,255 +Offline compilation is a game changer +for apps with many complex pipelines. + +98 +00:05:55,289 --> 00:05:58,625 +To learn more about offline compilation +and other improvements, + +99 +00:05:58,659 --> 00:06:02,629 +check out the "Target and optimize +GPU binaries with Metal 3" session. + +100 +00:06:04,164 --> 00:06:06,567 +Now, let's move on to MetalFX, + +101 +00:06:06,600 --> 00:06:09,336 +which provides platform-optimized +graphics effects + +102 +00:06:09,369 --> 00:06:11,738 +for Metal applications. + +103 +00:06:11,772 --> 00:06:16,343 +MetalFX Upscaling helps render +high-quality graphics in less time + +104 +00:06:16,376 --> 00:06:20,480 +through high-performance upscaling +and anti-aliasing. + +105 +00:06:20,514 --> 00:06:24,618 +You can choose a combination +of temporal or spatial algorithms + +106 +00:06:24,651 --> 00:06:26,987 +to help boost performance. + +107 +00:06:27,020 --> 00:06:29,223 +Here's why it matters. + +108 +00:06:29,256 --> 00:06:32,025 +While Retina resolution +provides crisp detail + +109 +00:06:32,059 --> 00:06:35,195 +that you want your apps +and games to take advantage of, + +110 +00:06:35,229 --> 00:06:39,132 +generating all those pixels +can also affect performance. + +111 +00:06:39,166 --> 00:06:40,901 +With MetalFX Upscaling, + +112 +00:06:40,934 --> 00:06:43,303 +you can generate pixels +at a lower resolution + +113 +00:06:43,337 --> 00:06:45,606 +and then let the framework +generate a high-quality, + +114 +00:06:45,639 --> 00:06:50,344 +high-resolution image at a lower cost +for a much higher frame rate. + +115 +00:06:50,377 --> 00:06:54,047 +MetalFX is a powerful framework +that makes high-performance, + +116 +00:06:54,081 --> 00:06:56,450 +high-quality upscaling a reality. + +117 +00:06:56,483 --> 00:06:58,952 +To learn more +about MetalFX Upscaling, + +118 +00:06:58,986 --> 00:07:03,390 +check out the "Boost performance +with MetalFX Upscaling" session. + +119 +00:07:03,423 --> 00:07:06,693 +Next up is Metal's new flexible +geometry pipeline: + +120 +00:07:06,727 --> 00:07:08,996 +Mesh Shaders. + +121 +00:07:09,029 --> 00:07:12,533 +The traditional programmable graphics +pipeline lets you transform vertices + +122 +00:07:12,566 --> 00:07:15,769 +in a shader, +that are then assembled into primitives + +123 +00:07:15,802 --> 00:07:18,772 +for rasterization +by fixed-function hardware. + +124 +00:07:18,805 --> 00:07:20,974 +That's enough for most applications, + +125 +00:07:21,008 --> 00:07:25,746 +but some use cases like culling +require access to the entire primitive. + +126 +00:07:25,779 --> 00:07:27,948 +Each vertex is also read, + +127 +00:07:27,981 --> 00:07:30,884 +transformed, and output independently. + +128 +00:07:30,918 --> 00:07:35,589 +So you can't add vertices or primitives +in the middle of your draw. + +129 +00:07:35,622 --> 00:07:39,359 +Advanced geometry processing +requires more flexibility. + +130 +00:07:39,393 --> 00:07:42,196 +And traditionally +that meant pre-processing your geometry + +131 +00:07:42,229 --> 00:07:43,931 +in a compute pass. + +132 +00:07:43,964 --> 00:07:47,034 +But that requires storing a variable +amount of intermediate geometry + +133 +00:07:47,067 --> 00:07:51,104 +to device memory, +which might be hard for you to budget for. + +134 +00:07:51,138 --> 00:07:55,108 +Metal mesh shaders introduce an +alternative geometry processing pipeline, + +135 +00:07:56,276 --> 00:08:00,948 +It replaces the traditional vertex stage +with a flexible 2-stage model + +136 +00:08:00,981 --> 00:08:05,152 +and enables hierarchical processing +of your geometry. + +137 +00:08:05,185 --> 00:08:09,623 +The first stage analyzes whole objects +to decide whether to expand, + +138 +00:08:09,656 --> 00:08:13,560 +contract, or refine geometry +in the second stage. + +139 +00:08:13,594 --> 00:08:17,798 +It achieves this by providing +compute capabilities in the render pass, + +140 +00:08:17,831 --> 00:08:21,902 +without the need +for intermediate device memory storage. + +141 +00:08:21,935 --> 00:08:26,673 +Mesh shaders are a great fit for apps +that perform GPU-driven culling, + +142 +00:08:26,707 --> 00:08:30,577 +LOD selection, +and procedural geometry generation. + +143 +00:08:30,611 --> 00:08:32,880 +Let's take a closer look. + +144 +00:08:32,913 --> 00:08:36,316 +In this example, +a compute pass evaluates the surface + +145 +00:08:36,350 --> 00:08:38,685 +and then generates its geometry. + +146 +00:08:38,719 --> 00:08:41,955 +That geometry and its draw commands +are then written to device memory + +147 +00:08:41,989 --> 00:08:44,758 +for consumption by a later render pass. + +148 +00:08:44,791 --> 00:08:48,061 +With high expansion factors +and indirect draw calls, + +149 +00:08:48,095 --> 00:08:51,064 +it can be hard to predict +the amount of memory needed. + +150 +00:08:53,367 --> 00:08:56,904 +Mesh shaders improve efficiency +by running two compute-like stages + +151 +00:08:56,937 --> 00:08:59,306 +inline in the render pipeline. + +152 +00:09:00,641 --> 00:09:04,011 +The Object stage evaluates the input +to decide how many meshes + +153 +00:09:04,044 --> 00:09:05,979 +need to be generated. + +154 +00:09:07,314 --> 00:09:10,751 +And the Mesh stage then +generates the actual geometry. + +155 +00:09:10,784 --> 00:09:13,487 +These meshes are sent directly +to the rasterizer, + +156 +00:09:13,520 --> 00:09:16,123 +bypassing the roundtrip to device memory, + +157 +00:09:16,156 --> 00:09:18,825 +and the need for vertex processing. + +158 +00:09:20,127 --> 00:09:23,564 +Mesh shaders will let you build +efficient procedural geometry, + +159 +00:09:23,597 --> 00:09:26,633 +culling, and LODing systems for your apps. + +160 +00:09:26,667 --> 00:09:28,402 +To learn more about mesh shaders, + +161 +00:09:28,435 --> 00:09:32,506 +check out the "Transform your geometry +with Metal mesh shaders" session. + +162 +00:09:34,107 --> 00:09:39,012 +Metal 3 also brings significant speedup +to the ray tracing pipeline. + +163 +00:09:39,046 --> 00:09:41,648 +Everything from +acceleration structure builds, + +164 +00:09:41,682 --> 00:09:45,385 +intersection +and shading have been optimized. + +165 +00:09:45,419 --> 00:09:49,089 +Metal also adds support for +GPU-driven ray tracing pipelines + +166 +00:09:49,122 --> 00:09:52,259 +to further optimize your app. + +167 +00:09:52,292 --> 00:09:55,896 +Let's compare Metal 3's ray tracing +to what was previously available. + +168 +00:09:56,964 --> 00:10:01,902 +Metal 3 ray tracing saves +a significant amount of CPU and GPU time. + +169 +00:10:01,935 --> 00:10:05,138 +First, acceleration structures +build in less time, + +170 +00:10:05,172 --> 00:10:08,375 +giving you more GPU time +to draw and trace rays. + +171 +00:10:08,408 --> 00:10:12,246 +Second, CPU operations such as culling +can move to the GPU + +172 +00:10:12,279 --> 00:10:16,283 +thanks to the new Indirect Command Buffer +support for Ray Tracing. + +173 +00:10:16,316 --> 00:10:20,187 +Finally, Metal 3 ray tracing +supports direct access to primitive data + +174 +00:10:20,220 --> 00:10:24,458 +to streamline and optimize +intersection and shading. + +175 +00:10:24,491 --> 00:10:29,630 +Metal 3 ray tracing continues to become +better and more powerful than before. + +176 +00:10:29,663 --> 00:10:31,431 +To learn more about ray tracing, + +177 +00:10:31,465 --> 00:10:36,203 +head over to the "Maximize your Metal +ray tracing performance" session. + +178 +00:10:36,236 --> 00:10:42,009 +Now, I'll show you how Metal 3 accelerates +machine learning inference and training. + +179 +00:10:42,042 --> 00:10:45,279 +Metal 3 has major improvements +to accelerate machine learning, + +180 +00:10:45,312 --> 00:10:49,283 +with additional support for +accelerating network training on the Mac, + +181 +00:10:49,316 --> 00:10:53,053 +and significant optimizations +for ML inference optimizations + +182 +00:10:53,086 --> 00:10:56,857 +in graphics +and media processing applications. + +183 +00:10:56,890 --> 00:10:59,693 +TensorFlow is a popular framework +for machine learning + +184 +00:10:59,726 --> 00:11:02,563 +that is GPU-accelerated on the Mac. + +185 +00:11:02,596 --> 00:11:06,934 +The recently released Mac Studio +provides up to a 16 times speedup + +186 +00:11:06,967 --> 00:11:10,137 +on M1 Ultra versus training on the CPU, + +187 +00:11:10,170 --> 00:11:13,006 +across a variety of networks. + +188 +00:11:13,040 --> 00:11:17,711 +And Metal 3 also accelerates +many new TensorFlow operations. + +189 +00:11:17,744 --> 00:11:20,480 +That means less synchronization +with the CPU + +190 +00:11:20,514 --> 00:11:24,852 +for even more scalable performance. + +191 +00:11:24,885 --> 00:11:28,622 +PyTorch is another very popular +ML framework for network training + +192 +00:11:28,655 --> 00:11:32,292 +that recently gained GPU acceleration +using Metal. + +193 +00:11:32,326 --> 00:11:34,862 +And on Mac Studio with an M1 Ultra + +194 +00:11:34,895 --> 00:11:39,933 +you can achieve significant training +speedups compared to the CPU. + +195 +00:11:39,967 --> 00:11:44,872 +For example, you can train the BERT model +up to 6.5 times faster + +196 +00:11:44,905 --> 00:11:49,943 +and train ResNet50 up to 8.5 times faster. + +197 +00:11:49,977 --> 00:11:53,413 +Metal optimizes ML inference across +Apple silicon + +198 +00:11:53,447 --> 00:11:55,682 +to maximize performance. + +199 +00:11:55,716 --> 00:11:59,419 +This is especially useful for +Metal-based high performance video + +200 +00:11:59,453 --> 00:12:01,555 +and image processing applications, + +201 +00:12:01,588 --> 00:12:04,992 +like BlackMagic Design's DaVinci Resolve. + +202 +00:12:05,025 --> 00:12:08,762 +DaVinci Resolve is a color-grading-focussed +video production platform + +203 +00:12:08,795 --> 00:12:13,867 +that uses Metal and machine learning +extensively in their workflows. + +204 +00:12:13,901 --> 00:12:16,103 +And the results are incredible. + +205 +00:12:16,136 --> 00:12:19,006 +With Metal's support +for accelerated machine learning, + +206 +00:12:19,039 --> 00:12:22,476 +BlackMagic Design achieved dramatic +performance improvements + +207 +00:12:22,509 --> 00:12:25,045 +to their editing and color grading +workflows + +208 +00:12:25,078 --> 00:12:28,448 +and their ML-based tools. + +209 +00:12:28,482 --> 00:12:30,918 +To learn more about updates +to machine learning, + +210 +00:12:30,951 --> 00:12:35,789 +head over to the "Accelerate +machine learning with Metal" session. + +211 +00:12:35,822 --> 00:12:39,193 +Now let me tell you what hardware +supports the Metal 3 features + +212 +00:12:39,226 --> 00:12:41,461 +that I just described. + +213 +00:12:41,495 --> 00:12:44,898 +Metal 3 is supported on all modern iOS, + +214 +00:12:44,932 --> 00:12:47,367 +iPadOS, and macOS devices, + +215 +00:12:47,401 --> 00:12:53,707 +including iPhone and iPad with A13 Bionic +or M1 chips or newer, + +216 +00:12:53,740 --> 00:12:56,109 +and all Apple silicon Mac systems + +217 +00:12:56,143 --> 00:12:58,011 +and Mac systems with recent AMD +and Intel GPUs. + +218 +00:13:03,784 --> 00:13:07,154 +And to find out whether +a given device supports Metal 3, + +219 +00:13:07,187 --> 00:13:10,824 +use the supportsFamily query +on the Metal device. + +220 +00:13:12,526 --> 00:13:15,062 +Metal 3 is much more than features; + +221 +00:13:15,095 --> 00:13:18,999 +it also includes a comprehensive set +of advanced developer tools. + +222 +00:13:19,032 --> 00:13:20,901 +Let me show you a few now. + +223 +00:13:20,934 --> 00:13:24,471 +The Metal Dependency Viewer +in Xcode 14 makes it even easier + +224 +00:13:24,505 --> 00:13:26,707 +to visualize your entire renderer + +225 +00:13:26,740 --> 00:13:30,244 +or zoom into a single pass. + +226 +00:13:30,277 --> 00:13:33,580 +And to make it easier to adopt +GPU-driven pipelines + +227 +00:13:33,614 --> 00:13:36,850 +or synchronize with fast resource loading +for example, + +228 +00:13:36,884 --> 00:13:40,287 +the Dependency Viewer now includes +synchronization edges + +229 +00:13:40,320 --> 00:13:44,157 +to help you analyze +and validate your dependencies. + +230 +00:13:44,191 --> 00:13:47,294 +The improved Acceleration +Structure Viewer in Xcode 14 + +231 +00:13:47,327 --> 00:13:51,164 +helps you get the most out +of Metal 3's optimized ray tracing. + +232 +00:13:51,198 --> 00:13:55,235 +First, you can now highlight +individual primitives in the scene. + +233 +00:13:57,037 --> 00:14:00,574 +And selecting a primitive +shows its associated primitive data + +234 +00:14:00,607 --> 00:14:02,743 +in the outline on the left. + +235 +00:14:05,646 --> 00:14:08,916 +Last, if your scene has +motion information, + +236 +00:14:08,949 --> 00:14:13,654 +the Acceleration Structure Viewer can +now visualize different points in time. + +237 +00:14:18,225 --> 00:14:22,596 +And that's just a quick look at a few of +the Developer Tools updates in Xcode 14. + +238 +00:14:22,629 --> 00:14:26,500 +There are a host of other new features +such as Dylib support, + +239 +00:14:26,533 --> 00:14:28,402 +a new resource list, + +240 +00:14:28,435 --> 00:14:30,737 +file navigation in the Shader editor, + +241 +00:14:30,771 --> 00:14:34,474 +custom Buffer Viewer layouts +and many more. + +242 +00:14:34,508 --> 00:14:38,278 +To learn more about tools and +how to get the most out of advancements + +243 +00:14:38,312 --> 00:14:41,915 +in Metal 3, +be sure to check out these other sessions, + +244 +00:14:41,949 --> 00:14:46,486 +which will help you build +advanced graphics, games and pro apps. + +245 +00:14:48,288 --> 00:14:51,225 +Today, I introduced you +to Metal 3's advanced features + +246 +00:14:51,258 --> 00:14:54,027 +for improving performance and quality: + +247 +00:14:54,061 --> 00:14:58,031 +fast resource loading +for higher-quality texture streaming; + +248 +00:14:58,065 --> 00:15:02,336 +Offline compilation for shorter load times +and less stuttering; + +249 +00:15:02,369 --> 00:15:06,874 +MetalFX Upscaling to render +at high resolution in less time; + +250 +00:15:06,907 --> 00:15:10,878 +Mesh shaders +for advanced geometry processing; + +251 +00:15:10,911 --> 00:15:12,913 +faster acceleration structure builds, + +252 +00:15:12,946 --> 00:15:16,283 +intersections, +and shading for ray tracing; + +253 +00:15:16,316 --> 00:15:19,253 +and more accelerated machine learning. + +254 +00:15:19,286 --> 00:15:22,589 +Finally, I showed you some of +advanced tools that help you + +255 +00:15:22,623 --> 00:15:27,294 +use advanced features such as +GPU-driven pipelines and ray tracing. + +256 +00:15:28,629 --> 00:15:31,698 +To learn more with new code samples +and documentation, + +257 +00:15:31,732 --> 00:15:34,434 +head over to developer.apple.com/Metal. + +258 +00:15:34,968 --> 00:15:36,970 +Thank you for joining. + diff --git a/eng/2022 Session 10068 What's new in UIKit en.srt b/eng/2022 Session 10068 What's new in UIKit en.srt new file mode 100644 index 0000000..b17f1fa --- /dev/null +++ b/eng/2022 Session 10068 What's new in UIKit en.srt @@ -0,0 +1,1514 @@ +1 +00:00:00,501 --> 00:00:06,507 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:14,214 +- Welcome to +"What's New in UIKit" in iOS 16. + +3 +00:00:14,248 --> 00:00:18,785 +I'm Dima, and I'm an engineering +manager on the UIKit team. + +4 +00:00:19,586 --> 00:00:23,757 +UIKit is the powerful framework +at the core of your apps. + +5 +00:00:23,790 --> 00:00:28,529 +It has been updated to support +new features in iOS 16. + +6 +00:00:29,663 --> 00:00:35,636 +In this video, I will cover +UI improvements for productivity, + +7 +00:00:35,669 --> 00:00:37,838 +Control enhancements, + +8 +00:00:37,871 --> 00:00:40,374 +API refinements, + +9 +00:00:40,407 --> 00:00:43,644 +and I will talk about exciting new ways + +10 +00:00:43,677 --> 00:00:48,048 +to use UIKit and SwiftUI together. + +11 +00:00:50,684 --> 00:00:57,124 +We made it even easier in UIKit to develop +streamlined, discoverable user interfaces + +12 +00:00:57,157 --> 00:01:01,395 +with improved navigation bars +that feature a new title menu, + +13 +00:01:01,428 --> 00:01:05,265 +find and replace, +and reimagined editing interactions + +14 +00:01:05,299 --> 00:01:07,134 +for cut, copy, and paste. + +15 +00:01:07,167 --> 00:01:10,704 +You will be able to enhance +your document-based applications. + +16 +00:01:11,405 --> 00:01:15,809 +I'll start by taking a closer look +at navigation bars, + +17 +00:01:15,843 --> 00:01:20,113 +updated to support desktop class +toolbar capabilities. + +18 +00:01:22,416 --> 00:01:28,055 +In iOS 16, UIKit introduces +two new navigation styles + +19 +00:01:28,088 --> 00:01:31,291 +to better support the needs +of document based apps: + +20 +00:01:31,325 --> 00:01:34,494 +browser and editor. + +21 +00:01:35,729 --> 00:01:41,335 +The browser style is designed for apps +that use history or folder structure + +22 +00:01:41,368 --> 00:01:45,539 +for navigation, +like web and document browsers. + +23 +00:01:47,040 --> 00:01:53,280 +The editor is intended for interfaces +centered around editing documents. + +24 +00:01:55,148 --> 00:02:00,721 +in iOS 16, you can add a variety +of bar button items to your app, + +25 +00:02:00,754 --> 00:02:06,059 +a subset of which will be displayed +in the center of the navigation bar. + +26 +00:02:07,294 --> 00:02:10,998 +By tapping the "customize toolbar" entry +in the menu, + +27 +00:02:11,031 --> 00:02:16,436 +items can be rearranged +by dragging to and from the items popup. + +28 +00:02:17,871 --> 00:02:22,409 +The resulting new configuration +persists across app launch. + +29 +00:02:25,312 --> 00:02:28,615 +To accommodate a size change, +for example, + +30 +00:02:28,649 --> 00:02:32,519 +when entering side-by-side mode +with another app, + +31 +00:02:32,553 --> 00:02:36,857 +the system automatically provides +an overflow menu + +32 +00:02:36,890 --> 00:02:40,794 +to access any items that don't fit. + +33 +00:02:42,796 --> 00:02:47,467 +We have added a title menu that works +with the new navigation styles + +34 +00:02:47,501 --> 00:02:50,704 +and supports a few standard functions: + +35 +00:02:50,737 --> 00:02:54,007 +duplicate, move, rename, + +36 +00:02:54,041 --> 00:02:56,844 +export, and print. + +37 +00:02:57,711 --> 00:03:00,981 +These are displayed in the menu +automatically + +38 +00:03:01,014 --> 00:03:05,185 +when the corresponding delegate methods +are implemented. + +39 +00:03:05,219 --> 00:03:10,624 +It's also possible to add +completely custom items to the title menu. + +40 +00:03:15,028 --> 00:03:18,832 +Additionally, +apps built with Mac Catalyst + +41 +00:03:18,866 --> 00:03:21,869 +take advantage +of the improved navigation bars + +42 +00:03:21,902 --> 00:03:25,005 +by seamlessly integrating with NSToolbar + +43 +00:03:25,038 --> 00:03:28,108 +with no additional code required. + +44 +00:03:29,309 --> 00:03:34,214 +iOS 16 introduces +new ways to manipulate text + +45 +00:03:34,248 --> 00:03:37,384 +consistently across various apps. + +46 +00:03:37,417 --> 00:03:41,588 +The first one is the new find and replace. + +47 +00:03:41,622 --> 00:03:46,760 +Conceptually, it is different +from the more high-level in-app search + +48 +00:03:46,793 --> 00:03:52,966 +that operates on data model objects +such as photos or calendar events. + +49 +00:03:53,000 --> 00:03:58,705 +Instead, find and replace +is purposefully built to work with text. + +50 +00:03:58,739 --> 00:04:02,409 +It only takes setting a flag +to activate the feature + +51 +00:04:02,442 --> 00:04:08,448 +for built-in UIKit views +such as UITextView and WKWebView. + +52 +00:04:09,650 --> 00:04:14,254 +Additionally, it seamlessly works +across multiple views and documents + +53 +00:04:14,288 --> 00:04:16,490 +that opted into this system. + +54 +00:04:17,891 --> 00:04:22,729 +Next, the edit menu +has received a major upgrade. + +55 +00:04:22,763 --> 00:04:26,934 +It now looks different depending +on the input method used. + +56 +00:04:26,967 --> 00:04:30,771 +On touch interaction, +you get a redesigned menu + +57 +00:04:30,804 --> 00:04:33,373 +that is more interactive. + +58 +00:04:35,209 --> 00:04:37,211 +When using a pointer, + +59 +00:04:37,244 --> 00:04:41,081 +you get a more full-featured context menu. + +60 +00:04:42,382 --> 00:04:46,420 +To provide both of these experiences +in a seamless way, + +61 +00:04:46,453 --> 00:04:50,224 +we've introduced UIEditMenuInteraction + +62 +00:04:50,257 --> 00:04:55,329 +as a full replacement for +the now-deprecated UIMenuController. + +63 +00:04:56,496 --> 00:05:01,768 +There is also new API +to insert actions into a text view's menu. + +64 +00:05:03,537 --> 00:05:06,673 +Watch "Adopt desktop class +editing interactions" + +65 +00:05:06,707 --> 00:05:10,944 +to learn all the details +on the new edit menu, + +66 +00:05:10,978 --> 00:05:15,015 +and learn how to adopt find interaction +for custom views. + +67 +00:05:17,718 --> 00:05:21,188 +There is one visual UI update +I'd like to cover. + +68 +00:05:22,055 --> 00:05:26,026 +In iOS 16, the sidebar in slide over mode + +69 +00:05:26,059 --> 00:05:28,395 +automatically becomes vibrant + +70 +00:05:28,428 --> 00:05:31,298 +without any additional code. + +71 +00:05:31,331 --> 00:05:36,170 +To achieve this, UIKit manages a set +of private views on your behalf. + +72 +00:05:38,005 --> 00:05:41,575 +Those are the new +productivity features in UIKit: + +73 +00:05:41,608 --> 00:05:44,811 +the new customizable navigation bars, + +74 +00:05:44,845 --> 00:05:48,448 +find and replace, editing interactions, + +75 +00:05:48,482 --> 00:05:52,119 +as well as the powerful title menu. + +76 +00:05:52,152 --> 00:05:54,621 +I am just scratching the surface here. + +77 +00:05:54,655 --> 00:05:59,293 +To learn a lot more, check +the "Meet desktop class iPad" session, + +78 +00:05:59,326 --> 00:06:03,830 +as well as a more in depth +"Build a desktop class iPad app," + +79 +00:06:03,864 --> 00:06:07,134 +where you'll be walked through +improving a sample app + +80 +00:06:07,167 --> 00:06:11,572 +with new advanced +UIKit features in iOS 16. + +81 +00:06:13,106 --> 00:06:17,211 +Now I'm going to introduce +two new controls we've added + +82 +00:06:17,244 --> 00:06:21,782 +and discuss some enhancements +to UIPageControl. + +83 +00:06:22,983 --> 00:06:28,488 +The inline calendar style +of UIDatePicker is now available + +84 +00:06:28,522 --> 00:06:31,158 +as a standalone fully-featured component, + +85 +00:06:31,191 --> 00:06:34,628 +in the form of UICalendarView. + +86 +00:06:34,661 --> 00:06:39,533 +UICalendarView supports different types +of selection behaviors, + +87 +00:06:39,566 --> 00:06:41,535 +like optional single dates, + +88 +00:06:41,568 --> 00:06:44,972 +as well as selecting multiple dates. + +89 +00:06:45,005 --> 00:06:47,407 +On top of the available date range, + +90 +00:06:47,441 --> 00:06:52,513 +it also supports +disabling individual dates from selection. + +91 +00:06:53,647 --> 00:06:58,919 +Moreover, you can annotate +individual dates with decorations. + +92 +00:07:00,287 --> 00:07:03,657 +One major difference +between UICalendarView + +93 +00:07:03,690 --> 00:07:08,662 +and UIDatePicker is that +UICalendarView represents dates + +94 +00:07:08,695 --> 00:07:13,400 +as NSDateComponents, +rather than NSDate. + +95 +00:07:13,433 --> 00:07:16,937 +Unlike NSDate, +date components are a better, + +96 +00:07:16,970 --> 00:07:20,507 +and a more correct representation +of a date + +97 +00:07:20,541 --> 00:07:25,712 +whereas NSDate is a representation +of a point in time. + +98 +00:07:27,381 --> 00:07:31,151 +Because NSDateComponents +offer many flexibilities, + +99 +00:07:31,185 --> 00:07:33,220 +you should be very explicit + +100 +00:07:33,253 --> 00:07:37,591 +about which NSCalendar +the components are represented by. + +101 +00:07:38,692 --> 00:07:43,530 +Note that you should not make assumptions +about the type of the current calendar. + +102 +00:07:43,564 --> 00:07:45,966 +If you need the calendar to be Gregorian, + +103 +00:07:45,999 --> 00:07:50,137 +explicitly specify the Gregorian calendar. + +104 +00:07:53,040 --> 00:07:57,277 +To configure a Calendar view +like the one shown earlier, + +105 +00:07:57,311 --> 00:08:02,816 +first, create the Calendar view +and set its delegate. + +106 +00:08:02,850 --> 00:08:08,188 +To ensure that the Calendar is backed +by the Gregorian NSCalendar, + +107 +00:08:08,222 --> 00:08:11,625 +set the calendar property +on the calendarView + +108 +00:08:11,658 --> 00:08:14,528 +to a Gregorian NSCalendar. + +109 +00:08:15,829 --> 00:08:19,733 +Next, to configure +the multi-date selection. + +110 +00:08:19,766 --> 00:08:24,304 +Create +a UICalendarSelectionMultiDate object, + +111 +00:08:24,338 --> 00:08:26,473 +and set the selected dates property + +112 +00:08:26,507 --> 00:08:31,979 +on the selection object to existing dates +you have from your data model + +113 +00:08:32,012 --> 00:08:34,481 +to show in the Calendar view. + +114 +00:08:35,782 --> 00:08:41,922 +Then, set the selection object +to the calendar view's selection behavior. + +115 +00:08:44,892 --> 00:08:49,229 +To prevent the selection of individual +dates in the Calendar, + +116 +00:08:49,263 --> 00:08:53,467 +implement the +multiDateSelection:canSelectDate: method + +117 +00:08:53,500 --> 00:08:56,170 +from the calendar's selection's delegate + +118 +00:08:56,203 --> 00:08:59,273 +to control which dates can be selected. + +119 +00:09:00,607 --> 00:09:06,213 +Dates that cannot be selected will appear +greyed out in the calendar view. + +120 +00:09:07,748 --> 00:09:11,552 +To annotate individual dates +with decorations, + +121 +00:09:11,585 --> 00:09:14,021 +implement the calendar delegate's + +122 +00:09:14,054 --> 00:09:17,691 +calendarView:decorationForDateComponents: +Method. + +123 +00:09:19,426 --> 00:09:22,462 +For no decorations, simply return nil. + +124 +00:09:23,564 --> 00:09:28,235 +For a default grey circle, +return the default decoration. + +125 +00:09:29,670 --> 00:09:35,909 +You can also create image decorations +with options to customize its color. + +126 +00:09:35,943 --> 00:09:40,247 +And if you need more, +use the customView decoration + +127 +00:09:40,280 --> 00:09:43,083 +and return your view in the view provider. + +128 +00:09:44,484 --> 00:09:48,889 +Please note that the custom view +decorations do not allow interaction + +129 +00:09:48,922 --> 00:09:52,226 +and are clipped to the available space. + +130 +00:09:53,594 --> 00:09:56,697 +The page control is also improved. + +131 +00:09:56,730 --> 00:10:01,268 +We added the support for custom +indicator images for the current page, + +132 +00:10:01,301 --> 00:10:03,737 +so you can now choose different images + +133 +00:10:03,770 --> 00:10:06,773 +depending on whether the page +is selected or not. + +134 +00:10:08,308 --> 00:10:12,479 +Now you can also fully customize +both the orientation + +135 +00:10:12,513 --> 00:10:14,748 +and the direction of the page control. + +136 +00:10:16,450 --> 00:10:20,888 +Here is an example of configuring +a vertical page control + +137 +00:10:20,921 --> 00:10:26,126 +whose indicators change between +the current versus the non-current pages. + +138 +00:10:27,794 --> 00:10:31,765 +I set the page control's direction +to top-to-bottom + +139 +00:10:31,798 --> 00:10:36,670 +and set preferred indicator image +and preferred current indicator image + +140 +00:10:36,703 --> 00:10:38,338 +and that's it! + +141 +00:10:41,241 --> 00:10:46,580 +Apple is committed to protecting +user privacy and security. + +142 +00:10:46,613 --> 00:10:49,950 +In iOS 15, +when an application programmatically + +143 +00:10:49,983 --> 00:10:54,788 +accessed the pasteboard without +using system provided Paste interfaces, + +144 +00:10:54,821 --> 00:10:59,359 +a banner would appear to indicate +that the pasteboard was accessed. + +145 +00:11:00,594 --> 00:11:04,831 +New to iOS 16, +the system behavior has changed. + +146 +00:11:04,865 --> 00:11:06,800 +Now, instead of a banner, + +147 +00:11:06,834 --> 00:11:11,738 +we will display an alert that asks +for permission to use the pasteboard. + +148 +00:11:13,073 --> 00:11:16,810 +System paste interfaces +that the users interact with + +149 +00:11:16,844 --> 00:11:19,780 +provide implicit access to the pasteboard + +150 +00:11:19,813 --> 00:11:22,349 +and will avoid the alert. + +151 +00:11:23,817 --> 00:11:26,253 +If you have custom paste controls, + +152 +00:11:26,286 --> 00:11:30,023 +you can replace them +with the new UIPasteControl + +153 +00:11:30,057 --> 00:11:33,560 +that looks and behaves +like a filled UIButton. + +154 +00:11:34,895 --> 00:11:39,266 +It is enabled whenever the pasteboard +gets content compatible + +155 +00:11:39,299 --> 00:11:42,202 +with the control's paste target. + +156 +00:11:43,670 --> 00:11:47,007 +So those are +the new powerful UICalendarView, + +157 +00:11:47,040 --> 00:11:49,643 +the improved UIPageControl, + +158 +00:11:49,676 --> 00:11:53,814 +as well as +the security-oriented UIPasteControl. + +159 +00:11:53,847 --> 00:11:56,783 +Go ahead and try them out. + +160 +00:11:57,718 --> 00:12:01,955 +Now I'll walk you through some +API refinements we've made. + +161 +00:12:03,690 --> 00:12:07,528 +In iOS 15, +detents were added to sheets + +162 +00:12:07,561 --> 00:12:11,765 +which enabled building flexible +and dynamic UIs. + +163 +00:12:11,798 --> 00:12:16,770 +In iOS 16, we added support +for custom detents + +164 +00:12:16,803 --> 00:12:20,007 +so you can make sheets any size. + +165 +00:12:21,308 --> 00:12:26,446 +To take advantage of this feature, +use the new ".custom" detent + +166 +00:12:26,480 --> 00:12:31,885 +and specify the sheet's height +in points in an associated block. + +167 +00:12:31,919 --> 00:12:35,055 +You can return a constant value, + +168 +00:12:35,088 --> 00:12:39,459 +or a percentage of +the maximum detent height. + +169 +00:12:42,396 --> 00:12:47,134 +And you can also give your custom detent +an identifier + +170 +00:12:47,167 --> 00:12:50,103 +if you need to refer to it +from other APIs, + +171 +00:12:50,137 --> 00:12:54,575 +for example, to disable dimming +above your custom detent. + +172 +00:12:57,444 --> 00:13:01,148 +Note that the value you return +from the custom block + +173 +00:13:01,181 --> 00:13:05,352 +shouldn't account for +the bottom safe area inset. + +174 +00:13:05,385 --> 00:13:11,725 +This is so the same calculation works for +both floating and edge-attached sheets. + +175 +00:13:13,894 --> 00:13:19,700 +To learn more about customizing sheets +with system detents and other options, + +176 +00:13:19,733 --> 00:13:24,872 +watch the Customize +and resize sheets in UIKit video. + +177 +00:13:24,905 --> 00:13:28,609 +The sample code for that video +has also been updated + +178 +00:13:28,642 --> 00:13:31,879 +to show these new custom detent APIs. + +179 +00:13:33,547 --> 00:13:37,851 +There are new features +for SF Symbols in UIKit. + +180 +00:13:38,785 --> 00:13:41,655 +Symbols support four rendering modes: + +181 +00:13:41,688 --> 00:13:44,057 +monochrome, multicolor, + +182 +00:13:44,091 --> 00:13:47,928 +hierarchical, and palette. + +183 +00:13:47,961 --> 00:13:51,164 +UIKit would use monochrome rendering +by default + +184 +00:13:51,198 --> 00:13:55,769 +unless the symbol was configured +with a different rendering mode. + +185 +00:13:55,802 --> 00:13:59,940 +In iOS 16, +UIKit may render individual symbols + +186 +00:13:59,973 --> 00:14:02,376 +with a mode other than monochrome + +187 +00:14:02,409 --> 00:14:05,279 +if no rendering mode is specified. + +188 +00:14:07,047 --> 00:14:10,284 +Take these device symbols, for example. + +189 +00:14:10,317 --> 00:14:13,453 +In iOS 15 and earlier, + +190 +00:14:13,487 --> 00:14:16,456 +these symbols use monochrome rendering + +191 +00:14:16,490 --> 00:14:19,226 +if no rendering mode is specified. + +192 +00:14:20,427 --> 00:14:25,699 +In iOS 16, these symbols instead default +to hierarchical rendering. + +193 +00:14:26,867 --> 00:14:29,736 +Generally, +a symbol's default rendering mode + +194 +00:14:29,770 --> 00:14:33,473 +is the preferred way +to display the symbol. + +195 +00:14:33,507 --> 00:14:38,245 +So in this case, you should allow +the default hierarchical rendering + +196 +00:14:38,278 --> 00:14:40,781 +to take effect. + +197 +00:14:40,814 --> 00:14:45,786 +However, monochrome rendering +can be explicitly requested + +198 +00:14:45,819 --> 00:14:51,825 +with the new UIImage.SymbolConfiguration. +preferringMonochrome() API. + +199 +00:14:55,596 --> 00:14:58,765 +UIKit added support for variable symbols, + +200 +00:14:58,799 --> 00:15:02,169 +which allows apps to display +variations of a symbol + +201 +00:15:02,202 --> 00:15:06,173 +based on a value from 0 to 1. + +202 +00:15:06,206 --> 00:15:10,978 +Suppose an app wants to depict +the current volume level with a symbol. + +203 +00:15:11,011 --> 00:15:15,682 +The app can use +the speaker.3.wave.fill symbol, + +204 +00:15:15,716 --> 00:15:20,254 +which has been updated +to support variable rendering. + +205 +00:15:20,287 --> 00:15:24,758 +At a value of 0, +the speaker waves are faded out, + +206 +00:15:24,791 --> 00:15:27,861 +indicating the lowest volume level. + +207 +00:15:27,895 --> 00:15:30,430 +As the value increases up to 1, + +208 +00:15:30,464 --> 00:15:33,800 +the speaker waves progressively fill in, + +209 +00:15:33,834 --> 00:15:36,170 +indicating higher volume levels. + +210 +00:15:37,471 --> 00:15:40,307 +If a symbol supports variable rendering, + +211 +00:15:40,340 --> 00:15:43,477 +then apps can request +a version of the symbol + +212 +00:15:43,510 --> 00:15:47,247 +reflecting a value between 0 and 1. + +213 +00:15:48,982 --> 00:15:53,687 +Using variable symbols is straightforward. + +214 +00:15:53,720 --> 00:15:57,457 +You can get the regular +non-variable version of a symbol + +215 +00:15:57,491 --> 00:16:02,095 +with the standard SF Symbols API +on UIImage. + +216 +00:16:03,830 --> 00:16:07,634 +To get a version of that symbol +with a particular variable value, + +217 +00:16:07,668 --> 00:16:10,771 +simply add the variableValue parameter. + +218 +00:16:12,039 --> 00:16:15,943 +You can even mix variable rendering +with other rendering modes, + +219 +00:16:15,976 --> 00:16:19,279 +such as palette, +to further style the symbol. + +220 +00:16:20,914 --> 00:16:25,686 +Many system symbols now +support variable rendering, + +221 +00:16:25,719 --> 00:16:30,757 +and apps can update their custom symbols +to support variability as well. + +222 +00:16:32,192 --> 00:16:35,529 +To learn how to create +custom variable symbols, + +223 +00:16:35,562 --> 00:16:40,267 +check out the sessions +"Adopt variable color in SF Symbols" + +224 +00:16:40,300 --> 00:16:43,604 +and "What's new in SF Symbols 4”. + +225 +00:16:45,439 --> 00:16:49,877 +We've modernized UIKit to work +with new Swift Concurrency features, + +226 +00:16:49,910 --> 00:16:52,045 +including making immutable types + +227 +00:16:52,079 --> 00:16:56,216 +such as UIImage and UIColor +conform to Sendable, + +228 +00:16:56,250 --> 00:16:58,986 +so you can send them between the MainActor + +229 +00:16:59,019 --> 00:17:02,456 +and custom actors +without compiler warning. + +230 +00:17:03,957 --> 00:17:09,930 +For example, here we have +a custom actor called Processor, + +231 +00:17:09,963 --> 00:17:12,900 +and a view controller called ImageViewer + +232 +00:17:12,933 --> 00:17:15,969 +which is bound to the MainActor. + +233 +00:17:16,003 --> 00:17:18,572 +In the method sendImageForProcessing, + +234 +00:17:18,605 --> 00:17:24,077 +the ImageViewer sends an image +to the Processor actor for processing, + +235 +00:17:24,111 --> 00:17:28,448 +to make it fancy like adding glitter +and rainbows to it. + +236 +00:17:28,482 --> 00:17:32,386 +This is safe because UIImage is immutable, + +237 +00:17:32,419 --> 00:17:34,755 +so Processor has to make new copy + +238 +00:17:34,788 --> 00:17:36,990 +to add the rainbows and glitter. + +239 +00:17:38,358 --> 00:17:41,862 +Any code that has a reference +to the original image + +240 +00:17:41,895 --> 00:17:44,364 +doesn't show these modifications, + +241 +00:17:44,398 --> 00:17:48,135 +and a shared state +is not unsafely mutated. + +242 +00:17:49,870 --> 00:17:52,973 +Contrast this with UIBezierPath, + +243 +00:17:53,006 --> 00:17:56,543 +which is not Sendable +because it is mutable. + +244 +00:17:57,845 --> 00:18:02,082 +How cool is it that something +that could previously only be expressed + +245 +00:18:02,115 --> 00:18:06,753 +in documentation +can now be checked by the compiler? + +246 +00:18:09,056 --> 00:18:12,693 +To learn more about +Sendable and Swift Concurrency, + +247 +00:18:12,726 --> 00:18:17,464 +check out "Eliminate data races +using Swift Concurrency" + +248 +00:18:17,497 --> 00:18:23,337 +and "Visualize and optimize +Swift Concurrency" videos. + +249 +00:18:25,405 --> 00:18:30,644 +iOS 16 features new powerful support +for external displays. + +250 +00:18:31,245 --> 00:18:36,583 +The great news is that you don't have to +update your app to take advantage of this, + +251 +00:18:36,617 --> 00:18:40,487 +unless you are using old UIScreen APIs. + +252 +00:18:41,588 --> 00:18:45,459 +You can no longer assume +your app is on the main screen. + +253 +00:18:46,260 --> 00:18:49,763 +Instead, defer to more specific APIs, + +254 +00:18:49,796 --> 00:18:53,000 +like trait collection and UIScene APIs, + +255 +00:18:53,033 --> 00:18:55,702 +to get the information you need. + +256 +00:18:55,736 --> 00:18:58,438 +If your app is still not using UIScene, + +257 +00:18:58,472 --> 00:19:03,911 +there's now even more reason to upgrade +and to support multiple windows. + +258 +00:19:05,445 --> 00:19:08,115 +Self-sizing cells in UICollectionView + +259 +00:19:08,148 --> 00:19:12,586 +and UITableView got a major upgrade. + +260 +00:19:12,619 --> 00:19:16,523 +Now cells are also self-resizing! + +261 +00:19:16,557 --> 00:19:21,595 +In iOS 16, when the content +inside a visible cell changes, + +262 +00:19:21,628 --> 00:19:26,633 +the cell will automatically be resized +to fit the new content. + +263 +00:19:28,235 --> 00:19:31,371 +This new behavior is enabled by default, + +264 +00:19:31,405 --> 00:19:34,141 +and UICollectionView and UITableView + +265 +00:19:34,174 --> 00:19:38,178 +each have a new selfSizingInvalidation +property + +266 +00:19:38,212 --> 00:19:41,548 +that gives you control +over this new functionality. + +267 +00:19:43,016 --> 00:19:44,785 +Here is how it works: + +268 +00:19:46,220 --> 00:19:49,690 +When selfSizingInvalidation is enabled, + +269 +00:19:49,723 --> 00:19:52,259 +cells can request to be resized + +270 +00:19:52,292 --> 00:19:55,162 +by their containing collection +or table view. + +271 +00:19:56,930 --> 00:20:01,768 +If you're using UIListContentConfiguration +to configure cells, + +272 +00:20:01,802 --> 00:20:04,204 +the invalidation happens automatically + +273 +00:20:04,238 --> 00:20:07,474 +whenever the cell's configuration changes. + +274 +00:20:08,942 --> 00:20:11,011 +For any other cases, + +275 +00:20:11,044 --> 00:20:14,915 +you can call +the invalidateIntrinsicContentSize method + +276 +00:20:14,948 --> 00:20:19,086 +on the cell +or its contentView to resize the cell. + +277 +00:20:20,888 --> 00:20:24,558 +By default, +cells will be resized with animation, + +278 +00:20:24,591 --> 00:20:28,428 +but you can wrap the call +to invalidateIntrinsicContentSize + +279 +00:20:28,462 --> 00:20:33,734 +inside performWithoutAnimation +to resize without animation. + +280 +00:20:33,767 --> 00:20:38,539 +UICollectionView and UITableView +intelligently coalesce + +281 +00:20:38,572 --> 00:20:40,474 +size invalidation from cells + +282 +00:20:40,507 --> 00:20:44,645 +into a single update +performed at the optimal time. + +283 +00:20:47,247 --> 00:20:49,950 +If you're using Auto Layout in your cells, + +284 +00:20:49,983 --> 00:20:53,754 +you can opt-in to an even more +comprehensive behavior + +285 +00:20:53,787 --> 00:20:57,691 +by choosing enabledIncludingConstraints. + +286 +00:20:57,724 --> 00:21:03,564 +This means when a cell detects any auto +layout changes inside its contentView, + +287 +00:21:03,597 --> 00:21:08,702 +it will automatically call +invalidateIntrinsicContentSize on itself + +288 +00:21:08,735 --> 00:21:14,074 +so that the containing collection +or table view can resize it if necessary. + +289 +00:21:15,008 --> 00:21:17,044 +This makes it incredibly easy + +290 +00:21:17,077 --> 00:21:20,147 +to have cells +that automatically adjust their size + +291 +00:21:20,180 --> 00:21:23,450 +in response to content +or layout updates. + +292 +00:21:25,419 --> 00:21:28,689 +UIKit is powerful and flexible. + +293 +00:21:28,722 --> 00:21:32,125 +You can also take advantage +of the expressiveness + +294 +00:21:32,159 --> 00:21:36,330 +of implementing UIs using SwiftUI. + +295 +00:21:36,363 --> 00:21:41,935 +We've made it much easier to incorporate +both frameworks in the same app. + +296 +00:21:43,971 --> 00:21:50,177 +In iOS 16, there is an entirely new way +to build cells for your collection + +297 +00:21:50,210 --> 00:21:53,647 +and table views using SwiftUI. + +298 +00:21:55,048 --> 00:21:59,520 +This is made possible +by a new content configuration type + +299 +00:21:59,553 --> 00:22:03,290 +named UIHostingConfiguration. + +300 +00:22:03,323 --> 00:22:05,592 +With just one line of code, + +301 +00:22:05,626 --> 00:22:10,063 +you can start writing SwiftUI +right inside your cells-- + +302 +00:22:10,097 --> 00:22:14,001 +no extra views +or view controllers needed at all. + +303 +00:22:15,869 --> 00:22:19,540 +Here is a simple custom cell +written in SwiftUI + +304 +00:22:19,573 --> 00:22:22,476 +using UIHostingConfiguration. + +305 +00:22:22,509 --> 00:22:25,646 +It is extremely easily +to build this cell. + +306 +00:22:27,147 --> 00:22:33,587 +Not only is this a great way to start +integrating SwiftUI into your app, + +307 +00:22:33,620 --> 00:22:39,426 +the expressive nature of SwiftUI means +there's never been a more powerful way + +308 +00:22:39,459 --> 00:22:42,996 +to build custom cells in UIKit. + +309 +00:22:43,797 --> 00:22:46,266 +There is a lot more to this topic, + +310 +00:22:46,300 --> 00:22:49,169 +so be sure to check out the video + +311 +00:22:49,203 --> 00:22:53,674 +"Use SwiftUI with UIKit" to learn more. + +312 +00:22:56,643 --> 00:23:02,249 +There are a couple of small but important +changes that you should be aware of. + +313 +00:23:02,983 --> 00:23:06,286 +To prevent users from being fingerprinted, + +314 +00:23:06,320 --> 00:23:10,123 +UIDevice.name now reports the model name + +315 +00:23:10,157 --> 00:23:14,027 +rather than the user's custom device name. + +316 +00:23:14,061 --> 00:23:18,799 +Using the customized name +now requires getting an entitlement. + +317 +00:23:20,534 --> 00:23:24,805 +Setting UIDevice.orientation +is no longer supported. + +318 +00:23:24,838 --> 00:23:28,509 +Instead, use UIViewController APIs + +319 +00:23:28,542 --> 00:23:31,512 +such as preferredInterfaceOrientation + +320 +00:23:31,545 --> 00:23:35,516 +to express the intended orientation +of your interface. + +321 +00:23:36,783 --> 00:23:38,785 +What's next? + +322 +00:23:38,819 --> 00:23:43,690 +Compile your app using iOS 16 SDK. + +323 +00:23:43,724 --> 00:23:47,594 +Test out the new features +such as text edit menus + +324 +00:23:47,628 --> 00:23:50,197 +and find and replace. + +325 +00:23:50,230 --> 00:23:55,002 +Adopt the new UIKit APIs +to use new enhanced controls + +326 +00:23:55,035 --> 00:23:57,571 +and productivity features. + +327 +00:23:57,604 --> 00:24:00,507 +And experiment with the new exciting ways + +328 +00:24:00,541 --> 00:24:04,311 +to incorporate SwiftUI in your UIKit app. + +329 +00:24:05,179 --> 00:24:06,680 +Thank you. ♪ ♪ + diff --git a/eng/2022 Session 10069 Meet desktop class iPad en.srt b/eng/2022 Session 10069 Meet desktop class iPad en.srt new file mode 100644 index 0000000..a0613b4 --- /dev/null +++ b/eng/2022 Session 10069 Meet desktop class iPad en.srt @@ -0,0 +1,1061 @@ +1 +00:00:01,134 --> 00:00:07,140 +[spacey music] + +2 +00:00:10,077 --> 00:00:13,614 +David Duncan: Hi, I’m David Duncan, +and in this video, + +3 +00:00:13,647 --> 00:00:17,384 +I’ll be introducing you +to desktop class iPad. + +4 +00:00:18,151 --> 00:00:24,992 +iOS 16 brings advances to the tools +used to design and build great apps, + +5 +00:00:25,025 --> 00:00:28,829 +apps that bring more and better tools +to the forefront + +6 +00:00:28,862 --> 00:00:34,368 +and take advantage of all the hardware, +both built in and attached. + +7 +00:00:34,401 --> 00:00:39,506 +UIKit adds many tools to help you +meet these goals for your apps. + +8 +00:00:39,540 --> 00:00:43,677 +Updates to UINavigationBar allow you +to take better advantage + +9 +00:00:43,710 --> 00:00:48,348 +of screen real estate and build +a great experience on all Apple platforms. + +10 +00:00:49,583 --> 00:00:54,288 +The new Find and Replace UI +is a snap to enable on built-in views + +11 +00:00:54,321 --> 00:00:57,291 +and easy to add to custom ones. + +12 +00:00:57,324 --> 00:01:01,828 +The Edit menu has been overhauled, +with a new interaction-based API + +13 +00:01:01,862 --> 00:01:03,897 +that integrates with the menu system. + +14 +00:01:04,665 --> 00:01:08,268 +And collection view improvements +make it easier than ever + +15 +00:01:08,302 --> 00:01:13,574 +to build interfaces that let your users +select and act on their content. + +16 +00:01:15,475 --> 00:01:19,546 +For more information +on Find and Replace and Edit Menu, + +17 +00:01:19,580 --> 00:01:24,451 +watch "Adopt desktop +class editing interactions." + +18 +00:01:24,484 --> 00:01:27,888 +And to see how all these features +work together, + +19 +00:01:27,921 --> 00:01:30,991 +watch "Build a desktop class iPad app." + +20 +00:01:32,059 --> 00:01:35,395 +In this video, I'll discuss changes +to navigation + +21 +00:01:35,429 --> 00:01:38,899 +that impact how you design +your app for iOS 16. + +22 +00:01:40,901 --> 00:01:47,508 +First are new features that make it easy +to build more discoverable interfaces. + +23 +00:01:47,541 --> 00:01:53,914 +Then features that are especially powerful +for document based apps. + +24 +00:01:53,947 --> 00:01:59,219 +And, finally, updates to Search to help +accelerate and polish the experience. + +25 +00:02:00,821 --> 00:02:05,759 +UINavigationBar is used +for many different purposes on iOS, + +26 +00:02:05,792 --> 00:02:08,362 +and iOS 16 acknowledges that + +27 +00:02:08,395 --> 00:02:13,033 +by providing +new optimized UI for many of these cases. + +28 +00:02:13,066 --> 00:02:16,803 +UINavigationItem adds a style property, + +29 +00:02:16,837 --> 00:02:22,843 +used to select from these styles: +navigator, browser, and editor. + +30 +00:02:22,876 --> 00:02:25,712 +I'll dive into each of these styles now. + +31 +00:02:26,580 --> 00:02:30,017 +The default style, navigator, + +32 +00:02:30,050 --> 00:02:34,021 +behaves exactly +as a traditional UINavigationBar. + +33 +00:02:35,389 --> 00:02:40,494 +The title is centered, there are +leading and trailing bar button items, + +34 +00:02:40,527 --> 00:02:45,532 +and a back button appears when +there is more than 1 item on the stack. + +35 +00:02:45,566 --> 00:02:50,938 +The browser style rearranges contents +to be better optimized for interfaces + +36 +00:02:50,971 --> 00:02:56,076 +where history matters as much as location, +like in Files or Safari. + +37 +00:02:57,144 --> 00:02:59,913 +The title is moved to the leading position +in this styling. + +38 +00:03:00,948 --> 00:03:04,651 +The editor style is optimized for +when the primary function + +39 +00:03:04,685 --> 00:03:06,486 +is document editing. + +40 +00:03:06,520 --> 00:03:10,724 +Just like the browser style, +the title is leading aligned. + +41 +00:03:10,757 --> 00:03:15,462 +Editor UIs are often a destination, +such as after selecting a document + +42 +00:03:15,495 --> 00:03:17,164 +with a document picker, + +43 +00:03:17,197 --> 00:03:21,201 +and so present +a back button for easy access to that UI. + +44 +00:03:23,103 --> 00:03:27,908 +The browser and editor styles both free up +a lot of space in the center of the bar. + +45 +00:03:29,543 --> 00:03:33,547 +iOS 16 takes advantage +of this liberated space + +46 +00:03:33,580 --> 00:03:37,050 +by allowing you to place +additional controls in this region. + +47 +00:03:38,852 --> 00:03:41,655 +Center items are part +of a suite of changes + +48 +00:03:41,688 --> 00:03:44,925 +to take better advantage +of screen real estate, + +49 +00:03:44,958 --> 00:03:48,428 +and include support for +UIBarButtonItemGroup, + +50 +00:03:48,462 --> 00:03:51,265 +customization support, and overflow. + +51 +00:03:52,666 --> 00:03:55,936 +Overflow support is available +in all modes, + +52 +00:03:55,969 --> 00:03:58,605 +and allows the navigator style + +53 +00:03:58,639 --> 00:04:01,175 +to indirectly support +center items as well. + +54 +00:04:02,676 --> 00:04:07,981 +Individual controls continue +to be specified as UIBarButtonItems, + +55 +00:04:08,015 --> 00:04:11,785 +but now are organized +as UIBarButtonItemGroups. + +56 +00:04:12,519 --> 00:04:17,257 +This allows for denser presentation +when space is at a premium. + +57 +00:04:17,291 --> 00:04:23,997 +In this example, there are 5 items +in the bar, consisting of 4 groups. + +58 +00:04:26,633 --> 00:04:30,904 +The first group contains +a single bar button item, + +59 +00:04:30,938 --> 00:04:34,041 +so this example uses a convenience method + +60 +00:04:34,074 --> 00:04:39,046 +of UIBarButtonItem, +creatingFixedGroup(), to create it. + +61 +00:04:40,447 --> 00:04:43,350 +If you need +a fixed group with more than 1 item, + +62 +00:04:43,383 --> 00:04:46,653 +you can use +the UIBarButtonItemGroup method instead. + +63 +00:04:47,788 --> 00:04:50,958 +Fixed groups +always appear first in the bar, + +64 +00:04:50,991 --> 00:04:54,795 +and cannot be removed +or moved by customization. + +65 +00:04:54,828 --> 00:04:58,866 +The draw group contains a single item, + +66 +00:04:58,899 --> 00:05:02,169 +so it also uses a convenience API, + +67 +00:05:02,202 --> 00:05:06,840 +creatingMovableGroup +(customizationIdentifier). + +68 +00:05:06,874 --> 00:05:11,912 +Like fixed groups, movable groups +cannot be removed, but can be moved. + +69 +00:05:13,113 --> 00:05:16,583 +Because of this, +they require a customizationIdentifier + +70 +00:05:16,617 --> 00:05:20,220 +so their position +can be tracked and saved. + +71 +00:05:20,254 --> 00:05:22,756 +If you need a group +with more than one item, + +72 +00:05:22,789 --> 00:05:25,726 +you can use the UIBarButtonItemGroup +method instead. + +73 +00:05:28,395 --> 00:05:32,499 +The shapes group contains multiple items, +and so uses + +74 +00:05:32,533 --> 00:05:35,836 +the UIBarButtonItemGroup API +to create the group. + +75 +00:05:37,437 --> 00:05:39,706 +This group +should be movable within the bar, + +76 +00:05:39,740 --> 00:05:43,810 +as well as removable, +and so is created as an optional group. + +77 +00:05:45,012 --> 00:05:48,582 +This group also specifies +a representativeItem, + +78 +00:05:48,615 --> 00:05:52,486 +allowing UIKit to collapse the group +to save space when necessary. + +79 +00:05:53,820 --> 00:05:58,592 +The representativeItem does not specify +an action, further allowing UIKit + +80 +00:05:58,625 --> 00:06:02,496 +to synthesize a menu allowing selection +of the items in the group. + +81 +00:06:05,499 --> 00:06:10,370 +When the customization UI is invoked, +UIKit automatically applies + +82 +00:06:10,404 --> 00:06:15,342 +the rules you've specified based +on how you've created your groups. + +83 +00:06:15,375 --> 00:06:18,912 +While fixed and movable groups +must stay in the bar, + +84 +00:06:18,946 --> 00:06:22,282 +optional groups can be added +or removed in any number. + +85 +00:06:23,450 --> 00:06:25,719 +UIKit will try collapsing groups + +86 +00:06:25,752 --> 00:06:29,189 +to keep as much functionality as possible +in the bar, + +87 +00:06:29,223 --> 00:06:33,427 +but if space isn't available, +extra items will be moved to overflow. + +88 +00:06:34,328 --> 00:06:40,067 +The overflow menu contains any items +that are part of the customization + +89 +00:06:40,100 --> 00:06:42,302 +but could not be fit into the bar, + +90 +00:06:42,336 --> 00:06:44,671 +as well as the option +to customize the bar. + +91 +00:06:45,839 --> 00:06:50,978 +While UIKit will synthesize default +menu elements for each bar button item, + +92 +00:06:51,011 --> 00:06:55,916 +you have the option to customize +the menuRepresentation if you wish. + +93 +00:06:55,949 --> 00:06:59,887 +Finally, this example +enables customization + +94 +00:06:59,920 --> 00:07:01,688 +and adds the centerItemGroups. + +95 +00:07:02,823 --> 00:07:08,295 +You enable customization by setting +UINavigationItem.customizationIdentifier. + +96 +00:07:09,363 --> 00:07:14,101 +The customizationIdentifier +defines a unique customization of the bar, + +97 +00:07:14,134 --> 00:07:18,272 +so pick a string that won't conflict +with other customizations within your app. + +98 +00:07:19,840 --> 00:07:24,745 +UIKit automatically saves and restores +customizations based on this identifier. + +99 +00:07:25,779 --> 00:07:30,450 +Next, provide +the centerItemGroups themselves. + +100 +00:07:30,484 --> 00:07:33,253 +The first four groups +I've already covered. + +101 +00:07:34,621 --> 00:07:40,494 +The format group is an optional group +that isn't in the default customization, + +102 +00:07:40,527 --> 00:07:44,198 +and so this code +overrides the default value + +103 +00:07:44,231 --> 00:07:48,569 +of the isInDefaultCustomization +parameter to exclude it. + +104 +00:07:49,169 --> 00:07:52,573 +You can still use centerItemGroups +without setting + +105 +00:07:52,606 --> 00:07:59,546 +UINavigationItem.customizationIdentifier, +but customization will not be available. + +106 +00:07:59,580 --> 00:08:02,583 +In Mac Catalyst, the UINavigationBar + +107 +00:08:02,616 --> 00:08:06,019 +automatically translates its content +to NSToolbar. + +108 +00:08:06,954 --> 00:08:10,123 +The leading, center, +and trailing item groups + +109 +00:08:10,157 --> 00:08:14,561 +are added in order, +and the customization properties + +110 +00:08:14,595 --> 00:08:19,800 +of the center item groups are respected +when using NSToolbar customization. + +111 +00:08:21,101 --> 00:08:25,272 +All of the expected NSToolbar behaviors +are available, + +112 +00:08:25,305 --> 00:08:29,443 +as well as other properties +such as the title & window proxy. + +113 +00:08:30,544 --> 00:08:35,249 +All of this occurs by default +when you optimize for the Mac. + +114 +00:08:35,282 --> 00:08:40,120 +Next, let’s focus in +on interactions that are powerful, + +115 +00:08:40,153 --> 00:08:43,056 +specifically when dealing with documents. + +116 +00:08:43,090 --> 00:08:47,828 +UINavigationBar now supports +adding a menu to the title view, + +117 +00:08:47,861 --> 00:08:54,134 +giving a central location to add actions +that operate on the content as a whole. + +118 +00:08:54,168 --> 00:08:57,538 +Additionally, you can add support +for the share sheet + +119 +00:08:57,571 --> 00:08:59,606 +and drag & drop from this menu. + +120 +00:09:00,374 --> 00:09:04,011 +First, I’ll focus +on the menu items themselves. + +121 +00:09:04,044 --> 00:09:08,282 +Once enabled, +the default title menu offers 5 commands: + +122 +00:09:08,315 --> 00:09:13,854 +duplicate, move, rename, export, +and print. + +123 +00:09:13,887 --> 00:09:15,656 +These items are filtered + +124 +00:09:15,689 --> 00:09:20,027 +based on specific methods +in your responder chain. + +125 +00:09:20,060 --> 00:09:23,864 +UINavigationBar +has specific support for renaming, + +126 +00:09:23,897 --> 00:09:27,868 +and so it will also be included +if you’ve implemented a renameDelegate. + +127 +00:09:30,103 --> 00:09:34,708 +To enable the title menu, +set the titleMenuProvider, + +128 +00:09:34,741 --> 00:09:38,078 +a closure that returns +the final menu to be displayed. + +129 +00:09:39,446 --> 00:09:43,383 +The closure is passed +an array of suggested elements. + +130 +00:09:43,417 --> 00:09:48,422 +You can use these as is, +filter them, or add your own. + +131 +00:09:48,455 --> 00:09:53,927 +In our example, we're adding +a single additional action to the menu. + +132 +00:09:53,961 --> 00:09:56,763 +Finally, you return the composed UIMenu. + +133 +00:09:58,398 --> 00:10:02,936 +The title menu also allows +sharing via the activity view controller + +134 +00:10:02,970 --> 00:10:04,671 +and support for drag & drop. + +135 +00:10:06,106 --> 00:10:10,711 +To enable these features, you provide +a UIDocumentProperties instance + +136 +00:10:10,744 --> 00:10:12,546 +that describes your document. + +137 +00:10:14,248 --> 00:10:18,585 +UIDocumentProperties represents +metadata about your document, + +138 +00:10:18,619 --> 00:10:21,121 +including a preview. + +139 +00:10:21,154 --> 00:10:23,924 +This example creates one with a URL, + +140 +00:10:23,957 --> 00:10:27,995 +allowing UIKit to fetch +the necessary metadata automatically. + +141 +00:10:29,329 --> 00:10:32,266 +To enable additional features, +this example creates + +142 +00:10:32,299 --> 00:10:34,968 +an NSItemProvider +to represent the document. + +143 +00:10:36,570 --> 00:10:40,607 +Set a dragItemsProvider +to enable drag & drop. + +144 +00:10:40,641 --> 00:10:47,181 +This closure is past a UIDragSession, +and returns an array of UIDragItems. + +145 +00:10:47,214 --> 00:10:50,417 +This example returns a single item +representing the document. + +146 +00:10:52,219 --> 00:10:56,123 +Setting a activityViewControllerProvider +enables sharing. + +147 +00:10:56,156 --> 00:11:00,360 +This closure configures and returns +a UIActivityViewController. + +148 +00:11:01,628 --> 00:11:04,164 +Finally, assign the filled-out object + +149 +00:11:04,198 --> 00:11:09,102 +to UINavigationItem.documentProperties, +and when the title is tapped, + +150 +00:11:09,136 --> 00:11:13,006 +UIKit presents the header +alongside other titleMenu items. + +151 +00:11:15,843 --> 00:11:18,745 +On Mac Catalyst, the suggested items + +152 +00:11:18,779 --> 00:11:22,082 +that would be passed +to the titleMenuProvider + +153 +00:11:22,115 --> 00:11:25,252 +already exist in the File menu. + +154 +00:11:25,285 --> 00:11:28,789 +Any items that you would have added +to the title menu + +155 +00:11:28,822 --> 00:11:31,158 +will need to be made available +by other means. + +156 +00:11:32,359 --> 00:11:36,630 +You can use +the UIMenuBuilder API to add these items, + +157 +00:11:36,663 --> 00:11:39,266 +or filter existing items as necessary. + +158 +00:11:40,267 --> 00:11:44,304 +If you specify document properties, +UIKit will automatically use + +159 +00:11:44,338 --> 00:11:48,041 +the URL provided +to manage the macOS proxy icon. + +160 +00:11:49,142 --> 00:11:53,180 +If you set the representedURL +for your windowScene manually, + +161 +00:11:53,213 --> 00:11:55,749 +that will supersede UIKit's management. + +162 +00:11:57,217 --> 00:12:01,588 +UIKit provides +two mechanisms to enable Rename. + +163 +00:12:01,622 --> 00:12:07,261 +Inline Rename is provided by setting +UINavigationItem.renameDelegate, + +164 +00:12:07,294 --> 00:12:11,732 +and provides a dedicated UI +for editing the title on all platforms. + +165 +00:12:12,966 --> 00:12:16,403 +When completed, the resulting name +is passed to the delegate. + +166 +00:12:17,504 --> 00:12:22,342 +Alternatively you can take full control +over the rename experience + +167 +00:12:22,376 --> 00:12:28,649 +by implementing UIResponder.rename(_:) +and providing whatever UI you prefer. + +168 +00:12:30,150 --> 00:12:34,655 +On iOS, the UINavigationBar +provides the rename UI + +169 +00:12:34,688 --> 00:12:37,257 +directly within the title view. + +170 +00:12:37,291 --> 00:12:41,428 +On macOS, the rename UI is provided +by the window's title + +171 +00:12:41,461 --> 00:12:44,464 +when the navigation bar is hosted +in an NSToolbar. + +172 +00:12:45,432 --> 00:12:50,470 +To implement inline rename, conform +to the UINavigationItemRenameDelegate + +173 +00:12:50,504 --> 00:12:56,076 +protocol and set +the navigation item's renameDelegate. + +174 +00:12:56,109 --> 00:13:01,815 +There is only one required method, +navigationItem(_:didEndRenamingWith:), + +175 +00:13:01,849 --> 00:13:04,985 +that is used to receive +the title accepted by the user. + +176 +00:13:06,253 --> 00:13:09,556 +For file based apps, +UIDocumentBrowserViewController + +177 +00:13:09,590 --> 00:13:11,258 +now offers a renamed API. + +178 +00:13:12,459 --> 00:13:16,864 +Search is how many users +find their most important data, + +179 +00:13:16,897 --> 00:13:23,170 +and advances in iOS 16 make it easier +to provide an excellent search experience. + +180 +00:13:23,203 --> 00:13:27,307 +The first thing to note is that search +now takes up less space + +181 +00:13:27,341 --> 00:13:31,111 +by being in line in the navigation bar +on iPadOS + +182 +00:13:31,144 --> 00:13:33,881 +and the toolbar on macOS. + +183 +00:13:33,914 --> 00:13:36,984 +On iPadOS, +you can restore the old behavior + +184 +00:13:37,017 --> 00:13:40,954 +with UINavigationItem +.preferredSearchBarPlacement. + +185 +00:13:40,988 --> 00:13:44,358 +Additionally, +the search bar can collapse to a button + +186 +00:13:44,391 --> 00:13:46,994 +to grant more space for other controls. + +187 +00:13:47,728 --> 00:13:51,298 +When search is activated, +search suggestions appear, + +188 +00:13:51,331 --> 00:13:55,736 +and they can be updated +alongside the updating search query, + +189 +00:13:55,769 --> 00:14:00,007 +allowing you the opportunity +to assist your users in their search. + +190 +00:14:00,807 --> 00:14:04,211 +Next, I'll describe the code +needed to setup search suggestions. + +191 +00:14:06,013 --> 00:14:11,685 +To manage search suggestions, +conform to UISearchResultsUpdating + +192 +00:14:11,718 --> 00:14:16,056 +and set your searchController's +searchResultsUpdater. + +193 +00:14:16,089 --> 00:14:19,927 +This allows you to update +suggestions as the query changes + +194 +00:14:19,960 --> 00:14:22,663 +and to act +on a selected search suggestion. + +195 +00:14:24,097 --> 00:14:29,303 +When the query changes, +updateSearchResults(for:) is called, + +196 +00:14:29,336 --> 00:14:31,805 +allowing you to update search suggestions. + +197 +00:14:33,040 --> 00:14:36,643 +What suggestions to provide is up to you. + +198 +00:14:36,677 --> 00:14:39,780 +Setting an empty array +will clear the suggestions UI. + +199 +00:14:41,114 --> 00:14:46,486 +UIKit provides UISearchSuggestionItem +to specify suggestion content. + +200 +00:14:48,021 --> 00:14:51,491 +To respond +to the selection of a suggestion, + +201 +00:14:51,525 --> 00:14:55,996 +implement +updateSearchResults(for:selecting:). + +202 +00:14:56,029 --> 00:14:59,333 +This method passes +the selected search suggestion, + +203 +00:14:59,366 --> 00:15:02,369 +so you may react to it appropriately. + +204 +00:15:02,402 --> 00:15:07,007 +In this example I update the search +by replacing the current query + +205 +00:15:07,040 --> 00:15:10,444 +with the one specified +by the search suggestion. + +206 +00:15:10,477 --> 00:15:13,947 +UISearchTextField also has +searchSuggestions, + +207 +00:15:13,981 --> 00:15:16,850 +so if you prefer to use that class +on its own, + +208 +00:15:16,884 --> 00:15:19,753 +you can still implement +search suggestions. + +209 +00:15:19,786 --> 00:15:24,558 +But if you are using UISearchController, +you should use its property instead. + +210 +00:15:25,759 --> 00:15:29,463 +In iOS 16, UIKit provides new API + +211 +00:15:29,496 --> 00:15:33,233 +to help you +bring more productivity to your users. + +212 +00:15:33,267 --> 00:15:36,570 +Bring more discoverability +to your advanced features + +213 +00:15:36,603 --> 00:15:38,772 +with center items and the title menu. + +214 +00:15:39,940 --> 00:15:43,177 +Improve your document support +by providing drag & drop + +215 +00:15:43,210 --> 00:15:46,113 +and sharing directly +from the navigation bar. + +216 +00:15:46,980 --> 00:15:52,920 +Make it easier and faster to search +by providing search suggestions + +217 +00:15:52,953 --> 00:15:56,190 +and get a great Mac experience +right out of the box, + +218 +00:15:56,223 --> 00:15:58,192 +with nearly zero effort. + +219 +00:15:58,225 --> 00:15:59,927 +Thanks for watching this video. + +220 +00:15:59,960 --> 00:16:03,864 +I can't wait to see how you +enhance your apps to be desktop class! + +221 +00:16:03,897 --> 00:16:05,966 +[spacey music] + diff --git a/eng/2022 Session 10070 Build a desktop-class iPad app en.srt b/eng/2022 Session 10070 Build a desktop-class iPad app en.srt new file mode 100644 index 0000000..befc9eb --- /dev/null +++ b/eng/2022 Session 10070 Build a desktop-class iPad app en.srt @@ -0,0 +1,1476 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,843 --> 00:00:12,112 +Mohammed: +Hi, I'm Mohammed from UIKit. + +3 +00:00:12,145 --> 00:00:14,114 +Thank you for joining me +for this deep dive + +4 +00:00:14,147 --> 00:00:17,584 +into building a Desktop Class iPad app. + +5 +00:00:17,618 --> 00:00:20,954 +In this video, +we'll use iPadOS 16 APIs + +6 +00:00:20,988 --> 00:00:25,092 +to update an existing iPad app +to a desktop class experience. + +7 +00:00:25,125 --> 00:00:28,362 +We'll start by using +new navigation bar API + +8 +00:00:28,395 --> 00:00:30,664 +to surface powerful functionality, + +9 +00:00:30,697 --> 00:00:34,334 +increase UI density, +and provide customizability. + +10 +00:00:35,836 --> 00:00:39,573 +Then, we'll adopt new UICollectionView +and menu API + +11 +00:00:39,606 --> 00:00:45,212 +to enable complex workflows +and quick actions on multiple selections. + +12 +00:00:45,245 --> 00:00:49,316 +And we'll round things out by enabling +the new Find and Replace experience + +13 +00:00:49,349 --> 00:00:52,753 +and enhancing text editing +with the new Edit Menu. + +14 +00:00:52,786 --> 00:00:57,858 +The app we'll be updating is +a Markdown editor built for iPadOS 15. + +15 +00:00:57,891 --> 00:01:01,495 +As we walk through each step +of the modernization process, + +16 +00:01:01,528 --> 00:01:05,299 +I'll discuss best practices +and motivations behind our choices, + +17 +00:01:05,332 --> 00:01:08,435 +giving you an idea of the factors +you should consider + +18 +00:01:08,468 --> 00:01:12,072 +while taking your own app +through a similar process. + +19 +00:01:13,140 --> 00:01:15,809 +If you'd like a bit of a primer +before getting started, + +20 +00:01:15,843 --> 00:01:18,045 +check out “Meet Desktop Class iPad” + +21 +00:01:18,078 --> 00:01:22,549 +for a breakdown of +all of UIKit's new iPadOS APIs, + +22 +00:01:22,583 --> 00:01:25,152 +and check out +“What's new in iPad app design” + +23 +00:01:25,185 --> 00:01:30,090 +for tips on how to design +the best Desktop Class iPad app possible. + +24 +00:01:30,123 --> 00:01:31,925 +All right, let's dive right in! + +25 +00:01:32,659 --> 00:01:37,197 +To start things off, let's consider +the organization of our app's controls. + +26 +00:01:37,231 --> 00:01:41,401 +Since the app is designed for iPadOS 15, +it already exposes + +27 +00:01:41,435 --> 00:01:44,571 +its most important controls +in the navigation bar + +28 +00:01:44,605 --> 00:01:49,276 +and places secondary controls +in various menus and popovers. + +29 +00:01:50,878 --> 00:01:55,916 +In iPadOS 16, UIKit formalizes +the existing navigation style + +30 +00:01:55,949 --> 00:02:01,188 +and introduces two new ones with +a denser and more customizable layout. + +31 +00:02:01,221 --> 00:02:05,826 +This allows apps to express the layout +that's most appropriate to their content + +32 +00:02:05,859 --> 00:02:09,229 +while bringing more functionality +to the forefront of the UI. + +33 +00:02:11,265 --> 00:02:16,603 +Navigator apps have a familiar +push / pop navigation model. + +34 +00:02:16,637 --> 00:02:20,340 +This is generally appropriate +for apps that display hierarchical data, + +35 +00:02:20,374 --> 00:02:22,142 +like Settings. + +36 +00:02:23,443 --> 00:02:27,714 +Browsers, like Safari or Files +are ideal for looking through + +37 +00:02:27,748 --> 00:02:32,386 +and navigating back and forth between +multiple documents or folder structures. + +38 +00:02:34,655 --> 00:02:39,993 +And Editors are great for focused viewing +or editing of individual documents. + +39 +00:02:42,029 --> 00:02:46,200 +Being a Markdown editor, this style +is the perfect choice for our app. + +40 +00:02:48,302 --> 00:02:52,506 +The editor style aligns the title +towards the leading edge of the bar, + +41 +00:02:52,539 --> 00:02:55,909 +opening up its center +for a new set of items. + +42 +00:02:55,943 --> 00:02:58,912 +This'll allow us to expose +additional functionality + +43 +00:02:58,946 --> 00:03:02,916 +that might've been hidden away +in other views or menus. + +44 +00:03:02,950 --> 00:03:07,454 +We're going to do a few things to make +as much use of this design as possible. + +45 +00:03:07,487 --> 00:03:12,059 +We'll start by customizing +the built-in back action to fit our needs. + +46 +00:03:12,092 --> 00:03:15,162 +Then we'll add a title menu +with some document info + +47 +00:03:15,195 --> 00:03:18,165 +and common document actions. + +48 +00:03:18,198 --> 00:03:23,237 +We'll also add support for renaming +via the new built-in rename UI. + +49 +00:03:23,270 --> 00:03:28,242 +And finally, we'll make previously buried +functionality more easily accessible + +50 +00:03:28,275 --> 00:03:30,177 +by bringing it to the center of the bar. + +51 +00:03:30,944 --> 00:03:34,047 +Let's start by opting into +the editor style by setting + +52 +00:03:34,081 --> 00:03:38,285 +our view controller's navigationItem's +style property to .editor. + +53 +00:03:39,987 --> 00:03:42,923 +This immediately gives us +the leading aligned title + +54 +00:03:42,956 --> 00:03:44,892 +and opens up the center area. + +55 +00:03:47,394 --> 00:03:51,098 +After that, +let's remove our trailing done button + +56 +00:03:51,131 --> 00:03:55,636 +and replace it +using the new backAction API. + +57 +00:03:55,669 --> 00:03:58,505 +This way we get a more standard look +for the action + +58 +00:03:58,539 --> 00:04:01,975 +that dismisses this view +and returns to the document picker. + +59 +00:04:05,078 --> 00:04:09,750 +Next let's figure out whether our app +would benefit from a title menu. + +60 +00:04:09,783 --> 00:04:12,653 +As the name implies, +the title menu is presented + +61 +00:04:12,686 --> 00:04:15,656 +from the navigation bar's title view. + +62 +00:04:15,689 --> 00:04:18,192 +It's a great place to show +document metadata + +63 +00:04:18,225 --> 00:04:22,062 +and surface actions +that apply to the whole document. + +64 +00:04:22,095 --> 00:04:26,366 +If your app isn't document based, +it may be a good place to surface actions + +65 +00:04:26,400 --> 00:04:28,335 +that apply to the entire view. + +66 +00:04:29,203 --> 00:04:33,240 +For our app, it makes sense +to use the document menu's header + +67 +00:04:33,273 --> 00:04:37,544 +to surface some useful information +about the document. + +68 +00:04:37,578 --> 00:04:41,548 +We'll also be able to provide +a draggable representation of the document + +69 +00:04:41,582 --> 00:04:45,686 +and easy access to sharing functionality. + +70 +00:04:45,719 --> 00:04:48,121 +And now it's time to write some code! + +71 +00:04:49,857 --> 00:04:51,892 +Our app is UIDocument backed, + +72 +00:04:51,925 --> 00:04:54,862 +so we can use the UIDocument's fileURL + +73 +00:04:54,895 --> 00:04:58,966 +to instantiate +a UIDocumentProperties object. + +74 +00:05:00,501 --> 00:05:05,105 +Next, we'll use that same URL +to create an NSItemProvider. + +75 +00:05:07,608 --> 00:05:11,879 +Then we'll use the item provider +to create a UIDragItem + +76 +00:05:11,912 --> 00:05:16,416 +which we'll return from the properties +object's dragItemsProvider. + +77 +00:05:18,252 --> 00:05:22,022 +We'll also use it to construct +a UIActivityViewController + +78 +00:05:22,055 --> 00:05:26,994 +which we'll return from the properties +object's activityViewControllerProvider. + +79 +00:05:27,027 --> 00:05:31,198 +And finally, we'll set the properties +object as the editor view controller's + +80 +00:05:31,231 --> 00:05:34,134 +navigationItem's documentProperties. + +81 +00:05:34,168 --> 00:05:37,371 +The code we just wrote results in +this document header, + +82 +00:05:37,404 --> 00:05:39,973 +which provides a quick overview +of the document + +83 +00:05:40,007 --> 00:05:44,211 +including its name, size, +and an icon representation. + +84 +00:05:44,244 --> 00:05:48,081 +Since we specified drag item +and activity view controller providers, + +85 +00:05:48,115 --> 00:05:51,485 +I can drag the icon to copy +the document outside the app + +86 +00:05:51,518 --> 00:05:55,355 +or tap the share button to bring up +an activity view controller. + +87 +00:05:57,057 --> 00:05:59,693 +In addition to displaying +the document header, + +88 +00:05:59,726 --> 00:06:02,696 +the title menu is a good place +to provide functionality + +89 +00:06:02,729 --> 00:06:05,799 +that applies to the entire document. + +90 +00:06:05,832 --> 00:06:09,536 +There are two kinds of actions +that can be displayed in this menu: + +91 +00:06:09,570 --> 00:06:14,842 +system-provided ones with pre-defined +localized titles and symbol images, + +92 +00:06:14,875 --> 00:06:17,945 +and entirely custom ones +that the app provides. + +93 +00:06:19,546 --> 00:06:21,481 +Since it comes with +some additional behavior, + +94 +00:06:21,515 --> 00:06:24,284 +let's start with the rename action. + +95 +00:06:24,318 --> 00:06:26,954 +We can add this action to our menu +by conforming + +96 +00:06:26,987 --> 00:06:30,057 +to the rename delegate protocol. + +97 +00:06:30,090 --> 00:06:34,561 +When triggered, the action presents +the bar's built-in rename UI. + +98 +00:06:35,963 --> 00:06:40,868 +First we'll assign our view controller +as its navigation item's renameDelegate. + +99 +00:06:43,403 --> 00:06:48,041 +Then, we'll implement +navigationItemDidEndRenamingWithTitle + +100 +00:06:48,075 --> 00:06:51,345 +to handle the actual renaming +of the displayed document. + +101 +00:06:52,846 --> 00:06:56,717 +This function is called +when the rename action is committed. + +102 +00:06:56,750 --> 00:07:02,322 +It's the app's responsibility to handle +this by actually renaming the document. + +103 +00:07:02,356 --> 00:07:04,825 +The API is intentionally open ended + +104 +00:07:04,858 --> 00:07:08,228 +to support any kind of data model +your app may have. + +105 +00:07:08,262 --> 00:07:10,797 +Moving on to other +system provided actions, + +106 +00:07:10,831 --> 00:07:15,602 +we'll first need to override their +functions on our editor view controller. + +107 +00:07:15,636 --> 00:07:20,474 +Here we've implemented +the duplicate and move functions. + +108 +00:07:20,507 --> 00:07:23,877 +UIKit automatically surfaces +system-provided actions, + +109 +00:07:23,911 --> 00:07:25,612 +including the rename action, + +110 +00:07:25,646 --> 00:07:28,482 +in the navigationItem's titleMenuProvider + +111 +00:07:28,515 --> 00:07:32,319 +as an array +of suggested UIMenuElements. + +112 +00:07:32,352 --> 00:07:34,321 +To include them in our title menu, + +113 +00:07:34,354 --> 00:07:37,558 +we'll just add them +to the returned menu's children. + +114 +00:07:39,159 --> 00:07:41,094 +In addition to the system-vended actions, + +115 +00:07:41,128 --> 00:07:43,797 +we can add entirely custom actions + +116 +00:07:43,830 --> 00:07:47,067 +or even whole menu hierarchies. + +117 +00:07:47,100 --> 00:07:53,006 +Here I've added an Export submenu +with export as HTML and PDF sub-actions. + +118 +00:07:54,408 --> 00:07:57,444 +And with that, tapping the title view +now brings up a menu + +119 +00:07:57,477 --> 00:08:01,481 +with the document header +and all the actions we just added. + +120 +00:08:01,515 --> 00:08:03,050 +And when I select rename, + +121 +00:08:03,083 --> 00:08:05,219 +the built-in rename UI is activated + +122 +00:08:05,252 --> 00:08:07,221 +and I'm able to rename the document. + +123 +00:08:08,755 --> 00:08:11,892 +Now that we've started establishing +the base structure of our app, + +124 +00:08:11,925 --> 00:08:14,161 +it's a good time to check in +on how things look + +125 +00:08:14,194 --> 00:08:17,064 +when we build our app with Mac catalyst. + +126 +00:08:17,097 --> 00:08:20,167 +When we run the app on a Mac, +we'll find that the editor style + +127 +00:08:20,200 --> 00:08:23,370 +with its leading aligned title +has been translated nicely. + +128 +00:08:24,872 --> 00:08:27,307 +Our back action +has also been carried over, + +129 +00:08:27,341 --> 00:08:30,444 +and when clicked, +brings up a file browser. + +130 +00:08:31,745 --> 00:08:34,381 +The system provided actions +and rename functionality + +131 +00:08:34,414 --> 00:08:37,584 +are automatically surfaced +in the app's File menu. + +132 +00:08:37,618 --> 00:08:41,655 +Note that the titleMenuProvider +is not called on Mac Catalyst, + +133 +00:08:41,688 --> 00:08:45,993 +so our custom actions are not included +in the File menu. + +134 +00:08:46,026 --> 00:08:48,962 +To expose these actions, +we would need to manually add them + +135 +00:08:48,996 --> 00:08:52,966 +to the app's main menu +using the main UIMenuSystem. + +136 +00:08:55,202 --> 00:08:57,905 +All right, let's continue +our modernization process. + +137 +00:08:57,938 --> 00:09:01,942 +We'll keep checking in on the Mac +as we make progress towards our goal. + +138 +00:09:01,975 --> 00:09:06,880 +Let's consider the opportunities +made available by the bar's center area. + +139 +00:09:06,914 --> 00:09:10,083 +The iOS 15 version of the app +has a menu that holds + +140 +00:09:10,117 --> 00:09:13,520 +a number of secondary controls and tools. + +141 +00:09:13,554 --> 00:09:17,524 +With center items, we're able to make +these tools more discoverable. + +142 +00:09:19,092 --> 00:09:23,463 +Since the center area is customizable, +we can include a large set of controls + +143 +00:09:23,497 --> 00:09:28,068 +without worrying about filling the UI +with less commonly used ones. + +144 +00:09:28,101 --> 00:09:32,973 +Each person can tailor the bar's contents +to fit their workflow. + +145 +00:09:33,006 --> 00:09:37,878 +The first step in enabling customization +is specifying a customizationIdentifier + +146 +00:09:37,911 --> 00:09:39,880 +on the navigation item. + +147 +00:09:41,181 --> 00:09:46,019 +Next, we'll define the center items +as UIBarButtonItemGroups. + +148 +00:09:46,053 --> 00:09:50,424 +Groups are an existing concept +that's been extended to UINavigationBar + +149 +00:09:50,457 --> 00:09:54,962 +and enhanced to support +customization in iOS 16. + +150 +00:09:54,995 --> 00:09:59,499 +This screenshot shows the set of +center items we'd like to show by default. + +151 +00:09:59,533 --> 00:10:01,835 +The synchronize scrolling button +all the way on the left + +152 +00:10:01,869 --> 00:10:04,872 +provides an important function +that can't be reached + +153 +00:10:04,905 --> 00:10:07,374 +by any other means, + +154 +00:10:07,407 --> 00:10:09,977 +so it makes sense to place it +in a fixed group + +155 +00:10:10,010 --> 00:10:15,682 +using UIBarButtonItem's +new creatingFixedGroup() function. + +156 +00:10:15,716 --> 00:10:20,087 +Fixed groups are not customizable +and cannot be moved by the user. + +157 +00:10:22,022 --> 00:10:25,993 +The add link button, on the other hand, +doesn't provide critical functionality, + +158 +00:10:26,026 --> 00:10:30,531 +and the same task can be achieved +by typing link tags in the editor, + +159 +00:10:30,564 --> 00:10:36,470 +so we'll use creatingOptionalGroup +to create a completely customizable item. + +160 +00:10:36,503 --> 00:10:39,773 +And we'll give it a unique +customizationIdentifier + +161 +00:10:39,806 --> 00:10:43,544 +so the customization is persisted +across app launches. + +162 +00:10:45,112 --> 00:10:47,848 +We'll follow a similar process +to define the remaining items + +163 +00:10:47,881 --> 00:10:51,385 +in the default set, +then move on to lower priority items + +164 +00:10:51,418 --> 00:10:54,488 +that don't need to be available +by default. + +165 +00:10:54,521 --> 00:10:58,392 +One such item is the text format group, +which includes the bold, + +166 +00:10:58,425 --> 00:11:00,761 +italics, and underline items. + +167 +00:11:01,995 --> 00:11:04,231 +It's not important enough +to show by default, + +168 +00:11:04,264 --> 00:11:08,836 +but we want it in the customization +popover so it can be dragged into the bar. + +169 +00:11:10,470 --> 00:11:13,540 +To achieve this, +we'll use UIBarButtonItemGroup's + +170 +00:11:13,574 --> 00:11:18,812 +optionalGroup initializer with +isInDefaultCustomization set to false. + +171 +00:11:21,248 --> 00:11:24,218 +We'll also be sure to give the group +a representative item + +172 +00:11:24,251 --> 00:11:26,353 +so it has a title in the popover, + +173 +00:11:26,386 --> 00:11:28,388 +and has a compact representation + +174 +00:11:28,422 --> 00:11:31,692 +that it can be collapsed to +when the bar runs out of space. + +175 +00:11:33,227 --> 00:11:35,662 +Back on the iPad, +the center items we defined + +176 +00:11:35,696 --> 00:11:38,465 +show up in the center of the bar. + +177 +00:11:38,498 --> 00:11:40,734 +If I click the newly added More button, + +178 +00:11:40,767 --> 00:11:44,705 +a menu shows up +with a Customize Toolbar action. + +179 +00:11:44,738 --> 00:11:48,108 +And if I click that, +the customization mode is activated. + +180 +00:11:49,376 --> 00:11:53,847 +The sync scrolling button that we marked +as fixed is de-emphasized and static, + +181 +00:11:53,881 --> 00:11:58,318 +while all the other items lift and shake +to show that they're customizable. + +182 +00:12:00,220 --> 00:12:04,258 +Optional items like the Format group +show up in the popover + +183 +00:12:04,291 --> 00:12:06,393 +and can be dragged into the bar. + +184 +00:12:09,596 --> 00:12:12,499 +When we run the app on a Mac, +we find that the center items + +185 +00:12:12,533 --> 00:12:16,837 +have been converted to fully customizable +macOS toolbar buttons. + +186 +00:12:19,106 --> 00:12:24,378 +Before we move on, let's go back to +the iPad for a minute and resize the app. + +187 +00:12:24,411 --> 00:12:27,080 +Now that we have less space available +in the toolbar, + +188 +00:12:27,114 --> 00:12:30,684 +the center items are no longer visible. + +189 +00:12:30,717 --> 00:12:34,821 +UIKit automatically handles +showing and hiding center items + +190 +00:12:34,855 --> 00:12:37,691 +in response to the available space. + +191 +00:12:37,724 --> 00:12:42,462 +Any items that don't fit +are displayed in the overflow menu. + +192 +00:12:42,496 --> 00:12:45,332 +Standard bar button items +are automatically converted + +193 +00:12:45,365 --> 00:12:47,401 +to their menu representation, + +194 +00:12:47,434 --> 00:12:52,873 +but we're also able to provide +a custom menu representation if we like. + +195 +00:12:52,906 --> 00:12:56,877 +Since UIKit has no insight +into the purpose of a custom view item, + +196 +00:12:56,910 --> 00:13:00,047 +our slider item +isn't automatically translated. + +197 +00:13:00,080 --> 00:13:03,317 +We'll need to manually +specify a menu representation. + +198 +00:13:04,751 --> 00:13:06,086 +Here's our slider item. + +199 +00:13:06,119 --> 00:13:08,722 +It's a single bar button item +with a custom view, + +200 +00:13:08,755 --> 00:13:12,426 +wrapped in an optional bar button group. + +201 +00:13:12,459 --> 00:13:15,162 +To provide the core functionality +of the slider, + +202 +00:13:15,195 --> 00:13:19,867 +we'll define the menu representation +as a UIMenu with Decrease, + +203 +00:13:19,900 --> 00:13:22,603 +Reset, and Increase actions. + +204 +00:13:25,038 --> 00:13:28,642 +Using UIMenu's +new preferredElementSize property, + +205 +00:13:28,675 --> 00:13:33,313 +we can give the menu a more +compact side by side appearance. + +206 +00:13:35,516 --> 00:13:38,585 +And using the +new keepsMenuPresented attribute, + +207 +00:13:38,619 --> 00:13:42,489 +we can keep the menu presented +after each action is performed, + +208 +00:13:42,523 --> 00:13:45,092 +allowing the font size to be changed +multiple times + +209 +00:13:45,125 --> 00:13:48,929 +without dismissing +and re-presenting the menu. + +210 +00:13:48,962 --> 00:13:51,331 +Let's run this on the iPad again. + +211 +00:13:51,365 --> 00:13:54,001 +Now when we bring up the overflow menu, + +212 +00:13:54,034 --> 00:13:58,739 +the slider appears as an inline menu +with three side-by-side actions, + +213 +00:13:58,772 --> 00:14:01,175 +covering the full functionality +of the slider. + +214 +00:14:02,576 --> 00:14:05,445 +Since the small element size +doesn't exist on the Mac, + +215 +00:14:05,479 --> 00:14:09,716 +the actions will appear +as standard macOS menu items. + +216 +00:14:09,750 --> 00:14:12,986 +And that's it for UI organization +and customization. + +217 +00:14:13,020 --> 00:14:16,023 +Next, let's look into speeding up +some workflows in the app + +218 +00:14:16,056 --> 00:14:19,259 +using new collection view and menu API. + +219 +00:14:19,293 --> 00:14:22,396 +Our app has a table of contents sidebar +that can be used + +220 +00:14:22,429 --> 00:14:27,401 +to quickly navigate the document +or take action on top level tags. + +221 +00:14:27,434 --> 00:14:31,371 +Prior to iOS 16, +adding the ability to edit multiple items + +222 +00:14:31,405 --> 00:14:34,775 +would've likely meant implementing +a distinct edit mode, + +223 +00:14:34,808 --> 00:14:37,911 +with bulk actions relegated +to buttons in a toolbar. + +224 +00:14:40,914 --> 00:14:44,718 +iOS 16 introduces a new design +for multi-item menus + +225 +00:14:44,751 --> 00:14:49,122 +with a flock of items that clearly +communicates which items the menu affects + +226 +00:14:49,156 --> 00:14:53,460 +and provides a direct transition +to a multi-item drag. + +227 +00:14:53,493 --> 00:14:57,464 +In a desktop class iPad app, +this new menu design is best paired + +228 +00:14:57,497 --> 00:15:00,734 +with a lighter weight selection style. + +229 +00:15:00,767 --> 00:15:03,637 +"Lightweight" here +means selecting multiple items + +230 +00:15:03,670 --> 00:15:06,540 +without kicking the collection view +into an edit mode + +231 +00:15:06,573 --> 00:15:09,610 +or making significant changes +to the app's UI. + +232 +00:15:09,643 --> 00:15:15,082 +We can achieve this and enable +keyboard focus using existing API. + +233 +00:15:15,115 --> 00:15:18,285 +First, we'll set +allowsMultipleSelection to true. + +234 +00:15:21,121 --> 00:15:25,626 +Then we'll enable keyboard focus +by setting allowsFocus to true. + +235 +00:15:28,128 --> 00:15:33,600 +And we'll allow focus to drive selection +by setting selectionFollowsFocus to true. + +236 +00:15:36,270 --> 00:15:39,306 +If we run this on our iPad, +we immediately notice + +237 +00:15:39,339 --> 00:15:41,775 +that as each item is added +to the selection, + +238 +00:15:41,808 --> 00:15:44,211 +it still fires its selection action, + +239 +00:15:44,244 --> 00:15:46,580 +causing the editor view to scroll. + +240 +00:15:46,613 --> 00:15:49,650 +Let's head back to our code and +figure out what's going on. + +241 +00:15:51,285 --> 00:15:52,286 +There it is! + +242 +00:15:52,319 --> 00:15:54,855 +The code in didSelectItemAtIndexPath + +243 +00:15:54,888 --> 00:15:57,724 +tries to disallow scrolling +while in edit mode + +244 +00:15:57,758 --> 00:16:01,695 +by checking +the collectionView's isEditing property. + +245 +00:16:01,728 --> 00:16:05,098 +Now that we allow multiple +selection outside of edit mode, + +246 +00:16:05,132 --> 00:16:08,001 +this code runs for every selection. + +247 +00:16:08,035 --> 00:16:11,972 +We can fix this using a new +UICollectionViewDelegate method. + +248 +00:16:12,005 --> 00:16:16,510 +We'll implement +performPrimaryActionForItemAtIndexPath + +249 +00:16:16,543 --> 00:16:21,215 +and just move our scrolling code +to this new function. + +250 +00:16:21,248 --> 00:16:24,251 +Since this function is only called +when a single item is tapped + +251 +00:16:24,284 --> 00:16:26,420 +and the collection view is not editing, + +252 +00:16:26,453 --> 00:16:29,089 +we no longer need the check for edit mode. + +253 +00:16:31,658 --> 00:16:34,795 +And since we don't have +any selection related behavior, + +254 +00:16:34,828 --> 00:16:39,333 +we can remove our implementation +of did select item at indexPath. + +255 +00:16:43,237 --> 00:16:46,874 +Back on the iPad, +selecting multiple items no longer scrolls + +256 +00:16:46,907 --> 00:16:50,177 +to the corresponding text +in the editor view. + +257 +00:16:50,210 --> 00:16:53,080 +With that done, +let's actually add support for the menu. + +258 +00:16:55,382 --> 00:17:00,554 +In iPadOS 16, UICollectionViewDelegate's +existing single item menu method + +259 +00:17:00,587 --> 00:17:02,256 +is deprecated. + +260 +00:17:02,289 --> 00:17:08,328 +Its replacement supports displaying menus +for anywhere from zero to many items. + +261 +00:17:08,362 --> 00:17:11,298 +The number of items in the given +indexPaths array + +262 +00:17:11,331 --> 00:17:13,500 +depends on how many items are selected, + +263 +00:17:13,534 --> 00:17:15,636 +and where the menu is invoked. + +264 +00:17:17,271 --> 00:17:22,042 +If the array is empty, then the menu was +invoked in the blank space between cells. + +265 +00:17:25,012 --> 00:17:26,914 +If it has a single indexPath, + +266 +00:17:26,947 --> 00:17:30,150 +then it was invoked on an item +that is either deselected + +267 +00:17:30,184 --> 00:17:32,386 +or is the sole selected one. + +268 +00:17:35,088 --> 00:17:37,824 +If it has more than one item, +then the menu was invoked + +269 +00:17:37,858 --> 00:17:41,461 +on an item that is part +of a multiple selection. + +270 +00:17:44,031 --> 00:17:47,668 +If I head back to the iPad, +select the top four items again, + +271 +00:17:47,701 --> 00:17:50,103 +and two-finger click +one of the selected items, + +272 +00:17:50,137 --> 00:17:52,706 +a new multi-item menu comes up. + +273 +00:17:56,343 --> 00:17:58,111 +When I do the same thing on a Mac, + +274 +00:17:58,145 --> 00:18:01,248 +a ring is drawn around the selected cells +to highlight them. + +275 +00:18:02,783 --> 00:18:04,685 +With multi-item menus done, + +276 +00:18:04,718 --> 00:18:07,321 +let's look into enhancing +the text editing experience + +277 +00:18:07,354 --> 00:18:10,624 +using the new Find and Replace +and edit menu features. + +278 +00:18:10,657 --> 00:18:13,660 +Our app uses a UITextView for its editor + +279 +00:18:13,694 --> 00:18:17,931 +and doesn't require +any custom Find and Replace behavior, + +280 +00:18:17,965 --> 00:18:21,602 +so all we need to do +to enable the default system functionality + +281 +00:18:21,635 --> 00:18:26,807 +is set the text view's +isFindInteractionEnabled property to true. + +282 +00:18:26,840 --> 00:18:29,743 +With that set, +hitting Command+F while editing text + +283 +00:18:29,776 --> 00:18:31,979 +brings up the Find and Replace UI. + +284 +00:18:33,180 --> 00:18:37,084 +Adding custom actions to the text view's +edit menu doesn't take much, + +285 +00:18:37,117 --> 00:18:40,988 +and can enable +some great quick editing features. + +286 +00:18:41,021 --> 00:18:44,057 +We'll just implement +the new UITextViewDelegate method + +287 +00:18:44,091 --> 00:18:48,495 +edit menu for text in range +suggested actions. + +288 +00:18:48,529 --> 00:18:52,699 +In the implementation, +we can construct and return a UIMenu + +289 +00:18:52,733 --> 00:18:55,636 +that combines custom actions, +like this Hide action, + +290 +00:18:55,669 --> 00:18:57,204 +with the system menu. + +291 +00:18:59,907 --> 00:19:01,308 +And this is the result. W + +292 +00:19:01,341 --> 00:19:04,011 +hen I select some text +and bring up the edit menu, + +293 +00:19:04,044 --> 00:19:08,615 +both our custom actions and +the system-provided ones are displayed. + +294 +00:19:08,649 --> 00:19:11,952 +For more information about +Find and Replace and the edit menu, + +295 +00:19:11,985 --> 00:19:16,223 +check out “Adopt desktop class +editing interactions.” + +296 +00:19:16,256 --> 00:19:17,090 +And that's it! + +297 +00:19:17,124 --> 00:19:20,127 +With these few changes, +we've taken some great basic steps + +298 +00:19:20,160 --> 00:19:22,496 +towards making our app desktop class + +299 +00:19:22,529 --> 00:19:25,599 +and translating it seamlessly to the Mac. + +300 +00:19:25,632 --> 00:19:28,435 +Use the APIs offered in iPadOS 16 + +301 +00:19:28,468 --> 00:19:31,338 +to take your own app +through a similar process. + +302 +00:19:31,371 --> 00:19:35,642 +Start by choosing a navigation style +that fits your app. + +303 +00:19:35,676 --> 00:19:39,947 +Enhance document workflows with +document properties and the title menu. + +304 +00:19:39,980 --> 00:19:45,452 +And surface important functionality and +provide customizability with center items. + +305 +00:19:45,485 --> 00:19:49,289 +Enable quickly acting on multiple +items with multi-item menus. + +306 +00:19:49,323 --> 00:19:53,093 +And enhance your app's text editing +experience using Find and Replace + +307 +00:19:53,126 --> 00:19:54,795 +and the new edit menu. + +308 +00:19:54,828 --> 00:19:58,031 +Whether you're building a new app +or updating an existing one, + +309 +00:19:58,065 --> 00:20:01,668 +I can't wait to use the apps you build +with these new tools. + +310 +00:20:01,702 --> 00:20:04,171 +Thanks for watching. + diff --git a/eng/2022 Session 10071 Adopt desktop class editing interactions en.srt b/eng/2022 Session 10071 Adopt desktop class editing interactions en.srt new file mode 100644 index 0000000..cc89907 --- /dev/null +++ b/eng/2022 Session 10071 Adopt desktop class editing interactions en.srt @@ -0,0 +1,1510 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:12,112 --> 00:00:13,180 +Andy: Hi there. + +3 +00:00:13,213 --> 00:00:17,117 +Welcome to "Adopt desktop class +editing interactions." + +4 +00:00:17,150 --> 00:00:20,654 +I'm Andy, a UIKit frameworks engineer, + +5 +00:00:20,687 --> 00:00:24,491 +and I will be joined later +by my colleague, James. + +6 +00:00:24,525 --> 00:00:27,027 +The iPad is continuously evolving, + +7 +00:00:27,060 --> 00:00:32,366 +without compromising the interactions +that make it simple and easy to use. + +8 +00:00:32,399 --> 00:00:36,670 +In this video, you'll learn about the +exciting new editing interactions + +9 +00:00:36,703 --> 00:00:41,642 +that will transform your app +to become more desktop class. + +10 +00:00:41,675 --> 00:00:48,282 +First, I'll go over the new edit menus, +which received a major facelift in iOS 16. + +11 +00:00:49,283 --> 00:00:55,322 +Later, James will dive into the new system +find and replace experience. + +12 +00:00:55,355 --> 00:00:59,860 +In iOS 16, the edit menu +features an all-new design + +13 +00:00:59,893 --> 00:01:03,063 +that remains familiar +but is more interactive + +14 +00:01:03,096 --> 00:01:05,799 +and is easier to discover actions. + +15 +00:01:06,733 --> 00:01:09,503 +The edit menu now +has alternate presentations + +16 +00:01:09,536 --> 00:01:12,673 +based on the input method used. + +17 +00:01:12,706 --> 00:01:13,974 +For touch interactions, + +18 +00:01:14,007 --> 00:01:17,811 +the edit menu still has +the familiar compact appearance, + +19 +00:01:17,845 --> 00:01:20,013 +but with an improved paging behavior, + +20 +00:01:20,047 --> 00:01:23,483 +allowing actions to be more +discoverable than before. + +21 +00:01:26,486 --> 00:01:29,423 +With the Magic Keyboard or a trackpad, + +22 +00:01:29,456 --> 00:01:33,160 +a context menu is presented +on secondary or right click + +23 +00:01:33,193 --> 00:01:38,966 +for a more desktop class experience. + +24 +00:01:38,999 --> 00:01:45,072 +Similarly, touch interaction on the iPhone +will display the new edit menu. + +25 +00:01:46,707 --> 00:01:48,809 +And for Mac Catalyst apps, + +26 +00:01:48,842 --> 00:01:53,647 +context menus that Mac users +are familiar with is presented. + +27 +00:01:53,680 --> 00:01:59,019 +In iOS 16, the text edit menu +gets a major power-up + +28 +00:01:59,052 --> 00:02:02,022 +with new data detectors integration. + +29 +00:02:02,055 --> 00:02:06,126 +This includes inline unit +and currency conversions, + +30 +00:02:06,159 --> 00:02:07,728 +as well as smart lookup + +31 +00:02:07,761 --> 00:02:13,467 +which displays contextual actions +depending on the selected text. + +32 +00:02:13,500 --> 00:02:14,868 +For example, + +33 +00:02:14,902 --> 00:02:17,404 +if you select an address in Safari, + +34 +00:02:17,437 --> 00:02:19,406 +you will get Maps-based actions, + +35 +00:02:19,439 --> 00:02:22,843 +like "Get Directions" or "Open in Maps," + +36 +00:02:22,876 --> 00:02:27,114 +on top of the existing edit menu actions. + +37 +00:02:27,147 --> 00:02:31,118 +The best part is, +there is no adoption required! + +38 +00:02:31,151 --> 00:02:34,955 +These features are available +in every text edit menu, + +39 +00:02:34,988 --> 00:02:40,794 +including text interaction views, +WebKit and Safari, as well as PDFKit. + +40 +00:02:42,996 --> 00:02:48,302 +To insert actions into a text view's menu, +implement the new TextViewDelegate method + +41 +00:02:48,335 --> 00:02:51,705 +to customize the displayed menu +for text in the given range + +42 +00:02:51,738 --> 00:02:55,008 +with system provided actions. + +43 +00:02:55,042 --> 00:02:57,244 +If you don't need to customize anything, + +44 +00:02:57,277 --> 00:03:01,415 +return nil +to get the standard system menu. + +45 +00:03:01,448 --> 00:03:06,119 +There are also similar methods +on UITextFieldDelegate and UITextInput + +46 +00:03:06,153 --> 00:03:08,655 +to customize the menu there too. + +47 +00:03:08,689 --> 00:03:13,026 +Please note that inserting menu items +using UIMenuController + +48 +00:03:13,060 --> 00:03:15,896 +is now deprecated in iOS 16, + +49 +00:03:15,929 --> 00:03:18,365 +and you should instead +move to use the new methods + +50 +00:03:18,398 --> 00:03:21,802 +to add menu elements +into your text edit menus, + +51 +00:03:21,835 --> 00:03:26,673 +because where we're going, +we don't need menu controllers! + +52 +00:03:27,574 --> 00:03:31,979 +Here is an example of a text view +with some custom actions. + +53 +00:03:32,012 --> 00:03:35,182 +When a menu is presented +on some text selection, + +54 +00:03:35,215 --> 00:03:38,452 +a custom "Highlight" and +"Insert Photo" action is shown + +55 +00:03:38,485 --> 00:03:41,622 +after the system suggested actions. + +56 +00:03:41,655 --> 00:03:47,661 +Selecting the highlight action performs +a highlight on the text as expected. + +57 +00:03:47,694 --> 00:03:52,332 +Next, when the menu is presented +without any text selection + +58 +00:03:52,366 --> 00:03:54,568 +where there is nothing to highlight, + +59 +00:03:54,601 --> 00:03:57,404 +the menu only displays +the "Insert Photo" action + +60 +00:03:57,437 --> 00:04:01,008 +after the system suggested actions. + +61 +00:04:01,041 --> 00:04:05,612 +I'll show you how to add these actions +using the new API. + +62 +00:04:05,646 --> 00:04:09,616 +To insert actions into the menu +dynamically on presentation, + +63 +00:04:09,650 --> 00:04:12,753 +implement the UITextViewDelegate method + +64 +00:04:12,786 --> 00:04:17,758 +textView editMenuForTextIn range +suggestedActions + +65 +00:04:17,791 --> 00:04:21,228 +In this example, +I only want to add the "Highlight" action + +66 +00:04:21,261 --> 00:04:23,363 +when there is selected text, + +67 +00:04:23,397 --> 00:04:27,467 +so I can add the action dynamically +through this method. + +68 +00:04:29,803 --> 00:04:32,906 +The "Insert Photo" action is always valid, + +69 +00:04:32,940 --> 00:04:39,847 +so I can add it into the array +to always display the action in the menu. + +70 +00:04:39,880 --> 00:04:44,551 +Finally, I'll append my actions +to the system suggested actions, + +71 +00:04:44,585 --> 00:04:49,990 +which includes items like Cut, Copy, +and Paste, and return the menu. + +72 +00:04:50,023 --> 00:04:54,361 +And that's it! + +73 +00:04:54,394 --> 00:05:01,001 +UIEditMenuInteraction is the UIInteraction +API that powers the new edit menu. + +74 +00:05:02,002 --> 00:05:04,471 +The interaction allows you +to programmatically present + +75 +00:05:04,505 --> 00:05:07,541 +the lightweight edit menu +outside of text views + +76 +00:05:07,574 --> 00:05:09,977 +based on your own gesture, + +77 +00:05:10,010 --> 00:05:15,782 +and has native support to present +a context menu on secondary click. + +78 +00:05:15,816 --> 00:05:20,821 +In iOS 16, UIMenuController +and all of its related APIs + +79 +00:05:20,854 --> 00:05:24,124 +are replaced by the new edit menu +interaction. + +80 +00:05:25,425 --> 00:05:28,095 +To present an edit menu from scratch, + +81 +00:05:28,128 --> 00:05:33,700 +first, create the interaction +and add it to the view. + +82 +00:05:33,734 --> 00:05:39,006 +Next, configure a gesture recognizer +to present the menu from. + +83 +00:05:39,039 --> 00:05:42,009 +To ensure that the menu +only appears on direct touch + +84 +00:05:42,042 --> 00:05:44,678 +and not from indirect pointer clicks, + +85 +00:05:44,711 --> 00:05:46,947 +be sure to set +the allowedTouchTypes property + +86 +00:05:46,980 --> 00:05:50,784 +of the gesture recognizer +to be direct touch only. + +87 +00:05:50,817 --> 00:05:56,290 +Then, add the gesture recognizer +to the view. + +88 +00:05:56,323 --> 00:05:59,626 +Finally, +when the gesture recognizer fires, + +89 +00:05:59,660 --> 00:06:02,796 +determine if there is content +at the location of the gesture + +90 +00:06:02,829 --> 00:06:06,066 +that could display the menu. + +91 +00:06:06,099 --> 00:06:08,936 +Then, create an edit menu configuration + +92 +00:06:08,969 --> 00:06:12,773 +with a source point +at the gesture's location. + +93 +00:06:12,806 --> 00:06:16,009 +The source point is used to +determine performable actions + +94 +00:06:16,043 --> 00:06:20,180 +in the interaction's view +to display in the menu. + +95 +00:06:22,316 --> 00:06:26,453 +Once configured, +call presentEditMenu(with: configuration) + +96 +00:06:26,486 --> 00:06:28,789 +to show the menu. + +97 +00:06:30,891 --> 00:06:35,295 +When I right-click anywhere within +the selected "Jello there!" view, + +98 +00:06:35,329 --> 00:06:38,832 +a context menu is presented +with performable system actions + +99 +00:06:38,866 --> 00:06:41,535 +for the app's content. + +100 +00:06:41,568 --> 00:06:44,838 +Even more, +when I tap on the selected view, + +101 +00:06:44,872 --> 00:06:48,041 +the edit menu is presented +where my touch occurred, + +102 +00:06:48,075 --> 00:06:52,012 +showing the same actions +as the context menu. + +103 +00:06:52,045 --> 00:06:55,682 +This is good, but it can be better. + +104 +00:06:55,716 --> 00:06:58,752 +While it is nice that the menu appears +where the touch occurred, + +105 +00:06:58,785 --> 00:07:02,422 +it's actually blocking +the selected view's content. + +106 +00:07:02,456 --> 00:07:07,127 +Moreover, I want to insert +a new "Duplicate" action into the menu, + +107 +00:07:07,160 --> 00:07:10,397 +which is not a system default action. + +108 +00:07:10,430 --> 00:07:12,900 +Let's go back and change this. + +109 +00:07:12,933 --> 00:07:15,836 +To show the menu around the selected view, + +110 +00:07:15,869 --> 00:07:17,571 +implement the delegate method + +111 +00:07:17,604 --> 00:07:22,176 +editMenuInteraction +targetRectFor configuration + +112 +00:07:22,209 --> 00:07:27,014 +This method returns a CGRect used to +determine where to present the menu from + +113 +00:07:27,047 --> 00:07:32,819 +and is in the coordinate space +of the interaction's view. + +114 +00:07:32,853 --> 00:07:37,958 +If the method is not implemented +or a null CGRect is provided, + +115 +00:07:37,991 --> 00:07:42,896 +the menu will be presented from +the source point of the configuration. + +116 +00:07:42,930 --> 00:07:46,867 +In this case, to prevent the menu +from occluding the selected view, + +117 +00:07:46,900 --> 00:07:49,403 +return its frame. + +118 +00:07:49,436 --> 00:07:52,272 +Next, to add the "Duplicate" action, + +119 +00:07:52,306 --> 00:07:57,578 +implement editMenuInteraction +menuFor configuration suggestedActions + +120 +00:07:57,611 --> 00:08:01,515 +and append the custom action +after the system suggested actions, + +121 +00:08:01,548 --> 00:08:06,587 +similar to how you would insert actions +into a text view's menu before. + +122 +00:08:07,588 --> 00:08:10,791 +Now, when I tap again +on the selected view, + +123 +00:08:10,824 --> 00:08:15,562 +the menu no longer occludes "Jello there!" +and instead presents around it. + +124 +00:08:15,596 --> 00:08:19,900 +The new "Duplicate" action is also +included when the menu is presented, + +125 +00:08:19,933 --> 00:08:22,669 +all with just a few lines of code. + +126 +00:08:22,703 --> 00:08:23,871 +Brilliant! + +127 +00:08:25,906 --> 00:08:27,574 +For Mac Catalyst apps, + +128 +00:08:27,608 --> 00:08:30,644 +the edit menu bridges +to the familiar context menus + +129 +00:08:30,677 --> 00:08:32,479 +that users expect on the Mac + +130 +00:08:32,513 --> 00:08:36,216 +when they right click +on the interaction's view. + +131 +00:08:36,250 --> 00:08:38,719 +For iPad idiom Mac Catalyst apps, + +132 +00:08:38,752 --> 00:08:44,291 +programmatically presented edit menus +also bridge into context menus. + +133 +00:08:44,324 --> 00:08:48,262 +Please note that programmatic presentation +of the edit menu is not supported + +134 +00:08:48,295 --> 00:08:53,734 +for Mac idiom apps. + +135 +00:08:53,767 --> 00:08:57,437 +To offer seamless bridging +between different presentation styles, + +136 +00:08:57,471 --> 00:09:02,976 +UIEditMenuInteraction is built on top +of the UIMenuElement family of APIs. + +137 +00:09:03,010 --> 00:09:07,014 +These offer more flexibility +and customizability than before, + +138 +00:09:07,047 --> 00:09:10,651 +including support for submenus and images. + +139 +00:09:10,684 --> 00:09:13,086 +If this is your first time using UIMenus, + +140 +00:09:13,120 --> 00:09:19,993 +watch "Modernizing Your UI for iOS 13" +to learn more about menus and actions. + +141 +00:09:20,027 --> 00:09:22,930 +Building on top of UIMenuElement +also means that + +142 +00:09:22,963 --> 00:09:26,633 +the edit menu has access +to a wide variety of APIs, + +143 +00:09:26,667 --> 00:09:31,238 +like UIMenuSystem, +that support menus already. + +144 +00:09:31,271 --> 00:09:36,176 +The edit menu uses the existing +UIMenuSystem.context system + +145 +00:09:36,210 --> 00:09:38,445 +to build its menus. + +146 +00:09:38,478 --> 00:09:40,614 +To find out more about the menu builder, + +147 +00:09:40,647 --> 00:09:45,219 +as well as a deeper dive on responder +chain traversal and command validation, + +148 +00:09:45,252 --> 00:09:50,290 +watch "Taking your iPad apps +to the next level." + +149 +00:09:51,892 --> 00:09:57,598 +Speaking of menus, there are several +new enhancements to UIMenu in iOS 16. + +150 +00:09:57,631 --> 00:10:00,701 +UIMenu now has a preferred +element size property that allows + +151 +00:10:00,734 --> 00:10:06,106 +you to choose between different layouts +in the context menu. + +152 +00:10:06,139 --> 00:10:10,811 +The small size gives the menu +a more compact side-by-side appearance, + +153 +00:10:10,844 --> 00:10:15,148 +fitting more actions in a single row. + +154 +00:10:15,182 --> 00:10:19,086 +The medium size also displays actions +in a side-by-side appearance + +155 +00:10:19,119 --> 00:10:21,388 +but with a little more detail. + +156 +00:10:21,421 --> 00:10:26,894 +This is used by the text edit menu +to display the standard edit menu. + +157 +00:10:26,927 --> 00:10:30,764 +And finally, the large element size +gives the menu its default, + +158 +00:10:30,797 --> 00:10:34,334 +full-width appearance. + +159 +00:10:34,368 --> 00:10:38,005 +Additionally, there is a new +.keepsMenuPresented attribute + +160 +00:10:38,038 --> 00:10:41,074 +on UIMenuElement to keep menus presented + +161 +00:10:41,108 --> 00:10:44,211 +after an action is performed. + +162 +00:10:44,244 --> 00:10:48,048 +Use this attribute to allow actions +to be performed multiple times + +163 +00:10:48,081 --> 00:10:50,984 +without re-presenting the menu. + +164 +00:10:51,018 --> 00:10:55,189 +That's just the tip of the iceberg +for the new edit menu. + +165 +00:10:55,222 --> 00:11:00,627 +Extend text editing functionality +by customizing the text edit menu. + +166 +00:11:00,661 --> 00:11:03,463 +Make sure that your actions +have titles and images + +167 +00:11:03,497 --> 00:11:08,468 +so that the menus look complete +in different presentation styles. + +168 +00:11:08,502 --> 00:11:12,472 +Most importantly, +adopt the new UIEditMenuInteraction + +169 +00:11:12,506 --> 00:11:16,710 +for better customizability +and improved consistency across platforms + +170 +00:11:16,743 --> 00:11:19,046 +and different input methods. + +171 +00:11:19,079 --> 00:11:24,084 +Adding support for the new edit menu +is a great first step. + +172 +00:11:24,117 --> 00:11:27,154 +To complete the desktop class +editing experience, + +173 +00:11:27,187 --> 00:11:29,289 +I'll hand it over to James to talk about + +174 +00:11:29,323 --> 00:11:32,492 +the new system +find and replace experience. + +175 +00:11:34,194 --> 00:11:37,197 +James Magahern: Ah, there it is! + +176 +00:11:37,231 --> 00:11:41,201 +Hi, I'm James Magahern, a UIKit engineer, + +177 +00:11:41,235 --> 00:11:44,905 +and I'm here to talk about +find and replace. + +178 +00:11:44,938 --> 00:11:46,707 +New in iOS 16, + +179 +00:11:46,740 --> 00:11:51,912 +we introduced a new UI component +for finding and replacing text in apps. + +180 +00:11:51,945 --> 00:11:53,881 +It's standard across the system + +181 +00:11:53,914 --> 00:11:56,283 +and included with +many of the built-in apps, + +182 +00:11:56,316 --> 00:11:58,552 +and allows your users to flex their +muscle memory + +183 +00:11:58,585 --> 00:12:02,322 +with even more commonly used +editing shortcuts. + +184 +00:12:02,356 --> 00:12:06,493 +This is the new +find panel running on iPad. + +185 +00:12:06,527 --> 00:12:10,464 +We will automatically transition from +floating inline with the shortcut bar + +186 +00:12:10,497 --> 00:12:12,766 +when a hardware keyboard is attached, + +187 +00:12:12,799 --> 00:12:19,206 +to resting on top of the software keyboard +when used without a hardware keyboard. + +188 +00:12:19,239 --> 00:12:22,543 +On iPhone, we'll adapt +to the smaller screen size + +189 +00:12:22,576 --> 00:12:25,779 +by using a more compact layout. + +190 +00:12:25,812 --> 00:12:29,316 +Automatic dismissal, minimization, +and keyboard avoidance + +191 +00:12:29,349 --> 00:12:33,387 +are all taken care of by the system. + +192 +00:12:33,420 --> 00:12:35,222 +When running your app on a Mac, + +193 +00:12:35,255 --> 00:12:38,192 +we'll present the find panel inline +with your content, + +194 +00:12:38,225 --> 00:12:41,128 +behaving just like the AppKit find bar, + +195 +00:12:41,161 --> 00:12:44,965 +and using a familiar layout +that users expect on the Mac. + +196 +00:12:46,466 --> 00:12:51,471 +If you're using UITextView, +WKWebView, or PDFViews + +197 +00:12:51,505 --> 00:12:54,408 +to display text content in your apps, + +198 +00:12:54,441 --> 00:12:59,847 +all you need to do to get started is set +isFindInteractionEnabled to true + +199 +00:12:59,880 --> 00:13:02,182 +on the built-in find interaction. + +200 +00:13:02,216 --> 00:13:04,551 +It's that simple! + +201 +00:13:04,585 --> 00:13:08,922 +In addition, if you're using +QuickLook to display text content, + +202 +00:13:08,956 --> 00:13:12,626 +this will already be available +without any work from you. + +203 +00:13:14,795 --> 00:13:16,597 +With a hardware keyboard, + +204 +00:13:16,630 --> 00:13:20,534 +all of the standard system shortcuts +like command+F for find, + +205 +00:13:20,567 --> 00:13:22,269 +command+G for find next, + +206 +00:13:22,302 --> 00:13:28,442 +command+shift+G for find previous, +et cetera, will work just as expected. + +207 +00:13:28,475 --> 00:13:33,213 +Access to these commands are available +via the menu bar when running on a Mac. + +208 +00:13:33,247 --> 00:13:36,850 +All you need to do is make sure +the view displaying the content + +209 +00:13:36,884 --> 00:13:40,521 +can and does become first responder. + +210 +00:13:40,554 --> 00:13:43,190 +For users who are not using +a hardware keyboard, + +211 +00:13:43,223 --> 00:13:46,293 +you can invoke the find interaction +programmatically + +212 +00:13:46,326 --> 00:13:51,732 +via presentFindNavigator, +on the included find interaction property. + +213 +00:13:51,765 --> 00:13:53,667 +It might be a good idea +to make this available + +214 +00:13:53,700 --> 00:13:56,703 +via a navigation bar item, for example. + +215 +00:13:58,172 --> 00:14:02,176 +When running on a Mac, there's a couple +other things to keep in mind. + +216 +00:14:02,209 --> 00:14:05,379 +For instance, on iOS, +the find panel is presented + +217 +00:14:05,412 --> 00:14:09,316 +as part of the software keyboard +or shortcut bar. + +218 +00:14:09,349 --> 00:14:13,554 +On the Mac, we'll display it +inline with your content. + +219 +00:14:13,587 --> 00:14:16,590 +If you're installing the find interaction +on a scroll view, + +220 +00:14:16,623 --> 00:14:21,195 +we’ll automatically adjust the content +insets to accommodate the find panel, + +221 +00:14:21,228 --> 00:14:25,866 +and adapt to trait collection +changes automatically. + +222 +00:14:25,899 --> 00:14:28,202 +You should otherwise make sure +that there's enough room + +223 +00:14:28,235 --> 00:14:31,805 +to host the find panel +in your UI on macOS. + +224 +00:14:33,140 --> 00:14:38,679 +Additionally, we'll show a menu containing +a standard set of find options available + +225 +00:14:38,712 --> 00:14:41,915 +when tapping on the magnifying glass icon. + +226 +00:14:41,949 --> 00:14:44,451 +You can customize the contents +of this menu + +227 +00:14:44,484 --> 00:14:49,857 +by using the optionsMenuProvider +property on UIFindInteraction. + +228 +00:14:49,890 --> 00:14:53,594 +This will be more important +with custom implementations. + +229 +00:14:53,627 --> 00:14:55,863 +And that's all it takes if you're using + +230 +00:14:55,896 --> 00:14:59,633 +one of the built-in views +that I mentioned before. + +231 +00:14:59,666 --> 00:15:02,870 +If your app is displaying textual content +by other means, + +232 +00:15:02,903 --> 00:15:08,075 +like a completely custom view +or something like a list view, shown here, + +233 +00:15:08,108 --> 00:15:11,478 +you can still add the find interaction +to your app. + +234 +00:15:11,512 --> 00:15:12,946 +Let me show you how. + +235 +00:15:14,515 --> 00:15:16,717 +The good news about find interaction + +236 +00:15:16,750 --> 00:15:21,021 +is that you can install it +on any arbitrary view. + +237 +00:15:21,054 --> 00:15:24,892 +If you have an existing find and replace +implementation in your app, + +238 +00:15:24,925 --> 00:15:28,061 +it's a snap to bridge over +to UIFindInteraction + +239 +00:15:28,095 --> 00:15:32,266 +and take advantage of the system's UI. + +240 +00:15:32,299 --> 00:15:37,037 +If you don't already have an existing find +implementation for your custom view, + +241 +00:15:37,070 --> 00:15:39,339 +it's still super easy to get started, + +242 +00:15:39,373 --> 00:15:43,677 +especially if you've already implemented +the UITextInput protocol + +243 +00:15:43,710 --> 00:15:46,246 +in order to work with the system keyboard. + +244 +00:15:46,280 --> 00:15:51,084 +Here's how UIFindInteraction works +with custom views. + +245 +00:15:51,118 --> 00:15:55,355 +After installing UIFindInteraction +on your custom view, + +246 +00:15:55,389 --> 00:15:58,892 +set up a find interaction delegate. + +247 +00:15:58,926 --> 00:16:01,995 +The find interaction delegate, +besides being notified + +248 +00:16:02,029 --> 00:16:05,499 +about when a find session begins or ends, + +249 +00:16:05,532 --> 00:16:10,537 +is responsible for dealing out +UIFindSessions. + +250 +00:16:10,571 --> 00:16:14,107 +UIFindSession is an abstract base class + +251 +00:16:14,141 --> 00:16:17,811 +that encapsulates all of the state +for a given session, + +252 +00:16:17,845 --> 00:16:21,582 +such as the currently highlighted result. + +253 +00:16:21,615 --> 00:16:25,352 +It also services all actions +requested from the UI, + +254 +00:16:25,385 --> 00:16:30,090 +such as "go to the next result," +or "search for this string." + +255 +00:16:30,123 --> 00:16:32,826 +If you want to manage +all of this state yourself, + +256 +00:16:32,860 --> 00:16:37,164 +you can choose to vend +a subclass of UIFindSession + +257 +00:16:37,197 --> 00:16:39,032 +from your find interaction delegate. + +258 +00:16:40,601 --> 00:16:43,770 +This is a good option +if you already have an existing + +259 +00:16:43,804 --> 00:16:46,573 +find and replace implementation +in your app, + +260 +00:16:46,607 --> 00:16:50,043 +and want to bridge it over +to the system UI. + +261 +00:16:50,077 --> 00:16:52,513 +Otherwise, it would be a much better idea + +262 +00:16:52,546 --> 00:16:55,482 +to let the system take care +of the state for you, + +263 +00:16:55,516 --> 00:16:59,186 +and instead adopt +the UITextSearching protocol + +264 +00:16:59,219 --> 00:17:05,259 +on whatever class encapsulates the content +of the document being displayed. + +265 +00:17:05,292 --> 00:17:09,897 +To do this, you would return +a UITextSearchingFindSession, + +266 +00:17:09,930 --> 00:17:12,733 +and connect it with your document class. + +267 +00:17:13,534 --> 00:17:16,403 +This is the best option +if you don't yet have + +268 +00:17:16,436 --> 00:17:19,640 +a find implementation +for your custom view. + +269 +00:17:19,673 --> 00:17:21,475 +Here's how to do this in code. + +270 +00:17:22,709 --> 00:17:25,546 +This example has a custom document class + +271 +00:17:25,579 --> 00:17:30,584 +and a custom view +which displays this document. + +272 +00:17:30,617 --> 00:17:34,421 +The UIFindInteraction +will be installed on this view, + +273 +00:17:34,454 --> 00:17:38,458 +and a UITextSearchingFindSession +will be provided with this document + +274 +00:17:38,492 --> 00:17:41,995 +as the "searchable object." + +275 +00:17:42,029 --> 00:17:45,299 +Make sure either your view controller +or your custom view + +276 +00:17:45,332 --> 00:17:50,537 +can become first responder +so keyboard shortcuts work as expected. + +277 +00:17:51,872 --> 00:17:55,042 +Create the find interaction, +and provide a session delegate + +278 +00:17:55,075 --> 00:17:57,311 +to deal out find sessions. + +279 +00:17:57,344 --> 00:18:01,181 +Here, the view controller +is the session delegate. + +280 +00:18:01,215 --> 00:18:04,718 +Then, when asked for a find session +by the interaction, + +281 +00:18:04,751 --> 00:18:08,088 +just return a new +UITextSearchingFindSession + +282 +00:18:08,121 --> 00:18:12,426 +providing your document +as the searchable object. + +283 +00:18:12,459 --> 00:18:15,062 +You will of course need to make sure +that your document class + +284 +00:18:15,095 --> 00:18:17,898 +conforms to the UITextSearching protocol. + +285 +00:18:19,867 --> 00:18:23,237 +The class which implements +the UITextSearching protocol + +286 +00:18:23,270 --> 00:18:27,975 +is responsible for actually finding +text in your document. + +287 +00:18:28,008 --> 00:18:31,111 +The system will call performTextSearch, + +288 +00:18:31,144 --> 00:18:36,750 +and hand you an aggregator object +to which you can provide results. + +289 +00:18:36,783 --> 00:18:42,823 +The aggregator works with UITextRange +to represent results in your document. + +290 +00:18:42,856 --> 00:18:46,293 +This is another abstract class +that you can use + +291 +00:18:46,326 --> 00:18:51,765 +to encapsulate whatever data +makes sense for how you store text. + +292 +00:18:51,798 --> 00:18:54,535 +For example, +this could represent a DOM range + +293 +00:18:54,568 --> 00:18:58,405 +for clients who use WebKit to render text. + +294 +00:18:58,438 --> 00:19:00,507 +The aggregator is also thread-safe, + +295 +00:19:00,541 --> 00:19:04,545 +so you can provide it results +on a background thread. + +296 +00:19:04,578 --> 00:19:06,780 +Finally, since the find interaction + +297 +00:19:06,813 --> 00:19:11,018 +doesn't know how to display results +using your custom view, + +298 +00:19:11,051 --> 00:19:16,490 +you'll also need to decorate results for +a given style when decorate() is called. + +299 +00:19:16,523 --> 00:19:19,393 +The UITextSearching find session +and protocol + +300 +00:19:19,426 --> 00:19:23,830 +also support multiplexing +across multiple visible documents + +301 +00:19:23,864 --> 00:19:25,699 +using the same interaction. + +302 +00:19:26,567 --> 00:19:28,936 +In other words, +if your app displays content + +303 +00:19:28,969 --> 00:19:32,439 +in a manner similar +to Mail's conversation view, + +304 +00:19:32,472 --> 00:19:36,343 +where each "document" in that case +is a mail message, + +305 +00:19:36,376 --> 00:19:41,381 +you can install a single find interaction +on the root level collection view + +306 +00:19:41,415 --> 00:19:45,719 +and perform a find across all documents +at the same time, + +307 +00:19:45,752 --> 00:19:52,059 +allowing your users to jump between +results in different documents with ease. + +308 +00:19:52,092 --> 00:19:57,331 +So that's all it takes to get started +with the new find interaction in iOS 16. + +309 +00:19:57,364 --> 00:20:00,334 +For system views +that display a lot of text content, + +310 +00:20:00,367 --> 00:20:04,371 +make sure to enable +isFindInteractionEnabled. + +311 +00:20:04,404 --> 00:20:08,709 +Move your existing find implementation +to UIFindInteraction. + +312 +00:20:08,742 --> 00:20:13,080 +Implement UITextSearching +and use UITextSearchingFindSession + +313 +00:20:13,113 --> 00:20:16,383 +if you don't yet have +text searching in your app. + +314 +00:20:16,416 --> 00:20:19,286 +And lastly, check and make sure +you don't have any conflicting + +315 +00:20:19,319 --> 00:20:22,723 +keyboard shortcuts in your app. + +316 +00:20:22,756 --> 00:20:27,761 +And that is what it takes to refresh your +app's editing interactions for iOS 16 + +317 +00:20:27,794 --> 00:20:30,764 +and make them truly desktop class. + +318 +00:20:31,498 --> 00:20:34,535 +Try the new text edit menu in your app, + +319 +00:20:34,568 --> 00:20:38,272 +and adopt the edit menu interaction +for custom UI. + +320 +00:20:38,305 --> 00:20:42,843 +And boost productivity by making +your app's text content searchable. + +321 +00:20:42,876 --> 00:20:47,714 +I'm looking forward to finding +these great new features in your app. + +322 +00:20:47,748 --> 00:20:52,286 +Thanks for watching! +Make sure to like, comment, and subscribe. + +323 +00:20:52,319 --> 00:20:55,322 +[upbeat music] + diff --git a/eng/2022 Session 10072 Use SwiftUI with UIKit en.srt b/eng/2022 Session 10072 Use SwiftUI with UIKit en.srt new file mode 100644 index 0000000..180427c --- /dev/null +++ b/eng/2022 Session 10072 Use SwiftUI with UIKit en.srt @@ -0,0 +1,2367 @@ +1 +00:00:00,000 --> 00:00:03,070 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,070 --> 00:00:09,943 +♪ + +3 +00:00:09,943 --> 00:00:14,047 +Hello, I'm Sara Frederixon, +an engineer on the Health app, + +4 +00:00:14,047 --> 00:00:18,051 +and I'm here to talk to you +about using SwiftUI with UIKit. + +5 +00:00:18,051 --> 00:00:22,189 +Like many of you, +I work on an existing UIKit app. + +6 +00:00:22,189 --> 00:00:25,158 +For me, this is the Health app. + +7 +00:00:25,158 --> 00:00:27,494 +The Health app +has many visualizations + +8 +00:00:27,494 --> 00:00:30,430 +to help people understand +their health data, + +9 +00:00:30,430 --> 00:00:33,867 +but building these views +can be quite complex. + +10 +00:00:33,867 --> 00:00:36,803 +I've been interested +in taking advantage of SwiftUI, + +11 +00:00:36,803 --> 00:00:39,740 +so I worked with the UIKit +and SwiftUI teams + +12 +00:00:39,740 --> 00:00:43,477 +to learn how to integrate +both into the same app. + +13 +00:00:43,477 --> 00:00:46,613 +In this video, +I will teach you how easy it is + +14 +00:00:46,613 --> 00:00:50,784 +to start using SwiftUI +in your own UIKit apps. + +15 +00:00:50,784 --> 00:00:54,354 +First I'll cover the existing +UIHostingController, + +16 +00:00:54,354 --> 00:00:58,959 +which has some new updates +that add even more flexibility. + +17 +00:00:58,959 --> 00:01:01,962 +Next I'll dive into populating +SwiftUI views + +18 +00:01:01,962 --> 00:01:05,165 +with data that already exist +in your app, + +19 +00:01:05,165 --> 00:01:07,935 +and how to ensure +the SwiftUI views update + +20 +00:01:07,935 --> 00:01:11,071 +when that data changes. + +21 +00:01:11,071 --> 00:01:14,107 +Then, I'll talk about +some exciting new functionality + +22 +00:01:14,107 --> 00:01:16,276 +that lets you build +UICollectionView + +23 +00:01:16,276 --> 00:01:20,514 +and UITableView cells +using SwiftUI. + +24 +00:01:20,514 --> 00:01:24,251 +Finally, I'll walk through +the unique aspects of data flow + +25 +00:01:24,251 --> 00:01:25,919 +with collection and table views + +26 +00:01:25,919 --> 00:01:29,890 +when you're using SwiftUI +inside cells. + +27 +00:01:29,890 --> 00:01:33,727 +Let's get started by talking +about UIHostingController. + +28 +00:01:33,727 --> 00:01:36,763 +UIHostingController +is a UIViewController + +29 +00:01:36,763 --> 00:01:39,967 +that contains a SwiftUI +view hierarchy. + +30 +00:01:39,967 --> 00:01:41,735 +You can use a hosting controller + +31 +00:01:41,735 --> 00:01:45,372 +anywhere you can use +a view controller in UIKit. + +32 +00:01:45,372 --> 00:01:46,974 +This makes UIHostingController + +33 +00:01:46,974 --> 00:01:50,444 +an easy way +to start using SwiftUI. + +34 +00:01:50,444 --> 00:01:53,814 +Let's examine how +a hosting controller works. + +35 +00:01:53,814 --> 00:01:56,817 +A hosting controller +is a view controller, + +36 +00:01:56,817 --> 00:02:01,355 +which means it has a UIView +stored in its view property. + +37 +00:02:01,355 --> 00:02:02,589 +And inside that view + +38 +00:02:02,589 --> 00:02:05,559 +is where your SwiftUI content +is rendered. + +39 +00:02:05,559 --> 00:02:09,696 +Let's go through an example of +how to use a hosting controller. + +40 +00:02:09,696 --> 00:02:14,434 +Here, we create a HeartRateView, +a SwiftUI view. + +41 +00:02:14,434 --> 00:02:17,571 +We then create a hosting +controller with HeartRateView + +42 +00:02:17,571 --> 00:02:21,041 +as its root view, +and present it. + +43 +00:02:21,041 --> 00:02:22,709 +UIHostingController works with + +44 +00:02:22,709 --> 00:02:26,880 +all of the UIKit +view controller APIs. + +45 +00:02:26,880 --> 00:02:30,484 +Let's go through +another example. + +46 +00:02:30,484 --> 00:02:32,119 +We have the same HeartRateView + +47 +00:02:32,119 --> 00:02:34,921 +and hosting controller +as before. + +48 +00:02:34,921 --> 00:02:36,957 +Here we add +the hosting controller + +49 +00:02:36,957 --> 00:02:39,192 +as a child view controller. + +50 +00:02:39,192 --> 00:02:43,830 +Then we can position and size +the hosting controller's view. + +51 +00:02:43,830 --> 00:02:47,968 +When your SwiftUI content inside +UIHostingController changes, + +52 +00:02:47,968 --> 00:02:51,471 +it may cause the view +to need to be resized. + +53 +00:02:51,471 --> 00:02:54,908 +New in iOS 16, +UIHostingController + +54 +00:02:54,908 --> 00:02:57,511 +allows you to enable +automatic updates + +55 +00:02:57,511 --> 00:03:00,380 +of the view controller's +preferred content size + +56 +00:03:00,380 --> 00:03:03,850 +and the view's intrinsic +content size. + +57 +00:03:03,850 --> 00:03:07,220 +You can enable this using +the new sizingOptions property + +58 +00:03:07,220 --> 00:03:09,189 +on UIHostingController. + +59 +00:03:09,189 --> 00:03:12,292 +Let's go through an example. + +60 +00:03:12,292 --> 00:03:14,795 +To start, we make +our HeartRateView, + +61 +00:03:14,795 --> 00:03:18,031 +and create +the hostingController. + +62 +00:03:18,031 --> 00:03:22,335 +We use the new sizingOptions API +to tell the hostingController + +63 +00:03:22,335 --> 00:03:27,641 +to automatically update +its preferredContentSize. + +64 +00:03:27,641 --> 00:03:30,077 +Then, we set +the modalPresentationStyle + +65 +00:03:30,077 --> 00:03:31,711 +to popover. + +66 +00:03:31,711 --> 00:03:35,682 +Using the new sizingOptions API +ensures that the popover + +67 +00:03:35,682 --> 00:03:41,221 +is always sized appropriately +to fit the SwiftUI content. + +68 +00:03:41,221 --> 00:03:43,690 +Now that you're familiar +with UIHostingController, + +69 +00:03:43,690 --> 00:03:46,827 +let's talk about +how to get data into SwiftUI + +70 +00:03:46,827 --> 00:03:49,729 +from other parts +of your UIKit app, + +71 +00:03:49,729 --> 00:03:52,399 +and ensure that +your SwiftUI views update + +72 +00:03:52,399 --> 00:03:55,502 +when that data changes. + +73 +00:03:55,502 --> 00:03:58,438 +Here's a diagram +of your UIKit app, + +74 +00:03:58,438 --> 00:04:00,440 +It contains +an existing model layer + +75 +00:04:00,440 --> 00:04:04,411 +that owns and manages +your app's data model objects. + +76 +00:04:04,411 --> 00:04:07,848 +Your app also contains +a number of view controllers. + +77 +00:04:07,848 --> 00:04:11,585 +If you want start using SwiftUI, +you'll need a hosting controller + +78 +00:04:11,585 --> 00:04:15,589 +with a SwiftUI view inside +one of the view controllers. + +79 +00:04:15,589 --> 00:04:18,892 +You will want to populate +this SwiftUI view with data + +80 +00:04:18,892 --> 00:04:22,496 +that is still owned +by your existing model layer. + +81 +00:04:22,496 --> 00:04:25,999 +In this section, we're going +to focus on how to bridge data + +82 +00:04:25,999 --> 00:04:30,370 +across the boundary +between UIKit and SwiftUI. + +83 +00:04:30,370 --> 00:04:33,573 +SwiftUI offers a variety +of data flow primitives + +84 +00:04:33,573 --> 00:04:36,443 +to help you manage the data +in your app. + +85 +00:04:36,443 --> 00:04:39,412 +Let's go through +the different options. + +86 +00:04:39,412 --> 00:04:44,417 +To store data that is created +and owned by a SwiftUI view, + +87 +00:04:44,417 --> 00:04:49,556 +SwiftUI provides the @State and +@StateObject property wrappers. + +88 +00:04:49,556 --> 00:04:52,993 +Since we're focused on data +owned outside of SwiftUI, + +89 +00:04:52,993 --> 00:04:56,296 +these property wrappers +aren't the right choice. + +90 +00:04:56,296 --> 00:04:58,765 +So, I'm not going +to cover these in this video. + +91 +00:04:58,765 --> 00:05:01,434 +Watch +"Data Essentials in SwiftUI" + +92 +00:05:01,434 --> 00:05:06,039 +to learn more about data +owned by a SwiftUI view. + +93 +00:05:06,039 --> 00:05:08,808 +One way to handle data +external to SwiftUI + +94 +00:05:08,808 --> 00:05:12,846 +is to pass values directly +when you initialize your views. + +95 +00:05:12,846 --> 00:05:15,248 +Because you are +just passing raw data + +96 +00:05:15,248 --> 00:05:18,818 +that is not owned +or managed by SwiftUI, + +97 +00:05:18,818 --> 00:05:22,622 +you are responsible for manually +updating the UIHostingController + +98 +00:05:22,622 --> 00:05:25,225 +when the data changes. + +99 +00:05:25,225 --> 00:05:27,527 +Let's go through an example. + +100 +00:05:27,527 --> 00:05:31,665 +Here is a SwiftUI view +named HeartRateView. + +101 +00:05:31,665 --> 00:05:33,600 +This view has +a single property -- + +102 +00:05:33,600 --> 00:05:37,003 +the heart rate beatsPerMinute +stored as an integer -- + +103 +00:05:37,003 --> 00:05:39,940 +and it displays +the value as text. + +104 +00:05:39,940 --> 00:05:41,608 +We're displaying +this HeartRateView + +105 +00:05:41,608 --> 00:05:43,777 +by embedding +a UIHostingController + +106 +00:05:43,777 --> 00:05:45,812 +inside an existing +view controller + +107 +00:05:45,812 --> 00:05:48,515 +named HeartRateViewController. + +108 +00:05:48,515 --> 00:05:50,684 +We save a reference +to the hosting controller + +109 +00:05:50,684 --> 00:05:53,253 +so we can update +its root view later. + +110 +00:05:53,253 --> 00:05:57,357 +Remember, the SwiftUI +HeartRateView is a value type, + +111 +00:05:57,357 --> 00:06:00,827 +so storing it by itself +would create a separate copy, + +112 +00:06:00,827 --> 00:06:04,431 +and wouldn't allow us +to update the UI. + +113 +00:06:04,431 --> 00:06:06,900 +The HeartRateViewController +owns the data + +114 +00:06:06,900 --> 00:06:09,769 +used to populate +the HeartRateView. + +115 +00:06:09,769 --> 00:06:13,240 +This data is stored +in the beatsPerMinute property, + +116 +00:06:13,240 --> 00:06:15,542 +and when the beatsPerMinute +value changes, + +117 +00:06:15,542 --> 00:06:19,412 +we call a method +to update the view. + +118 +00:06:19,412 --> 00:06:23,383 +Inside of the update method, +we create a new HeartRateView + +119 +00:06:23,383 --> 00:06:26,119 +using the latest +beatsPerMinute value, + +120 +00:06:26,119 --> 00:06:28,154 +and then assign that view +as the rootView + +121 +00:06:28,154 --> 00:06:31,224 +of our hosting controller. + +122 +00:06:31,224 --> 00:06:37,063 +This is a simple way to get data +from UIKit into SwiftUI, + +123 +00:06:37,063 --> 00:06:39,766 +but it requires manually +updating the rootView + +124 +00:06:39,766 --> 00:06:43,870 +of the hosting controller +any time the data changes. + +125 +00:06:43,870 --> 00:06:46,539 +Let's go through some other +SwiftUI data primitives + +126 +00:06:46,539 --> 00:06:49,743 +to make these updates +happen automatically. + +127 +00:06:49,743 --> 00:06:52,245 +The @ObservedObject +and @EnvironmentObject + +128 +00:06:52,245 --> 00:06:54,547 +property wrappers +allow you to reference + +129 +00:06:54,547 --> 00:06:56,283 +an external model object + +130 +00:06:56,283 --> 00:06:59,853 +that conforms to +the ObservableObject protocol. + +131 +00:06:59,853 --> 00:07:01,688 +When you use +these property wrappers, + +132 +00:07:01,688 --> 00:07:07,294 +SwiftUI automatically updates +your view when the data changes. + +133 +00:07:07,294 --> 00:07:10,363 +We're going to focus on the +@ObservedObject property wrapper + +134 +00:07:10,363 --> 00:07:12,032 +in this video, + +135 +00:07:12,032 --> 00:07:14,901 +but you can learn more about +EnvironmentObject in the + +136 +00:07:14,901 --> 00:07:18,805 +"Data Essentials in SwiftUI" +video mentioned earlier. + +137 +00:07:18,805 --> 00:07:22,575 +Let's go through how to create +an @ObservedObject. + +138 +00:07:22,575 --> 00:07:25,078 +The first step +is to take a model object + +139 +00:07:25,078 --> 00:07:27,614 +that is owned by an existing +part of your app + +140 +00:07:27,614 --> 00:07:31,985 +and make it conform to +the ObservableObject protocol. + +141 +00:07:31,985 --> 00:07:35,522 +Next, we store the model +as an @ObservedObject property + +142 +00:07:35,522 --> 00:07:38,758 +in our SwiftUI view. + +143 +00:07:38,758 --> 00:07:42,128 +Connecting the ObservableObject +to SwiftUI allows it + +144 +00:07:42,128 --> 00:07:46,566 +to update the view when +one of its properties change. + +145 +00:07:46,566 --> 00:07:48,768 +Let's go back to our +HeartRateView example + +146 +00:07:48,768 --> 00:07:51,037 +and wire this up. + +147 +00:07:51,037 --> 00:07:53,340 +Our app has a class named +HeartData + +148 +00:07:53,340 --> 00:07:56,776 +that contains the property +beatsPerMinute. + +149 +00:07:56,776 --> 00:08:01,548 +We make it an ObservableObject +by conforming to the protocol. + +150 +00:08:01,548 --> 00:08:04,217 +Then we add +the @Published property wrapper + +151 +00:08:04,217 --> 00:08:06,720 +to the beatsPerMinute property. + +152 +00:08:06,720 --> 00:08:09,456 +This property wrapper +is what triggers SwiftUI + +153 +00:08:09,456 --> 00:08:12,926 +to update our views on changes. + +154 +00:08:12,926 --> 00:08:15,462 +In the HeartRateView, +we store the HeartData + +155 +00:08:15,462 --> 00:08:17,397 +in a property annotated with + +156 +00:08:17,397 --> 00:08:20,567 +the @ObservedObject +property wrapper. + +157 +00:08:20,567 --> 00:08:23,636 +In the body of the view, +we display the beatsPerMinute + +158 +00:08:23,636 --> 00:08:26,239 +directly from the HeartData. + +159 +00:08:26,239 --> 00:08:30,043 +Now, let's use these together +in our view controller. + +160 +00:08:30,043 --> 00:08:33,346 +Here is our +HeartRateViewController. + +161 +00:08:33,346 --> 00:08:37,751 +It stores the HeartData +ObservableObject in a property. + +162 +00:08:37,751 --> 00:08:40,820 +Because this property +is not inside a SwiftUI view, + +163 +00:08:40,820 --> 00:08:44,157 +we don't need to use +a property wrapper here. + +164 +00:08:44,157 --> 00:08:46,192 +The HeartRateViewController +is initialized + +165 +00:08:46,192 --> 00:08:47,894 +with a HeartData instance, + +166 +00:08:47,894 --> 00:08:50,130 +which is used to create +a HeartRateView + +167 +00:08:50,130 --> 00:08:54,467 +that becomes the rootView +of the hosting controller. + +168 +00:08:54,467 --> 00:08:57,670 +The diagram illustrates +how this comes together. + +169 +00:08:57,670 --> 00:09:00,306 +We fetch the current +HeartData instance, + +170 +00:09:00,306 --> 00:09:04,277 +which contains a heart rate +of 78 beat per minute. + +171 +00:09:04,277 --> 00:09:07,747 +Then we create a new +HeartRateViewController + +172 +00:09:07,747 --> 00:09:09,949 +with that HeartData instance, + +173 +00:09:09,949 --> 00:09:14,554 +which wires it up +to the SwiftUI HeartRateView. + +174 +00:09:14,554 --> 00:09:15,922 +After a few seconds, + +175 +00:09:15,922 --> 00:09:18,691 +when the next heart rate +data sample arrives, + +176 +00:09:18,691 --> 00:09:24,230 +the heart data's beatsPerMinute +property is updated to 94. + +177 +00:09:24,230 --> 00:09:26,433 +Because this changes +a published property + +178 +00:09:26,433 --> 00:09:29,035 +on an ObservableObject, +the HeartRateView + +179 +00:09:29,035 --> 00:09:33,306 +is automatically updated +to display the new value. + +180 +00:09:33,306 --> 00:09:36,443 +We no longer need to manually +update the hosting controller + +181 +00:09:36,443 --> 00:09:38,178 +when the data changes. + +182 +00:09:38,178 --> 00:09:41,781 +This is why ObservableObject +is a great way to bridge data + +183 +00:09:41,781 --> 00:09:44,784 +from UIKit to SwiftUI. + +184 +00:09:44,784 --> 00:09:48,888 +Next, let's talk about +using SwiftUI in collection view + +185 +00:09:48,888 --> 00:09:52,292 +and table view cells. + +186 +00:09:52,292 --> 00:09:56,296 +New in iOS 16 +is UIHostingConfiguration, + +187 +00:09:56,296 --> 00:09:58,998 +which lets you harness +the power of SwiftUI + +188 +00:09:58,998 --> 00:10:03,470 +inside your existing UIKit, +collection, and table views. + +189 +00:10:03,470 --> 00:10:06,272 +UIHostingConfiguration +makes it easy to implement + +190 +00:10:06,272 --> 00:10:09,642 +custom cells using SwiftUI, +without needing to worry + +191 +00:10:09,642 --> 00:10:13,513 +about embedding an extra view +or view controller. + +192 +00:10:13,513 --> 00:10:16,382 +Before we dive deeper +on UIHostingConfiguration, + +193 +00:10:16,382 --> 00:10:20,420 +let's review +cell configurations in UIKit. + +194 +00:10:20,420 --> 00:10:23,189 +Cell configurations +are a modern way + +195 +00:10:23,189 --> 00:10:28,228 +to define the content, styling, +and behavior of cells in UIKit. + +196 +00:10:28,228 --> 00:10:31,197 +Unlike a UIView +or UIViewController, + +197 +00:10:31,197 --> 00:10:34,100 +a configuration is just +a lightweight struct. + +198 +00:10:34,100 --> 00:10:36,769 +It's inexpensive to create. + +199 +00:10:36,769 --> 00:10:38,972 +A configuration +is only a description + +200 +00:10:38,972 --> 00:10:41,741 +of a cell's appearance, +so it needs to be applied + +201 +00:10:41,741 --> 00:10:45,078 +to a cell to have an effect. + +202 +00:10:45,078 --> 00:10:46,880 +Configurations are composable, + +203 +00:10:46,880 --> 00:10:48,882 +and work with both +UICollectionView + +204 +00:10:48,882 --> 00:10:51,384 +and UITableView cells. + +205 +00:10:51,384 --> 00:10:56,122 +For more details, you can watch +"Modern cell configuration." + +206 +00:10:56,122 --> 00:10:58,324 +With that in mind, let's dive in + +207 +00:10:58,324 --> 00:11:02,428 +and start using +UIHostingConfiguration! + +208 +00:11:02,428 --> 00:11:05,331 +UIHostingConfiguration +is a content configuration + +209 +00:11:05,331 --> 00:11:08,902 +that is initialized +with a SwiftUI ViewBuilder. + +210 +00:11:08,902 --> 00:11:11,304 +That means we can start +writing SwiftUI code + +211 +00:11:11,304 --> 00:11:15,141 +to create views +directly inside it. + +212 +00:11:15,141 --> 00:11:17,744 +In order to render +the hosting configuration, + +213 +00:11:17,744 --> 00:11:20,346 +we set it to the +contentConfiguration property + +214 +00:11:20,346 --> 00:11:23,950 +of the UICollectionView +or UITableView cell. + +215 +00:11:23,950 --> 00:11:25,985 +Let's start writing +some SwiftUI code + +216 +00:11:25,985 --> 00:11:30,623 +in this hosting configuration to +build a custom heart rate cell. + +217 +00:11:30,623 --> 00:11:34,394 +We'll start by creating a label +with the text "Heart Rate" + +218 +00:11:34,394 --> 00:11:36,462 +and the image of a heart. + +219 +00:11:36,462 --> 00:11:38,998 +SwiftUI views +receive default styling + +220 +00:11:38,998 --> 00:11:41,834 +based on the context +they are used in. + +221 +00:11:41,834 --> 00:11:43,770 +But we can start +customizing the styling + +222 +00:11:43,770 --> 00:11:47,840 +using standard +SwiftUI view modifiers. + +223 +00:11:47,840 --> 00:11:51,344 +Let's make the image +and text pink with a bold font, + +224 +00:11:51,344 --> 00:11:55,648 +by adding the foregroundStyle +and font modifiers to our label. + +225 +00:11:55,648 --> 00:11:58,484 +Since we're just writing +regular SwiftUI code, + +226 +00:11:58,484 --> 00:12:01,020 +we can factor out our code +into a standalone view + +227 +00:12:01,020 --> 00:12:03,856 +anytime we want. + +228 +00:12:03,856 --> 00:12:06,826 +Here, we create +a new SwiftUI view + +229 +00:12:06,826 --> 00:12:09,696 +named HeartRateTitleView, + +230 +00:12:09,696 --> 00:12:12,298 +moved the code we had +into its body, + +231 +00:12:12,298 --> 00:12:14,634 +and then used +that HeartRateTitleView + +232 +00:12:14,634 --> 00:12:16,636 +in the hosting configuration. + +233 +00:12:16,636 --> 00:12:17,837 +As shown in the cell, + +234 +00:12:17,837 --> 00:12:22,275 +it produces +the exact same result. + +235 +00:12:22,275 --> 00:12:24,811 +Now we can start +adding more views + +236 +00:12:24,811 --> 00:12:27,513 +inside the HeartRateTitleView. + +237 +00:12:27,513 --> 00:12:32,151 +I've put the label inside +of an HStack with a spacer, + +238 +00:12:32,151 --> 00:12:36,155 +then added the current time +in a Text view next to it. + +239 +00:12:36,155 --> 00:12:38,391 +That's looking +pretty good so far. + +240 +00:12:38,391 --> 00:12:41,427 +Let's add some more content +to this custom cell + +241 +00:12:41,427 --> 00:12:44,864 +below the HeartRateTitleView. + +242 +00:12:44,864 --> 00:12:47,433 +To do this, +we'll insert a VStack + +243 +00:12:47,433 --> 00:12:49,202 +inside the hosting +configuration + +244 +00:12:49,202 --> 00:12:53,773 +so we can add more content +below the HeartRateTitleView. + +245 +00:12:53,773 --> 00:12:56,609 +Then we'll put +two Text views together + +246 +00:12:56,609 --> 00:13:00,713 +in a HStack to display 90 BPM + +247 +00:13:00,713 --> 00:13:05,318 +and then apply a few modifiers +to style them the way we want. + +248 +00:13:05,318 --> 00:13:08,154 +Just like we did before +with the HeartRateTitleView, + +249 +00:13:08,154 --> 00:13:14,293 +we can move this new code +into its own SwiftUI view. + +250 +00:13:14,293 --> 00:13:17,430 +Now the same code +is extracted into the body + +251 +00:13:17,430 --> 00:13:20,033 +of the HeartRateBPMView. + +252 +00:13:20,033 --> 00:13:21,768 +Our cell is looking great, + +253 +00:13:21,768 --> 00:13:25,304 +but I have an idea +for another thing we could add. + +254 +00:13:25,304 --> 00:13:29,742 +New in iOS 16 is +the Swift Charts framework, + +255 +00:13:29,742 --> 00:13:33,112 +which lets you visualize data +with beautiful graphs + +256 +00:13:33,112 --> 00:13:35,815 +in only a few lines of code. + +257 +00:13:35,815 --> 00:13:38,551 +Let's try using it to display +a small line chart + +258 +00:13:38,551 --> 00:13:41,888 +right inside the cell. + +259 +00:13:41,888 --> 00:13:45,458 +Using the new Chart view, +we create a small line chart + +260 +00:13:45,458 --> 00:13:47,894 +that visualizes +recent heart rate samples + +261 +00:13:47,894 --> 00:13:52,565 +and display that next +to the BPM view in the cell. + +262 +00:13:52,565 --> 00:13:53,700 +To generate the chart, + +263 +00:13:53,700 --> 00:13:56,936 +we pass in a collection +of heart rate samples, + +264 +00:13:56,936 --> 00:14:01,074 +and draw a LineMark that +connects all of the samples. + +265 +00:14:01,074 --> 00:14:02,608 +We can add a circle symbol + +266 +00:14:02,608 --> 00:14:04,811 +to indicate each sample +on the line + +267 +00:14:04,811 --> 00:14:07,213 +and apply +a pink foreground style, + +268 +00:14:07,213 --> 00:14:11,284 +to tint the chart to match +the HeartRateTitleView. + +269 +00:14:11,284 --> 00:14:14,153 +We're only scratching +the surface of what you can do + +270 +00:14:14,153 --> 00:14:16,889 +with the new +Swift Charts framework, + +271 +00:14:16,889 --> 00:14:20,259 +so be sure to check out +the video "Hello Swift Charts" + +272 +00:14:20,259 --> 00:14:22,228 +to learn more about it. + +273 +00:14:22,228 --> 00:14:25,732 +Not only does our finished +heart rate cell look awesome, + +274 +00:14:25,732 --> 00:14:29,268 +but it was easy to make +in just a couple of minutes. + +275 +00:14:29,268 --> 00:14:32,038 +That's how easy it is +to start building custom cells + +276 +00:14:32,038 --> 00:14:35,508 +with UIHostingConfiguration +and SwiftUI. + +277 +00:14:35,508 --> 00:14:37,376 +Let's talk about +four special features + +278 +00:14:37,376 --> 00:14:40,847 +that UIHostingConfiguration +supports. + +279 +00:14:40,847 --> 00:14:44,417 +By default, +the root-level SwiftUI content + +280 +00:14:44,417 --> 00:14:46,686 +is inset from the edges +of the cell, + +281 +00:14:46,686 --> 00:14:50,690 +based on the cell's layout +margins in UIKit. + +282 +00:14:50,690 --> 00:14:52,625 +This ensures +that the cell content + +283 +00:14:52,625 --> 00:14:55,728 +is properly aligned with +the content of adjacent cells + +284 +00:14:55,728 --> 00:14:59,899 +and other UI elements, +such as the navigation bar. + +285 +00:14:59,899 --> 00:15:02,935 +Sometimes, you may want to use +different margins, + +286 +00:15:02,935 --> 00:15:07,073 +or have the content extend +to the edges of the cell. + +287 +00:15:07,073 --> 00:15:10,176 +For these cases, you can change +the default margins + +288 +00:15:10,176 --> 00:15:15,414 +by using the margins modifier +on UIHostingConfiguration. + +289 +00:15:15,414 --> 00:15:18,050 +If you want to customize +a cell's background appearance + +290 +00:15:18,050 --> 00:15:21,587 +using SwiftUI, you can use +the background modifier + +291 +00:15:21,587 --> 00:15:24,490 +on the UIHostingConfiguration. + +292 +00:15:24,490 --> 00:15:26,559 +There are a few +key differences between + +293 +00:15:26,559 --> 00:15:31,631 +a UIHostingConfiguration's +background and its content. + +294 +00:15:31,631 --> 00:15:34,734 +The background is hosted +at the the back of the cell, + +295 +00:15:34,734 --> 00:15:39,172 +underneath your SwiftUI content +in the cell's content view. + +296 +00:15:39,172 --> 00:15:41,507 +Additionally, +while content is typically + +297 +00:15:41,507 --> 00:15:43,943 +inset from the cell's edges, + +298 +00:15:43,943 --> 00:15:48,181 +backgrounds extend +edge to edge in the cell. + +299 +00:15:48,181 --> 00:15:51,150 +Finally, when using +self-sizing cells, + +300 +00:15:51,150 --> 00:15:56,689 +only the cell content influences +the size of the cell. + +301 +00:15:56,689 --> 00:15:59,625 +Next, let's examine +two more special features + +302 +00:15:59,625 --> 00:16:02,094 +of UIHostingConfiguration +that you can use + +303 +00:16:02,094 --> 00:16:04,730 +when you have a cell +inside a collection view list + +304 +00:16:04,730 --> 00:16:06,699 +or table view. + +305 +00:16:06,699 --> 00:16:08,968 +In a list, the separator +below the cell + +306 +00:16:08,968 --> 00:16:11,904 +is automatically aligned +to the SwiftUI text + +307 +00:16:11,904 --> 00:16:14,874 +in your hosting configuration +by default. + +308 +00:16:14,874 --> 00:16:18,177 +In this example, notice how the +leading edge of the separator + +309 +00:16:18,177 --> 00:16:21,914 +is inset past the image +so that it aligns with the text + +310 +00:16:21,914 --> 00:16:23,382 +in the cell. + +311 +00:16:23,382 --> 00:16:24,984 +If you need to align +the separator + +312 +00:16:24,984 --> 00:16:28,421 +to a different SwiftUI view +in your hosting configuration, + +313 +00:16:28,421 --> 00:16:32,225 +use the alignmentGuide +modifier. + +314 +00:16:32,225 --> 00:16:34,994 +When inside a collection view +list or table view, + +315 +00:16:34,994 --> 00:16:40,299 +you can configure swipe actions +for a row directly with SwiftUI. + +316 +00:16:40,299 --> 00:16:43,769 +By creating your buttons inside +the swipeActions modifier, + +317 +00:16:43,769 --> 00:16:45,638 +you'll be able to swipe the cell + +318 +00:16:45,638 --> 00:16:49,141 +to reveal and perform +your custom actions. + +319 +00:16:49,141 --> 00:16:50,910 +Download the sample code +for this video + +320 +00:16:50,910 --> 00:16:53,713 +to find a complete example. + +321 +00:16:53,713 --> 00:16:55,481 +When defining swipe actions, + +322 +00:16:55,481 --> 00:16:57,984 +make sure your buttons +perform their actions + +323 +00:16:57,984 --> 00:17:01,754 +using a stable identifier +for the item represented. + +324 +00:17:01,754 --> 00:17:03,522 +Do not use the index path, + +325 +00:17:03,522 --> 00:17:06,058 +as it may change +while the cell is visible, + +326 +00:17:06,058 --> 00:17:10,596 +causing the swipe actions +to act on the wrong item. + +327 +00:17:10,596 --> 00:17:12,899 +When you're using +UIHostingConfiguration + +328 +00:17:12,899 --> 00:17:16,369 +in a cell, keep in mind +that the cell interactions + +329 +00:17:16,369 --> 00:17:18,804 +such as tap handling, +highlighting, + +330 +00:17:18,804 --> 00:17:21,207 +and selection +will still be handled + +331 +00:17:21,207 --> 00:17:24,410 +by the collection view +or table view. + +332 +00:17:24,410 --> 00:17:27,280 +If you need to customize +your SwiftUI views + +333 +00:17:27,280 --> 00:17:29,682 +for any of these +UIKit cell states, + +334 +00:17:29,682 --> 00:17:32,084 +you can create +your hosting configuration + +335 +00:17:32,084 --> 00:17:35,922 +inside of the cell's +configurationUpdateHandler, + +336 +00:17:35,922 --> 00:17:40,393 +and use the state provided +in your SwiftUI code. + +337 +00:17:40,393 --> 00:17:43,029 +The configurationUpdateHandler +runs again + +338 +00:17:43,029 --> 00:17:45,698 +any time +the cell's state changes, + +339 +00:17:45,698 --> 00:17:47,867 +creating a new +UIHostingConfiguration + +340 +00:17:47,867 --> 00:17:51,203 +for the new state +and applying it to the cell. + +341 +00:17:51,203 --> 00:17:54,540 +In this example, we use the +state to add a checkmark image + +342 +00:17:54,540 --> 00:17:57,510 +when the cell is selected. + +343 +00:17:57,510 --> 00:18:00,012 +Now that you're familiar +with UIHostingConfiguration, + +344 +00:18:00,012 --> 00:18:04,216 +let's discuss how to manage +data flow from your model layer + +345 +00:18:04,216 --> 00:18:07,019 +to a UICollectionView +or UITableView + +346 +00:18:07,019 --> 00:18:10,556 +filled with cells using SwiftUI. + +347 +00:18:10,556 --> 00:18:14,560 +Our goal is to build +this list of medical conditions. + +348 +00:18:14,560 --> 00:18:17,930 +In this example, +we're using a UICollectionView, + +349 +00:18:17,930 --> 00:18:22,768 +but everything we discuss +applies equally to UITableView. + +350 +00:18:22,768 --> 00:18:25,938 +Let's go through +the components involved. + +351 +00:18:25,938 --> 00:18:30,376 +Our app has a collection of +MedicalCondition model objects, + +352 +00:18:30,376 --> 00:18:33,346 +which we are going to display +in the collection view. + +353 +00:18:33,346 --> 00:18:35,614 +For each item +in this collection, + +354 +00:18:35,614 --> 00:18:38,317 +we want to create a cell +in the collection view + +355 +00:18:38,317 --> 00:18:41,587 +to display that +medical condition. + +356 +00:18:41,587 --> 00:18:44,991 +In order to do this, we'll +create a diffable data source + +357 +00:18:44,991 --> 00:18:47,560 +connected +to the collection view. + +358 +00:18:47,560 --> 00:18:51,964 +Then, we need to populate +a diffable data source snapshot + +359 +00:18:51,964 --> 00:18:55,668 +with the identifiers of the +MedicalCondition model objects + +360 +00:18:55,668 --> 00:18:58,738 +in the data collection. + +361 +00:18:58,738 --> 00:19:01,774 +It's important that the diffable +data source snapshot + +362 +00:19:01,774 --> 00:19:05,544 +contains the unique identifier +of each MedicalCondition, + +363 +00:19:05,544 --> 00:19:08,881 +and not the MedicalCondition +objects themselves. + +364 +00:19:08,881 --> 00:19:13,052 +This ensures that the diffable +data source can accurately track + +365 +00:19:13,052 --> 00:19:15,187 +the identity of each item, + +366 +00:19:15,187 --> 00:19:17,757 +and compute the correct changes + +367 +00:19:17,757 --> 00:19:20,626 +when new snapshots +are applied later. + +368 +00:19:20,626 --> 00:19:23,496 +By applying a snapshot +with these item identifiers + +369 +00:19:23,496 --> 00:19:25,364 +to the diffable data source, + +370 +00:19:25,364 --> 00:19:28,067 +it will automatically update +the collection view, + +371 +00:19:28,067 --> 00:19:32,238 +which will create a new cell +for each item. + +372 +00:19:32,238 --> 00:19:36,375 +Each cell is configured +to display one MedicalCondition, + +373 +00:19:36,375 --> 00:19:42,281 +using a SwiftUI view +in a UIHostingConfiguration. + +374 +00:19:42,281 --> 00:19:45,017 +Now that we're displaying cells +built with SwiftUI, + +375 +00:19:45,017 --> 00:19:49,321 +we need to handle updating +the UI when the data changes. + +376 +00:19:49,321 --> 00:19:51,057 +There are two different +types of changes + +377 +00:19:51,057 --> 00:19:54,060 +that we need +to handle separately. + +378 +00:19:54,060 --> 00:19:58,464 +The first type is when the +data collection itself changes. + +379 +00:19:58,464 --> 00:20:04,537 +For example, when items are +inserted, reordered, or deleted. + +380 +00:20:04,537 --> 00:20:07,840 +These changes are handled +by applying a new snapshot + +381 +00:20:07,840 --> 00:20:10,843 +to the diffable data source. + +382 +00:20:10,843 --> 00:20:14,747 +Diffable data source will diff +the old and new snapshots, + +383 +00:20:14,747 --> 00:20:18,217 +and perform the necessary +updates to the collection view, + +384 +00:20:18,217 --> 00:20:23,255 +causing cells to be inserted, +moved, or deleted. + +385 +00:20:23,255 --> 00:20:25,891 +Because changes to the +collection of data itself + +386 +00:20:25,891 --> 00:20:28,761 +don't affect anything +inside of cells, + +387 +00:20:28,761 --> 00:20:31,363 +you handle these types +of changes the same, + +388 +00:20:31,363 --> 00:20:35,801 +whether you build your cells +using UIKit or SwiftUI. + +389 +00:20:35,801 --> 00:20:37,970 +The second type of change +we need to handle + +390 +00:20:37,970 --> 00:20:43,342 +are when the properties of +individual model objects change. + +391 +00:20:43,342 --> 00:20:46,145 +These changes often require +updating the views + +392 +00:20:46,145 --> 00:20:48,114 +in existing cells. + +393 +00:20:48,114 --> 00:20:49,882 +Because the diffable data source + +394 +00:20:49,882 --> 00:20:53,119 +only contains item identifiers +in its snapshot, + +395 +00:20:53,119 --> 00:20:55,087 +it doesn't know +when the properties + +396 +00:20:55,087 --> 00:20:57,990 +of existing items change. + +397 +00:20:57,990 --> 00:21:01,560 +Traditionally, when using UIKit, +you would need to manually + +398 +00:21:01,560 --> 00:21:04,997 +tell the diffable data source +about these changes + +399 +00:21:04,997 --> 00:21:08,801 +by reconfiguring or reloading +items in the snapshot. + +400 +00:21:08,801 --> 00:21:13,372 +But when using SwiftUI in cells, +this isn't necessary anymore. + +401 +00:21:13,372 --> 00:21:16,408 +By storing +the ObservableObject model + +402 +00:21:16,408 --> 00:21:20,446 +in an ObservedObject property +in our SwiftUI view, + +403 +00:21:20,446 --> 00:21:22,581 +changes to published properties + +404 +00:21:22,581 --> 00:21:25,684 +of the model will automatically +trigger SwiftUI + +405 +00:21:25,684 --> 00:21:27,253 +to refresh the view. + +406 +00:21:27,253 --> 00:21:30,789 +This establishes a direct +connection between the model + +407 +00:21:30,789 --> 00:21:34,160 +and the SwiftUI view +inside of the cell. + +408 +00:21:34,160 --> 00:21:35,427 +When a change is made, + +409 +00:21:35,427 --> 00:21:38,931 +the SwiftUI views in the cell +are updated directly, + +410 +00:21:38,931 --> 00:21:41,333 +without going through +the diffable data source + +411 +00:21:41,333 --> 00:21:44,403 +or the UICollectionView. + +412 +00:21:44,403 --> 00:21:45,971 +When a cell's data changes, + +413 +00:21:45,971 --> 00:21:48,674 +it may cause the cell +to need to grow or shrink + +414 +00:21:48,674 --> 00:21:51,143 +to fit the new content. + +415 +00:21:51,143 --> 00:21:54,980 +But if the SwiftUI cell content +is being updated directly + +416 +00:21:54,980 --> 00:21:56,916 +without going through UIKit, + +417 +00:21:56,916 --> 00:22:00,753 +how does the collection view +know to resize the cell? + +418 +00:22:00,753 --> 00:22:04,356 +UIHostingConfiguration takes +advantage of a brand-new feature + +419 +00:22:04,356 --> 00:22:07,326 +in UIKit to make this work. + +420 +00:22:07,326 --> 00:22:11,997 +In iOS 16, self-sizing cells +in UICollectionView + +421 +00:22:11,997 --> 00:22:16,535 +and UITableView +are now also self-resizing! + +422 +00:22:16,535 --> 00:22:18,170 +This is enabled by default, + +423 +00:22:18,170 --> 00:22:21,040 +so that when you're using +UIHostingConfiguration + +424 +00:22:21,040 --> 00:22:24,410 +and the SwiftUI content changes, +the containing cell + +425 +00:22:24,410 --> 00:22:28,681 +is automatically resized +if necessary. + +426 +00:22:28,681 --> 00:22:31,150 +You can learn more +about how this new feature works + +427 +00:22:31,150 --> 00:22:37,690 +in the "What's New in UIKit" +video from WWDC 2022. + +428 +00:22:37,690 --> 00:22:39,491 +There's one more aspect +of data flow + +429 +00:22:39,491 --> 00:22:43,462 +that you may need to handle, +and that's sending data out + +430 +00:22:43,462 --> 00:22:48,033 +from a SwiftUI view +back to other parts of your app. + +431 +00:22:48,033 --> 00:22:52,605 +Once again, ObservableObject +has got you covered! + +432 +00:22:52,605 --> 00:22:55,641 +You can create a two-way binding +to a published property + +433 +00:22:55,641 --> 00:22:57,910 +of an ObservableObject. + +434 +00:22:57,910 --> 00:23:02,681 +Not only will data flow from the +ObservableObject into SwiftUI, + +435 +00:23:02,681 --> 00:23:06,018 +but SwiftUI can write back +changes to the property + +436 +00:23:06,018 --> 00:23:08,754 +on the model object. + +437 +00:23:08,754 --> 00:23:10,489 +Let's go through +a simple example + +438 +00:23:10,489 --> 00:23:12,224 +of creating a two-way binding + +439 +00:23:12,224 --> 00:23:16,996 +by making the text in our +MedicalCondition cell editable. + +440 +00:23:16,996 --> 00:23:21,400 +Here is our ObservableObject, +MedicalCondition. + +441 +00:23:21,400 --> 00:23:25,671 +It stores a unique identifier +in an ID property. + +442 +00:23:25,671 --> 00:23:27,673 +This is the identifier +used to populate + +443 +00:23:27,673 --> 00:23:30,709 +the diffable +data source snapshot. + +444 +00:23:30,709 --> 00:23:33,045 +And this published property +stores the text + +445 +00:23:33,045 --> 00:23:35,914 +of the medical condition. + +446 +00:23:35,914 --> 00:23:37,650 +Here's the MedicalConditionView + +447 +00:23:37,650 --> 00:23:42,421 +that displays the medical +condition text inside each cell. + +448 +00:23:42,421 --> 00:23:44,890 +Right now this text +is read-only, + +449 +00:23:44,890 --> 00:23:47,593 +so let's make it editable. + +450 +00:23:47,593 --> 00:23:52,398 +All we need to do is to change +the Text view to a TextField + +451 +00:23:52,398 --> 00:23:54,933 +and create a binding +to the text property + +452 +00:23:54,933 --> 00:23:59,438 +of our MedicalCondition +by adding a dollar sign prefix. + +453 +00:23:59,438 --> 00:24:01,140 +When you type +into the text field, + +454 +00:24:01,140 --> 00:24:04,643 +this binding allows SwiftUI +to write back changes + +455 +00:24:04,643 --> 00:24:07,479 +directly to +the ObservableObject. + +456 +00:24:07,479 --> 00:24:09,181 +That's really how simple it is + +457 +00:24:09,181 --> 00:24:14,453 +to set up two-way +data flow with SwiftUI. + +458 +00:24:14,453 --> 00:24:16,922 +UIHostingController +is a powerful way + +459 +00:24:16,922 --> 00:24:21,593 +to embed SwiftUI content +into your UIKit app. + +460 +00:24:21,593 --> 00:24:23,395 +Your SwiftUI view is rendered + +461 +00:24:23,395 --> 00:24:25,831 +inside the hosting +controller's view, + +462 +00:24:25,831 --> 00:24:27,599 +and you can use +the hosting controller + +463 +00:24:27,599 --> 00:24:33,439 +anywhere that you can use +a view controller in UIKit. + +464 +00:24:33,439 --> 00:24:35,341 +When using UIHostingController, + +465 +00:24:35,341 --> 00:24:37,943 +make sure to always +add the view controller + +466 +00:24:37,943 --> 00:24:41,380 +together with the view +to your app. + +467 +00:24:41,380 --> 00:24:45,751 +Many SwiftUI features, such as +toolbars, keyboard shortcuts, + +468 +00:24:45,751 --> 00:24:49,088 +and views that use +UIViewControllerRepresentable, + +469 +00:24:49,088 --> 00:24:51,990 +require a connection +to the view controller hierarchy + +470 +00:24:51,990 --> 00:24:54,993 +in UIKit to integrate properly, + +471 +00:24:54,993 --> 00:24:57,663 +so never separate +a hosting controller's view + +472 +00:24:57,663 --> 00:25:01,166 +from the hosting controller +itself. + +473 +00:25:01,166 --> 00:25:03,068 +For comparison, when you apply + +474 +00:25:03,068 --> 00:25:05,938 +a UIHostingConfiguration +to a cell, + +475 +00:25:05,938 --> 00:25:08,307 +your SwiftUI view +is hosted in the cell + +476 +00:25:08,307 --> 00:25:11,110 +without a UIViewController. + +477 +00:25:11,110 --> 00:25:14,079 +UIHostingConfiguration supports +the vast majority + +478 +00:25:14,079 --> 00:25:16,048 +of SwiftUI features. + +479 +00:25:16,048 --> 00:25:18,283 +But keep in mind +that SwiftUI views + +480 +00:25:18,283 --> 00:25:20,986 +that depend on +UIViewControllerRepresentable + +481 +00:25:20,986 --> 00:25:23,589 +can't be used inside of cells. + +482 +00:25:23,589 --> 00:25:26,859 +With UIHostingController +and UIHostingConfiguration, + +483 +00:25:26,859 --> 00:25:29,895 +you have two great ways +to incorporate SwiftUI + +484 +00:25:29,895 --> 00:25:32,965 +into your UIKit app. + +485 +00:25:32,965 --> 00:25:37,636 +SwiftUI integrates seamlessly +into existing UIKit apps + +486 +00:25:37,636 --> 00:25:42,941 +Use UIHostingController to add +SwiftUI throughout your app. + +487 +00:25:42,941 --> 00:25:46,044 +Create custom cells in +your collection and table view + +488 +00:25:46,044 --> 00:25:49,448 +using UIHostingConfiguration. + +489 +00:25:49,448 --> 00:25:51,950 +And take advantage +of ObservableObject, + +490 +00:25:51,950 --> 00:25:55,421 +so your data and UI +is always in sync. + +491 +00:25:55,421 --> 00:25:59,258 +Add SwiftUI to your app today! + +492 +00:25:59,258 --> 00:26:00,726 +Thank you for watching! + +493 +00:26:00,726 --> 00:26:05,564 +♪ + diff --git a/eng/2022 Session 10074 What's new in AppKit en.srt b/eng/2022 Session 10074 What's new in AppKit en.srt new file mode 100644 index 0000000..7d1f709 --- /dev/null +++ b/eng/2022 Session 10074 What's new in AppKit en.srt @@ -0,0 +1,1849 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:10,043 --> 00:00:13,447 +Jeff Nadeau: Hello, and welcome +to What's new in AppKit. + +3 +00:00:13,480 --> 00:00:16,717 +I'm Jeff Nadeau, +an engineer on the AppKit team, + +4 +00:00:16,750 --> 00:00:19,686 +and I'm here to share the latest +and greatest in building apps + +5 +00:00:19,720 --> 00:00:21,622 +for macOS Ventura. + +6 +00:00:21,655 --> 00:00:24,591 +It's never been a more exciting time +for the Mac, + +7 +00:00:24,625 --> 00:00:28,195 +between the performance +and efficiency of Apple Silicon, + +8 +00:00:28,228 --> 00:00:29,897 +the power of macOS, + +9 +00:00:29,930 --> 00:00:33,567 +and an app ecosystem +that's richer than ever. + +10 +00:00:33,600 --> 00:00:36,236 +Your apps are an important part of +that story, + +11 +00:00:36,270 --> 00:00:38,305 +and we're continuing +to push AppKit forward + +12 +00:00:38,338 --> 00:00:41,742 +so that you can keep building +the very best apps. + +13 +00:00:42,342 --> 00:00:45,179 +I'll be covering a wide variety of topics, + +14 +00:00:45,212 --> 00:00:47,915 +starting with Stage Manager, + +15 +00:00:47,948 --> 00:00:50,417 +moving on to Preferences, + +16 +00:00:50,450 --> 00:00:52,753 +followed by controls, + +17 +00:00:52,786 --> 00:00:54,688 +SF Symbols, + +18 +00:00:54,721 --> 00:00:57,257 +and sharing. + +19 +00:00:57,291 --> 00:00:59,259 +I'll start off with Stage Manager. + +20 +00:01:00,460 --> 00:01:04,398 +Stage Manager cleans up inactive windows +in your workspace + +21 +00:01:04,431 --> 00:01:07,701 +while your active window +takes center stage. + +22 +00:01:07,734 --> 00:01:11,071 +For a more advanced workflow, +you can also pull windows together + +23 +00:01:11,104 --> 00:01:14,541 +into sets +which swap in and out as a group. + +24 +00:01:15,709 --> 00:01:19,847 +This has an impact on how +your app windows present themselves. + +25 +00:01:19,880 --> 00:01:23,016 +Stage Manager is trying to keep +the working space tidy, + +26 +00:01:23,050 --> 00:01:24,885 +so when a new window is presented, + +27 +00:01:24,918 --> 00:01:29,156 +existing windows will exit the stage +to make room. + +28 +00:01:29,189 --> 00:01:33,293 +That's what you want for +"primary" windows, like your documents. + +29 +00:01:33,327 --> 00:01:38,599 +Auxiliary windows like panels, +popovers, settings, and others + +30 +00:01:38,632 --> 00:01:42,970 +should continue to appear +above the existing windows. + +31 +00:01:43,003 --> 00:01:45,472 +NSWindow already has a lot of APIs + +32 +00:01:45,506 --> 00:01:49,343 +that can help you define the behavior +you'd like for a particular window. + +33 +00:01:51,545 --> 00:01:54,915 +By default, +Stage Manager won't swap out other windows + +34 +00:01:54,948 --> 00:01:58,318 +if you present a floating panel, +a modal window, + +35 +00:01:58,352 --> 00:02:01,388 +or a window with +a preference-style toolbar. + +36 +00:02:03,824 --> 00:02:07,628 +Stage Manager also respects +your window's collectionBehavior. + +37 +00:02:07,661 --> 00:02:10,264 +This OptionSet defines how +your window behaves + +38 +00:02:10,297 --> 00:02:12,599 +in spaces and full screen, + +39 +00:02:12,633 --> 00:02:15,102 +and it now also helps Stage Manager +understand + +40 +00:02:15,135 --> 00:02:18,639 +that a window is considered +to be auxiliary or floating. + +41 +00:02:19,540 --> 00:02:23,977 +If a window's collectionBehavior +includes the auxiliary, moveToActiveSpace, + +42 +00:02:24,011 --> 00:02:26,947 +stationary, or transient options, + +43 +00:02:26,980 --> 00:02:30,784 +then it won't displace +the active window in center stage. + +44 +00:02:31,718 --> 00:02:34,454 +By setting up your windows +with the right collection behaviors, + +45 +00:02:34,488 --> 00:02:37,257 +you can make sure +that they work great in every context, + +46 +00:02:37,291 --> 00:02:42,829 +whether it's a desktop space, +full screen, or now in Stage Manager. + +47 +00:02:42,863 --> 00:02:46,934 +Next, I'd like to cover some +important changes to Preferences. + +48 +00:02:46,967 --> 00:02:49,937 +In macOS Ventura, +the System Preferences app + +49 +00:02:49,970 --> 00:02:52,239 +has taken on a whole new look, + +50 +00:02:52,272 --> 00:02:57,277 +with a refreshed navigation scheme +and all-new visual design. + +51 +00:02:57,311 --> 00:03:00,948 +To align with the settings experience +on our other operating systems, + +52 +00:03:00,981 --> 00:03:04,451 +we've also renamed the app +to System Settings. + +53 +00:03:05,219 --> 00:03:08,222 +These changes extend +to your application too. + +54 +00:03:08,255 --> 00:03:11,091 +For example, +you might have a preference pane bundle + +55 +00:03:11,124 --> 00:03:14,695 +that appears in +the System Preferences app today. + +56 +00:03:14,728 --> 00:03:18,532 +You might also have +a settings area inside of your app. + +57 +00:03:18,565 --> 00:03:21,935 +There's also a new design system +for control-rich forms + +58 +00:03:21,969 --> 00:03:26,640 +that may be the perfect fit +for settings interfaces or inspectors. + +59 +00:03:26,673 --> 00:03:29,209 +If you ship a custom prefpane bundle, + +60 +00:03:29,243 --> 00:03:32,145 +it'll continue to work +with the new Settings app. + +61 +00:03:32,179 --> 00:03:34,748 +Your custom pane will appear +in the sidebar, + +62 +00:03:34,781 --> 00:03:36,617 +and the app will load up your bundle + +63 +00:03:36,650 --> 00:03:40,988 +and present your settings UI +just like it did in Monterey and earlier. + +64 +00:03:41,855 --> 00:03:44,691 +To match the newly-renamed +System Settings app, + +65 +00:03:44,725 --> 00:03:48,529 +we've renamed in-app preferences +to "settings" as well. + +66 +00:03:48,562 --> 00:03:52,399 +To help get you started, +once you build against the newest SDK, + +67 +00:03:52,432 --> 00:03:55,702 +AppKit will automatically update +the name of the "Preferences," menu item + +68 +00:03:55,736 --> 00:03:57,804 +in your app menu. + +69 +00:03:57,838 --> 00:04:02,042 +However, you might be using the word +"Preferences" in a number of other places, + +70 +00:04:02,075 --> 00:04:04,645 +like window titles, descriptive labels, + +71 +00:04:04,678 --> 00:04:07,414 +or other controls around your app. + +72 +00:04:07,447 --> 00:04:11,552 +Search through your localized text +to find places that also need updating. + +73 +00:04:12,986 --> 00:04:18,058 +For example, TextEdit's settings window +used to be called "preferences" + +74 +00:04:18,091 --> 00:04:23,030 +and we chose to rename that window to +Settings to match the rest of the system. + +75 +00:04:23,063 --> 00:04:26,333 +The System Settings app also uses +a new interface style + +76 +00:04:26,366 --> 00:04:29,269 +for presenting +all of its configuration options. + +77 +00:04:29,303 --> 00:04:32,239 +Settings interfaces +are often control-heavy, + +78 +00:04:32,272 --> 00:04:36,176 +so this style is designed to present forms +containing many controls + +79 +00:04:36,210 --> 00:04:39,079 +in a clear and well-organized fashion. + +80 +00:04:40,948 --> 00:04:44,184 +Since the form itself provides a lot +of visual structure, + +81 +00:04:44,218 --> 00:04:46,854 +many system controls adapt to this context + +82 +00:04:46,887 --> 00:04:49,389 +by drawing with a lower visual weight, + +83 +00:04:49,423 --> 00:04:53,293 +while revealing more prominent +control backings on rollover. + +84 +00:04:54,828 --> 00:04:57,764 +If you want to write interfaces +that use this new design, + +85 +00:04:57,798 --> 00:05:00,901 +SwiftUI makes it super easy. + +86 +00:05:00,934 --> 00:05:03,537 +Place your controls into a Form view, + +87 +00:05:03,570 --> 00:05:06,640 +and then apply +the "insetGrouped" form style. + +88 +00:05:06,673 --> 00:05:08,842 +SwiftUI takes care of the rest: + +89 +00:05:08,876 --> 00:05:11,211 +the visual style, scrolling behavior, + +90 +00:05:11,245 --> 00:05:15,015 +and layout of the form +are all applied automatically. + +91 +00:05:16,650 --> 00:05:18,952 +If you haven't gotten started +with SwiftUI yet, + +92 +00:05:18,986 --> 00:05:22,089 +this is a great opportunity +to give it a try. + +93 +00:05:22,122 --> 00:05:26,360 +A Settings window is often +a standalone area of your app's interface, + +94 +00:05:26,393 --> 00:05:29,363 +so it's the perfect place +to do some incremental adoption. + +95 +00:05:30,264 --> 00:05:34,434 +We've even created a video +about using SwiftUI and AppKit together, + +96 +00:05:34,468 --> 00:05:37,538 +which is a great place to learn more. + +97 +00:05:37,571 --> 00:05:42,176 +Next, I'd like to share some updates +to our controls. + +98 +00:05:42,209 --> 00:05:45,345 +We've got a lot of exciting +control enhancements to share, + +99 +00:05:45,379 --> 00:05:49,449 +starting with a new control +called NSComboButton. + +100 +00:05:49,483 --> 00:05:52,085 +We've also updated NSColorWell, + +101 +00:05:52,119 --> 00:05:55,689 +made some enhancements +to the NSToolbar API, + +102 +00:05:55,722 --> 00:05:58,525 +adjusted the design of NSAlert, + +103 +00:05:58,559 --> 00:06:02,629 +and improved the performance +of NSTableView. + +104 +00:06:02,663 --> 00:06:05,566 +First, NSComboButton. + +105 +00:06:05,599 --> 00:06:09,469 +NSComboButton is all about +combining an immediate button action, + +106 +00:06:09,503 --> 00:06:11,738 +and a menu for additional options. + +107 +00:06:13,240 --> 00:06:15,142 +In today's control landscape, + +108 +00:06:15,175 --> 00:06:18,545 +you'd traditionally use +a button to perform some immediate action, + +109 +00:06:18,579 --> 00:06:23,684 +or you would use a pull-down button +to show a menu with many options. + +110 +00:06:23,717 --> 00:06:28,589 +NSComboButton combines both elements +into a single control, + +111 +00:06:28,622 --> 00:06:33,694 +which joins a primary action +and a pull-down menu together. + +112 +00:06:33,727 --> 00:06:37,664 +This design is commonly used +for use cases like this one in Mail, + +113 +00:06:37,698 --> 00:06:40,667 +where the predicted folder +is a single click away, + +114 +00:06:40,701 --> 00:06:44,738 +but you can still access a menu +to file your messages anywhere. + +115 +00:06:44,771 --> 00:06:47,641 +Previously, +you might've assembled something like this + +116 +00:06:47,674 --> 00:06:50,244 +using the segmented control API, + +117 +00:06:50,277 --> 00:06:52,913 +but now there's +a dedicated control for it. + +118 +00:06:53,881 --> 00:06:56,416 +NSComboButton comes in two styles, + +119 +00:06:56,450 --> 00:07:00,287 +which dictate both the appearance +and the behavior of the button. + +120 +00:07:00,320 --> 00:07:02,623 +The default style is called "split," + +121 +00:07:02,656 --> 00:07:06,426 +and it includes a separate arrow portion +just for the menu. + +122 +00:07:08,262 --> 00:07:13,600 +The second style, "unified," +looks much more like an ordinary button. + +123 +00:07:13,634 --> 00:07:16,436 +This style performs +its primary action on click, + +124 +00:07:16,470 --> 00:07:20,174 +and it presents its menu +if you click-and-hold. + +125 +00:07:20,207 --> 00:07:21,942 +And that's NSComboButton. + +126 +00:07:21,975 --> 00:07:25,379 +We've also got some great new updates +to NSColorWell, + +127 +00:07:25,412 --> 00:07:27,781 +starting with a brand-new look. + +128 +00:07:27,814 --> 00:07:31,151 +In place of the classic square +gradient appearance, + +129 +00:07:31,185 --> 00:07:33,720 +the color well has adopted a new style + +130 +00:07:33,754 --> 00:07:37,591 +reminiscent of other button bezels +across the system. + +131 +00:07:37,624 --> 00:07:40,060 +This change is completely automatic, + +132 +00:07:40,093 --> 00:07:43,597 +so you don't need to do any adoption +to get this modern appearance. + +133 +00:07:44,231 --> 00:07:47,334 +However, we know that color picking +is an important part + +134 +00:07:47,367 --> 00:07:49,937 +of creative and professional applications, + +135 +00:07:49,970 --> 00:07:51,505 +so we've gone a step further + +136 +00:07:51,538 --> 00:07:54,508 +and introduced two new styles +for NSColorWell. + +137 +00:07:55,843 --> 00:07:58,278 +The first is a minimal style, + +138 +00:07:58,312 --> 00:08:00,747 +which shows a disclosure +arrow on rollover, + +139 +00:08:00,781 --> 00:08:03,116 +and provides a quick +color picking experience + +140 +00:08:03,150 --> 00:08:07,254 +by showing a popover to immediately +select from a palette of colors, + +141 +00:08:07,287 --> 00:08:11,525 +with the option to break out +into the full NSColorPanel. + +142 +00:08:11,558 --> 00:08:14,995 +By default, +it uses a system standard grid of colors, + +143 +00:08:15,028 --> 00:08:16,797 +but you can customize what appears here + +144 +00:08:16,830 --> 00:08:19,933 +if you've got a different UI +or palette in mind. + +145 +00:08:22,269 --> 00:08:24,705 +The second is an expanded style, + +146 +00:08:24,738 --> 00:08:27,941 +which you might recognize +from the iWork apps. + +147 +00:08:27,975 --> 00:08:30,944 +This style combines both +interaction models: + +148 +00:08:30,978 --> 00:08:33,480 +the well on the left has +the same disclosure arrow + +149 +00:08:33,514 --> 00:08:35,682 +and popover for quick picking, + +150 +00:08:35,716 --> 00:08:38,385 +while the button on the right +pulls up the full panel + +151 +00:08:38,418 --> 00:08:40,654 +for more detailed color picking. + +152 +00:08:43,123 --> 00:08:48,095 +And with that, NSColorWell now offers +three different ways to pick colors. + +153 +00:08:49,997 --> 00:08:55,035 +You can access these styles using the new +colorWellStyle property on NSColorWell, + +154 +00:08:55,068 --> 00:08:57,504 +which has cases for each style: + +155 +00:08:57,538 --> 00:09:00,941 +default, expanded, and minimal. + +156 +00:09:02,342 --> 00:09:05,879 +NSColorWell has also gained +a new target-and-action pair + +157 +00:09:05,913 --> 00:09:08,248 +for its "pulldown action." + +158 +00:09:08,282 --> 00:09:11,652 +This action determines what happens +when you click the pulldown portion + +159 +00:09:11,685 --> 00:09:15,088 +of a minimal or expanded color well. + +160 +00:09:15,122 --> 00:09:17,457 +By default, +these properties are nil, + +161 +00:09:17,491 --> 00:09:21,995 +which signifies that NSColorWell +should use the system-standard popover. + +162 +00:09:22,029 --> 00:09:24,398 +However, you can customize this action + +163 +00:09:24,431 --> 00:09:27,768 +and use it to present +your own custom popover, + +164 +00:09:27,801 --> 00:09:32,840 +Or you could even present a different +picking interface, like a menu. + +165 +00:09:32,873 --> 00:09:35,008 +And that's the new NSColorWell. + +166 +00:09:35,042 --> 00:09:39,580 +It's got a brand new look +and two new ways to quickly pick colors. + +167 +00:09:40,314 --> 00:09:42,850 +Next up, some news on NSToolbar, + +168 +00:09:42,883 --> 00:09:44,985 +where we've made a variety +of API enhancements + +169 +00:09:45,018 --> 00:09:47,821 +to give you better control +over customization + +170 +00:09:47,855 --> 00:09:50,824 +and increased flexibility for your layout. + +171 +00:09:51,725 --> 00:09:53,293 +On the customization front, + +172 +00:09:53,327 --> 00:09:55,095 +we've added two new delegate methods + +173 +00:09:55,128 --> 00:09:58,999 +to give you better control over +the customizability of your toolbar. + +174 +00:09:59,032 --> 00:10:03,604 +The first is +"toolbarImmovableItemIdentifiers". + +175 +00:10:03,637 --> 00:10:07,140 +If you implement this method +to return a set of item identifiers, + +176 +00:10:07,174 --> 00:10:11,378 +those items won't be movable +or removable by the user, + +177 +00:10:11,411 --> 00:10:14,781 +and they won't animate +when you enter customization mode. + +178 +00:10:16,183 --> 00:10:19,353 +For example, +the Mail app wants the Filter button + +179 +00:10:19,386 --> 00:10:22,990 +to always appear here, +above the message list. + +180 +00:10:23,023 --> 00:10:27,561 +Using this API, they can prevent it +from being moved away from this spot. + +181 +00:10:29,963 --> 00:10:35,636 +The second method is called "toolbar +itemIdentifier canBeInsertedAt." + +182 +00:10:35,669 --> 00:10:39,773 +This delegate method gives you veto power +over any particular reordering, + +183 +00:10:39,806 --> 00:10:43,343 +insertion, or removal from the toolbar. + +184 +00:10:43,377 --> 00:10:47,047 +You can use it to implement +your own set of customization rules – + +185 +00:10:47,080 --> 00:10:48,382 +for example, + +186 +00:10:48,415 --> 00:10:52,219 +you could create a toolbar item that's +allowed within one section of the toolbar, + +187 +00:10:52,252 --> 00:10:54,521 +but it's disallowed +within another section. + +188 +00:10:56,957 --> 00:11:00,093 +You can now specify multiple +centered items for your toolbar + +189 +00:11:00,127 --> 00:11:03,664 +using the new +centeredItemIdentifiers property. + +190 +00:11:03,697 --> 00:11:06,633 +If your toolbar is customizable, +items in this set + +191 +00:11:06,667 --> 00:11:09,369 +can still be added or removed +from the toolbar, + +192 +00:11:09,403 --> 00:11:12,806 +but they can only be reordered +within the centered group. + +193 +00:11:12,840 --> 00:11:15,409 +In this example, +the photo editing tools + +194 +00:11:15,442 --> 00:11:18,278 +all stick together in the center +of the toolbar + +195 +00:11:18,312 --> 00:11:22,850 +regardless of how many items are placed +in the leading and trailing sections. + +196 +00:11:23,584 --> 00:11:26,019 +Once your toolbar is customized +the way you like it, + +197 +00:11:26,053 --> 00:11:28,589 +you don't want the items to shift around, + +198 +00:11:28,622 --> 00:11:31,325 +and that can be difficult +for toolbar items that change meaning + +199 +00:11:31,358 --> 00:11:33,260 +based on some other state, + +200 +00:11:33,293 --> 00:11:35,929 +like the Mute and Unmute button in Mail, + +201 +00:11:35,963 --> 00:11:37,831 +which toggles when you click it. + +202 +00:11:39,433 --> 00:11:41,502 +Since the labels have different sizes, + +203 +00:11:41,535 --> 00:11:46,039 +the other items in the toolbar have to +shift around to accommodate the change. + +204 +00:11:48,542 --> 00:11:52,679 +In a scenario like this, you can +use the new possibleLabels property + +205 +00:11:52,713 --> 00:11:56,116 +on NSToolbarItem to provide a set +of the localized strings + +206 +00:11:56,149 --> 00:11:58,252 +that you'll use for the item. + +207 +00:11:59,887 --> 00:12:04,157 +NSToolbar will automatically size the item +to fit the longest label, + +208 +00:12:04,191 --> 00:12:08,829 +so your layout stays the same +even when the item is reconfigured. + +209 +00:12:08,862 --> 00:12:12,232 +Next, a design update for alerts. + +210 +00:12:12,266 --> 00:12:15,269 +Alerts on macOS use a compact layout, + +211 +00:12:15,302 --> 00:12:17,738 +which is optimized +for a small amount of text + +212 +00:12:17,771 --> 00:12:20,807 +accompanied by a few clear choices. + +213 +00:12:20,841 --> 00:12:24,144 +And in general, that's a great way +to put together an alert. + +214 +00:12:24,178 --> 00:12:26,947 +Alerts work best with shorter text: + +215 +00:12:26,980 --> 00:12:29,483 +you can communicate your message +more directly, + +216 +00:12:29,516 --> 00:12:31,718 +and people are more likely to read +what you've written + +217 +00:12:31,752 --> 00:12:34,321 +before pushing their way +through the alert. + +218 +00:12:35,989 --> 00:12:39,593 +However, sometimes you really can't +shorten your description, + +219 +00:12:39,626 --> 00:12:43,830 +especially if you need to communicate +something complex and subtle, + +220 +00:12:43,864 --> 00:12:45,866 +like this Disk Utility alert, + +221 +00:12:45,899 --> 00:12:50,204 +which conveys a really important choice +about your filesystem data. + +222 +00:12:50,237 --> 00:12:54,575 +The compact layout just isn't optimal +for this situation. + +223 +00:12:54,608 --> 00:12:58,812 +For these cases, we've adapted NSAlert +to provide a wider layout + +224 +00:12:58,846 --> 00:13:01,582 +that's suitable for longer text. + +225 +00:13:01,615 --> 00:13:05,686 +This adaptation happens automatically +for alerts where the informative text + +226 +00:13:05,719 --> 00:13:09,923 +is too long to fit comfortably +in the compact size. + +227 +00:13:09,957 --> 00:13:12,659 +We'll also use this style +if you have an accessory view + +228 +00:13:12,693 --> 00:13:16,797 +that's too large to fit +in a compact alert window. + +229 +00:13:16,830 --> 00:13:19,800 +There's no need for your app +to opt in to this behavior – + +230 +00:13:19,833 --> 00:13:22,903 +it's applied automatically system-wide. + +231 +00:13:22,936 --> 00:13:25,005 +It's important to note +that the layout is determined + +232 +00:13:25,038 --> 00:13:26,974 +at the time you present the alert, + +233 +00:13:27,007 --> 00:13:29,743 +so an alert won't swap styles +if you modify it + +234 +00:13:29,776 --> 00:13:31,845 +while it's already on screen. + +235 +00:13:33,881 --> 00:13:37,684 +You should still aim to reduce the length +of your alert text wherever possible, + +236 +00:13:37,718 --> 00:13:40,220 +but this design update +will improve the user experience + +237 +00:13:40,254 --> 00:13:42,456 +for those cases where you can't. + +238 +00:13:43,657 --> 00:13:47,261 +Next, an important new feature +of NSTableView. + +239 +00:13:47,294 --> 00:13:51,999 +NSTableView is designed to efficiently +handle a very large number of rows, + +240 +00:13:52,032 --> 00:13:55,636 +by lazily populating +and reusing the views as you scroll. + +241 +00:13:56,370 --> 00:13:59,573 +However, for tables where each row +can have a different height, + +242 +00:13:59,606 --> 00:14:01,308 +that can be a challenge, + +243 +00:14:01,341 --> 00:14:04,178 +because in order to provide +a good scrolling experience + +244 +00:14:04,211 --> 00:14:06,280 +the table needs to know its total height + +245 +00:14:06,313 --> 00:14:10,083 +and the location of each row +in the scrolling region. + +246 +00:14:11,185 --> 00:14:15,889 +Historically, NSTableView does this by +sizing all of the rows in the table, + +247 +00:14:15,923 --> 00:14:19,526 +which can have an impact +on initial load times. + +248 +00:14:19,560 --> 00:14:22,963 +In macOS Ventura, +NSTableView achieves those goals + +249 +00:14:22,996 --> 00:14:25,866 +while providing much better performance. + +250 +00:14:27,968 --> 00:14:31,138 +Instead of eagerly calculating +the height for each row, + +251 +00:14:31,171 --> 00:14:33,974 +NSTableView now lazily calculates +row heights + +252 +00:14:34,007 --> 00:14:38,278 +based on which rows are within +or near the scrolling viewport. + +253 +00:14:39,646 --> 00:14:41,748 +For the rows that haven't +been measured yet, + +254 +00:14:41,782 --> 00:14:44,818 +NSTableView uses +a running estimated height + +255 +00:14:44,852 --> 00:14:48,255 +based on the row heights +that it's already measured. + +256 +00:14:48,288 --> 00:14:49,890 +As you scroll through the table, + +257 +00:14:49,923 --> 00:14:52,793 +NSTableView requests row heights +as needed, + +258 +00:14:52,826 --> 00:14:56,296 +replacing the estimated heights +with real measurements, + +259 +00:14:56,330 --> 00:14:59,867 +while taking care to maintain +the correct scrolling position. + +260 +00:15:02,269 --> 00:15:07,174 +This optimization significantly improves +the load times for very large tables. + +261 +00:15:07,207 --> 00:15:09,576 +The change does alter the timing +of delegate calls + +262 +00:15:09,610 --> 00:15:11,678 +like "table view: height of row", + +263 +00:15:11,712 --> 00:15:13,113 +so you shouldn't make assumptions + +264 +00:15:13,146 --> 00:15:16,850 +about when NSTableView +will request row heights from you. + +265 +00:15:18,185 --> 00:15:23,023 +This optimization applies to both +NSTableView and SwiftUI's List view, + +266 +00:15:23,056 --> 00:15:26,326 +and it's automatically used for all apps +on macOS Ventura + +267 +00:15:26,360 --> 00:15:28,695 +with no adoption required. + +268 +00:15:28,729 --> 00:15:31,298 +And that's NSTableView performance. + +269 +00:15:32,232 --> 00:15:35,736 +Next, some updates on SF Symbols. + +270 +00:15:35,769 --> 00:15:39,106 +macOS Ventura includes SF Symbols 4, + +271 +00:15:39,139 --> 00:15:42,109 +which adds more than 450 new symbol images + +272 +00:15:42,142 --> 00:15:44,678 +covering all kinds of subjects. + +273 +00:15:45,979 --> 00:15:48,949 +These new symbols include laurels, + +274 +00:15:48,982 --> 00:15:51,919 +all kinds of household objects, + +275 +00:15:51,952 --> 00:15:54,922 +currency symbols from around the world, + +276 +00:15:54,955 --> 00:15:58,258 +and even a variety +of sports-related symbols. + +277 +00:15:58,292 --> 00:16:00,694 +With a catalog of thousands of symbols, + +278 +00:16:00,727 --> 00:16:04,031 +it's likely that SF Symbols includes +a professionally-designed icon + +279 +00:16:04,064 --> 00:16:07,134 +for any idea that you want to represent. + +280 +00:16:07,167 --> 00:16:08,936 +But we haven't stopped there. + +281 +00:16:08,969 --> 00:16:11,471 +SF Symbols 4 also includes +some new features + +282 +00:16:11,505 --> 00:16:13,907 +to further enhance your iconography. + +283 +00:16:14,808 --> 00:16:18,278 +To recap, symbol images support +a number of rendering modes + +284 +00:16:18,312 --> 00:16:21,448 +that you can choose from +depending on your design. + +285 +00:16:21,481 --> 00:16:24,384 +There's monochrome, +which uses a single color; + +286 +00:16:24,418 --> 00:16:27,521 +hierarchical, +which uses different opacities of a color + +287 +00:16:27,554 --> 00:16:30,724 +to emphasize certain portions +of a symbol; + +288 +00:16:30,757 --> 00:16:32,926 +palette, +which allows you to specify + +289 +00:16:32,960 --> 00:16:35,629 +distinct colors +for each part of a symbol; + +290 +00:16:35,662 --> 00:16:40,400 +and multicolor, which uses colors +designed directly into the symbol artwork. + +291 +00:16:41,568 --> 00:16:45,973 +These choices give you the flexibility +to realize a wide variety of designs, + +292 +00:16:46,006 --> 00:16:49,910 +but we also want symbol images +to look their best right out of the box, + +293 +00:16:49,943 --> 00:16:52,746 +without the need +to apply any configuration. + +294 +00:16:53,747 --> 00:16:56,216 +That's why we've introduced +a new feature to symbols + +295 +00:16:56,250 --> 00:16:59,953 +in macOS Ventura: +preferred rendering mode. + +296 +00:16:59,987 --> 00:17:01,555 +With preferred rendering mode, + +297 +00:17:01,588 --> 00:17:04,925 +symbols can specify the style of rendering +that they prefer, + +298 +00:17:04,958 --> 00:17:09,429 +and at runtime +AppKit will use that style automatically. + +299 +00:17:09,463 --> 00:17:12,366 +This is great for symbols +like AirPods Pro, + +300 +00:17:12,399 --> 00:17:14,601 +which prefers a hierarchical style + +301 +00:17:14,635 --> 00:17:18,172 +to increase the clarity +of those fine details. + +302 +00:17:18,205 --> 00:17:20,841 +Of course, if you have +a different design in mind, + +303 +00:17:20,874 --> 00:17:24,444 +you can always use an +NSImageSymbolConfiguration object + +304 +00:17:24,478 --> 00:17:26,880 +to choose your preferred style. + +305 +00:17:28,615 --> 00:17:31,118 +Some symbols +don't just represent a concept, + +306 +00:17:31,151 --> 00:17:34,821 +they're also meant to communicate +some value or quantity, + +307 +00:17:34,855 --> 00:17:38,725 +like your Wi-Fi signal strength, +or audio volume. + +308 +00:17:38,759 --> 00:17:41,995 +For cases like these, +we've introduced a new type of symbol + +309 +00:17:42,029 --> 00:17:44,898 +that we call a "variable symbol." + +310 +00:17:44,932 --> 00:17:48,335 +With a variable symbol, +you supply a floating point value + +311 +00:17:48,368 --> 00:17:50,304 +directly to NSImage, + +312 +00:17:50,337 --> 00:17:52,539 +and the symbol embeds numeric thresholds + +313 +00:17:52,573 --> 00:17:56,743 +to decide how each path should vary +based on that value. + +314 +00:17:56,777 --> 00:17:58,445 +Here's the API. + +315 +00:17:58,478 --> 00:18:02,015 +Variable symbols are created +using a new initializer. + +316 +00:18:02,049 --> 00:18:05,252 +It's similar to the existing +symbol image initializer, + +317 +00:18:05,285 --> 00:18:07,988 +with the addition of a value parameter, + +318 +00:18:08,021 --> 00:18:11,758 +which is a floating point number +between zero and one. + +319 +00:18:11,792 --> 00:18:15,229 +If the symbol image doesn't define +any variable thresholds, + +320 +00:18:15,262 --> 00:18:19,566 +this value is ignored +and the symbol draws as it normally would. + +321 +00:18:19,600 --> 00:18:23,003 +If it does, you'll see +the symbol paths drawing differently + +322 +00:18:23,036 --> 00:18:25,472 +based on the value you supplied. + +323 +00:18:26,807 --> 00:18:31,111 +Each variable symbol can represent +a value in its own unique way, + +324 +00:18:31,144 --> 00:18:33,447 +and by providing that value +at the API level, + +325 +00:18:33,480 --> 00:18:36,149 +you get access to all of those variations + +326 +00:18:36,183 --> 00:18:40,120 +without having to know the fine details +of how the symbol is composed. + +327 +00:18:41,188 --> 00:18:44,491 +Variable symbols work great +in combination with rendering styles + +328 +00:18:44,525 --> 00:18:47,361 +like palette color and multicolor, + +329 +00:18:47,394 --> 00:18:50,297 +so you can adapt them +to almost any design. + +330 +00:18:50,330 --> 00:18:54,701 +Finally, I'd like to cover +some big updates to Sharing + +331 +00:18:54,735 --> 00:18:58,305 +macOS Ventura elevates +the sharing experience on the Mac, + +332 +00:18:58,338 --> 00:19:00,841 +introducing features like suggested people + +333 +00:19:00,874 --> 00:19:05,812 +and new ways to invite and manage +the people that you're collaborating with. + +334 +00:19:05,846 --> 00:19:09,249 +There are some new APIs that you can adopt +so that your app gets the most + +335 +00:19:09,283 --> 00:19:11,151 +out of these enhancements. + +336 +00:19:13,754 --> 00:19:18,692 +The most prominent update to the sharing +experience is the new sharing popover. + +337 +00:19:18,725 --> 00:19:22,095 +This replaces the existing share menu +with a rich interface + +338 +00:19:22,129 --> 00:19:25,132 +that includes more information +about the document you're sharing + +339 +00:19:25,165 --> 00:19:28,602 +and familiar features +like suggested people. + +340 +00:19:28,635 --> 00:19:33,173 +It supports all of the same APIs and +delegate methods as the previous picker, + +341 +00:19:33,207 --> 00:19:36,610 +so you can still do things like +filter the list of sharing services, + +342 +00:19:36,643 --> 00:19:40,380 +or insert your own custom +services into the picker. + +343 +00:19:42,382 --> 00:19:44,418 +If you're sharing a file URL, + +344 +00:19:44,451 --> 00:19:48,822 +NSSharingServicePicker can automatically +populate the header with an icon, + +345 +00:19:48,856 --> 00:19:52,426 +name, and other metadata about the file. + +346 +00:19:52,459 --> 00:19:55,395 +But if you're sharing +a custom type instead, + +347 +00:19:55,429 --> 00:19:57,831 +you can conform your items +to a new protocol + +348 +00:19:57,865 --> 00:20:02,236 +that NSSharingServicePicker will use +to request that information. + +349 +00:20:03,437 --> 00:20:08,041 +The protocol is called +NSPreviewRepresentableActivityItem. + +350 +00:20:08,075 --> 00:20:12,145 +Conforming types must be able to return +the underlying item to share, + +351 +00:20:12,179 --> 00:20:14,414 +like an NSItemProvider, + +352 +00:20:14,448 --> 00:20:16,884 +and they can optionally return a title, + +353 +00:20:16,917 --> 00:20:19,853 +an image provider, and an icon provider. + +354 +00:20:21,555 --> 00:20:24,491 +For convenience, +there's a conforming class in AppKit + +355 +00:20:24,525 --> 00:20:27,694 +called NSPreviewRepresentingActivityItem + +356 +00:20:27,728 --> 00:20:32,866 +which you can use to bundle up an existing +sharing item with its metadata. + +357 +00:20:32,900 --> 00:20:36,637 +You can provide each image parameter +directly as an NSImage, + +358 +00:20:36,670 --> 00:20:40,474 +or you can use NSItemProvider +if it's too performance-intensive + +359 +00:20:40,507 --> 00:20:42,910 +to generate those images up front. + +360 +00:20:44,378 --> 00:20:47,381 +The new sharing picker is great for +kicking off sharing from somewhere + +361 +00:20:47,414 --> 00:20:48,949 +like a toolbar button, + +362 +00:20:48,982 --> 00:20:51,385 +but sometimes you want to start sharing +from a menu, + +363 +00:20:51,418 --> 00:20:57,491 +like the main menu bar or the context menu +for a selected view inside your app. + +364 +00:20:57,524 --> 00:21:01,228 +Previously, you might've constructed +your own menu to handle this, + +365 +00:21:01,261 --> 00:21:06,433 +by enumerating sharing services +and then building menu items for each one. + +366 +00:21:06,466 --> 00:21:10,103 +Although that does work, +it bypasses the standard picker, + +367 +00:21:10,137 --> 00:21:13,674 +so now you're missing out +on all of those new features. + +368 +00:21:13,707 --> 00:21:16,543 +In macOS Ventura, +NSSharingServicePicker + +369 +00:21:16,577 --> 00:21:20,247 +can create +a "standardShareMenuItem" for you. + +370 +00:21:20,280 --> 00:21:24,852 +You can add the standard item +to any menu to easily kick off sharing. + +371 +00:21:24,885 --> 00:21:28,589 +Once selected, +the menu item summons the sharing popover, + +372 +00:21:28,622 --> 00:21:31,792 +and for context menus, +it'll even anchor the popover + +373 +00:21:31,825 --> 00:21:34,328 +to the same view that produced the menu. + +374 +00:21:34,995 --> 00:21:39,333 +There's a lot of new support for +managing collaboration in macOS Ventura. + +375 +00:21:40,000 --> 00:21:43,470 +With some extra adoption, +your shareable items can also become + +376 +00:21:43,504 --> 00:21:45,506 +invitations to collaborate, + +377 +00:21:45,539 --> 00:21:48,108 +which users can initiate +via the sharing picker, + +378 +00:21:48,141 --> 00:21:52,346 +drag-and-drop into Messages, +or even via FaceTime. + +379 +00:21:52,379 --> 00:21:55,983 +You can share content +using CloudKit or iCloud Drive, + +380 +00:21:56,016 --> 00:22:00,787 +or you can connect the invitation flow +with your own collaboration server. + +381 +00:22:00,821 --> 00:22:02,689 +Now this is a really big topic, + +382 +00:22:02,723 --> 00:22:06,560 +so we've made a few videos +to explain it in much greater depth. + +383 +00:22:06,593 --> 00:22:09,363 +They're a must-see +if your app supports collaboration, + +384 +00:22:09,396 --> 00:22:12,499 +or if you'd like +to get started with adding it. + +385 +00:22:12,533 --> 00:22:15,502 +As you get started with macOS Ventura, + +386 +00:22:15,536 --> 00:22:19,873 +make sure you're setting up your windows +to work best with Stage Manager. + +387 +00:22:19,907 --> 00:22:24,077 +Then, consider how your design +might benefit from control enhancements + +388 +00:22:24,111 --> 00:22:27,881 +like NSComboButton +and NSColorWell. + +389 +00:22:27,915 --> 00:22:33,921 +Improve your iconography using the newest +symbols and features of SF Symbols. + +390 +00:22:33,954 --> 00:22:36,290 +And finally, for collaboration, + +391 +00:22:36,323 --> 00:22:39,126 +adopt the latest APIs +so that you get the most + +392 +00:22:39,159 --> 00:22:42,796 +out of macOS Ventura's +new sharing experience. + +393 +00:22:43,797 --> 00:22:45,199 +Thanks so much for watching, + +394 +00:22:45,232 --> 00:22:48,869 +and thanks for continuing +to build great Mac applications. + diff --git a/eng/2022 Session 10075 Use SwiftUI with AppKit en.srt b/eng/2022 Session 10075 Use SwiftUI with AppKit en.srt new file mode 100644 index 0000000..d215797 --- /dev/null +++ b/eng/2022 Session 10075 Use SwiftUI with AppKit en.srt @@ -0,0 +1,1737 @@ +1 +00:00:00,000 --> 00:00:03,036 +♪ instrumental hip hop music ♪ + +2 +00:00:03,036 --> 00:00:09,610 +♪ + +3 +00:00:09,610 --> 00:00:12,045 +Welcome to +"Use SwiftUI with AppKit." + +4 +00:00:12,045 --> 00:00:16,350 +I'm Ian, an engineer +working on Shortcuts. + +5 +00:00:16,350 --> 00:00:20,120 +In macOS Monterey, +Shortcuts came to macOS. + +6 +00:00:20,120 --> 00:00:23,757 +Shortcuts uses a lot +of SwiftUI on the Mac. + +7 +00:00:23,757 --> 00:00:26,760 +SwiftUI helps customize +the experience for the platform, + +8 +00:00:26,760 --> 00:00:28,862 +while sharing common views +with the apps + +9 +00:00:28,862 --> 00:00:31,398 +on iOS and watchOS. + +10 +00:00:31,398 --> 00:00:33,333 +In this video, I'm going to show + +11 +00:00:33,333 --> 00:00:36,703 +how you can start adopting +SwiftUI in your Mac app, + +12 +00:00:36,703 --> 00:00:40,707 +by looking at some examples +from Shortcuts. + +13 +00:00:40,707 --> 00:00:42,542 +First, I'll show you an example + +14 +00:00:42,542 --> 00:00:45,512 +of how to host +SwiftUI views in your app, + +15 +00:00:45,512 --> 00:00:47,447 +and then talk about +how to pass data + +16 +00:00:47,447 --> 00:00:50,817 +between AppKit and SwiftUI. + +17 +00:00:50,817 --> 00:00:53,086 +I'll also cover +hosting SwiftUI views + +18 +00:00:53,086 --> 00:00:55,989 +in the cells of a collection +or table view, + +19 +00:00:55,989 --> 00:00:58,926 +how to handle layout +and sizing of SwiftUI views + +20 +00:00:58,926 --> 00:01:01,495 +when they are embedded +in AppKit, + +21 +00:01:01,495 --> 00:01:03,931 +how to make +your SwiftUI views participate + +22 +00:01:03,931 --> 00:01:07,000 +in the responder chain +and be focusable, + +23 +00:01:07,000 --> 00:01:12,439 +and, finally, how to host +an AppKit view in SwiftUI. + +24 +00:01:12,439 --> 00:01:16,877 +Alright, I'll start with +how to host SwiftUI in AppKit. + +25 +00:01:16,877 --> 00:01:18,578 +In Shortcuts, + +26 +00:01:18,578 --> 00:01:23,150 +the main window contains +an AppKit split view controller + +27 +00:01:23,150 --> 00:01:28,588 +and the sidebar on the left +is written using SwiftUI. + +28 +00:01:28,588 --> 00:01:32,326 +The sidebar view is implemented +as a SwiftUI List, + +29 +00:01:32,326 --> 00:01:35,896 +and the list shows sections +with rows for all of the places + +30 +00:01:35,896 --> 00:01:38,899 +you can navigate to in the app. + +31 +00:01:38,899 --> 00:01:42,302 +The view keeps track +of which item is selected, + +32 +00:01:42,302 --> 00:01:45,806 +through the selected item +binding. + +33 +00:01:45,806 --> 00:01:47,908 +The possible items +that can be selected + +34 +00:01:47,908 --> 00:01:53,113 +are represented as cases +in the SidebarItem type. + +35 +00:01:53,113 --> 00:01:56,850 +In this case, since there's a +split view controller already. + +36 +00:01:56,850 --> 00:01:58,986 +To host this sidebar view, + +37 +00:01:58,986 --> 00:02:03,890 +we use a class from SwiftUI +called NSHostingController. + +38 +00:02:03,890 --> 00:02:06,727 +The SwiftUI sidebar view +is passed in + +39 +00:02:06,727 --> 00:02:10,897 +as the root view +of that hosting controller. + +40 +00:02:10,897 --> 00:02:12,632 +Since a hosting controller +can be used + +41 +00:02:12,632 --> 00:02:14,801 +like any other view controller, + +42 +00:02:14,801 --> 00:02:19,072 +here, we configure it +as a splitViewItem + +43 +00:02:19,072 --> 00:02:23,076 +and add that +to the splitViewController. + +44 +00:02:23,076 --> 00:02:26,213 +Now the sidebar is hosted +in the split view, + +45 +00:02:26,213 --> 00:02:29,983 +but for it to work +when the selection changes, + +46 +00:02:29,983 --> 00:02:35,522 +the right side of the split view +needs to show a different page. + +47 +00:02:35,522 --> 00:02:37,791 +Currently, +the selected item state + +48 +00:02:37,791 --> 00:02:40,527 +only exists within SwiftUI. + +49 +00:02:40,527 --> 00:02:43,864 +What we need to do is move that +to a place that can be shared + +50 +00:02:43,864 --> 00:02:47,234 +between the split view +and the sidebar. + +51 +00:02:47,234 --> 00:02:50,704 +A good way to do this +is to create a model object + +52 +00:02:50,704 --> 00:02:54,241 +that can be stored outside +of SwiftUI and contain the state + +53 +00:02:54,241 --> 00:02:56,109 +that needs to be shared. + +54 +00:02:56,109 --> 00:02:59,479 +I'll call this object +the SelectionModel. + +55 +00:02:59,479 --> 00:03:02,582 +Now, the sidebar can still +read and write the state + +56 +00:03:02,582 --> 00:03:05,285 +in the SelectionModel. + +57 +00:03:05,285 --> 00:03:08,989 +In code, the SelectionModel +is a class that conforms + +58 +00:03:08,989 --> 00:03:11,425 +to ObservableObject. + +59 +00:03:11,425 --> 00:03:15,195 +Being an observable object +lets SwiftUI reload the view + +60 +00:03:15,195 --> 00:03:18,799 +when the state stored +in the model changes. + +61 +00:03:18,799 --> 00:03:23,103 +It stores which sidebar item +is currently selected. + +62 +00:03:23,103 --> 00:03:27,240 +This property is published +so that the SwiftUI sidebar view + +63 +00:03:27,240 --> 00:03:31,211 +can update when +the selected item changes. + +64 +00:03:31,211 --> 00:03:34,314 +Whenever someone changes +the selection in the sidebar, + +65 +00:03:34,314 --> 00:03:39,119 +the model can show a new page +in the detail view. + +66 +00:03:39,119 --> 00:03:42,856 +Now that I've covered +how to host SwiftUI in AppKit, + +67 +00:03:42,856 --> 00:03:46,760 +let's move on to collection +and table cells. + +68 +00:03:46,760 --> 00:03:50,730 +When bringing Shortcuts +from other platforms to macOS, + +69 +00:03:50,730 --> 00:03:53,600 +there was already +an iconic SwiftUI view + +70 +00:03:53,600 --> 00:03:57,137 +built to display a shortcut +in a collection view cell + +71 +00:03:57,137 --> 00:04:00,006 +or a Home Screen widget. + +72 +00:04:00,006 --> 00:04:03,777 +On macOS, these same views +are displayed in the cells + +73 +00:04:03,777 --> 00:04:06,880 +of an NSCollectionView. + +74 +00:04:06,880 --> 00:04:10,317 +In a collection or table view +with lots of items, + +75 +00:04:10,317 --> 00:04:13,653 +each cell view is recycled +as you scroll, + +76 +00:04:13,653 --> 00:04:17,023 +showing different content +over time. + +77 +00:04:17,023 --> 00:04:20,026 +To make sure the cell reuse +is performant, + +78 +00:04:20,026 --> 00:04:22,763 +you need to avoid +adding and removing subviews + +79 +00:04:22,763 --> 00:04:26,800 +from the cells +as the user scrolls. + +80 +00:04:26,800 --> 00:04:29,803 +When displaying a SwiftUI view +in each cell, + +81 +00:04:29,803 --> 00:04:32,539 +use a single hosting view +and update it + +82 +00:04:32,539 --> 00:04:35,542 +with a different root view +when the cell's content + +83 +00:04:35,542 --> 00:04:37,778 +needs to change. + +84 +00:04:37,778 --> 00:04:40,280 +Here's all you need +to build a collection view cell + +85 +00:04:40,280 --> 00:04:42,249 +to host SwiftUI. + +86 +00:04:42,249 --> 00:04:43,683 +In the example here, + +87 +00:04:43,683 --> 00:04:47,687 +I'm building the cell +that displays a shortcut view. + +88 +00:04:47,687 --> 00:04:52,359 +Each cell contains an +NSHostingView to host SwiftUI. + +89 +00:04:52,359 --> 00:04:54,094 +Since cells are created + +90 +00:04:54,094 --> 00:04:56,663 +before they are configured +with any content, + +91 +00:04:56,663 --> 00:04:58,565 +this will start off as nil, + +92 +00:04:58,565 --> 00:05:00,300 +and will be set the first time + +93 +00:05:00,300 --> 00:05:03,503 +a shortcut +is ready to be displayed. + +94 +00:05:03,503 --> 00:05:06,773 +The displayShortcut method +is called by the data source + +95 +00:05:06,773 --> 00:05:10,343 +when configuring the cell +to display a shortcut. + +96 +00:05:10,343 --> 00:05:14,614 +This method creates +a SwiftUI ShortcutView. + +97 +00:05:14,614 --> 00:05:17,551 +Then, if there's already +a hostingView, + +98 +00:05:17,551 --> 00:05:21,988 +the rootView of that hostingView +is set to the new view. + +99 +00:05:21,988 --> 00:05:24,291 +Otherwise, +if it's the first time, + +100 +00:05:24,291 --> 00:05:26,927 +a newHostingView +is created and added + +101 +00:05:26,927 --> 00:05:30,263 +as a subview of the cell. + +102 +00:05:30,263 --> 00:05:34,000 +Here's the lifecycle of the cell +that's hosting SwiftUI. + +103 +00:05:34,000 --> 00:05:38,772 +First, the cell is initialized +and it starts with no subviews, + +104 +00:05:38,772 --> 00:05:41,908 +since there is not +a shortcut to display yet. + +105 +00:05:41,908 --> 00:05:44,711 +The first time +displayShortcut is called, + +106 +00:05:44,711 --> 00:05:49,683 +the hostingView is created with +the shortcutView to display. + +107 +00:05:49,683 --> 00:05:52,319 +This creates +a SwiftUI view hierarchy, + +108 +00:05:52,319 --> 00:05:59,993 +containing a VStack, an image, +a spacer, and two text views. + +109 +00:05:59,993 --> 00:06:02,896 +If this cell is then scrolled +off screen, + +110 +00:06:02,896 --> 00:06:05,565 +it will be potentially +dequeued by the system + +111 +00:06:05,565 --> 00:06:08,268 +and need to show +a different shortcut. + +112 +00:06:08,268 --> 00:06:12,072 +When this happens, +a new ShortcutView is created + +113 +00:06:12,072 --> 00:06:15,041 +and given to the HostingView. + +114 +00:06:15,041 --> 00:06:17,510 +Since the HostingView +was already displaying + +115 +00:06:17,510 --> 00:06:19,312 +a different shortcut view, + +116 +00:06:19,312 --> 00:06:22,549 +it will reuse the overall +structure of the view, + +117 +00:06:22,549 --> 00:06:25,385 +including the VStack +and the spacer, + +118 +00:06:25,385 --> 00:06:30,790 +and only update the image, text, +and background that changed. + +119 +00:06:30,790 --> 00:06:35,595 +Alright, next, let's talk about +layout and sizing. + +120 +00:06:35,595 --> 00:06:37,797 +Hosting controllers +and hosting views + +121 +00:06:37,797 --> 00:06:39,899 +have intrinsic sizes + +122 +00:06:39,899 --> 00:06:43,603 +based on the SwiftUI view's +ideal width and height. + +123 +00:06:43,603 --> 00:06:46,273 +SwiftUI automatically +creates and updates + +124 +00:06:46,273 --> 00:06:49,843 +Auto Layout constraints, +which the AppKit layout system + +125 +00:06:49,843 --> 00:06:53,313 +uses to size the view +appropriately. + +126 +00:06:53,313 --> 00:06:56,349 +Views are also flexible, +meaning they support + +127 +00:06:56,349 --> 00:07:00,854 +a variety of sizes, +between a minimum and a maximum. + +128 +00:07:00,854 --> 00:07:05,358 +SwiftUI creates constraints +for these as well. + +129 +00:07:05,358 --> 00:07:08,762 +When embedding SwiftUI +hosting views in your hierarchy, + +130 +00:07:08,762 --> 00:07:11,865 +you should apply your own +Auto Layout constraints + +131 +00:07:11,865 --> 00:07:15,935 +to the superview +or to other adjacent views. + +132 +00:07:15,935 --> 00:07:19,506 +Using the frame modifier +or other SwiftUI layout + +133 +00:07:19,506 --> 00:07:23,376 +will result in an update to the +constraints that are created, + +134 +00:07:23,376 --> 00:07:28,214 +such as overriding the width +to be a fixed size. + +135 +00:07:28,214 --> 00:07:31,184 +Since windows can be resized +by the user, + +136 +00:07:31,184 --> 00:07:35,088 +they have a minimum +and a maximum size. + +137 +00:07:35,088 --> 00:07:36,723 +When HostingViews are set + +138 +00:07:36,723 --> 00:07:39,793 +as the top-level contentView +of a window, + +139 +00:07:39,793 --> 00:07:43,096 +SwiftUI will automatically +update that window's minimum + +140 +00:07:43,096 --> 00:07:47,400 +and maximum size based on +the content being displayed. + +141 +00:07:47,400 --> 00:07:49,669 +And this lets windows +be resizable + +142 +00:07:49,669 --> 00:07:52,672 +either vertically, +horizontally, or both, + +143 +00:07:52,672 --> 00:07:55,275 +depending on the content. + +144 +00:07:55,275 --> 00:07:58,411 +SwiftUI views, +placed in hosting controllers, + +145 +00:07:58,411 --> 00:08:02,349 +also are sized based on the +content when presented modally. + +146 +00:08:02,349 --> 00:08:05,585 +For example, you can easily +place SwiftUI views + +147 +00:08:05,585 --> 00:08:07,520 +into an AppKit popover, + +148 +00:08:07,520 --> 00:08:09,556 +by presenting +a hosting controller + +149 +00:08:09,556 --> 00:08:13,259 +using the popover presentation +API on NSViewController, + +150 +00:08:13,259 --> 00:08:15,261 +as shown here. + +151 +00:08:15,261 --> 00:08:18,031 +You can also present +SwiftUI views as sheets, + +152 +00:08:18,031 --> 00:08:20,967 +using the presentAsSheet +method. + +153 +00:08:20,967 --> 00:08:23,570 +And finally, for a modal window, + +154 +00:08:23,570 --> 00:08:26,606 +you can use the +presentAsModalWindow method + +155 +00:08:26,606 --> 00:08:30,777 +to present a window that +blocks interaction until closed. + +156 +00:08:30,777 --> 00:08:34,614 +The window is sized +to fit the content. + +157 +00:08:34,614 --> 00:08:37,550 +In macOS Ventura, +there are new APIs + +158 +00:08:37,550 --> 00:08:40,720 +on NSHostingView +and NSHostingController + +159 +00:08:40,720 --> 00:08:42,922 +that allow you to customize +the constraints + +160 +00:08:42,922 --> 00:08:45,458 +that are automatically added. + +161 +00:08:45,458 --> 00:08:47,961 +By default, +hosting controllers and views + +162 +00:08:47,961 --> 00:08:50,497 +create constraints +for the minimum size, + +163 +00:08:50,497 --> 00:08:53,867 +intrinsic size, +and maximum size. + +164 +00:08:53,867 --> 00:08:56,903 +You may want to disable some of +these for performance reasons + +165 +00:08:56,903 --> 00:09:00,173 +if you want the view +to always be flexibly sized, + +166 +00:09:00,173 --> 00:09:02,208 +or the constraints +are already added + +167 +00:09:02,208 --> 00:09:05,345 +to surrounding views in AppKit. + +168 +00:09:05,345 --> 00:09:06,746 +For hosting controllers, + +169 +00:09:06,746 --> 00:09:08,681 +to let the ideal size +of the view + +170 +00:09:08,681 --> 00:09:11,618 +determine the preferred +content size, + +171 +00:09:11,618 --> 00:09:15,622 +you can enable the +preferredContentSize option. + +172 +00:09:15,622 --> 00:09:18,825 +When you start adding +SwiftUI views to your app, + +173 +00:09:18,825 --> 00:09:21,594 +it's important that they take +part in the responder chain + +174 +00:09:21,594 --> 00:09:26,199 +and focus system just like +other views in your app. + +175 +00:09:26,199 --> 00:09:29,302 +In Shortcuts, +our editor is implemented + +176 +00:09:29,302 --> 00:09:32,038 +as a SwiftUI View. + +177 +00:09:32,038 --> 00:09:35,008 +But the editor needs to handle +menu bar commands + +178 +00:09:35,008 --> 00:09:40,013 +defined in the main menu, +which is implemented in AppKit. + +179 +00:09:40,013 --> 00:09:45,084 +These commands include +cut, copy, paste, and others. + +180 +00:09:45,084 --> 00:09:49,088 +We implemented a few of our +own custom menu items as well, + +181 +00:09:49,088 --> 00:09:52,525 +for moving actions up and down. + +182 +00:09:52,525 --> 00:09:56,229 +In AppKit, your view hierarchy +makes up a chain of views + +183 +00:09:56,229 --> 00:09:58,898 +called "the responder chain." + +184 +00:09:58,898 --> 00:10:02,902 +The focused responder +is called the first responder. + +185 +00:10:02,902 --> 00:10:04,838 +When a menu item is selected, + +186 +00:10:04,838 --> 00:10:08,708 +the selector for that item +is sent to the first responder. + +187 +00:10:08,708 --> 00:10:10,210 +But if the first responder + +188 +00:10:10,210 --> 00:10:12,145 +doesn't respond +to that selector, + +189 +00:10:12,145 --> 00:10:15,215 +then the selector is sent +to each next responder, + +190 +00:10:15,215 --> 00:10:17,650 +until something +handles the selector, + +191 +00:10:17,650 --> 00:10:20,653 +or it reaches the app. + +192 +00:10:20,653 --> 00:10:23,790 +The equivalent to the first +responder in SwiftUI + +193 +00:10:23,790 --> 00:10:26,993 +is the focused view. + +194 +00:10:26,993 --> 00:10:31,097 +Focusable SwiftUI views +can respond to keyboard input + +195 +00:10:31,097 --> 00:10:35,602 +and handle selectors +sent to the responder chain. + +196 +00:10:35,602 --> 00:10:40,206 +Some views like text fields +are already focusable, + +197 +00:10:40,206 --> 00:10:42,909 +but you can use +the focusable modifier + +198 +00:10:42,909 --> 00:10:46,312 +to make other views +focusable too. + +199 +00:10:46,312 --> 00:10:50,016 +SwiftUI has a few modifiers +to handle common commands, + +200 +00:10:50,016 --> 00:10:52,886 +such as copy, cut, and paste. + +201 +00:10:52,886 --> 00:10:55,555 +These pass values +in and out of the pasteboard, + +202 +00:10:55,555 --> 00:10:58,391 +and it's an easy way +to let people transfer data + +203 +00:10:58,391 --> 00:11:00,994 +in and out of your app. + +204 +00:11:00,994 --> 00:11:03,763 +The shortcuts editor uses +the onMoveCommand + +205 +00:11:03,763 --> 00:11:07,333 +and onExit command modifiers +to handle the arrow keys + +206 +00:11:07,333 --> 00:11:09,602 +and escape keys. + +207 +00:11:09,602 --> 00:11:12,272 +The onCommand modifier +can be used to handle + +208 +00:11:12,272 --> 00:11:14,674 +any of the common selectors +from AppKit + +209 +00:11:14,674 --> 00:11:17,844 +or your own custom selectors +defined in your app. + +210 +00:11:17,844 --> 00:11:21,748 +Here, we handle the +selectAll command from AppKit + +211 +00:11:21,748 --> 00:11:25,685 +and the moveActionUp +and moveActionDown commands + +212 +00:11:25,685 --> 00:11:28,855 +defined in the Shortcuts app. + +213 +00:11:28,855 --> 00:11:32,091 +When testing focus and keyboard +navigability in your app, + +214 +00:11:32,091 --> 00:11:34,627 +make sure to open +Keyboard System Settings + +215 +00:11:34,627 --> 00:11:36,896 +and test with +Full Keyboard Navigation + +216 +00:11:36,896 --> 00:11:39,632 +turned both on and off, + +217 +00:11:39,632 --> 00:11:44,370 +since many controls are only +focusable when that's enabled. + +218 +00:11:44,370 --> 00:11:46,005 +There's a lot more you can do + +219 +00:11:46,005 --> 00:11:48,775 +to make your app work great +with the keyboard. + +220 +00:11:48,775 --> 00:11:52,579 +For example, there are APIs +such as FocusState + +221 +00:11:52,579 --> 00:11:54,314 +and the focused modifier + +222 +00:11:54,314 --> 00:11:58,217 +that let you programmatically +change which view is focused. + +223 +00:11:58,217 --> 00:12:01,054 +To learn more about focus +and the keyboard, + +224 +00:12:01,054 --> 00:12:02,221 +you should go watch + +225 +00:12:02,221 --> 00:12:06,326 +the "Direct and reflect +focus in SwiftUI" video. + +226 +00:12:06,326 --> 00:12:11,931 +Finally, let's talk about +hosting AppKit views in SwiftUI. + +227 +00:12:11,931 --> 00:12:13,266 +There are some instances + +228 +00:12:13,266 --> 00:12:15,602 +where Shortcuts +is hosting AppKit views + +229 +00:12:15,602 --> 00:12:17,971 +inside of a SwiftUI layout, + +230 +00:12:17,971 --> 00:12:20,640 +and you may need to host +AppKit views, too, + +231 +00:12:20,640 --> 00:12:24,243 +as you adopt SwiftUI +in your app. + +232 +00:12:24,243 --> 00:12:29,482 +One example is inside +of the SwiftUI shortcuts editor, + +233 +00:12:29,482 --> 00:12:32,051 +where there's an embedded +AppleScript editor view, + +234 +00:12:32,051 --> 00:12:33,920 +which is an AppKit control + +235 +00:12:33,920 --> 00:12:37,624 +shared with a few +other system apps on macOS. + +236 +00:12:37,624 --> 00:12:40,693 +SwiftUI provides two +representable protocols + +237 +00:12:40,693 --> 00:12:43,363 +that allow AppKit views +and view controllers + +238 +00:12:43,363 --> 00:12:48,401 +to be embedded within +a SwiftUI view hierarchy. + +239 +00:12:48,401 --> 00:12:50,336 +Like SwiftUI views, + +240 +00:12:50,336 --> 00:12:54,007 +representables are descriptions +for how to create and update + +241 +00:12:54,007 --> 00:12:57,310 +AppKit views. + +242 +00:12:57,310 --> 00:13:01,481 +Since many classes in AppKit +have delegates, observers, + +243 +00:13:01,481 --> 00:13:06,052 +or rely on KVO or notifications +to be observed, + +244 +00:13:06,052 --> 00:13:09,822 +the protocols also include +an optional coordinator object + +245 +00:13:09,822 --> 00:13:12,325 +that you can implement +to accompany your view + +246 +00:13:12,325 --> 00:13:15,662 +or view controller. + +247 +00:13:15,662 --> 00:13:18,231 +Here's the lifecycle +of the hosted object + +248 +00:13:18,231 --> 00:13:20,266 +and its coordinator. + +249 +00:13:20,266 --> 00:13:23,603 +We start with the hosted view +being initialized. + +250 +00:13:23,603 --> 00:13:26,139 +This happens when the view +is about to be displayed + +251 +00:13:26,139 --> 00:13:28,608 +for the first time. + +252 +00:13:28,608 --> 00:13:31,644 +The first thing SwiftUI +does during initialization + +253 +00:13:31,644 --> 00:13:33,446 +is make the coordinator. + +254 +00:13:33,446 --> 00:13:35,948 +This is optional, +but you can define your own type + +255 +00:13:35,948 --> 00:13:37,950 +and return it from +makeCoordinator + +256 +00:13:37,950 --> 00:13:41,821 +if you need it for delegation +or state management. + +257 +00:13:41,821 --> 00:13:43,690 +A single instance +of the coordinator + +258 +00:13:43,690 --> 00:13:47,593 +will stay around +for the lifetime of the view. + +259 +00:13:47,593 --> 00:13:50,329 +Second, either the makeNSView + +260 +00:13:50,329 --> 00:13:53,966 +or makeNSViewController method +is called. + +261 +00:13:53,966 --> 00:13:56,202 +This is where +you describe to SwiftUI + +262 +00:13:56,202 --> 00:14:00,406 +how to create +a new instance of your view. + +263 +00:14:00,406 --> 00:14:03,810 +The context contains the +coordinator that was just made, + +264 +00:14:03,810 --> 00:14:07,447 +if any, so here's a good place +to assign the coordinator + +265 +00:14:07,447 --> 00:14:13,386 +as the view's delegate +or other type of observer. + +266 +00:14:13,386 --> 00:14:15,354 +Once the view has been created, + +267 +00:14:15,354 --> 00:14:17,490 +the update view method +will be called + +268 +00:14:17,490 --> 00:14:22,128 +whenever the SwiftUI state +or environment changes. + +269 +00:14:22,128 --> 00:14:25,965 +Here, it's your responsibility +to update any properties + +270 +00:14:25,965 --> 00:14:28,835 +or state stored +in the AppKit view + +271 +00:14:28,835 --> 00:14:31,637 +to keep it in sync with +the surrounding SwiftUI state + +272 +00:14:31,637 --> 00:14:33,473 +and environment. + +273 +00:14:33,473 --> 00:14:35,842 +The update method +can be called often, + +274 +00:14:35,842 --> 00:14:38,010 +so the changes you make +to the view + +275 +00:14:38,010 --> 00:14:40,747 +should be +as minimal as possible. + +276 +00:14:40,747 --> 00:14:42,749 +You should check +for what has changed + +277 +00:14:42,749 --> 00:14:45,184 +and only reload the affected +part of the view + +278 +00:14:45,184 --> 00:14:47,854 +when changes are made. + +279 +00:14:47,854 --> 00:14:50,690 +When SwiftUI is done displaying +the hosted view, + +280 +00:14:50,690 --> 00:14:53,092 +it will be dismantled. + +281 +00:14:53,092 --> 00:14:57,296 +The hosted view and coordinator +will both be deallocated. + +282 +00:14:57,296 --> 00:15:00,633 +Before these are deallocated, +the representable protocols + +283 +00:15:00,633 --> 00:15:02,835 +give you an optional method +to implement, + +284 +00:15:02,835 --> 00:15:06,105 +where you can clean up +state if needed. + +285 +00:15:06,105 --> 00:15:08,674 +Alright, now that you know +the lifecycle + +286 +00:15:08,674 --> 00:15:12,111 +and are familiar with +the representable protocols, + +287 +00:15:12,111 --> 00:15:14,380 +I'm going to show you +how Shortcuts hosts + +288 +00:15:14,380 --> 00:15:18,117 +the custom script editor view +in the app. + +289 +00:15:18,117 --> 00:15:22,989 +The script editor is an NSView +called ScriptEditorView. + +290 +00:15:22,989 --> 00:15:25,658 +The code that's written +in the editor can be accessed + +291 +00:15:25,658 --> 00:15:28,661 +and modified through +the sourceCode property, + +292 +00:15:28,661 --> 00:15:30,363 +and the view can be disabled + +293 +00:15:30,363 --> 00:15:32,999 +to prevent changes +from being made. + +294 +00:15:32,999 --> 00:15:35,735 +The script editor +also has a delegate, + +295 +00:15:35,735 --> 00:15:38,371 +which is notified any time +someone modifies + +296 +00:15:38,371 --> 00:15:40,773 +the source code. + +297 +00:15:40,773 --> 00:15:42,975 +When hosting an AppKit view, + +298 +00:15:42,975 --> 00:15:46,612 +first think about where the +view will be placed in SwiftUI, + +299 +00:15:46,612 --> 00:15:50,449 +and what data needs +to be passed in and out. + +300 +00:15:50,449 --> 00:15:54,153 +In Shortcuts, this view +is placed into a container view + +301 +00:15:54,153 --> 00:15:57,290 +next to the compile button. + +302 +00:15:57,290 --> 00:16:00,760 +The compile button's handler +needs to access the source code + +303 +00:16:00,760 --> 00:16:03,596 +that's entered into the view. + +304 +00:16:03,596 --> 00:16:05,998 +The source code +is stored in SwiftUI + +305 +00:16:05,998 --> 00:16:08,501 +using the State property +wrapper. + +306 +00:16:08,501 --> 00:16:10,436 +The representable +will need to both read + +307 +00:16:10,436 --> 00:16:13,406 +and write to this state. + +308 +00:16:13,406 --> 00:16:15,107 +To build the representable, + +309 +00:16:15,107 --> 00:16:19,212 +start by creating a type that +conforms to NSViewRepresentable, + +310 +00:16:19,212 --> 00:16:22,915 +since it will host an NSView. + +311 +00:16:22,915 --> 00:16:26,352 +Add properties for each thing +that needs to be configurable + +312 +00:16:26,352 --> 00:16:28,154 +from SwiftUI. + +313 +00:16:28,154 --> 00:16:31,057 +For the source code +a binding is used, + +314 +00:16:31,057 --> 00:16:36,128 +which will read and write +the state stored in SwiftUI. + +315 +00:16:36,128 --> 00:16:40,166 +The first method you need +to implement is makeNSView. + +316 +00:16:40,166 --> 00:16:41,467 +Here is where you describe + +317 +00:16:41,467 --> 00:16:44,003 +how to create a new instance +of the view, + +318 +00:16:44,003 --> 00:16:47,974 +and where you should do any +one-time setup that's required. + +319 +00:16:47,974 --> 00:16:51,143 +Here, the delegate is set +to the coordinator. + +320 +00:16:51,143 --> 00:16:55,248 +I'll talk about the coordinator +more in a bit. + +321 +00:16:55,248 --> 00:16:58,551 +Next, implement updateNSView. + +322 +00:16:58,551 --> 00:17:01,220 +This will be called when +either the sourceCode changes, + +323 +00:17:01,220 --> 00:17:05,091 +or when the +SwiftUI environment changes. + +324 +00:17:05,091 --> 00:17:07,627 +Since the script editor +does a bunch of work + +325 +00:17:07,627 --> 00:17:09,829 +when the sourceCode property +is set, + +326 +00:17:09,829 --> 00:17:12,198 +we compare the value +already in the view, + +327 +00:17:12,198 --> 00:17:15,034 +and only set the property +if it changes + +328 +00:17:15,034 --> 00:17:17,937 +to avoid unnecessary work. + +329 +00:17:17,937 --> 00:17:20,907 +The context passed +to updateNSView + +330 +00:17:20,907 --> 00:17:23,542 +contains +the SwiftUI environment. + +331 +00:17:23,542 --> 00:17:25,745 +The isEnabled environment key + +332 +00:17:25,745 --> 00:17:30,016 +is passed to the isEditable +property on the script editor, + +333 +00:17:30,016 --> 00:17:31,717 +so editing is disabled + +334 +00:17:31,717 --> 00:17:35,688 +if the rest of the +SwiftUI view hierarchy is. + +335 +00:17:35,688 --> 00:17:39,191 +Whenever someone modifies +the source code in the view, + +336 +00:17:39,191 --> 00:17:42,828 +the source code binding +needs to capture the new value. + +337 +00:17:42,828 --> 00:17:44,931 +To do this, +we'll build a coordinator + +338 +00:17:44,931 --> 00:17:49,268 +that conforms to the +ScriptEditorViewDelegate. + +339 +00:17:49,268 --> 00:17:52,204 +The coordinator will store +the representable value, + +340 +00:17:52,204 --> 00:17:56,242 +which contains the source code +binding that it needs to update. + +341 +00:17:56,242 --> 00:17:59,178 +And in the +sourceCodeDidChange method, + +342 +00:17:59,178 --> 00:18:01,514 +the binding is set +to the new string value + +343 +00:18:01,514 --> 00:18:04,317 +from the view. + +344 +00:18:04,317 --> 00:18:07,553 +Finally, we need to tell +the SwiftUI representable + +345 +00:18:07,553 --> 00:18:10,489 +how to make and update +the coordinator. + +346 +00:18:10,489 --> 00:18:14,093 +First, you need to implement +the makeCoordinator method + +347 +00:18:14,093 --> 00:18:17,296 +to create a new coordinator. + +348 +00:18:17,296 --> 00:18:21,067 +Coordinators have the same +lifetime as the hosted view, + +349 +00:18:21,067 --> 00:18:23,202 +and like hosted views, + +350 +00:18:23,202 --> 00:18:25,304 +properties you add +to the coordinator + +351 +00:18:25,304 --> 00:18:29,375 +need to remain up to date +as the representable changes. + +352 +00:18:29,375 --> 00:18:31,544 +Since updateNSView is called + +353 +00:18:31,544 --> 00:18:34,947 +when the values stored +in the representable change, + +354 +00:18:34,947 --> 00:18:40,319 +here, the representable property +on the coordinator is updated. + +355 +00:18:40,319 --> 00:18:43,622 +Now that you know how to add +AppKit into SwiftUI, + +356 +00:18:43,622 --> 00:18:46,726 +and also add SwiftUI +into AppKit, + +357 +00:18:46,726 --> 00:18:50,396 +you should start integrating +SwiftUI into your app. + +358 +00:18:50,396 --> 00:18:52,765 +A great place to start +is in your sidebar, + +359 +00:18:52,765 --> 00:18:55,534 +or table +and collection view cells. + +360 +00:18:55,534 --> 00:18:58,070 +Make sure your views are sizing +themselves correctly + +361 +00:18:58,070 --> 00:19:01,507 +and handling common commands +and focus. + +362 +00:19:01,507 --> 00:19:02,508 +Thanks for your time, + +363 +00:19:02,508 --> 00:19:05,044 +and I can't wait +to see what you build + +364 +00:19:05,044 --> 00:19:08,981 +♪ + diff --git a/eng/2022 Session 10076 Bring your iOS app to the Mac en.srt b/eng/2022 Session 10076 Bring your iOS app to the Mac en.srt new file mode 100644 index 0000000..56c0ad2 --- /dev/null +++ b/eng/2022 Session 10076 Bring your iOS app to the Mac en.srt @@ -0,0 +1,1580 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,576 --> 00:00:14,314 +Owen: Hello, and welcome +to Bring your iOS app to the Mac. + +3 +00:00:14,348 --> 00:00:17,784 +My name is Owen, +and I'm a Mac Catalyst engineer. + +4 +00:00:19,286 --> 00:00:24,291 +Whether you ship your iOS app +on M1 Macs with no changes, + +5 +00:00:24,324 --> 00:00:28,395 +have started thinking about +going further with Mac Catalyst, + +6 +00:00:28,428 --> 00:00:33,033 +or already ship a Catalyst app you want to +make even better, + +7 +00:00:33,066 --> 00:00:36,203 +I'm excited to share new APIs +and techniques + +8 +00:00:36,236 --> 00:00:39,773 +to make your app the best it can be +on Mac. + +9 +00:00:39,806 --> 00:00:43,610 +But first, I'd love to show off +some of the amazing work + +10 +00:00:43,644 --> 00:00:49,249 +done by developers that showcases +what's possible with Mac Catalyst. + +11 +00:00:49,283 --> 00:00:52,719 +Craft is an incredible app +that makes it effortless + +12 +00:00:52,753 --> 00:00:56,023 +to start writing +and produce beautiful documents, + +13 +00:00:56,056 --> 00:01:00,327 +and is the App Store's +2021 Mac App of the Year winner. + +14 +00:01:00,360 --> 00:01:01,995 +And because of Mac Catalyst, + +15 +00:01:02,029 --> 00:01:05,933 +this great experience is available +across devices, + +16 +00:01:05,966 --> 00:01:10,671 +so it's always ready +when your next idea strikes. + +17 +00:01:10,704 --> 00:01:15,909 +Darkroom's smart AI editing tools +streamline your photography workflow, + +18 +00:01:15,943 --> 00:01:20,080 +and you can start your edits in the field +on your iPhone or iPad, + +19 +00:01:20,113 --> 00:01:23,851 +and finish at home +with their Mac Catalyst app. + +20 +00:01:23,884 --> 00:01:27,421 +It won an Apple Design Award +for outstanding design, + +21 +00:01:27,454 --> 00:01:30,023 +innovation, and ingenuity, + +22 +00:01:30,057 --> 00:01:34,761 +and has been an App Store Editor's Choice +app since 2018. + +23 +00:01:36,330 --> 00:01:42,035 +Night Sky is a stunning way to explore +the cosmos right from your computer. + +24 +00:01:42,069 --> 00:01:44,204 +You can learn about the constellations, + +25 +00:01:44,238 --> 00:01:48,842 +and get a closer look +with detailed 3D models of the moon, + +26 +00:01:48,876 --> 00:01:51,712 +planets, and satellites. + +27 +00:01:51,745 --> 00:01:57,284 +It has won multiple Webby and Lovie awards +for its design and breathtaking visuals. + +28 +00:01:59,319 --> 00:02:04,358 +Asphalt 9 - Legends is an exhilarating +high-octane racing simulation, + +29 +00:02:04,391 --> 00:02:08,428 +and the only racing game to ever win +an Apple Design Award, + +30 +00:02:08,462 --> 00:02:11,532 +and the action is even more exciting +on the big screen + +31 +00:02:11,565 --> 00:02:13,667 +with their Mac Catalyst version. + +32 +00:02:15,135 --> 00:02:19,840 +Now, I'll get into all the ways +you can create a great experience + +33 +00:02:19,873 --> 00:02:23,043 +on Mac for your app. + +34 +00:02:23,076 --> 00:02:25,979 +I'm going to start +with a couple of new options + +35 +00:02:26,013 --> 00:02:29,550 +for running your iOS app natively on M1, + +36 +00:02:29,583 --> 00:02:33,954 +the easiest way +to get your existing iOS app onto Mac. + +37 +00:02:35,389 --> 00:02:40,527 +Then I'll walk through adding +a Mac Catalyst run destination in Xcode, + +38 +00:02:40,561 --> 00:02:42,763 +and go over what changes. + +39 +00:02:44,031 --> 00:02:48,268 +I'll show off the new behaviors +your Catalyst app will get for free + +40 +00:02:48,302 --> 00:02:52,039 +when you adopt +the new Desktop-class iPad APIs. + +41 +00:02:53,607 --> 00:02:56,376 +Then dig in to Mac-specific refinements + +42 +00:02:56,410 --> 00:02:59,980 +made possible with new Mac Catalyst APIs. + +43 +00:03:01,849 --> 00:03:07,321 +I'll start with some new options +for native iOS apps on Macs with M1. + +44 +00:03:09,323 --> 00:03:14,895 +Your iOS apps are already available +on M1 Macs in the Mac App Store. + +45 +00:03:14,928 --> 00:03:17,231 +As long as you haven't opted out, + +46 +00:03:17,264 --> 00:03:22,870 +your app is found in a search +under the "iPhone and iPad Apps" tab. + +47 +00:03:22,903 --> 00:03:25,439 +And we have a couple new additions + +48 +00:03:25,472 --> 00:03:28,976 +that can improve your app experience +on Mac. + +49 +00:03:29,009 --> 00:03:33,080 +There are two new keys +you can add to your Info plist, + +50 +00:03:33,113 --> 00:03:38,385 +which allow you to specify +how your iOS app should launch on Mac. + +51 +00:03:38,418 --> 00:03:41,688 +These keys aren't bound +to any SDK version, + +52 +00:03:41,722 --> 00:03:47,127 +and are ignored on iOS and on Mac +prior to macOS 12.1, + +53 +00:03:47,160 --> 00:03:50,330 +so they're safe to add +to any app that would benefit. + +54 +00:03:51,865 --> 00:03:55,769 +The first key, +"UISupportsTrueScreenSizeOnMac," + +55 +00:03:55,802 --> 00:03:58,138 +indicates that your app is prepared + +56 +00:03:58,172 --> 00:04:03,143 +for the wide variety of display +configurations it may encounter. + +57 +00:04:03,177 --> 00:04:05,879 +This way, +your app gets the true screen size + +58 +00:04:05,913 --> 00:04:10,250 +and pixel density, +rather than a compatible iPad size. + +59 +00:04:13,387 --> 00:04:18,025 +The second key, +"UILaunchToFullScreenByDefaultOnMac," + +60 +00:04:18,058 --> 00:04:21,695 +tells the system that your app prefers +to enter fullscreen mode + +61 +00:04:21,728 --> 00:04:23,997 +as soon as it's launched. + +62 +00:04:24,031 --> 00:04:26,200 +These two keys work great together, + +63 +00:04:26,233 --> 00:04:29,102 +especially for games and multimedia apps, + +64 +00:04:29,136 --> 00:04:34,007 +delivering a pixel-perfect, +edge-to-edge, full screen experience. + +65 +00:04:35,609 --> 00:04:36,977 +These keys have been adopted + +66 +00:04:37,010 --> 00:04:40,314 +by the award-winning +Sky: Children of the Light + +67 +00:04:40,347 --> 00:04:43,784 +to provide an immersive experience +immediately. + +68 +00:04:43,817 --> 00:04:47,855 +As soon as the game is launched, +it pulls you in to its beautiful world + +69 +00:04:47,888 --> 00:04:51,992 +of exploration +by filling the screen with rich detail. + +70 +00:04:52,025 --> 00:04:57,130 +Another way to improve your app experience +on Mac is with Touch Alternatives, + +71 +00:04:57,164 --> 00:05:01,869 +which automatically converts keyboard, +mouse, and trackpad input + +72 +00:05:01,902 --> 00:05:07,074 +into iOS multitouch gestures +and device motion expected by your app. + +73 +00:05:08,008 --> 00:05:11,245 +We've already added built-in +touch alternative support + +74 +00:05:11,278 --> 00:05:14,982 +for many of the most popular games +on the app store. + +75 +00:05:15,015 --> 00:05:18,418 +When launched, they automatically show +a tutorial + +76 +00:05:18,452 --> 00:05:22,189 +explaining how their touch controls +translate to a keyboard and mouse + +77 +00:05:22,222 --> 00:05:24,157 +or trackpad. + +78 +00:05:24,191 --> 00:05:28,295 +For example, the arrow keys +can be used to simulate swipes + +79 +00:05:28,328 --> 00:05:30,163 +from the center of the window, + +80 +00:05:30,197 --> 00:05:33,000 +and the spacebar to perform a tap. + +81 +00:05:35,202 --> 00:05:38,138 +To opt into automatic Touch Alternatives, + +82 +00:05:38,172 --> 00:05:41,041 +create a new plist file in your app named + +83 +00:05:41,074 --> 00:05:46,847 +"com.apple.uikit.inputalternatives.plist". + +84 +00:05:48,649 --> 00:05:52,186 +First, add a key called +"defaultEnablement" + +85 +00:05:52,219 --> 00:05:54,454 +with a string value of "enabled", + +86 +00:05:54,488 --> 00:05:58,992 +which tells the system that Touch +Alternatives should be on immediately. + +87 +00:06:01,028 --> 00:06:05,232 +Below that, add another key +called "requiredOnboarding". + +88 +00:06:05,265 --> 00:06:08,735 +This is an array containing a list +of which controls + +89 +00:06:08,769 --> 00:06:11,638 +you've decided work best for your app. + +90 +00:06:14,074 --> 00:06:16,109 +You have the choice to onboard + +91 +00:06:16,143 --> 00:06:18,879 +for five different control options: + +92 +00:06:18,912 --> 00:06:23,183 +tap, tilt, drag, swipe, + +93 +00:06:23,217 --> 00:06:25,953 +and direct touch input from a trackpad. + +94 +00:06:27,454 --> 00:06:30,257 +Note that when +Touch Alternatives is enabled, + +95 +00:06:30,290 --> 00:06:32,659 +all of these controls will be active, + +96 +00:06:32,693 --> 00:06:36,430 +but you should still decide +which make the most sense for your app, + +97 +00:06:36,463 --> 00:06:41,802 +and only add the controls that you want +highlighted to your onboarding plist. + +98 +00:06:41,835 --> 00:06:44,705 +This is detected +when your app is run on Mac, + +99 +00:06:44,738 --> 00:06:50,744 +and on first launch, the onboarding sheet +is presented highlighting those controls. + +100 +00:06:50,777 --> 00:06:54,147 +In your app settings, +people can switch between displaying + +101 +00:06:54,181 --> 00:06:58,585 +the preferred controls specified +in the plist + +102 +00:06:58,619 --> 00:07:00,721 +and all controls. + +103 +00:07:02,289 --> 00:07:06,960 +Of course, the best option +is to implement keyboard and cursor support + +104 +00:07:06,994 --> 00:07:09,530 +directly in your app. + +105 +00:07:09,563 --> 00:07:14,001 +By doing this, you ensure +your app behaves well on all devices, + +106 +00:07:14,034 --> 00:07:18,572 +including iPads with an attached keyboard, +and on the Mac. + +107 +00:07:19,973 --> 00:07:22,609 +For more information, watch the videos + +108 +00:07:22,643 --> 00:07:25,145 +"Support hardware keyboards in your app" + +109 +00:07:25,179 --> 00:07:28,215 +and "Handle trackpad and mouse input". + +110 +00:07:28,248 --> 00:07:34,521 +Now I'll quickly cover the process +of becoming a Mac Catalyst app. + +111 +00:07:35,556 --> 00:07:39,726 +By adding a Mac Catalyst destination +in your project settings, + +112 +00:07:39,760 --> 00:07:44,231 +your app will automatically be converted +to a full Mac Catalyst app + +113 +00:07:44,264 --> 00:07:46,667 +capable of running on every Mac, + +114 +00:07:46,700 --> 00:07:52,005 +and allowing you to customize it further +using Mac Catalyst API. + +115 +00:07:52,039 --> 00:07:56,043 +You also have the choice +to optimize the interface for Mac, + +116 +00:07:56,076 --> 00:07:59,279 +which will give you +native AppKit styled controls, + +117 +00:07:59,313 --> 00:08:02,883 +and ensure your app content +renders at native scale. + +118 +00:08:04,918 --> 00:08:08,722 +I'll be using the Markdown demo app +that the team has been working on + +119 +00:08:08,755 --> 00:08:11,525 +to highlight the new iPad features. + +120 +00:08:11,558 --> 00:08:16,430 +When I first bring it to Mac, it's running +in "Scaled to Match iPad" mode. + +121 +00:08:18,198 --> 00:08:20,734 +When I switch to Optimize for Mac, + +122 +00:08:20,767 --> 00:08:23,437 +many changes happen automatically. + +123 +00:08:25,239 --> 00:08:27,241 +When compared side-by-side, + +124 +00:08:27,274 --> 00:08:29,877 +the differences are apparent. + +125 +00:08:29,910 --> 00:08:32,012 +When the app is optimized for Mac, + +126 +00:08:32,045 --> 00:08:34,781 +the UINavigationBar gets translated into + +127 +00:08:34,815 --> 00:08:36,884 +a rich NSToolbar, + +128 +00:08:36,917 --> 00:08:39,887 +providing native AppKit controls. + +129 +00:08:39,920 --> 00:08:43,824 +This translation happens +for other controls as well. + +130 +00:08:43,857 --> 00:08:47,394 +And the text size adjusts as well. + +131 +00:08:47,427 --> 00:08:50,931 +Text in the iPad idiom +renders at iPad size, + +132 +00:08:50,964 --> 00:08:56,303 +and then is scaled down +to 77% of its original size. + +133 +00:08:56,336 --> 00:09:00,841 +Text in the Mac idiom is handled +with native Mac font rendering, + +134 +00:09:00,874 --> 00:09:04,244 +which happens at pixel-perfect scale. + +135 +00:09:04,278 --> 00:09:06,246 +This is ideal for our app, + +136 +00:09:06,280 --> 00:09:10,484 +since it ensures +the text always looks crisp. + +137 +00:09:10,517 --> 00:09:13,387 +With the app opted into Mac Catalyst, + +138 +00:09:13,420 --> 00:09:15,956 +I want to check out how well it works, + +139 +00:09:15,989 --> 00:09:19,960 +and go deeper +into the new automatic Mac behaviors + +140 +00:09:19,993 --> 00:09:24,231 +provided by iPadOS 16's new API. + +141 +00:09:24,264 --> 00:09:27,601 +If you've already watched +"Meet desktop-class iPad" + +142 +00:09:27,634 --> 00:09:30,370 +and "Build a desktop-class iPad app", + +143 +00:09:30,404 --> 00:09:32,639 +you've learned about the many new tools + +144 +00:09:32,673 --> 00:09:37,044 +to enhance your app with iPadOS 16. + +145 +00:09:37,077 --> 00:09:39,780 +And these new APIs translate beautifully + +146 +00:09:39,813 --> 00:09:43,183 +into native Mac representations. + +147 +00:09:43,217 --> 00:09:45,552 +I'll take a tour of the markdown app, + +148 +00:09:45,586 --> 00:09:48,288 +and highlight some of these new behaviors. + +149 +00:09:49,623 --> 00:09:55,963 +Controls and navigation move +from UINavigationBar into the NSToolbar. + +150 +00:09:55,996 --> 00:09:58,699 +And if you don't already create a toolbar, + +151 +00:09:58,732 --> 00:10:01,668 +we give you one automatically. + +152 +00:10:01,702 --> 00:10:05,105 +If you already manage +your own NSToolbar in Catalyst, + +153 +00:10:05,138 --> 00:10:07,207 +we stay out of your way. + +154 +00:10:09,009 --> 00:10:13,180 +Center item controls +become NSToolbarItems. + +155 +00:10:15,082 --> 00:10:16,750 +For document based apps, + +156 +00:10:16,783 --> 00:10:19,987 +your window title shows the document name, + +157 +00:10:20,020 --> 00:10:25,058 +and if "Show window title icons" is +enabled in Accessibility System Settings, + +158 +00:10:25,092 --> 00:10:27,895 +the file proxy icon appears as well, + +159 +00:10:27,928 --> 00:10:30,864 +exactly how you would expect it on Mac. + +160 +00:10:32,432 --> 00:10:35,202 +And if you use a navigation controller, + +161 +00:10:35,235 --> 00:10:38,105 +the back button +and other navigation controls + +162 +00:10:38,138 --> 00:10:42,142 +are also brought into the toolbar. + +163 +00:10:42,176 --> 00:10:45,579 +Additionally, +you get new document-centric menu items + +164 +00:10:45,612 --> 00:10:47,347 +in the File menu: + +165 +00:10:47,381 --> 00:10:51,885 +Duplicate, Move, Rename, and Export As. + +166 +00:10:53,887 --> 00:10:55,856 +To enable these menu items, + +167 +00:10:55,889 --> 00:11:01,862 +ensure some object in your responder chain +overrides UIResponder's duplicate, + +168 +00:11:01,895 --> 00:11:05,299 +move, rename, and export functions. + +169 +00:11:07,067 --> 00:11:10,003 +If your app doesn't need +these File menu items, + +170 +00:11:10,037 --> 00:11:13,440 +they should be removed with UIMenuBuilder. + +171 +00:11:13,473 --> 00:11:18,779 +Implement the buildMenu method on your app +delegate to control your app menus. + +172 +00:11:20,247 --> 00:11:26,887 +The new Document menu items have +a UIMenuIdentifier value of .document. + +173 +00:11:28,188 --> 00:11:30,257 +If your app includes a search bar, + +174 +00:11:30,290 --> 00:11:34,461 +it gets automatically pulled +into the NSToolbar as well, + +175 +00:11:34,494 --> 00:11:38,298 +first showing as a search button + +176 +00:11:38,332 --> 00:11:42,069 +that expands into the bar on click. + +177 +00:11:42,102 --> 00:11:47,774 +The UISearchTextField that backs it +is hosted inside of an NSToolbarItem, + +178 +00:11:47,808 --> 00:11:51,545 +and this is available +for custom views as well. + +179 +00:11:51,578 --> 00:11:54,781 +The search suggestions menu +and search scope bar + +180 +00:11:54,815 --> 00:11:58,285 +also get converted +into native AppKit controls. + +181 +00:12:00,153 --> 00:12:04,124 +In addition to all the new Mac behaviors +you get for free + +182 +00:12:04,157 --> 00:12:07,394 +by adopting desktop-class iPad features, + +183 +00:12:07,427 --> 00:12:12,199 +we've also added several new +Catalyst-specific APIs + +184 +00:12:12,232 --> 00:12:15,903 +to improve multi-window +and toolbar behaviors. + +185 +00:12:15,936 --> 00:12:19,306 +I'll go through our Markdown app +one more time, + +186 +00:12:19,339 --> 00:12:25,012 +and add these Mac-specific features +using a mix of old and new API. + +187 +00:12:25,045 --> 00:12:27,848 +From the existing Mac Catalyst APIs, + +188 +00:12:27,881 --> 00:12:31,185 +I've chosen a few +that really enhance our App. + +189 +00:12:33,187 --> 00:12:36,490 +I've adopted the Mac idiom +to get native controls + +190 +00:12:36,523 --> 00:12:39,459 +and the automatic NSToolbar. + +191 +00:12:39,493 --> 00:12:43,897 +I added a custom pointer +so moving the mouse over the slider handle + +192 +00:12:43,931 --> 00:12:47,768 +in the middle of the custom split view +switches the pointer + +193 +00:12:47,801 --> 00:12:50,070 +to horizontal double-arrows + +194 +00:12:50,103 --> 00:12:52,706 +indicating that the split can be resized. + +195 +00:12:53,841 --> 00:12:55,742 +And I've added printing support + +196 +00:12:55,776 --> 00:12:59,880 +with the UIApplicationSupportsPrintCommand +plist key. + +197 +00:13:02,316 --> 00:13:04,251 +When considering your app, + +198 +00:13:04,284 --> 00:13:08,422 +don't feel like you need to add +every single one of these features. + +199 +00:13:08,455 --> 00:13:11,725 +Instead, think about +the type of app that you create, + +200 +00:13:11,758 --> 00:13:14,361 +and which features work best for it. + +201 +00:13:14,394 --> 00:13:18,599 +Check out the Mac and Mac Catalyst +Human Interface Guidelines, + +202 +00:13:18,632 --> 00:13:22,135 +and look for inspiration +in other apps you use. + +203 +00:13:23,303 --> 00:13:25,339 +With these features implemented, + +204 +00:13:25,372 --> 00:13:28,642 +the app is already in a wonderful spot. + +205 +00:13:28,675 --> 00:13:34,014 +And now with some new Mac Catalyst API, +I can do even more. + +206 +00:13:34,047 --> 00:13:38,785 +We've added new options for customizing +app window frame and controls, + +207 +00:13:38,819 --> 00:13:42,756 +for displaying UIView content +in the NSToolbar, + +208 +00:13:42,789 --> 00:13:46,527 +and for showing popovers +from toolbar items. + +209 +00:13:46,560 --> 00:13:52,699 +I'll go ahead and use these new APIs +to improve our app even further on Mac + +210 +00:13:52,733 --> 00:13:54,701 +starting with the windows. + +211 +00:13:56,436 --> 00:14:02,910 +Catalyst apps support multiple windows +using the existing UIWindowScene API. + +212 +00:14:02,943 --> 00:14:05,045 +And new in macOS Ventura, + +213 +00:14:05,078 --> 00:14:09,716 +we've added API to let you customize +your app windows even further + +214 +00:14:09,750 --> 00:14:13,187 +by setting the style +of the traffic-light window controls, + +215 +00:14:13,220 --> 00:14:17,124 +programmatically resizing +and repositioning your windows, + +216 +00:14:17,157 --> 00:14:20,827 +and disabling fullscreen +for specific scenes. + +217 +00:14:22,663 --> 00:14:25,899 +I'll use these tools +to add a feature to the markdown app + +218 +00:14:25,933 --> 00:14:30,604 +to show a helpful auxiliary panel +with markdown syntax. + +219 +00:14:30,637 --> 00:14:35,642 +This panel will be sized smaller +with the new geometry request API, + +220 +00:14:35,676 --> 00:14:39,246 +and have its minimize +and zoom buttons disabled. + +221 +00:14:41,782 --> 00:14:47,020 +At scene creation, in my Scene Delegate's +"scene willConnectTo session" method, + +222 +00:14:47,054 --> 00:14:49,456 +I create my desired frame. + +223 +00:14:49,489 --> 00:14:55,562 +It's good practice to always start with +the current frame from effectiveGeometry. + +224 +00:14:55,596 --> 00:15:00,267 +Before the scene is created, +this is initialized to CGRectNull, + +225 +00:15:00,300 --> 00:15:05,639 +whose values the system knows to ignore +for initial scene creation. + +226 +00:15:05,672 --> 00:15:09,643 +I modify the size, +then give my scene its new frame + +227 +00:15:09,676 --> 00:15:13,814 +by creating a new +UIWindowScene.macGeometryPreferences + +228 +00:15:13,847 --> 00:15:18,352 +object and passing the frame +as its systemFrame argument. + +229 +00:15:20,354 --> 00:15:25,859 +I then submit the update using +the scene.requestGeometryUpdate() method. + +230 +00:15:25,893 --> 00:15:27,828 +Because this is a request, + +231 +00:15:27,861 --> 00:15:31,698 +the system reserves the right +to reject the new geometry, + +232 +00:15:31,732 --> 00:15:36,270 +in which case it will call +the error handler callback with details. + +233 +00:15:36,303 --> 00:15:40,374 +When done in the scene +willConnectToSession method as shown here, + +234 +00:15:40,407 --> 00:15:43,911 +the request will apply to +your application's first launch, + +235 +00:15:43,944 --> 00:15:48,148 +but will be superseded by any +applicable state restoration + +236 +00:15:48,182 --> 00:15:51,151 +performed by the system on later launches. + +237 +00:15:52,920 --> 00:15:55,589 +And because this is a small helper panel, + +238 +00:15:55,622 --> 00:15:57,724 +I also disable the minimize button + +239 +00:15:57,758 --> 00:16:01,395 +from the new windowScene +windowingBehaviors object, + +240 +00:16:01,428 --> 00:16:04,398 +and the ability +to make the window fullscreen, + +241 +00:16:04,431 --> 00:16:07,601 +from the new property on sizeRestrictions. + +242 +00:16:09,603 --> 00:16:12,372 +When this new Markdown Hints +window is shown, + +243 +00:16:12,406 --> 00:16:16,276 +it appears with the size we requested. + +244 +00:16:16,310 --> 00:16:21,315 +The yellow minimize and green zoom +window control buttons are disabled, + +245 +00:16:21,348 --> 00:16:23,951 +leaving only the red close button. + +246 +00:16:26,520 --> 00:16:30,524 +These options aren't reserved +just for scene creation. + +247 +00:16:30,557 --> 00:16:32,960 +At any time in a scene's life, + +248 +00:16:32,993 --> 00:16:34,828 +you can check its current frame + +249 +00:16:34,862 --> 00:16:38,131 +from the readonly effectiveGeometry +property, + +250 +00:16:38,165 --> 00:16:40,367 +modify it however you need, + +251 +00:16:40,400 --> 00:16:44,438 +and request a new geometry update. + +252 +00:16:44,471 --> 00:16:48,675 +Here's another example showing +how to modify the origin of the window, + +253 +00:16:48,709 --> 00:16:51,211 +keeping its size the same. + +254 +00:16:53,146 --> 00:16:57,751 +There are two important aspects +of the geometry to keep in mind. + +255 +00:16:57,784 --> 00:17:01,655 +First, because systemFrame +dictates the frame of the window + +256 +00:17:01,688 --> 00:17:03,557 +on the Mac desktop, + +257 +00:17:03,590 --> 00:17:06,827 +the size of one point +for the systemFrame property + +258 +00:17:06,860 --> 00:17:10,531 +is always the size of one AppKit point. + +259 +00:17:10,564 --> 00:17:13,200 +If your app is optimized for Mac, + +260 +00:17:13,233 --> 00:17:16,803 +this is the same scale +as your UI elements. + +261 +00:17:16,837 --> 00:17:19,973 +However, +if your app is scaled to match iPad, + +262 +00:17:20,007 --> 00:17:24,578 +it will differ by the 77% scale factor. + +263 +00:17:24,611 --> 00:17:27,481 +Second, the origin of the coordinate space + +264 +00:17:27,514 --> 00:17:31,051 +is the upper-left corner +of the main display. + +265 +00:17:31,084 --> 00:17:33,187 +If you have multiple displays, + +266 +00:17:33,220 --> 00:17:36,557 +the main display is the one that shows +the Menu bar + +267 +00:17:36,590 --> 00:17:39,459 +in the system display settings. + +268 +00:17:41,128 --> 00:17:43,630 +With new Mac Catalyst API, + +269 +00:17:43,664 --> 00:17:45,599 +you can take control over the state + +270 +00:17:45,632 --> 00:17:48,702 +of each of +the three window control buttons. + +271 +00:17:48,735 --> 00:17:53,907 +The new windowingBehaviors object +on UIWindowScene has two properties, + +272 +00:17:53,941 --> 00:17:56,677 +closable and miniaturizable, + +273 +00:17:56,710 --> 00:17:59,680 +that will adjust these respective window +behaviors + +274 +00:17:59,713 --> 00:18:03,450 +and enable or disable the red + +275 +00:18:03,483 --> 00:18:06,553 +and yellow window buttons. + +276 +00:18:07,521 --> 00:18:12,292 +The green window button handles both +resizing your window on the desktop, + +277 +00:18:12,326 --> 00:18:15,829 +and taking the window into fullscreen. + +278 +00:18:15,863 --> 00:18:19,700 +You can disable fullscreen +with the new property on sizeRestrictions, + +279 +00:18:19,733 --> 00:18:22,936 +"allowsFullScreen" + +280 +00:18:22,970 --> 00:18:27,074 +or disable resizing +by using size restrictions + +281 +00:18:27,107 --> 00:18:31,912 +and setting the minimum +and maximum size to the same size. + +282 +00:18:31,945 --> 00:18:37,284 +And by doing both, +the green button also becomes disabled. + +283 +00:18:38,919 --> 00:18:42,456 +And you can check whether +your scene is currently fullscreen + +284 +00:18:42,489 --> 00:18:46,426 +with the new "isFullScreen" property. + +285 +00:18:46,460 --> 00:18:50,764 +Now, I'll focus on +refining the toolbar for Mac. + +286 +00:18:50,797 --> 00:18:54,168 +We've added new options +for toolbar customization, + +287 +00:18:54,201 --> 00:18:58,438 +whether you rely on the automatic +conversion from a UINavigationBar + +288 +00:18:58,472 --> 00:19:02,142 +or manage your own NSToolbar +in your app code. + +289 +00:19:04,077 --> 00:19:05,579 +New in Mac Catalyst, + +290 +00:19:05,612 --> 00:19:10,317 +UIViews can be added +as items to the NSToolbar. + +291 +00:19:10,350 --> 00:19:13,620 +I designed a custom UIView for our App + +292 +00:19:13,654 --> 00:19:16,156 +that shows the current word count. + +293 +00:19:16,190 --> 00:19:19,059 +And when clicked, +it presents a popover + +294 +00:19:19,092 --> 00:19:22,930 +with additional details +like paragraph and section counts, + +295 +00:19:22,963 --> 00:19:26,300 +reading time, and translation. + +296 +00:19:26,333 --> 00:19:29,670 +Because I'm using +the new desktop-class iPad API, + +297 +00:19:29,703 --> 00:19:33,307 +the customView property +on my UIBarButton item + +298 +00:19:33,340 --> 00:19:36,977 +is automatically wrapped +and added to the toolbar. + +299 +00:19:37,010 --> 00:19:40,480 +But if you manage +your NSToolbar independently, + +300 +00:19:40,514 --> 00:19:44,051 +we've also added +a new NSToolbarItem subclass, + +301 +00:19:44,084 --> 00:19:47,454 +called NSUIViewToolbarItem. + +302 +00:19:48,222 --> 00:19:53,694 +You can use NSUIViewToolbarItem +just like any other NSToolbarItem, + +303 +00:19:53,727 --> 00:19:55,696 +to instantiate a toolbar item + +304 +00:19:55,729 --> 00:20:00,567 +from your NSToolbarDelegate's +itemForIdentifier method. + +305 +00:20:00,601 --> 00:20:03,504 +The initializer takes a UIView to wrap, + +306 +00:20:03,537 --> 00:20:06,673 +which is then inserted into the NSToolbar. + +307 +00:20:09,776 --> 00:20:13,580 +NSToolbar expects +unique toolbar item instances + +308 +00:20:13,614 --> 00:20:16,383 +for toolbar customization mode. + +309 +00:20:16,416 --> 00:20:21,388 +If you are using automatic NSToolbar +hosting from your UINavigationBar, + +310 +00:20:21,421 --> 00:20:26,660 +your view is cloned by the system +for toolbar customization automatically. + +311 +00:20:26,693 --> 00:20:29,897 +However, if you manage your own NSToolbar, + +312 +00:20:29,930 --> 00:20:32,699 +your delegate will need to create +unique instances + +313 +00:20:32,733 --> 00:20:37,804 +of your UIView +for each NSUIViewToolbarItem instance, + +314 +00:20:37,838 --> 00:20:41,842 +rather than reusing +the same UIView instance. + +315 +00:20:43,744 --> 00:20:45,846 +With my new item in the toolbar, + +316 +00:20:45,879 --> 00:20:48,549 +I'll add the popover behavior. + +317 +00:20:48,582 --> 00:20:50,651 +In my toolbar item's action, + +318 +00:20:50,684 --> 00:20:53,187 +I create the popover view controller, + +319 +00:20:53,220 --> 00:20:57,124 +and set its source item +to be my toolbar item. + +320 +00:20:57,157 --> 00:20:59,426 +Then I present the popover. + +321 +00:21:01,595 --> 00:21:03,664 +With the popover code in place, + +322 +00:21:03,697 --> 00:21:06,800 +clicking on the toolbar item +presents additional detail + +323 +00:21:06,834 --> 00:21:11,104 +in a popover presentation +anchored on my toolbar item. + +324 +00:21:12,806 --> 00:21:15,142 +Like other controls in Mac Catalyst, + +325 +00:21:15,175 --> 00:21:19,813 +you can opt out of navigation bar +translation by using the new property + +326 +00:21:19,847 --> 00:21:23,984 +preferredBehavioralStyle +on UINavigationBar. + +327 +00:21:24,017 --> 00:21:28,088 +Its default value is automatic, +but you can set it to .mac + +328 +00:21:28,121 --> 00:21:31,959 +to explicitly request translation. + +329 +00:21:31,992 --> 00:21:33,760 +and by setting it to .pad, + +330 +00:21:33,794 --> 00:21:37,731 +your navigation bar +no longer gets automatically translated. + +331 +00:21:39,833 --> 00:21:43,770 +With these options, +you can add a new layer of customization + +332 +00:21:43,804 --> 00:21:45,639 +to your app's toolbar. + +333 +00:21:46,974 --> 00:21:50,844 +We're so excited to see how +you use these new tools + +334 +00:21:50,878 --> 00:21:53,881 +to make your apps even better. + +335 +00:21:53,914 --> 00:21:57,684 +With your iPad app +automatically on the Mac App Store, + +336 +00:21:57,718 --> 00:22:00,954 +it's available for a whole new audience. + +337 +00:22:00,988 --> 00:22:03,123 +When you upload your iPad app, + +338 +00:22:03,156 --> 00:22:06,426 +run it on the Mac and try it for yourself. + +339 +00:22:06,460 --> 00:22:11,298 +Or take it to the next level +by becoming a Mac Catalyst app, + +340 +00:22:11,331 --> 00:22:16,103 +and use the new API in iOS 16 +and macOS Ventura + +341 +00:22:16,136 --> 00:22:18,205 +to make your app its best. + +342 +00:22:19,339 --> 00:22:22,075 +It's easy to bring your app to the Mac. + +343 +00:22:22,109 --> 00:22:24,311 +We love showcasing your hard work + +344 +00:22:24,344 --> 00:22:28,282 +and can't wait try what you build next. + +345 +00:22:28,315 --> 00:22:30,217 +Thank you. + diff --git a/eng/2022 Session 10077 Replace CAPTCHAs with Private Access Tokens en.srt b/eng/2022 Session 10077 Replace CAPTCHAs with Private Access Tokens en.srt new file mode 100644 index 0000000..2f97292 --- /dev/null +++ b/eng/2022 Session 10077 Replace CAPTCHAs with Private Access Tokens en.srt @@ -0,0 +1,902 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,843 --> 00:00:12,212 +Tommy Pauly: Hi, I'm Tommy Pauly, + +3 +00:00:12,246 --> 00:00:16,483 +and I'm excited to share how your apps +and websites can work together + +4 +00:00:16,517 --> 00:00:21,021 +with Apple and fraud prevention providers +across the industry + +5 +00:00:21,054 --> 00:00:23,857 +to reduce the need for CAPTCHAs. + +6 +00:00:23,891 --> 00:00:27,027 +Today, I'll talk about: +Private Access Tokens, + +7 +00:00:27,060 --> 00:00:31,431 +and how they can be powerful tools +for fraud prevention; + +8 +00:00:31,465 --> 00:00:34,334 +how to enable support +for Private Access Tokens + +9 +00:00:34,368 --> 00:00:37,171 +on the servers you operate; + +10 +00:00:37,204 --> 00:00:40,908 +and, how you can use these tokens +in your apps. + +11 +00:00:42,276 --> 00:00:44,678 +To introduce Private Access Tokens, + +12 +00:00:44,711 --> 00:00:49,583 +I'll start by explaining why +CAPTCHAs are used in the first place. + +13 +00:00:49,616 --> 00:00:53,954 +Chances are, if you've signed up +for a new account on a website, + +14 +00:00:53,987 --> 00:00:56,857 +or tried to sign in +with an existing account, + +15 +00:00:56,890 --> 00:01:00,694 +you've encountered CAPTCHAs like these +at some point. + +16 +00:01:00,727 --> 00:01:03,897 +Sometimes, +a CAPTCHA is just a button to press, + +17 +00:01:03,931 --> 00:01:06,466 +but others can be a challenge to fill out. + +18 +00:01:07,768 --> 00:01:11,004 +You likely don't enjoy being +interrupted by these. + +19 +00:01:11,038 --> 00:01:13,473 +I certainly don't. + +20 +00:01:13,507 --> 00:01:16,276 +The reason these experiences exist + +21 +00:01:16,310 --> 00:01:19,179 +is to prevent fraudulent activity. + +22 +00:01:20,380 --> 00:01:25,185 +If you run a server, you don't want it +to be overwhelmed by fraud. + +23 +00:01:25,219 --> 00:01:29,089 +Some attempts to create accounts +or buy products + +24 +00:01:29,122 --> 00:01:31,592 +come from legitimate users. + +25 +00:01:31,625 --> 00:01:36,096 +But other attempts +may be from attackers or bots. + +26 +00:01:36,129 --> 00:01:39,733 +Unfortunately, +the common tools to prevent fraud, + +27 +00:01:39,766 --> 00:01:41,468 +like CAPTCHAs, + +28 +00:01:41,502 --> 00:01:45,405 +often make it harder +for people to use your app or website. + +29 +00:01:45,973 --> 00:01:49,009 +Finding the right balance +between a good experience + +30 +00:01:49,042 --> 00:01:52,379 +and preventing fraud is a challenge. + +31 +00:01:53,881 --> 00:01:58,886 +CAPTCHAs often lead to a slower +and more complex user experience. + +32 +00:01:58,919 --> 00:02:00,954 +By trying to prevent attacks, + +33 +00:02:00,988 --> 00:02:03,624 +you may also lose valuable customers. + +34 +00:02:04,992 --> 00:02:08,595 +CAPTCHAs can also pose a privacy risk. + +35 +00:02:08,629 --> 00:02:11,465 +In order to determine +if a client is trusted + +36 +00:02:11,498 --> 00:02:13,734 +and can get an easier CAPTCHA, + +37 +00:02:13,767 --> 00:02:17,738 +servers often rely on tracking +or fingerprinting clients + +38 +00:02:17,771 --> 00:02:20,941 +by using their IP address. + +39 +00:02:20,974 --> 00:02:25,279 +This kind of tracking is at odds +with the direction of internet privacy + +40 +00:02:25,312 --> 00:02:29,316 +being taken by Safari, +Mail Privacy Protection, + +41 +00:02:29,349 --> 00:02:31,418 +and iCloud Private Relay. + +42 +00:02:32,886 --> 00:02:37,024 +And CAPTCHAs can pose a serious problem +for accessibility. + +43 +00:02:37,057 --> 00:02:39,927 +By trying to prevent access from bots, + +44 +00:02:39,960 --> 00:02:43,864 +they also block out real humans +who have disabilities + +45 +00:02:43,897 --> 00:02:46,133 +or language barriers. + +46 +00:02:46,166 --> 00:02:48,635 +There is a better way. + +47 +00:02:48,669 --> 00:02:52,840 +Even if someone is interacting +with your website for the first time, + +48 +00:02:52,873 --> 00:02:56,710 +if they are loading it through an app +or browser like Safari, + +49 +00:02:56,743 --> 00:03:02,482 +they've already performed many actions +that are hard for a bot to imitate. + +50 +00:03:02,516 --> 00:03:06,286 +First, they have an iPhone, iPad, or Mac, + +51 +00:03:06,320 --> 00:03:09,323 +and they've unlocked the device +with their password, + +52 +00:03:09,356 --> 00:03:12,092 +Touch ID, or Face ID. + +53 +00:03:12,125 --> 00:03:16,697 +They're almost always signed in +to the device with their Apple ID. + +54 +00:03:16,730 --> 00:03:19,399 +And they've launched a code-signed app. + +55 +00:03:20,767 --> 00:03:25,239 +This information can help your servers +trust legitimate clients + +56 +00:03:25,272 --> 00:03:28,976 +and prevent fraud, +without relying on CAPTCHAs, + +57 +00:03:29,009 --> 00:03:34,181 +and without compromising privacy +by tracking clients. + +58 +00:03:34,214 --> 00:03:37,784 +Private Access Tokens +are what allow your servers + +59 +00:03:37,818 --> 00:03:40,420 +to automatically trust clients, + +60 +00:03:40,454 --> 00:03:44,725 +new in iOS 16 and macOS Ventura. + +61 +00:03:44,758 --> 00:03:47,561 +Before explaining how these tokens work, + +62 +00:03:47,594 --> 00:03:49,863 +I'm going to show them in action. + +63 +00:03:49,897 --> 00:03:52,032 +You're going to love this. + +64 +00:03:52,065 --> 00:03:55,602 +I want to read an article +on the Financial Times website. + +65 +00:03:55,636 --> 00:03:58,639 +I'm very excited +about these cinnamon buns. + +66 +00:03:58,672 --> 00:04:01,408 +And I've loaded the site +on two different phones: + +67 +00:04:01,441 --> 00:04:06,280 +one running iOS 15 +and one running iOS 16, + +68 +00:04:06,313 --> 00:04:09,683 +which supports Private Access Tokens. + +69 +00:04:09,716 --> 00:04:12,119 +Starting with the iOS 15 phone, + +70 +00:04:12,152 --> 00:04:14,221 +I click Sign In, + +71 +00:04:14,254 --> 00:04:17,558 +and fill out my account and password. + +72 +00:04:17,591 --> 00:04:20,494 +But then, I get hit with a CAPTCHA. + +73 +00:04:20,527 --> 00:04:25,265 +I need to type in the letters +before being able to read that article. + +74 +00:04:26,800 --> 00:04:30,470 +When I do the exact same thing +on the iOS 16 phone + +75 +00:04:30,504 --> 00:04:33,740 +that supports Private Access Tokens, + +76 +00:04:33,774 --> 00:04:36,610 +I get right through. + +77 +00:04:36,643 --> 00:04:40,781 +This is going to save a lot of people, +a lot of time, + +78 +00:04:40,814 --> 00:04:44,685 +and your customers +will appreciate being trusted. + +79 +00:04:44,718 --> 00:04:50,224 +Private Access Tokens let servers +avoid CAPTCHAs, like you just saw, + +80 +00:04:50,257 --> 00:04:56,663 +by using technology being standardized +in the IETF Privacy Pass working group. + +81 +00:04:56,697 --> 00:05:00,801 +Apple is working with companies +across the industry to make this possible. + +82 +00:05:02,302 --> 00:05:06,139 +Using this protocol, +servers can request tokens + +83 +00:05:06,173 --> 00:05:09,443 +using a new HTTP authentication method, + +84 +00:05:09,476 --> 00:05:11,612 +PrivateToken. + +85 +00:05:11,645 --> 00:05:14,982 +These tokens use RSA Blind Signatures + +86 +00:05:15,015 --> 00:05:17,284 +to cryptographically sign the fact + +87 +00:05:17,317 --> 00:05:21,488 +that a client was able to pass +an attestation check. + +88 +00:05:21,522 --> 00:05:23,991 +These signatures are "unlinkable", + +89 +00:05:24,024 --> 00:05:27,027 +which means that servers +that receive tokens + +90 +00:05:27,060 --> 00:05:29,296 +can only check that they are valid, + +91 +00:05:29,329 --> 00:05:35,469 +but they cannot discover client identities +or recognize clients over time. + +92 +00:05:35,502 --> 00:05:37,504 +Here's how the protocol works. + +93 +00:05:39,206 --> 00:05:45,712 +First, when the iOS or macOS client +accesses a server over HTTP, + +94 +00:05:45,746 --> 00:05:48,348 +the server sends back a challenge using + +95 +00:05:48,382 --> 00:05:51,785 +the PrivateToken authentication scheme. + +96 +00:05:51,818 --> 00:05:56,089 +This specifies a token issuer +that is trusted by the server. + +97 +00:05:58,292 --> 00:06:00,627 +When the client needs to fetch a token, + +98 +00:06:00,661 --> 00:06:03,063 +it contacts an iCloud attester + +99 +00:06:03,096 --> 00:06:05,566 +and sends a token request. + +100 +00:06:05,599 --> 00:06:07,734 +This token request is "blinded" + +101 +00:06:07,768 --> 00:06:11,805 +so it can't be linked +to the server challenge. + +102 +00:06:11,839 --> 00:06:14,608 +The attester performs device attestation, + +103 +00:06:14,641 --> 00:06:18,879 +using certificates stored +in the device's Secure Enclave, + +104 +00:06:18,912 --> 00:06:21,982 +and verifies +that the account is in good standing. + +105 +00:06:23,116 --> 00:06:25,819 +This attester +can also perform rate-limiting, + +106 +00:06:25,853 --> 00:06:30,190 +to recognize if the client device +is following normal patterns, + +107 +00:06:30,224 --> 00:06:34,962 +or may have been compromised +or used as part of a farm of devices. + +108 +00:06:36,129 --> 00:06:38,098 +If the client can be validated, + +109 +00:06:38,131 --> 00:06:42,436 +the attester sends the request +for a new token to the issuer. + +110 +00:06:44,204 --> 00:06:46,507 +When the token issuer gets the request, + +111 +00:06:46,540 --> 00:06:49,309 +it doesn't know anything about the client. + +112 +00:06:49,343 --> 00:06:52,012 +But since it trusts the iCloud attester, + +113 +00:06:52,045 --> 00:06:53,747 +it signs the token. + +114 +00:06:56,216 --> 00:07:00,020 +The client then receives the signed token, +and transforms it + +115 +00:07:00,053 --> 00:07:02,389 +in a process called "unblinding" + +116 +00:07:02,422 --> 00:07:05,158 +so the original server can verify it. + +117 +00:07:06,393 --> 00:07:11,365 +And finally, the client presents +the signed token to the server. + +118 +00:07:11,398 --> 00:07:15,035 +The server can check +that this token is signed by the Issuer, + +119 +00:07:15,068 --> 00:07:19,940 +but it cannot use the token +to identify or recognize the client. + +120 +00:07:19,973 --> 00:07:26,146 +So how can you take advantage +of this technology on your servers? + +121 +00:07:26,180 --> 00:07:31,351 +There are three steps to adopting +Private Access Tokens on your server. + +122 +00:07:31,385 --> 00:07:34,588 +First, you'll need to select +a token issuer. + +123 +00:07:34,621 --> 00:07:39,993 +Second, your server will need to send out +HTTP authentication challenges + +124 +00:07:40,027 --> 00:07:42,629 +when you want to validate clients. + +125 +00:07:42,663 --> 00:07:47,935 +And third, your server will need +to validate the tokens sent by clients. + +126 +00:07:48,836 --> 00:07:52,406 +The token issuer you select +is a trusted provider + +127 +00:07:52,439 --> 00:07:55,943 +that can sign tokens +that your server validates. + +128 +00:07:55,976 --> 00:07:58,745 +This may be your existing +CAPTCHA provider, + +129 +00:07:58,779 --> 00:08:00,747 +your web hosting service, + +130 +00:08:00,781 --> 00:08:03,350 +or your content delivery network, + +131 +00:08:03,383 --> 00:08:06,453 +also called a CDN. + +132 +00:08:06,486 --> 00:08:10,490 +In the iOS 16 and macOS Ventura betas, + +133 +00:08:10,524 --> 00:08:15,262 +there are two token issuers +that you can already start testing with. + +134 +00:08:15,295 --> 00:08:18,899 +Fastly and Cloudflare are two CDNs + +135 +00:08:18,932 --> 00:08:22,336 +that have been developing +the Privacy Pass standards, + +136 +00:08:22,369 --> 00:08:27,274 +and have already made +their issuer services available. + +137 +00:08:27,307 --> 00:08:32,079 +Other CAPTCHA providers, +web hosting services, and CDNs + +138 +00:08:32,112 --> 00:08:37,751 +will also be able to run token issuers +that will work with Apple devices. + +139 +00:08:37,784 --> 00:08:43,457 +Issuers will be able to sign up +later this year at register.apple.com. + +140 +00:08:44,992 --> 00:08:48,395 +It's important that the use +of a specific token issuer + +141 +00:08:48,428 --> 00:08:53,267 +doesn't become a way to identify +what websites a client is accessing– + +142 +00:08:53,300 --> 00:08:56,069 +that would be a problem for privacy. + +143 +00:08:56,103 --> 00:09:00,407 +So each token issuer needs to be +a large service + +144 +00:09:00,440 --> 00:09:03,410 +that work with +at least hundreds of servers. + +145 +00:09:06,446 --> 00:09:08,849 +When a client accesses your server, + +146 +00:09:08,882 --> 00:09:13,487 +you can request tokens by sending +an HTTP authentication challenge + +147 +00:09:13,520 --> 00:09:16,290 +with the PrivateToken scheme. + +148 +00:09:16,323 --> 00:09:19,126 +To do this, you have two options: + +149 +00:09:19,159 --> 00:09:23,664 +Either you can work with your existing +CAPTCHA or fraud prevention provider + +150 +00:09:23,697 --> 00:09:26,400 +to build the challenge into their scripts, + +151 +00:09:26,433 --> 00:09:29,503 +so it is handled automatically for you, + +152 +00:09:29,536 --> 00:09:34,041 +or you can choose to send these challenges +directly from your server. + +153 +00:09:35,342 --> 00:09:37,845 +If you're doing this +as part of your website, + +154 +00:09:37,878 --> 00:09:41,114 +the challenge must come from +a first-party domain– + +155 +00:09:41,148 --> 00:09:43,817 +a subdomain of your main URL + +156 +00:09:43,851 --> 00:09:48,121 +and not a separate third-party domain +embedded on your site. + +157 +00:09:50,023 --> 00:09:52,326 +When clients return tokens to you, + +158 +00:09:52,359 --> 00:09:57,264 +you'll need to check their validity +using your issuer's public key. + +159 +00:09:57,297 --> 00:10:01,001 +You may also want to enforce checks +to prevent replay attacks, + +160 +00:10:01,034 --> 00:10:05,138 +where a client could try to present +a token multiple times. + +161 +00:10:05,172 --> 00:10:09,042 +Tokens are meant to be one-time use only. + +162 +00:10:09,076 --> 00:10:12,479 +You can prevent replay attacks +either by remembering + +163 +00:10:12,513 --> 00:10:15,249 +what tokens were sent previously, + +164 +00:10:15,282 --> 00:10:21,121 +or requiring that tokens sign +a unique value sent in your challenge. + +165 +00:10:23,323 --> 00:10:25,993 +Your site still needs to work +with legacy clients + +166 +00:10:26,026 --> 00:10:29,563 +that won't respond +to this authentication challenge. + +167 +00:10:29,596 --> 00:10:34,434 +So it's important that the authentication +should not block your main page load, + +168 +00:10:34,468 --> 00:10:38,972 +but instead be treated +as an optional way to trust a client. + +169 +00:10:39,006 --> 00:10:44,578 +Web servers that are accessed through +Safari and WebKit will work automatically, + +170 +00:10:44,611 --> 00:10:49,116 +but you can also use Private Access Tokens +within your app directly. + +171 +00:10:49,149 --> 00:10:54,488 +Private Access Tokens require iOS 16 +or macOS Ventura + +172 +00:10:54,521 --> 00:10:58,258 +on a device that has +an Apple ID signed in. + +173 +00:10:58,292 --> 00:11:01,461 +This Apple ID is only used +for attestation, + +174 +00:11:01,495 --> 00:11:05,999 +and is not shared with the servers +that receive tokens. + +175 +00:11:06,033 --> 00:11:11,738 +Within your app, tokens are available +if you use WebKit or URLSession + +176 +00:11:11,772 --> 00:11:15,275 +to contact your servers using HTTP. + +177 +00:11:15,309 --> 00:11:19,680 +Then anytime your app receives +a challenge while it's in the foreground, + +178 +00:11:19,713 --> 00:11:23,617 +the system will automatically send a token +as authentication. + +179 +00:11:25,118 --> 00:11:27,187 +If you're using URLSession, + +180 +00:11:27,221 --> 00:11:29,389 +you don't need to do anything explicitly + +181 +00:11:29,423 --> 00:11:32,392 +to make Private Access Tokens work. + +182 +00:11:32,426 --> 00:11:35,996 +URLSession will automatically respond +to challenges + +183 +00:11:36,029 --> 00:11:41,034 +using the PrivateToken HTTP +authentication scheme. + +184 +00:11:41,068 --> 00:11:44,171 +However, if there's an error +in fetching a token, + +185 +00:11:44,204 --> 00:11:46,773 +such as if your app +isn't in the foreground + +186 +00:11:46,807 --> 00:11:50,043 +or the device doesn't have +an Apple ID signed in, + +187 +00:11:50,077 --> 00:11:53,847 +your app will receive +the 401 HTTP response + +188 +00:11:53,881 --> 00:11:56,316 +that included the token challenge. + +189 +00:11:56,350 --> 00:12:00,454 +This allows your app to know +that the token challenge was received, + +190 +00:12:00,487 --> 00:12:05,425 +and provides a chance to handle the error +correctly for your use case. + +191 +00:12:05,459 --> 00:12:08,996 +Make apps and websites better +experiences for everyone + +192 +00:12:09,029 --> 00:12:13,734 +by avoiding CAPTCHAs whenever +Private Access Tokens are available. + +193 +00:12:13,767 --> 00:12:19,540 +Go and enable your servers to +send token challenges and validate tokens. + +194 +00:12:19,573 --> 00:12:23,377 +And, in your apps, +use URLSession or WebKit + +195 +00:12:23,410 --> 00:12:27,014 +to automatically support +Private Access Tokens. + +196 +00:12:27,047 --> 00:12:31,518 +I'm looking forward to the CAPTCHA-free +experiences you will build. + diff --git a/eng/2022 Session 10078 Reduce networking delays for a more responsive app en.srt b/eng/2022 Session 10078 Reduce networking delays for a more responsive app en.srt new file mode 100644 index 0000000..3d5f29e --- /dev/null +++ b/eng/2022 Session 10078 Reduce networking delays for a more responsive app en.srt @@ -0,0 +1,1335 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hip music ♪ + +2 +00:00:03,003 --> 00:00:10,143 +♪ + +3 +00:00:10,143 --> 00:00:14,348 +Hi, I am Vidhi Goel, +and in this video, + +4 +00:00:14,348 --> 00:00:19,052 +I will talk about how to reduce +networking delays in your apps + +5 +00:00:19,052 --> 00:00:21,722 +and make them more responsive. + +6 +00:00:21,722 --> 00:00:25,959 +First, I will explain why +reducing latency is crucial + +7 +00:00:25,959 --> 00:00:29,563 +in making your apps responsive. + +8 +00:00:29,563 --> 00:00:31,832 +Next, I will go over a list + +9 +00:00:31,832 --> 00:00:34,902 +of things that you can do +in your app + +10 +00:00:34,902 --> 00:00:40,240 +and on your server to get rid of +unnecessary delays. + +11 +00:00:40,240 --> 00:00:44,177 +Finally, I will show what +you can do to reduce delays + +12 +00:00:44,177 --> 00:00:47,948 +in the network itself. + +13 +00:00:47,948 --> 00:00:51,451 +Network latency +is the time it takes for data + +14 +00:00:51,451 --> 00:00:55,055 +to get from +one endpoint to another. + +15 +00:00:55,055 --> 00:00:58,725 +It determines how quickly +content can be delivered + +16 +00:00:58,725 --> 00:01:01,028 +to your app. + +17 +00:01:01,028 --> 00:01:04,531 +All apps that use networking +can be affected + +18 +00:01:04,531 --> 00:01:06,767 +by slow network transactions + +19 +00:01:06,767 --> 00:01:11,138 +that result in +a poor app experience. + +20 +00:01:11,138 --> 00:01:15,676 +For example, +video calls can sometimes freeze + +21 +00:01:15,676 --> 00:01:20,380 +or become laggy, +which can interrupt meetings. + +22 +00:01:20,380 --> 00:01:23,550 +To address this, +people often call up + +23 +00:01:23,550 --> 00:01:27,421 +their service provider +to upgrade their bandwidth, + +24 +00:01:27,421 --> 00:01:31,792 +and yet, +the problem still exists. + +25 +00:01:31,792 --> 00:01:34,594 +To get to the root cause +of this problem, + +26 +00:01:34,594 --> 00:01:38,265 +you need to understand +how your app's packets + +27 +00:01:38,265 --> 00:01:40,767 +travel in a network. + +28 +00:01:40,767 --> 00:01:44,805 +When your app or framework +requests data from a server, + +29 +00:01:44,805 --> 00:01:48,675 +packets are sent +out by the networking stack. + +30 +00:01:48,675 --> 00:01:52,079 +It is often assumed +that the packets go directly + +31 +00:01:52,079 --> 00:01:56,216 +to the server with no delays +in the network. + +32 +00:01:56,216 --> 00:01:59,920 +But, in reality, +the slowest link of the network + +33 +00:01:59,920 --> 00:02:05,592 +usually has a large queue +of packets to process. + +34 +00:02:05,592 --> 00:02:07,561 +So, the packet from your app + +35 +00:02:07,561 --> 00:02:10,864 +actually waits behind +this large queue + +36 +00:02:10,864 --> 00:02:16,036 +until the packets ahead of it +are processed. + +37 +00:02:16,036 --> 00:02:19,840 +This queuing at the slowest link +increases the duration + +38 +00:02:19,840 --> 00:02:24,745 +of each round trip between +your app and your server. + +39 +00:02:24,745 --> 00:02:29,916 +This problem is aggravated when +it takes multiple round trips + +40 +00:02:29,916 --> 00:02:34,855 +to get the first response +for your app's request. + +41 +00:02:34,855 --> 00:02:39,159 +For example, the time to get +the first response packet + +42 +00:02:39,159 --> 00:02:43,830 +when using TLS 1.2 over TCP + +43 +00:02:43,830 --> 00:02:46,400 +is the duration +of each round trip + +44 +00:02:46,400 --> 00:02:50,871 +multiplied by four trips. + +45 +00:02:50,871 --> 00:02:54,541 +Given that each round-trip time +is already inflated + +46 +00:02:54,541 --> 00:02:56,810 +by queuing in the network, + +47 +00:02:56,810 --> 00:03:02,215 +the resulting total time +is simply too long. + +48 +00:03:02,215 --> 00:03:05,152 +There are two factors +that multiply together + +49 +00:03:05,152 --> 00:03:09,156 +to determine +your app's responsiveness: + +50 +00:03:09,156 --> 00:03:15,128 +the duration of each round trip +and the number of round trips. + +51 +00:03:15,128 --> 00:03:18,865 +Reducing these will lower +your app's latency, + +52 +00:03:18,865 --> 00:03:24,071 +and increase your app's +responsiveness. + +53 +00:03:24,071 --> 00:03:26,606 +There was a study +examining the impact + +54 +00:03:26,606 --> 00:03:30,677 +of increasing bandwidth +versus decreasing latency + +55 +00:03:30,677 --> 00:03:33,680 +on page load time. + +56 +00:03:33,680 --> 00:03:37,484 +In the first test, +latency is kept fixed + +57 +00:03:37,484 --> 00:03:44,224 +and bandwidth is increased +incrementally from 1 to 10Mbps. + +58 +00:03:44,224 --> 00:03:49,129 +At first, increasing the +bandwidth from 1 to 2Mbps + +59 +00:03:49,129 --> 00:03:53,767 +reduces page load time +by almost 40 percent, + +60 +00:03:53,767 --> 00:03:56,036 +which is great. + +61 +00:03:56,036 --> 00:04:01,441 +But after 4Mbps, +each incremental increase + +62 +00:04:01,441 --> 00:04:07,180 +results in almost no improvement +in the page load time. + +63 +00:04:07,180 --> 00:04:11,618 +This is why apps can be slow +even after upgrading + +64 +00:04:11,618 --> 00:04:14,187 +to Gigabit Internet. + +65 +00:04:14,187 --> 00:04:19,226 +On the other hand, the results +for the latency test show + +66 +00:04:19,226 --> 00:04:24,097 +that for every 20 millisecond +decrease in latency, + +67 +00:04:24,097 --> 00:04:28,869 +there is a linear improvement +in page load time. + +68 +00:04:28,869 --> 00:04:33,473 +And these results apply +to all network activity + +69 +00:04:33,473 --> 00:04:35,642 +in your apps. + +70 +00:04:35,642 --> 00:04:40,413 +Now, I will go over a few +simple actions you can take + +71 +00:04:40,413 --> 00:04:45,285 +to reduce latency and make +your app more responsive. + +72 +00:04:45,285 --> 00:04:48,221 +You can reduce +your app's latency significantly + +73 +00:04:48,221 --> 00:04:50,624 +by adopting modern protocols + +74 +00:04:50,624 --> 00:04:57,097 +such as IPv6, TLS 1.3 +and HTTP/3. + +75 +00:04:57,097 --> 00:05:01,067 +And all you need to do +is use URLSession + +76 +00:05:01,067 --> 00:05:04,371 +and Network.framework APIs +in your app + +77 +00:05:04,371 --> 00:05:07,741 +and these protocols +will be used automatically + +78 +00:05:07,741 --> 00:05:12,879 +once they are enabled +on your server. + +79 +00:05:12,879 --> 00:05:17,350 +Since its rollout, +we have seen a constant increase + +80 +00:05:17,350 --> 00:05:22,355 +in HTTP/3 usage, +and within just a year, + +81 +00:05:22,355 --> 00:05:28,261 +20 percent of web traffic +already uses HTTP/3, + +82 +00:05:28,261 --> 00:05:32,499 +and it continues to grow. + +83 +00:05:32,499 --> 00:05:37,604 +Comparing Safari traffic +for different HTTP versions, + +84 +00:05:37,604 --> 00:05:42,876 +HTTP/3 is the fastest +of them all. + +85 +00:05:42,876 --> 00:05:47,847 +HTTP/3 requests take +a little over half the time + +86 +00:05:47,847 --> 00:05:50,550 +as compared to HTTP/1, + +87 +00:05:50,550 --> 00:05:53,720 +when looking at median +request completion time + +88 +00:05:53,720 --> 00:05:57,557 +as a multiple +of round-trip time. + +89 +00:05:57,557 --> 00:06:03,663 +This means your app's requests +will complete much faster. + +90 +00:06:03,663 --> 00:06:07,100 +When a device moves +from Wi-Fi to cellular, + +91 +00:06:07,100 --> 00:06:11,071 +it takes time to reestablish +new connections + +92 +00:06:11,071 --> 00:06:15,275 +and that can make +your application stall. + +93 +00:06:15,275 --> 00:06:20,614 +Using connection migration +eliminates those stalls. + +94 +00:06:20,614 --> 00:06:24,684 +To opt in, set the +multipathServiceType property + +95 +00:06:24,684 --> 00:06:28,888 +to .handover on your URLSession +configuration, + +96 +00:06:28,888 --> 00:06:32,325 +or on your NWParameters. + +97 +00:06:32,325 --> 00:06:39,232 +Enable this option and make sure +it works with your app. + +98 +00:06:39,232 --> 00:06:44,337 +If you design your own protocol +that uses UDP directly, + +99 +00:06:44,337 --> 00:06:49,809 +iOS 16 and macOS Ventura +introduce a better way + +100 +00:06:49,809 --> 00:06:52,445 +to send datagrams. + +101 +00:06:52,445 --> 00:06:57,584 +QUIC datagrams provide +many benefits over plain UDP, + +102 +00:06:57,584 --> 00:07:00,787 +the most important being +that QUIC datagrams + +103 +00:07:00,787 --> 00:07:03,023 +react to congestion +in the network + +104 +00:07:03,023 --> 00:07:07,894 +which keeps the round-trip time +low and reduces packet loss. + +105 +00:07:07,894 --> 00:07:10,030 +To opt in on the client, + +106 +00:07:10,030 --> 00:07:13,733 +set isDatagram to true +on your QUIC options + +107 +00:07:13,733 --> 00:07:19,606 +and set the maximum datagram +frame size you want to use. + +108 +00:07:19,606 --> 00:07:22,409 +After creating +the datagram flow, + +109 +00:07:22,409 --> 00:07:27,714 +you can send and receive on it +just like any other QUIC stream. + +110 +00:07:27,714 --> 00:07:30,216 +Now you know +what to do in your app + +111 +00:07:30,216 --> 00:07:32,519 +to reduce latency. + +112 +00:07:32,519 --> 00:07:36,156 +Next, I will explain +how servers impact + +113 +00:07:36,156 --> 00:07:39,392 +your app's responsiveness. + +114 +00:07:39,392 --> 00:07:42,762 +Despite often running +on top-of-the-line hardware, + +115 +00:07:42,762 --> 00:07:46,666 +it is possible that your server +actually becomes the reason + +116 +00:07:46,666 --> 00:07:49,302 +for slowness in your app. + +117 +00:07:49,302 --> 00:07:54,307 +We introduced the network +quality tool in macOS Monterey, + +118 +00:07:54,307 --> 00:07:56,443 +and you can use this tool + +119 +00:07:56,443 --> 00:08:00,480 +to measure buffer bloat in +your service provider's network + +120 +00:08:00,480 --> 00:08:03,183 +as well as on your server. + +121 +00:08:03,183 --> 00:08:06,886 +You need to configure your +server to act as a destination + +122 +00:08:06,886 --> 00:08:09,622 +for the network quality tool. + +123 +00:08:09,622 --> 00:08:14,394 +Once you have done that, +run the networkQuality tool, + +124 +00:08:14,394 --> 00:08:17,597 +first against +Apple's default server + +125 +00:08:17,597 --> 00:08:22,569 +and then against +your own configured server. + +126 +00:08:22,569 --> 00:08:26,406 +If the tool scores well +using the default server, + +127 +00:08:26,406 --> 00:08:30,543 +but not so well when talking +to your own server, + +128 +00:08:30,543 --> 00:08:33,580 +there may be room to improve +the responsiveness + +129 +00:08:33,580 --> 00:08:35,949 +of your server. + +130 +00:08:35,949 --> 00:08:40,887 +Now, I will show you an example +where we used this technique + +131 +00:08:40,887 --> 00:08:45,792 +to improve something that all +of you are doing right now -- + +132 +00:08:45,792 --> 00:08:48,695 +streaming video. + +133 +00:08:50,397 --> 00:08:52,399 +You may have had the experience + +134 +00:08:52,399 --> 00:08:55,869 +where you skip ahead +to a different place in a video + +135 +00:08:55,869 --> 00:09:01,808 +and you end up waiting +a long time while it rebuffers. + +136 +00:09:01,808 --> 00:09:05,512 +So, we investigated +the reason for this slowness + +137 +00:09:05,512 --> 00:09:08,281 +in random access. + +138 +00:09:08,281 --> 00:09:10,583 +We used the network quality tool + +139 +00:09:10,583 --> 00:09:13,820 +to test the behavior +of a streaming server + +140 +00:09:13,820 --> 00:09:18,758 +and we found that the +responsiveness score was poor. + +141 +00:09:18,758 --> 00:09:23,897 +On the right side, +I streamed a WWDC video. + +142 +00:09:23,897 --> 00:09:26,900 +Then, I skipped ahead +in the video. + +143 +00:09:26,900 --> 00:09:28,835 +The screen +didn't display anything + +144 +00:09:28,835 --> 00:09:32,205 +while the video rebuffered. + +145 +00:09:32,205 --> 00:09:36,309 +After a few seconds, +the video showed up. + +146 +00:09:36,309 --> 00:09:38,678 +With the help of detailed output + +147 +00:09:38,678 --> 00:09:41,948 +from the network quality tool +on macOS, + +148 +00:09:41,948 --> 00:09:46,920 +we found that there was +huge queuing at the server. + +149 +00:09:46,920 --> 00:09:52,225 +So we took a look at +the server configuration. + +150 +00:09:52,225 --> 00:09:58,865 +Specifically we looked at TCP, +TLS, and HTTP buffer sizes, + +151 +00:09:58,865 --> 00:10:09,142 +which were configured to 4MB, +256KB, and 4MB, respectively. + +152 +00:10:09,142 --> 00:10:14,013 +The buffers were huge +because RAM is plentiful. + +153 +00:10:14,013 --> 00:10:17,450 +But just because +some buffering is good, + +154 +00:10:17,450 --> 00:10:22,155 +doesn't always mean +that more buffering is better. + +155 +00:10:22,155 --> 00:10:27,427 +Our responsiveness measurements +highlighted this exact issue -- + +156 +00:10:27,427 --> 00:10:32,065 +a newly generated packet +was queued behind stale data + +157 +00:10:32,065 --> 00:10:34,167 +in these large buffers, + +158 +00:10:34,167 --> 00:10:37,237 +and this created a lot +of additional delay + +159 +00:10:37,237 --> 00:10:41,241 +in delivering +the most recent packet. + +160 +00:10:41,241 --> 00:10:47,981 +So, we reduced the buffer size +to 256KB for HTTP, + +161 +00:10:47,981 --> 00:10:54,854 +16KB for TLS, +and 128KB for TCP. + +162 +00:10:57,690 --> 00:11:01,327 +This is the config file +for Apache Traffic Server + +163 +00:11:01,327 --> 00:11:05,398 +which shows the options +that were configured. + +164 +00:11:05,398 --> 00:11:10,870 +TCP not-sent low-water mark +was set to 128KB + +165 +00:11:10,870 --> 00:11:16,976 +along with other options that +were enabled to lower buffering. + +166 +00:11:16,976 --> 00:11:21,748 +For TLS, we enabled +dynamic record sizes + +167 +00:11:21,748 --> 00:11:26,753 +and for HTTP/2, +we reduced the low-water mark + +168 +00:11:26,753 --> 00:11:28,788 +and buffer block size. + +169 +00:11:28,788 --> 00:11:31,724 +We recommend +using these configurations + +170 +00:11:31,724 --> 00:11:34,627 +for your Apache Traffic Server, + +171 +00:11:34,627 --> 00:11:38,031 +and if you are using +a different web server, + +172 +00:11:38,031 --> 00:11:41,301 +look for its equivalent options. + +173 +00:11:41,301 --> 00:11:43,736 +After making these changes, + +174 +00:11:43,736 --> 00:11:47,540 +we ran the network +quality tool again. + +175 +00:11:47,540 --> 00:11:52,645 +And this time we got +a high RPM score! + +176 +00:11:52,645 --> 00:11:56,249 +On the right, +I streamed the same video, + +177 +00:11:56,249 --> 00:11:58,918 +but this time +when I skipped ahead, + +178 +00:11:58,918 --> 00:12:03,590 +the video resumed instantly. + +179 +00:12:03,590 --> 00:12:07,594 +By getting rid of unnecessary +queuing at the server, + +180 +00:12:07,594 --> 00:12:11,698 +we made random access +much more responsive. + +181 +00:12:11,698 --> 00:12:15,868 +Regardless of how your app +uses networking, + +182 +00:12:15,868 --> 00:12:20,840 +these changes on your server can +make your app more responsive + +183 +00:12:20,840 --> 00:12:24,877 +and deliver +a better user experience. + +184 +00:12:24,877 --> 00:12:29,916 +That's how to improve your app +and update your server. + +185 +00:12:29,916 --> 00:12:34,854 +There is a third factor that +affects responsiveness greatly; + +186 +00:12:34,854 --> 00:12:37,223 +the network itself. + +187 +00:12:37,223 --> 00:12:41,527 +Apple introduced the +network quality tool in iOS 15 + +188 +00:12:41,527 --> 00:12:44,364 +and macOS Monterey. + +189 +00:12:44,364 --> 00:12:48,401 +Since then, others have used +the same methodology + +190 +00:12:48,401 --> 00:12:52,972 +to develop +network quality tests. + +191 +00:12:52,972 --> 00:12:56,976 +Waveform has launched +a Bufferbloat test. + +192 +00:12:56,976 --> 00:12:59,245 +There's an open source +implementation + +193 +00:12:59,245 --> 00:13:03,983 +of the responsiveness test, +written in Go. + +194 +00:13:03,983 --> 00:13:07,854 +And Ookla has added +a responsiveness measurement + +195 +00:13:07,854 --> 00:13:11,524 +to their Speedtest app. + +196 +00:13:11,524 --> 00:13:15,395 +Ookla's app shows +round trip time in milliseconds, + +197 +00:13:15,395 --> 00:13:18,965 +and if you divide 60,000 +by that number, + +198 +00:13:18,965 --> 00:13:24,103 +you get the number of +round trips per minute, or RPM. + +199 +00:13:24,103 --> 00:13:26,673 +You can use these tools +to measure + +200 +00:13:26,673 --> 00:13:30,943 +how well your own network +is performing. + +201 +00:13:30,943 --> 00:13:34,113 +The best way to understand +delays in a network + +202 +00:13:34,113 --> 00:13:37,750 +is with a delay-sensitive +application. + +203 +00:13:37,750 --> 00:13:41,054 +So, I will show you +my screen sharing experience + +204 +00:13:41,054 --> 00:13:43,823 +to a remote machine. + +205 +00:13:43,823 --> 00:13:45,558 +I set up network conditions + +206 +00:13:45,558 --> 00:13:48,828 +to mimic a representative +access network, + +207 +00:13:48,828 --> 00:13:54,233 +with traffic from other devices +sharing that network. + +208 +00:13:54,233 --> 00:13:59,105 +Here, I logged on to my remote +machine using Screen Sharing. + +209 +00:14:01,174 --> 00:14:04,377 +I clicked on different +Finder menus + +210 +00:14:04,377 --> 00:14:09,148 +but the display of each menu +was very sluggish. + +211 +00:14:09,148 --> 00:14:12,552 +To check how much +this interaction was delayed, + +212 +00:14:12,552 --> 00:14:16,823 +I launched an app that displays +time on my local machine, + +213 +00:14:16,823 --> 00:14:21,360 +and I launched the same app +on my remote machine. + +214 +00:14:21,360 --> 00:14:25,832 +Even though time on these +computers is synchronized, + +215 +00:14:25,832 --> 00:14:29,702 +my remote screen +didn't update regularly + +216 +00:14:29,702 --> 00:14:34,907 +and showed time delayed +by a few seconds. + +217 +00:14:34,907 --> 00:14:37,343 +The reason +for this delayed update + +218 +00:14:37,343 --> 00:14:39,979 +was the presence +of a large queue + +219 +00:14:39,979 --> 00:14:42,782 +at the slowest link +of the network + +220 +00:14:42,782 --> 00:14:45,485 +and packets from +the Screen Sharing app + +221 +00:14:45,485 --> 00:14:48,821 +were stuck in this large queue. + +222 +00:14:50,823 --> 00:14:53,259 +To solve this queuing issue, + +223 +00:14:53,259 --> 00:14:56,028 +Apple is working with +the networking community + +224 +00:14:56,028 --> 00:14:59,899 +on a new technology called L4S. + +225 +00:14:59,899 --> 00:15:06,773 +It is available as a beta +in iOS 16 and macOS Ventura. + +226 +00:15:06,773 --> 00:15:10,109 +L4S reduces +queuing delay significantly + +227 +00:15:10,109 --> 00:15:14,747 +and also achieves +zero congestion loss. + +228 +00:15:14,747 --> 00:15:17,550 +To keep a consistently +short queue, + +229 +00:15:17,550 --> 00:15:20,419 +the network explicitly +signals congestion + +230 +00:15:20,419 --> 00:15:22,622 +instead of dropping packets, + +231 +00:15:22,622 --> 00:15:25,491 +and the sender adjusts +its sending rate + +232 +00:15:25,491 --> 00:15:29,529 +based on the congestion feedback +from the network. + +233 +00:15:29,529 --> 00:15:34,133 +This makes it possible to keep +very low queuing in the network + +234 +00:15:34,133 --> 00:15:37,069 +without any packet loss, + +235 +00:15:37,069 --> 00:15:41,874 +and that will make your app +highly responsive. + +236 +00:15:41,874 --> 00:15:48,281 +Now, let's look at how L4S +improved Screen Sharing. + +237 +00:15:48,281 --> 00:15:52,585 +Here, I used the same machines +and the same network + +238 +00:15:52,585 --> 00:15:57,557 +except this time, +I enabled L4S. + +239 +00:15:57,557 --> 00:16:00,359 +When I clicked on +different Finder menus, + +240 +00:16:00,359 --> 00:16:02,895 +they opened immediately. + +241 +00:16:02,895 --> 00:16:06,899 +I launched the Time app +on both the machines. + +242 +00:16:06,899 --> 00:16:09,769 +And now, time on both +the remote screen + +243 +00:16:09,769 --> 00:16:16,943 +and the local machine +is almost perfectly in sync. + +244 +00:16:16,943 --> 00:16:21,380 +This technology is not just +for screen sharing. + +245 +00:16:21,380 --> 00:16:25,484 +L4S improves +all of today's apps, + +246 +00:16:25,484 --> 00:16:28,120 +and opens the door +for future apps + +247 +00:16:28,120 --> 00:16:31,958 +that wouldn't even +be possible today. + +248 +00:16:31,958 --> 00:16:36,229 +This chart plots the observed +average round trip time + +249 +00:16:36,229 --> 00:16:39,165 +of packets from +the Screen Sharing app + +250 +00:16:39,165 --> 00:16:41,534 +which was running +concurrently with traffic + +251 +00:16:41,534 --> 00:16:46,005 +from other devices +sharing the same network. + +252 +00:16:46,005 --> 00:16:49,508 +Comparing classic queuing +versus L4S + +253 +00:16:49,508 --> 00:16:52,712 +shows that there +is a massive reduction + +254 +00:16:52,712 --> 00:16:56,415 +in round trip time with L4S. + +255 +00:16:56,415 --> 00:17:00,386 +This is the primary reason +for the dramatic improvement + +256 +00:17:00,386 --> 00:17:05,157 +in my screen-sharing experience. + +257 +00:17:05,157 --> 00:17:11,130 +Test your app that uses +HTTP/3 or QUIC with L4S. + +258 +00:17:11,130 --> 00:17:16,969 +You can enable L4S in iOS 16 +inside Developer settings + +259 +00:17:16,969 --> 00:17:22,808 +or on macOS Ventura +via a defaults write. + +260 +00:17:22,808 --> 00:17:25,611 +To test using a Linux server, + +261 +00:17:25,611 --> 00:17:30,082 +your QUIC implementation +needs to support accurate ECN + +262 +00:17:30,082 --> 00:17:34,520 +and a scalable congestion +control algorithm. + +263 +00:17:34,520 --> 00:17:36,622 +To ensure that you are ready + +264 +00:17:36,622 --> 00:17:40,226 +when L4S-capable networks +are deployed, + +265 +00:17:40,226 --> 00:17:44,263 +test your app +for compatibility with L4S, + +266 +00:17:44,263 --> 00:17:50,503 +and provide feedback with any +issues you might encounter. + +267 +00:17:50,503 --> 00:17:54,440 +Now you know that +reducing latency is crucial + +268 +00:17:54,440 --> 00:17:58,044 +to improve your app's +responsiveness. + +269 +00:17:58,044 --> 00:18:02,381 +So, adopt HTTP/3 and QUIC, + +270 +00:18:02,381 --> 00:18:04,717 +to reduce the number +of round trips + +271 +00:18:04,717 --> 00:18:09,822 +and for faster delivery +of content to your app. + +272 +00:18:09,822 --> 00:18:13,459 +Eliminate unnecessary queuing +on your server + +273 +00:18:13,459 --> 00:18:17,363 +to provide a more +responsive interaction. + +274 +00:18:17,363 --> 00:18:21,701 +Test your app's compatibility +with L4S by enabling it + +275 +00:18:21,701 --> 00:18:25,938 +in Developer settings +and provide feedback. + +276 +00:18:25,938 --> 00:18:29,041 +And finally, +talk to your server provider + +277 +00:18:29,041 --> 00:18:32,878 +about enabling L4S support. + +278 +00:18:32,878 --> 00:18:34,747 +Thanks for watching! + +279 +00:18:34,747 --> 00:18:39,118 +♪ + diff --git a/eng/2022 Session 10079 Improve DNS security for apps and servers en.srt b/eng/2022 Session 10079 Improve DNS security for apps and servers en.srt new file mode 100644 index 0000000..4ff1f80 --- /dev/null +++ b/eng/2022 Session 10079 Improve DNS security for apps and servers en.srt @@ -0,0 +1,1064 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,810 --> 00:00:11,078 +Qiaoyu Deng: Hello. + +3 +00:00:11,111 --> 00:00:17,384 +Welcome to “Improve DNS security +for apps and servers.” + +4 +00:00:17,417 --> 00:00:20,687 +My name is Qiaoyu Deng. + +5 +00:00:20,721 --> 00:00:27,694 +In this video, we are going to talk about +why DNS is often not secure + +6 +00:00:27,728 --> 00:00:32,332 +and how to protect it using DNSSEC + +7 +00:00:32,366 --> 00:00:37,171 +and encrypted DNS with DDR. + +8 +00:00:37,204 --> 00:00:43,210 +First, let’s talk about +why DNS is not secure. + +9 +00:00:44,912 --> 00:00:49,917 +DNS is the phone book of the internet. + +10 +00:00:49,950 --> 00:00:53,120 +It translates domain names, + +11 +00:00:53,153 --> 00:00:57,124 +which are human readable and easy to +remember, + +12 +00:00:57,157 --> 00:01:02,262 +to IP addresses, +which are made for machines. + +13 +00:01:02,296 --> 00:01:04,798 +Other internet protocols, + +14 +00:01:04,831 --> 00:01:09,469 +such as TCP, TLS, and QUIC, + +15 +00:01:09,503 --> 00:01:12,973 +rely on having an IP address, + +16 +00:01:13,006 --> 00:01:17,110 +so everything starts with DNS. + +17 +00:01:17,144 --> 00:01:20,948 +Today, TLS is widely used + +18 +00:01:20,981 --> 00:01:24,551 +to secure internet communication. + +19 +00:01:24,585 --> 00:01:29,556 +That’s great, +but DNS, the foundational layer, + +20 +00:01:29,590 --> 00:01:31,592 +has some security issues. + +21 +00:01:31,625 --> 00:01:35,696 +DNS is historically not secure. + +22 +00:01:35,729 --> 00:01:41,902 +It was designed back in 1983 +with few security considerations. + +23 +00:01:41,935 --> 00:01:46,840 +In the years since, +many DNS attacks have been created. + +24 +00:01:46,874 --> 00:01:52,112 +One example is DNS cache poisoning, + +25 +00:01:52,145 --> 00:01:57,117 +where the attacker +exploits flaws of DNS resolvers + +26 +00:01:57,150 --> 00:02:01,488 +and make them cache +incorrect IP addresses, + +27 +00:02:01,522 --> 00:02:05,526 +causing clients +to connect to malicious hosts. + +28 +00:02:05,559 --> 00:02:09,963 +This reveals one vulnerability of DNS: + +29 +00:02:09,997 --> 00:02:12,866 +it is unauthenticated. + +30 +00:02:12,900 --> 00:02:17,971 +Traditional DNS clients today +have no way to validate answers, + +31 +00:02:18,005 --> 00:02:21,375 +so they can easily be spoofed. + +32 +00:02:21,408 --> 00:02:25,479 +Another common attack is DNS sniffing, + +33 +00:02:25,512 --> 00:02:32,252 +where the attacker watches DNS traffic +between a client and a DNS server, + +34 +00:02:32,286 --> 00:02:35,455 +collecting the client's history. + +35 +00:02:35,489 --> 00:02:39,860 +This is a serious problem +for user privacy. + +36 +00:02:39,893 --> 00:02:43,230 +The reason this attack is possible is that + +37 +00:02:43,263 --> 00:02:49,336 +DNS traffic was originally unencrypted. + +38 +00:02:49,369 --> 00:02:55,909 +In order to be a secure starting point +for the protocols that build on top of it, + +39 +00:02:55,943 --> 00:03:01,882 +DNS needs to be both +authenticated and encrypted. + +40 +00:03:01,915 --> 00:03:05,886 +When we use DNSSEC to sign DNS response, + +41 +00:03:05,919 --> 00:03:09,289 +it provides authentication. + +42 +00:03:09,323 --> 00:03:15,395 +When we use TLS and HTTPS +to encrypt DNS resolution, + +43 +00:03:15,429 --> 00:03:18,365 +it ensures privacy. + +44 +00:03:18,398 --> 00:03:23,203 +Next, let’s talk about DNSSEC. + +45 +00:03:23,237 --> 00:03:27,608 +DNSSEC is a suite +of extension specifications + +46 +00:03:27,641 --> 00:03:30,577 +created by IETF. + +47 +00:03:30,611 --> 00:03:35,082 +Many DNS service providers +already support it, + +48 +00:03:35,115 --> 00:03:38,986 +but client support is still ramping up. + +49 +00:03:39,019 --> 00:03:42,422 +iOS 16 and macOS Ventura + +50 +00:03:42,456 --> 00:03:47,828 +now support client side DNSSEC validation. + +51 +00:03:47,861 --> 00:03:53,967 +DNSSEC ensures the authentication of data +by adding digital signatures. + +52 +00:03:54,001 --> 00:03:57,337 +It protects data integrity. + +53 +00:03:57,371 --> 00:04:00,541 +It authenticates denial of existence + +54 +00:04:00,574 --> 00:04:03,977 +when answers do not exist. + +55 +00:04:04,011 --> 00:04:09,316 +It also provides cryptographic +authentication. + +56 +00:04:09,349 --> 00:04:12,920 +DNSSEC protects data integrity + +57 +00:04:12,953 --> 00:04:17,024 +by attaching signatures in responses. + +58 +00:04:17,057 --> 00:04:20,294 +If a response is altered by an attacker, + +59 +00:04:20,327 --> 00:04:26,567 +the signature of the altered data +will not match the original one. + +60 +00:04:26,600 --> 00:04:33,507 +In that case, the client can detect +the altered response and discard it. + +61 +00:04:33,540 --> 00:04:37,277 +DNSSEC also asserts the existence + +62 +00:04:37,311 --> 00:04:40,848 +and nonexistence of records in a zone, + +63 +00:04:40,881 --> 00:04:44,051 +by using special types of DNS records + +64 +00:04:44,084 --> 00:04:47,921 +such as the NSEC record. + +65 +00:04:47,955 --> 00:04:52,826 +The NSEC record tells you +what the next record name is, + +66 +00:04:52,860 --> 00:04:56,663 +securely, in alphabetical order. + +67 +00:04:56,697 --> 00:05:01,401 +The names listed by it +are the ones that exist, + +68 +00:05:01,435 --> 00:05:06,273 +and any name not listed does not exist. + +69 +00:05:06,306 --> 00:05:10,777 +For example, +we have three NSEC records here. + +70 +00:05:10,811 --> 00:05:17,184 +The record set reveals that zone org +only has three record names, + +71 +00:05:17,217 --> 00:05:20,687 +A.org, C.org, and E.org. + +72 +00:05:20,721 --> 00:05:27,728 +Now, if there is an attacker +that says A.org does not exist, + +73 +00:05:27,761 --> 00:05:31,164 +the client can detect this attack. + +74 +00:05:31,198 --> 00:05:33,834 +A.org does exist + +75 +00:05:33,867 --> 00:05:39,873 +because it is listed +in the first NSEC record. + +76 +00:05:39,907 --> 00:05:45,946 +Likewise, if an attacker says +that D.org exists, + +77 +00:05:45,979 --> 00:05:49,449 +the client can also detect that, + +78 +00:05:49,483 --> 00:05:53,220 +because according to +the second NSEC record, + +79 +00:05:53,253 --> 00:05:57,824 +D.org is in between C.org and E.org + +80 +00:05:57,858 --> 00:06:03,030 +and no name should exist +in between the two names. + +81 +00:06:03,063 --> 00:06:09,369 +DNSSEC authenticates records +by establishing a chain of trust. + +82 +00:06:09,403 --> 00:06:12,339 +Here is an example. + +83 +00:06:12,372 --> 00:06:17,344 +A device wants to resolve www.example.org + +84 +00:06:17,377 --> 00:06:21,515 +with DNSSEC validation enabled. + +85 +00:06:21,548 --> 00:06:25,085 +It sends queries +asking for the IP addresses, + +86 +00:06:25,118 --> 00:06:28,288 +signatures, and keys. + +87 +00:06:28,322 --> 00:06:32,893 +With the responses, +the trust relationship can be built + +88 +00:06:32,926 --> 00:06:37,197 +from the IP addresses to key number 1. + +89 +00:06:37,231 --> 00:06:42,436 +Then the client sends queries +to the parent zone, org, + +90 +00:06:42,469 --> 00:06:47,774 +asking for the records that can be used +to authenticate key number 1, + +91 +00:06:47,808 --> 00:06:53,680 +so it can build the trust relationship +from key number 1 to key number 2. + +92 +00:06:53,714 --> 00:06:58,418 +So the device repeats +this process recursively, + +93 +00:06:58,452 --> 00:07:01,355 +until it has reached the root. + +94 +00:07:01,388 --> 00:07:06,627 +Now, if the root key, +which is key number 3 in the diagram, + +95 +00:07:06,660 --> 00:07:12,466 +can be trusted, the trust relationship +from the IP addresses + +96 +00:07:12,499 --> 00:07:16,370 +to key number 3 can be authenticated. + +97 +00:07:16,403 --> 00:07:21,875 +The hash of the root key is always +stored in the device securely. + +98 +00:07:21,909 --> 00:07:26,380 +In DNSSEC, +it is called the root trust anchor. + +99 +00:07:26,413 --> 00:07:31,952 +If the hash of key number 3 +matches the pre-installed anchor, + +100 +00:07:31,985 --> 00:07:35,822 +a trust chain can be established securely. + +101 +00:07:35,856 --> 00:07:37,658 +With the trust chain, + +102 +00:07:37,691 --> 00:07:44,698 +the IP addresses of www.example.org +are now authenticated. + +103 +00:07:45,432 --> 00:07:50,504 +If you want to require DNSSEC validation +in your apps, + +104 +00:07:50,537 --> 00:07:54,274 +here are the things you need to do. + +105 +00:07:54,308 --> 00:07:58,245 +Support IPv6 for your domains. + +106 +00:07:58,278 --> 00:08:04,618 +In an IPv6-only environment, +IPv4-only addresses are translated + +107 +00:08:04,651 --> 00:08:08,455 +into synthetic IPv6 addresses. + +108 +00:08:08,488 --> 00:08:12,326 +If domains are signed, +synthesized addresses + +109 +00:08:12,359 --> 00:08:16,330 +cannot pass DNSSEC validation; + +110 +00:08:16,363 --> 00:08:20,934 +they are unreachable with DNSSEC enabled. + +111 +00:08:20,968 --> 00:08:26,373 +So make sure that +your domains support IPv6. + +112 +00:08:26,406 --> 00:08:33,180 +Make sure your DNS service provider +signs your domain with DNSSEC. + +113 +00:08:33,213 --> 00:08:38,385 +If you enable DNSSEC in your app +without signing your domain, + +114 +00:08:38,418 --> 00:08:40,587 +you will get no benefit, + +115 +00:08:40,621 --> 00:08:46,627 +but you will get additional DNS traffic +and extended resolution time + +116 +00:08:46,660 --> 00:08:51,632 +to attempt authentication +for your unsigned domain. + +117 +00:08:51,665 --> 00:08:55,035 +Once you have +the corresponding infrastructure support, + +118 +00:08:55,068 --> 00:09:01,341 +here is the code needed to adopt +DNSSEC for your apps. + +119 +00:09:01,375 --> 00:09:04,845 +If you are an NSURLSession client, + +120 +00:09:04,878 --> 00:09:10,551 +you can require DNSSEC validation +for your URL request. + +121 +00:09:10,584 --> 00:09:13,420 +Here is an example. + +122 +00:09:13,453 --> 00:09:17,858 +You will first create a default session +configuration. + +123 +00:09:17,891 --> 00:09:23,263 +Then you will require DNSSEC validation. + +124 +00:09:23,297 --> 00:09:28,969 +Next, you will create the session +with the modified configuration. + +125 +00:09:29,002 --> 00:09:33,841 +It enables DNSSEC for all URL requests + +126 +00:09:33,874 --> 00:09:37,544 +created from this session. + +127 +00:09:37,578 --> 00:09:42,716 +If you do not want to enable DNSSEC +for the entire session, + +128 +00:09:42,749 --> 00:09:46,653 +you can also do this at the request level. + +129 +00:09:46,687 --> 00:09:50,557 +First, create a session with +the default configuration + +130 +00:09:50,591 --> 00:09:54,728 +where DNSSEC validation is disabled. + +131 +00:09:54,761 --> 00:09:57,297 +Then enable it in the request. + +132 +00:09:57,331 --> 00:10:01,401 +Now, this session task +will only be started + +133 +00:10:01,435 --> 00:10:06,640 +when DNSSEC validation is completed. + +134 +00:10:06,673 --> 00:10:10,010 +If you are a Network.framework client, + +135 +00:10:10,043 --> 00:10:15,816 +you can also require DNSSEC validation +for your connections. + +136 +00:10:15,849 --> 00:10:20,287 +First, when you create +a parameters object, + +137 +00:10:20,320 --> 00:10:24,291 +require DNSSEC validation. + +138 +00:10:24,324 --> 00:10:30,364 +Then create the NWConnection +with the parameters object. + +139 +00:10:30,397 --> 00:10:36,003 +Now, when you start your connection, +it will only move into the ready state + +140 +00:10:36,036 --> 00:10:39,540 +when the DNSSEC validation has completed + +141 +00:10:39,573 --> 00:10:46,113 +and a connection to the validated +IP address has been established. + +142 +00:10:46,146 --> 00:10:48,916 +When DNSSEC is enabled, + +143 +00:10:48,949 --> 00:10:55,222 +only the validated addresses +will be used to establish a connection. + +144 +00:10:55,255 --> 00:11:01,195 +In HTTPS, +errors are reported through the APIs. + +145 +00:11:01,228 --> 00:11:06,700 +In DNSSEC, no error is returned +for the validation failure. + +146 +00:11:06,733 --> 00:11:13,540 +Receiving a response that fails validation +is equal to not receiving any response. + +147 +00:11:13,574 --> 00:11:18,245 +If there is a DNS provider +that tampers with the response, + +148 +00:11:18,278 --> 00:11:22,616 +the addresses will not pass +the authentication check, + +149 +00:11:22,649 --> 00:11:25,652 +so they will be discarded directly. + +150 +00:11:25,686 --> 00:11:30,324 +When the device joins a new network +where the DNS provider + +151 +00:11:30,357 --> 00:11:33,026 +is not tampering with responses, + +152 +00:11:33,060 --> 00:11:36,230 +the validation will make progress again + +153 +00:11:36,263 --> 00:11:40,534 +and the resolution will be back +to normal automatically. + +154 +00:11:41,602 --> 00:11:47,841 +Here are a few cases +that can cause DNSSEC failure. + +155 +00:11:47,875 --> 00:11:51,411 +When the original DNS response is altered, + +156 +00:11:51,445 --> 00:11:57,184 +the mismatched signature +will not pass the DNSSEC check, + +157 +00:11:57,217 --> 00:12:00,254 +causing a validation failure. + +158 +00:12:00,287 --> 00:12:05,626 +When the device is unable to reach +any pre-installed trust anchor + +159 +00:12:05,659 --> 00:12:11,098 +and unable to establish +a trust chain from it. + +160 +00:12:11,131 --> 00:12:13,934 +When the network does not support + +161 +00:12:13,967 --> 00:12:18,739 +the necessary protocols +required by DNSSEC, + +162 +00:12:18,772 --> 00:12:24,811 +such as DNS over TCP and EDNS0 option, + +163 +00:12:24,845 --> 00:12:30,284 +that carries the DNSSEC enablement bit. + +164 +00:12:30,317 --> 00:12:35,389 +When the signed domain +does not support IPv6, + +165 +00:12:35,422 --> 00:12:38,759 +the synthesized IPv6 addresses + +166 +00:12:38,792 --> 00:12:45,098 +provided by the internet service provider +will fail the validation. + +167 +00:12:45,132 --> 00:12:52,139 +So that’s how to authenticate +DNS responses with DNSSEC, + +168 +00:12:52,172 --> 00:12:55,576 +but if they are still unencrypted, + +169 +00:12:55,609 --> 00:12:59,413 +anyone on the network can see them. + +170 +00:12:59,446 --> 00:13:02,416 +Next, we will talk about + +171 +00:13:02,449 --> 00:13:07,788 +how to enable DNS encryption +automatically with DDR. + +172 +00:13:09,122 --> 00:13:12,926 +In iOS 14 and macOS Big Sur, + +173 +00:13:12,960 --> 00:13:19,533 +we introduced encrypted DNS +to help preserve privacy. + +174 +00:13:19,566 --> 00:13:24,137 +You can use NEDNSSettingsManager in an app + +175 +00:13:24,171 --> 00:13:27,708 +or DNSSettings in a profile + +176 +00:13:27,741 --> 00:13:32,779 +to manually configure encrypted DNS +system-wide. + +177 +00:13:32,813 --> 00:13:36,250 +You can also opt into +encrypted DNS for your app + +178 +00:13:36,283 --> 00:13:41,455 +using the PrivacyContext on NWParameters. + +179 +00:13:41,488 --> 00:13:47,661 +For more information, please watch +"Enable encrypted DNS." + +180 +00:13:47,694 --> 00:13:51,932 +New in iOS 16 and macOS Ventura, + +181 +00:13:51,965 --> 00:13:56,103 +encrypted DNS can be used automatically. + +182 +00:13:56,136 --> 00:14:03,143 +If your network supports Discovery of +Designated Resolvers, also called DDR, + +183 +00:14:03,177 --> 00:14:09,983 +DNS queries will automatically +use TLS or HTTPS. + +184 +00:14:10,017 --> 00:14:12,819 +To use encrypted DNS, + +185 +00:14:12,853 --> 00:14:15,289 +your device needs to know that + +186 +00:14:15,322 --> 00:14:21,662 +a resolver supports TLS or HTTPS, + +187 +00:14:21,695 --> 00:14:28,669 +and it may need to learn +a port or URL path as well. + +188 +00:14:28,702 --> 00:14:35,409 +Common mechanisms such as DHCP +or Router Advertisements + +189 +00:14:35,442 --> 00:14:40,180 +only provide a plain IP address. + +190 +00:14:40,214 --> 00:14:46,053 +DDR is a new protocol +developed in the IETF by Apple + +191 +00:14:46,086 --> 00:14:49,556 +working with other industry partners. + +192 +00:14:49,590 --> 00:14:55,662 +It provides a way for DNS clients +to learn this necessary information + +193 +00:14:55,696 --> 00:14:59,399 +by using a special DNS query. + +194 +00:14:59,433 --> 00:15:02,970 +When your device joins a new network, + +195 +00:15:03,003 --> 00:15:05,839 +it will issue a Service Binding query + +196 +00:15:05,873 --> 00:15:10,777 +for “_dns.resolver.arpa”. + +197 +00:15:10,811 --> 00:15:13,914 +If the DNS server supports DDR, + +198 +00:15:13,947 --> 00:15:18,218 +it will reply with +one or more configurations. + +199 +00:15:18,252 --> 00:15:22,122 +Then, the device uses this information + +200 +00:15:22,155 --> 00:15:28,028 +to set up an encrypted connection +to the designated resolver. + +201 +00:15:28,061 --> 00:15:33,300 +It verifies that the IP address +of the unencrypted resolver + +202 +00:15:33,333 --> 00:15:38,639 +is included in the TLS certificate +of the designated resolver. + +203 +00:15:38,672 --> 00:15:43,410 +This is done to ensure that +the unencrypted resolver + +204 +00:15:43,443 --> 00:15:47,581 +and the encrypted one +belong to the same entity. + +205 +00:15:47,614 --> 00:15:54,087 +If everything looks good, the device now +uses encrypted DNS by default. + +206 +00:15:55,088 --> 00:15:59,693 +DDR applies to a single network at a time. + +207 +00:15:59,726 --> 00:16:04,665 +Your device will use +encrypted DNS automatically + +208 +00:16:04,698 --> 00:16:09,369 +only if the current network supports it. + +209 +00:16:09,403 --> 00:16:14,441 +It's also important to note that +DDR does not work + +210 +00:16:14,474 --> 00:16:20,013 +if your DNS server's IP address +is a private IP address. + +211 +00:16:20,047 --> 00:16:23,383 +This is because such IP addresses + +212 +00:16:23,417 --> 00:16:27,521 +are not allowed in TLS certificates, + +213 +00:16:27,554 --> 00:16:32,125 +since their ownership cannot be verified. + +214 +00:16:32,159 --> 00:16:36,330 +In iOS 16 and macOS Ventura, + +215 +00:16:36,363 --> 00:16:41,869 +we also support the ability +to specify client authentication + +216 +00:16:41,902 --> 00:16:45,939 +when using encrypted DNS +for a configuration setup, + +217 +00:16:45,973 --> 00:16:52,179 +using NEDNSSettingsManager +or the DNSSettings profile. + +218 +00:16:53,180 --> 00:16:58,085 +Client authentication allows +encrypted DNS servers to be used + +219 +00:16:58,118 --> 00:17:00,587 +in enterprise environments + +220 +00:17:00,621 --> 00:17:06,960 +where servers need to validate clients +before allowing access. + +221 +00:17:06,994 --> 00:17:10,430 +You can now configure a client certificate + +222 +00:17:10,464 --> 00:17:16,003 +using the identityReference property +of NEDNSSettings. + +223 +00:17:16,036 --> 00:17:22,676 +This behaves just like client certificates +for VPNs. + +224 +00:17:22,709 --> 00:17:26,647 +These can apply to both DNS over TLS + +225 +00:17:26,680 --> 00:17:30,617 +and DNS over HTTPS. + +226 +00:17:30,651 --> 00:17:35,489 +This is the path to securing DNS. + +227 +00:17:35,522 --> 00:17:38,859 +Sign your domain with DNSSEC + +228 +00:17:38,892 --> 00:17:43,297 +and require DNSSEC validation in your apps + +229 +00:17:43,330 --> 00:17:47,234 +to authenticate your IP addresses. + +230 +00:17:47,267 --> 00:17:50,237 +Enable DDR on your network + +231 +00:17:50,270 --> 00:17:55,876 +so that the clients can switch +to encrypted DNS automatically + +232 +00:17:55,909 --> 00:17:58,879 +for better user privacy. + +233 +00:17:58,912 --> 00:18:02,749 +Adopt client authentication in enterprises + +234 +00:18:02,783 --> 00:18:06,687 +where better access control is needed. + +235 +00:18:06,720 --> 00:18:10,824 +I am looking forward to +a more secure DNS foundation + +236 +00:18:10,858 --> 00:18:14,828 +that you help to build in the future. + +237 +00:18:14,862 --> 00:18:16,263 +Thanks for watching! + +238 +00:18:16,296 --> 00:18:20,300 +[upbeat music] + diff --git a/eng/2022 Session 10082 Track down hangs with Xcode and on-device detection en.srt b/eng/2022 Session 10082 Track down hangs with Xcode and on-device detection en.srt new file mode 100644 index 0000000..f58c62f --- /dev/null +++ b/eng/2022 Session 10082 Track down hangs with Xcode and on-device detection en.srt @@ -0,0 +1,1720 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,376 +♪ + +3 +00:00:09,376 --> 00:00:12,079 +Hi everyone, +my name is John Crowson, + +4 +00:00:12,079 --> 00:00:14,414 +and I'm a software engineer +on the Performance Tools team + +5 +00:00:14,414 --> 00:00:15,682 +here at Apple. + +6 +00:00:15,682 --> 00:00:17,451 +In this talk, +I'm excited to introduce you + +7 +00:00:17,451 --> 00:00:20,554 +to several new tools +to track down hangs in your app + +8 +00:00:20,554 --> 00:00:24,258 +with Xcode +and on-device hang detection. + +9 +00:00:24,258 --> 00:00:26,360 +I'll be your guide +as we visit different phases + +10 +00:00:26,360 --> 00:00:29,363 +of your app development process, +and consider which tools + +11 +00:00:29,363 --> 00:00:32,266 +are best suited +to help during each phase. + +12 +00:00:32,266 --> 00:00:35,235 +This talk is broken +into four sections: + +13 +00:00:35,235 --> 00:00:38,405 +First, I will cover, +What are hangs? + +14 +00:00:38,405 --> 00:00:40,774 +Then, I will present tooling +to help you discover + +15 +00:00:40,774 --> 00:00:44,912 +and diagnose hangs +while developing your app, + +16 +00:00:44,912 --> 00:00:49,716 +while beta testing your app, +and after releasing your app. + +17 +00:00:49,716 --> 00:00:52,619 +Let's get started! + +18 +00:00:52,619 --> 00:00:55,455 +I want to tell you about +a new app my team is developing: + +19 +00:00:55,455 --> 00:00:58,025 +Food Truck, which will help +manage food trucks + +20 +00:00:58,025 --> 00:01:00,694 +that specifically sell donuts. + +21 +00:01:00,694 --> 00:01:04,398 +Let me introduce you to some of +the donut types I've created. + +22 +00:01:07,868 --> 00:01:09,836 +Huh, that took +a really long time + +23 +00:01:09,836 --> 00:01:11,838 +to scroll through +my list of donuts. + +24 +00:01:11,838 --> 00:01:15,809 +The app was laggy and would not +respond to any of my touches. + +25 +00:01:15,809 --> 00:01:20,047 +At Apple, we call this period +of unresponsiveness a "hang." + +26 +00:01:20,047 --> 00:01:22,616 +An app's main thread is +responsible for processing + +27 +00:01:22,616 --> 00:01:26,119 +user interactions +and updating the view content. + +28 +00:01:26,119 --> 00:01:29,556 +A hang is reported when the main +thread is busy doing work + +29 +00:01:29,556 --> 00:01:32,759 +or waiting on another +thread or system resource, + +30 +00:01:32,759 --> 00:01:35,295 +causing a delay +in updating the view content + +31 +00:01:35,295 --> 00:01:39,232 +by at least 250 milliseconds. + +32 +00:01:39,232 --> 00:01:41,068 +The main thread +is also unavailable + +33 +00:01:41,068 --> 00:01:46,006 +to process new user interactions +until the hang is resolved. + +34 +00:01:46,006 --> 00:01:51,378 +To the user, it appears +the app is completely stuck. + +35 +00:01:51,378 --> 00:01:52,879 +Creating a responsive app + +36 +00:01:52,879 --> 00:01:56,717 +is critical to providing +a positive user experience. + +37 +00:01:56,717 --> 00:01:59,519 +Consistently unresponsive apps +may result in users + +38 +00:01:59,519 --> 00:02:03,123 +force quitting the app, +switching to a different app, + +39 +00:02:03,123 --> 00:02:05,559 +and in some cases, +even deleting your app + +40 +00:02:05,559 --> 00:02:08,061 +and writing a negative review. + +41 +00:02:08,061 --> 00:02:10,697 +Because of this, +tracking down hangs in your app + +42 +00:02:10,697 --> 00:02:15,202 +is critical to gaining +and maintaining your user base. + +43 +00:02:15,202 --> 00:02:17,504 +Providing a responsive +experience ensures people + +44 +00:02:17,504 --> 00:02:19,973 +will enjoy using your app. + +45 +00:02:21,975 --> 00:02:24,778 +For more information on hangs +and what causes them, + +46 +00:02:24,778 --> 00:02:27,514 +as well as strategies to +eliminate them from your code, + +47 +00:02:27,514 --> 00:02:30,217 +checkout the "Understand and +eliminate hangs from your app" + +48 +00:02:30,217 --> 00:02:32,819 +talk from 2021. + +49 +00:02:32,819 --> 00:02:34,087 +The app-development process + +50 +00:02:34,087 --> 00:02:36,723 +can be broken down +into three phases. + +51 +00:02:36,723 --> 00:02:41,395 +First, developing the latest +app version at desk using Xcode. + +52 +00:02:41,395 --> 00:02:43,997 +Then, testing the app +and collecting feedback + +53 +00:02:43,997 --> 00:02:46,967 +in a beta environment +without Xcode. + +54 +00:02:46,967 --> 00:02:48,835 +For example, +you may have an app version + +55 +00:02:48,835 --> 00:02:50,971 +that's development-signed +on your device + +56 +00:02:50,971 --> 00:02:53,540 +or distributed +through TestFlight. + +57 +00:02:53,540 --> 00:02:57,677 +Finally, releasing the latest +app version on the App Store. + +58 +00:02:57,677 --> 00:02:59,913 +Even for the most +proactive developers, + +59 +00:02:59,913 --> 00:03:02,582 +new hang issues +can arise at any phase, + +60 +00:03:02,582 --> 00:03:05,085 +so it's important that you +know the tools to resolve them + +61 +00:03:05,085 --> 00:03:07,120 +during each one. + +62 +00:03:07,120 --> 00:03:09,856 +Before iOS 16 and Xcode 14, + +63 +00:03:09,856 --> 00:03:12,626 +we offered two tools +that assisted with discovering + +64 +00:03:12,626 --> 00:03:14,995 +and diagnosing +the hangs in your app. + +65 +00:03:14,995 --> 00:03:17,697 +MetricKit is a framework +that supports collecting + +66 +00:03:17,697 --> 00:03:21,201 +nonaggregated hang rate metrics +and diagnostic reports + +67 +00:03:21,201 --> 00:03:26,106 +from individual users on your +beta or public release app. + +68 +00:03:26,106 --> 00:03:29,776 +The Xcode Organizer provides +an aggregated hang rate metric + +69 +00:03:29,776 --> 00:03:32,946 +from users on your +public release app. + +70 +00:03:32,946 --> 00:03:35,782 +There are gaps here, +specifically when developing + +71 +00:03:35,782 --> 00:03:39,052 +your app or when trying +to understand what source code + +72 +00:03:39,052 --> 00:03:43,657 +has caused the public release +hang-rate metric to rise. + +73 +00:03:43,657 --> 00:03:46,226 +In iOS 16 and Xcode 14, + +74 +00:03:46,226 --> 00:03:50,697 +we've been busy introducing +several new tools to help. + +75 +00:03:50,697 --> 00:03:52,799 +Let's introduce +each of them briefly + +76 +00:03:52,799 --> 00:03:55,669 +before we cover them +in more detail. + +77 +00:03:55,669 --> 00:03:57,938 +The Thread Performance +Checker in Xcode + +78 +00:03:57,938 --> 00:04:00,407 +alerts you to hang-causing +threading issues + +79 +00:04:00,407 --> 00:04:04,578 +while debugging your app +without actively tracing it. + +80 +00:04:04,578 --> 00:04:07,914 +Instruments in Xcode +now detects and labels hangs + +81 +00:04:07,914 --> 00:04:10,484 +while tracing your app. + +82 +00:04:10,484 --> 00:04:13,420 +On-device hang detection +provides hang detection + +83 +00:04:13,420 --> 00:04:16,256 +without using Xcode or tracing, + +84 +00:04:16,256 --> 00:04:18,458 +providing real-time +hang notifications + +85 +00:04:18,458 --> 00:04:20,827 +and supporting diagnostics +while using + +86 +00:04:20,827 --> 00:04:24,164 +your development-signed +or TestFlight app. + +87 +00:04:24,164 --> 00:04:26,399 +And finally, +the organizer in Xcode + +88 +00:04:26,399 --> 00:04:29,035 +now supports hang reports, +which provides + +89 +00:04:29,035 --> 00:04:33,373 +aggregated hang diagnostics +from users in the field. + +90 +00:04:33,373 --> 00:04:35,108 +Now that you know +what hangs are + +91 +00:04:35,108 --> 00:04:37,077 +and the different phases +they can arise, + +92 +00:04:37,077 --> 00:04:39,312 +I will cover +how to track down hangs + +93 +00:04:39,312 --> 00:04:43,350 +while developing +an app with Xcode. + +94 +00:04:43,350 --> 00:04:46,620 +In Xcode 14, the new +Thread Performance Checker tool + +95 +00:04:46,620 --> 00:04:49,823 +notifies you in the Xcode +Issue Navigator when it detects + +96 +00:04:49,823 --> 00:04:52,425 +priority inversions +and non-UI work + +97 +00:04:52,425 --> 00:04:54,327 +on the main thread of your app, + +98 +00:04:54,327 --> 00:04:58,298 +both of which +are common causes of hangs. + +99 +00:04:58,298 --> 00:04:59,633 +I have now returned to Xcode + +100 +00:04:59,633 --> 00:05:03,036 +to diagnose the hang I observed +earlier in the Food Truck app + +101 +00:05:03,036 --> 00:05:06,606 +when I was scrolling through +the donuts I've created. + +102 +00:05:06,606 --> 00:05:08,375 +When I built and ran the app, + +103 +00:05:08,375 --> 00:05:10,577 +and repeated +the user interaction, + +104 +00:05:10,577 --> 00:05:13,713 +the Thread Performance Checker +tool alerted me to a hang risk + +105 +00:05:13,713 --> 00:05:16,983 +caused by a priority inversion. + +106 +00:05:16,983 --> 00:05:19,586 +This means a higher priority +thread was attempting + +107 +00:05:19,586 --> 00:05:22,556 +to be synchronized +with a lower priority thread. + +108 +00:05:22,556 --> 00:05:24,791 +This may indicate the hang +we are noticing + +109 +00:05:24,791 --> 00:05:27,861 +is caused by the main thread +waiting on different + +110 +00:05:27,861 --> 00:05:30,063 +lower-priority threads. + +111 +00:05:30,063 --> 00:05:32,599 +To detect priority inversions +and non-UI work + +112 +00:05:32,599 --> 00:05:34,134 +on the main thread of your app, + +113 +00:05:34,134 --> 00:05:36,369 +enable the Thread Performance +Checker tool + +114 +00:05:36,369 --> 00:05:40,607 +from the Diagnostics section +of the appropriate scheme. + +115 +00:05:40,607 --> 00:05:43,243 +The Thread Performance Checker +alert has helped me discover + +116 +00:05:43,243 --> 00:05:45,745 +the potential culprit +of my hang, + +117 +00:05:45,745 --> 00:05:48,114 +but in order to triage further, +I will want to know + +118 +00:05:48,114 --> 00:05:51,551 +what the other thread was doing +during the hang duration. + +119 +00:05:51,551 --> 00:05:56,456 +Let's use another tool +to dive deeper. + +120 +00:05:56,456 --> 00:05:58,992 +The Time Profiler instrument +gives you the ability to know + +121 +00:05:58,992 --> 00:06:01,828 +what each thread +in your app was doing over time + +122 +00:06:01,828 --> 00:06:04,264 +by providing call stacks. + +123 +00:06:04,264 --> 00:06:08,235 +New in Xcode 14, the Time +Profiler also detects hangs + +124 +00:06:08,235 --> 00:06:12,239 +and labels them directly in +the corresponding process track. + +125 +00:06:12,239 --> 00:06:14,741 +In the Food Truck app, +I'll use the Time Profiler + +126 +00:06:14,741 --> 00:06:16,610 +to confirm a hang is occurring + +127 +00:06:16,610 --> 00:06:18,445 +when scrolling through +my donuts, + +128 +00:06:18,445 --> 00:06:21,681 +that it was caused by a priority +inversion on the main thread, + +129 +00:06:21,681 --> 00:06:23,783 +and figure out +what the lower priority threads + +130 +00:06:23,783 --> 00:06:28,255 +were busy doing that caused +the main thread to wait. + +131 +00:06:28,255 --> 00:06:31,324 +I start from the +Product Profile menu in Xcode. + +132 +00:06:31,324 --> 00:06:33,326 +This builds the app for release + +133 +00:06:33,326 --> 00:06:37,464 +and launches Instruments +already setup to target the app. + +134 +00:06:37,464 --> 00:06:40,233 +I launch +the Time Profiler template + +135 +00:06:40,233 --> 00:06:43,436 +and begin recording a trace of +the problematic user interaction + +136 +00:06:43,436 --> 00:06:45,572 +in the Food Truck app. + +137 +00:06:49,542 --> 00:06:51,411 +I see there is a hang +being detected + +138 +00:06:51,411 --> 00:06:53,213 +and labeled in the timeline. + +139 +00:06:53,213 --> 00:06:55,115 +The hang duration +is also specified + +140 +00:06:55,115 --> 00:06:58,718 +to help evaluate +the severity of the issue. + +141 +00:06:58,718 --> 00:07:01,588 +Next, I can triple-click +the hang interval + +142 +00:07:01,588 --> 00:07:04,391 +which creates a time filter +for the duration of the hang + +143 +00:07:04,391 --> 00:07:06,793 +and filters the information +in the detail views + +144 +00:07:06,793 --> 00:07:09,296 +at the bottom +to only events occurring + +145 +00:07:09,296 --> 00:07:11,431 +in this selected time interval. + +146 +00:07:11,431 --> 00:07:14,034 +It also makes it easier +to see what was happening + +147 +00:07:14,034 --> 00:07:17,737 +during this time period +in other tracks. + +148 +00:07:17,737 --> 00:07:19,839 +The first thing I notice +is that the main thread + +149 +00:07:19,839 --> 00:07:23,510 +has barely any CPU usage +during the hang interval, + +150 +00:07:23,510 --> 00:07:25,912 +meaning the main thread +was unresponsive + +151 +00:07:25,912 --> 00:07:27,981 +because it was waiting +on another thread, + +152 +00:07:27,981 --> 00:07:31,284 +not because it was doing +too much work itself. + +153 +00:07:31,284 --> 00:07:33,953 +This aligns with +the Thread Performance Checker's + +154 +00:07:33,953 --> 00:07:37,424 +priority inversion alert +from earlier. + +155 +00:07:37,424 --> 00:07:39,159 +Next, I see a worker thread + +156 +00:07:39,159 --> 00:07:42,295 +that has lots of CPU usage +during the hang. + +157 +00:07:42,295 --> 00:07:46,566 +This is likely the thread that +the main thread is waiting for. + +158 +00:07:46,566 --> 00:07:48,101 +The next step +would be to examine + +159 +00:07:48,101 --> 00:07:50,737 +what the worker thread +was doing during the hang + +160 +00:07:50,737 --> 00:07:53,640 +and resolve +the priority inversion. + +161 +00:07:53,640 --> 00:07:55,442 +Hang detection +and labeling in Instruments + +162 +00:07:55,442 --> 00:07:58,345 +is a great way to surface +any hangs that are encountered + +163 +00:07:58,345 --> 00:08:00,347 +while profiling your app. + +164 +00:08:00,347 --> 00:08:02,816 +It is available by default +in the Time Profiler + +165 +00:08:02,816 --> 00:08:06,019 +and CPU Profiler instruments. + +166 +00:08:06,019 --> 00:08:09,255 +There is also a new standalone +hang tracing instrument + +167 +00:08:09,255 --> 00:08:11,524 +that you can add +to any trace documents + +168 +00:08:11,524 --> 00:08:15,495 +to surface hangs in combination +with other instruments. + +169 +00:08:15,495 --> 00:08:17,897 +In addition to hang detection +and labeling, + +170 +00:08:17,897 --> 00:08:20,900 +it allows you to configure +a hang duration threshold + +171 +00:08:20,900 --> 00:08:24,838 +to find specific periods +of unresponsiveness. + +172 +00:08:24,838 --> 00:08:26,773 +You have now learned +how to use Xcode + +173 +00:08:26,773 --> 00:08:30,043 +to discover +and diagnose hangs at desk. + +174 +00:08:30,043 --> 00:08:32,846 +Even with great testing +coverage during development, + +175 +00:08:32,846 --> 00:08:35,181 +beta and public release +environments are likely + +176 +00:08:35,181 --> 00:08:39,586 +to uncover hangs in code paths +you hadn't considered. + +177 +00:08:39,586 --> 00:08:42,255 +Next, I will introduce +how to track down hangs + +178 +00:08:42,255 --> 00:08:45,525 +once the app is deployed +in a beta environment. + +179 +00:08:45,525 --> 00:08:48,161 +I've now deployed +a build of the Food Truck app + +180 +00:08:48,161 --> 00:08:50,430 +to TestFlight through +App Store Connect + +181 +00:08:50,430 --> 00:08:53,400 +and it is downloaded +on my personal device. + +182 +00:08:53,400 --> 00:08:56,302 +I'll test the app when +selling donuts around town, + +183 +00:08:56,302 --> 00:08:58,505 +including in places where +I don't always have + +184 +00:08:58,505 --> 00:09:00,507 +a strong network connection. + +185 +00:09:00,507 --> 00:09:03,343 +But how do I discover +and diagnose hangs + +186 +00:09:03,343 --> 00:09:08,314 +if my device +is not connected to Xcode? + +187 +00:09:08,314 --> 00:09:11,317 +To continue to monitor for hangs +under these conditions, + +188 +00:09:11,317 --> 00:09:16,122 +iOS 16 introduces on-device hang +detection in Developer settings, + +189 +00:09:16,122 --> 00:09:18,758 +which provides real-time +hang notifications + +190 +00:09:18,758 --> 00:09:21,461 +and supporting diagnostics. + +191 +00:09:21,461 --> 00:09:23,363 +This is available +for development-signed + +192 +00:09:23,363 --> 00:09:25,765 +or TestFlight apps. + +193 +00:09:25,765 --> 00:09:28,435 +It's time to start +selling orders. + +194 +00:09:28,435 --> 00:09:30,703 +When I attempt to open +my current orders, + +195 +00:09:30,703 --> 00:09:33,473 +I receive an on-device +hang detection notification + +196 +00:09:33,473 --> 00:09:35,108 +that my app is hanging, + +197 +00:09:35,108 --> 00:09:38,311 +this time for over +three seconds. + +198 +00:09:38,311 --> 00:09:40,146 +I wonder why +I didn't notice this hang + +199 +00:09:40,146 --> 00:09:42,749 +when I was developing +with Xcode? + +200 +00:09:42,749 --> 00:09:45,051 +I will need to use +the diagnostic information + +201 +00:09:45,051 --> 00:09:49,389 +provided by the on-device hang +detection tool to learn more. + +202 +00:09:49,389 --> 00:09:51,324 +Once your app is setup +for development, + +203 +00:09:51,324 --> 00:09:52,992 +this feature can be +enabled by opening + +204 +00:09:52,992 --> 00:09:55,728 +Settings Developer +Hang Detection, + +205 +00:09:55,728 --> 00:09:58,798 +and toggling the switch. + +206 +00:09:58,798 --> 00:10:00,633 +The Hang Threshold setting +allows you to configure + +207 +00:10:00,633 --> 00:10:03,303 +the minimum duration +of hangs to detect. + +208 +00:10:03,303 --> 00:10:06,539 +The shortest hang threshold +is 250 milliseconds + +209 +00:10:06,539 --> 00:10:10,009 +and can be bumped +to 500 milliseconds or higher. + +210 +00:10:10,009 --> 00:10:12,812 +Long hangs tend to have +a higher user-impact, + +211 +00:10:12,812 --> 00:10:15,849 +but even shorter ones can be +disruptive to the experience, + +212 +00:10:15,849 --> 00:10:17,517 +depending on the context, + +213 +00:10:17,517 --> 00:10:20,286 +especially if they happen +consecutively. + +214 +00:10:20,286 --> 00:10:21,621 +After installing your app, + +215 +00:10:21,621 --> 00:10:24,724 +it will appear in the list +of monitored apps. + +216 +00:10:24,724 --> 00:10:27,293 +The final section +shows a chronological list + +217 +00:10:27,293 --> 00:10:30,763 +of available logs for the hangs +you were alerted to. + +218 +00:10:30,763 --> 00:10:33,199 +Note that these diagnostics +are best-effort + +219 +00:10:33,199 --> 00:10:36,035 +and processed in the background +at a low priority + +220 +00:10:36,035 --> 00:10:39,072 +to minimize +performance overhead. + +221 +00:10:39,072 --> 00:10:41,241 +This means the processing +can take longer, + +222 +00:10:41,241 --> 00:10:44,377 +especially if the system +is busy. + +223 +00:10:44,377 --> 00:10:47,046 +Fortunately, a passive +notification is displayed + +224 +00:10:47,046 --> 00:10:50,283 +when new diagnostics +become available. + +225 +00:10:50,283 --> 00:10:53,620 +Let's examine the diagnostics +for the hang that was detected + +226 +00:10:53,620 --> 00:10:55,588 +when I was opening +the orders in the app + +227 +00:10:55,588 --> 00:10:58,591 +while selling donuts +around town. + +228 +00:10:58,591 --> 00:11:00,760 +I am given both +a text-based hang log + +229 +00:11:00,760 --> 00:11:03,596 +and a tailspin +for the detected hang. + +230 +00:11:03,596 --> 00:11:06,065 +A text-based hang log +has less information, + +231 +00:11:06,065 --> 00:11:08,201 +but can give us +an understanding of the hang + +232 +00:11:08,201 --> 00:11:10,069 +at a glance. + +233 +00:11:10,069 --> 00:11:12,605 +For a deeper investigation, +open the tailspin + +234 +00:11:12,605 --> 00:11:15,642 +in Instruments for viewing +the thread interaction within + +235 +00:11:15,642 --> 00:11:19,412 +your process or identifying +the usage of system resources, + +236 +00:11:19,412 --> 00:11:21,381 +for example. + +237 +00:11:21,381 --> 00:11:23,216 +To start, I'll use +the Share button + +238 +00:11:23,216 --> 00:11:25,652 +to send the text-based hang log +to a Mac, + +239 +00:11:25,652 --> 00:11:30,423 +where I can symbolicate it +and view it on a larger screen. + +240 +00:11:30,423 --> 00:11:33,126 +From viewing an excerpt +of the text-based hang log + +241 +00:11:33,126 --> 00:11:36,396 +I transferred and symbolicated, +I see that during the hang, + +242 +00:11:36,396 --> 00:11:38,531 +I am calling a method +on the main thread + +243 +00:11:38,531 --> 00:11:42,735 +that I know performs synchronous +requests to the network. + +244 +00:11:42,735 --> 00:11:44,804 +When I am testing +the application at my desk + +245 +00:11:44,804 --> 00:11:47,674 +with Xcode and a strong +network connection, + +246 +00:11:47,674 --> 00:11:49,075 +there may not be any delay + +247 +00:11:49,075 --> 00:11:51,644 +when requesting data +from the network. + +248 +00:11:51,644 --> 00:11:53,880 +However, when testing +the app in places + +249 +00:11:53,880 --> 00:11:55,848 +with a limited +network connection, + +250 +00:11:55,848 --> 00:12:00,086 +the request takes longer +and results in a hang. + +251 +00:12:00,086 --> 00:12:02,255 +It is important to test +the beta version of your app + +252 +00:12:02,255 --> 00:12:04,624 +under these different, +real-world conditions, + +253 +00:12:04,624 --> 00:12:07,760 +and on-device hang detection +allows you to monitor for hangs + +254 +00:12:07,760 --> 00:12:10,663 +using just your device. + +255 +00:12:10,663 --> 00:12:13,499 +At this point, I have discovered +and diagnosed hangs + +256 +00:12:13,499 --> 00:12:16,869 +using the available tooling in +the development and beta phases, + +257 +00:12:16,869 --> 00:12:19,305 +and I am ready to make +the Food Truck app available + +258 +00:12:19,305 --> 00:12:22,075 +to customers in the App Store. + +259 +00:12:22,075 --> 00:12:24,711 +I'll now present how to track +down hangs once your app + +260 +00:12:24,711 --> 00:12:26,512 +is in the hands +of your customers, + +261 +00:12:26,512 --> 00:12:28,047 +on various OS versions, + +262 +00:12:28,047 --> 00:12:30,583 +devices, and in other +real-world conditions + +263 +00:12:30,583 --> 00:12:35,855 +you may not have been able to +replicate in your prior testing. + +264 +00:12:35,855 --> 00:12:38,891 +New in Xcode 14, +the Xcode Organizer + +265 +00:12:38,891 --> 00:12:42,762 +supports hang reports to deliver +aggregated hang diagnostics + +266 +00:12:42,762 --> 00:12:46,332 +from customer devices. + +267 +00:12:46,332 --> 00:12:49,035 +The collected data is from +customers which have consented + +268 +00:12:49,035 --> 00:12:51,838 +to share app analytics, +and they contain information + +269 +00:12:51,838 --> 00:12:57,377 +about the main thread stack +trace that led to the hangs. + +270 +00:12:57,377 --> 00:13:00,613 +Hang reports are available +from the left-side navigation + +271 +00:13:00,613 --> 00:13:03,750 +of the Xcode Organizer. + +272 +00:13:03,750 --> 00:13:05,785 +When similar stack traces +are collected, + +273 +00:13:05,785 --> 00:13:09,555 +they are grouped together +to form a single signature. + +274 +00:13:09,555 --> 00:13:11,924 +In the list, +the signatures are shown sorted + +275 +00:13:11,924 --> 00:13:14,427 +based on the user impact. + +276 +00:13:14,427 --> 00:13:18,031 +For each signature, you can +find a few sample hang logs. + +277 +00:13:18,031 --> 00:13:20,933 +Each hang log contains +the main thread stack trace + +278 +00:13:20,933 --> 00:13:24,871 +containing the code responsible +for the hang, the hang duration, + +279 +00:13:24,871 --> 00:13:28,641 +and the device and OS version +from which the log originated. + +280 +00:13:28,641 --> 00:13:32,145 +Each signature also provides +aggregate statistics about + +281 +00:13:32,145 --> 00:13:35,448 +how many hang logs the signature +was responsible for + +282 +00:13:35,448 --> 00:13:39,385 +and a breakdown of those logs +by OS Version and device. + +283 +00:13:39,385 --> 00:13:42,288 +To identify the hangs +most affecting your customers, + +284 +00:13:42,288 --> 00:13:45,692 +pay close attention +to your top signatures. + +285 +00:13:45,692 --> 00:13:49,662 +In this case, the top signature +is responsible for 21 percent + +286 +00:13:49,662 --> 00:13:53,266 +of the hang time +in this release. + +287 +00:13:53,266 --> 00:13:55,168 +Since I've submitted +the app to the App Store + +288 +00:13:55,168 --> 00:13:58,004 +with symbol information, +the hangs report shows me + +289 +00:13:58,004 --> 00:14:02,341 +all of the functions named just +as they are in the source code. + +290 +00:14:02,341 --> 00:14:05,812 +By examining the functions in +this main thread's call stack, + +291 +00:14:05,812 --> 00:14:08,881 +I can infer that this hang was +caused by synchronously reading + +292 +00:14:08,881 --> 00:14:11,617 +a file from disk +on the main thread, + +293 +00:14:11,617 --> 00:14:15,955 +which is known to block +for significant periods of time. + +294 +00:14:15,955 --> 00:14:18,091 +It is important to tackle +the performance problems + +295 +00:14:18,091 --> 00:14:20,093 +that are most affecting +your customers, + +296 +00:14:20,093 --> 00:14:23,663 +and the Organizer is +a great tool to identify them. + +297 +00:14:23,663 --> 00:14:25,898 +Check this data after +each app release + +298 +00:14:25,898 --> 00:14:28,634 +to validate the previous hangs +have been resolved, + +299 +00:14:28,634 --> 00:14:31,738 +and to address new hangs +that may appear. + +300 +00:14:33,740 --> 00:14:36,109 +You can also retrieve +the same hang report data + +301 +00:14:36,109 --> 00:14:39,145 +through the App Store +Connect REST APIs. + +302 +00:14:39,145 --> 00:14:41,147 +This can help you integrate +performance data + +303 +00:14:41,147 --> 00:14:45,118 +with your own systems, +or run additional analysis. + +304 +00:14:45,118 --> 00:14:46,886 +I highly recommend you check out + +305 +00:14:46,886 --> 00:14:50,223 +the "Identify trends with the +Power and Performance API" video + +306 +00:14:50,223 --> 00:14:54,393 +to learn more about using +the Power and Performance APIs. + +307 +00:14:54,393 --> 00:14:58,464 +Added in Xcode 13.2, you can +now receive notifications + +308 +00:14:58,464 --> 00:15:02,535 +when monitoring power and +performance metrics in your app. + +309 +00:15:02,535 --> 00:15:04,570 +I recommend +you enable notifications + +310 +00:15:04,570 --> 00:15:07,306 +by clicking the Notifications +button in the top right + +311 +00:15:07,306 --> 00:15:10,510 +of the Xcode Organizer's +Regressions view. + +312 +00:15:10,510 --> 00:15:14,514 +This will alert you to sudden +rises in your app's hang rate. + +313 +00:15:14,514 --> 00:15:16,649 +Find out more about performance +regressions in the + +314 +00:15:16,649 --> 00:15:19,652 +"Diagnose Power and Performance +Regressions in your app" talk + +315 +00:15:19,652 --> 00:15:21,754 +from 2021. + +316 +00:15:21,754 --> 00:15:24,423 +To improve your experience +in the Xcode Organizer, + +317 +00:15:24,423 --> 00:15:27,460 +I strongly recommend +you build and submit your app + +318 +00:15:27,460 --> 00:15:30,563 +to the App Store +with symbol information. + +319 +00:15:30,563 --> 00:15:33,332 +This symbol information is used +to add function names + +320 +00:15:33,332 --> 00:15:36,569 +from your app to reports +in the Xcode Organizer. + +321 +00:15:36,569 --> 00:15:37,804 +This makes stack traces + +322 +00:15:37,804 --> 00:15:40,373 +significantly easier +to understand. + +323 +00:15:40,373 --> 00:15:42,742 +It also enables +one-click navigation + +324 +00:15:42,742 --> 00:15:44,477 +from a function name +in a stack trace + +325 +00:15:44,477 --> 00:15:47,880 +to the function definition +in the Xcode source editor. + +326 +00:15:47,880 --> 00:15:49,448 +The information extracted + +327 +00:15:49,448 --> 00:15:51,584 +is limited to function +and methods, + +328 +00:15:51,584 --> 00:15:53,686 +names and paths +of source code files, + +329 +00:15:53,686 --> 00:15:55,655 +and line number information. + +330 +00:15:55,655 --> 00:15:58,491 +It is important to note that +the limited symbol information + +331 +00:15:58,491 --> 00:16:02,295 +will be securely stored +and will never be shared. + +332 +00:16:02,295 --> 00:16:03,830 +Fantastic! + +333 +00:16:03,830 --> 00:16:06,032 +You now know how to discover +and diagnose hangs + +334 +00:16:06,032 --> 00:16:09,602 +at each phase +in the development process. + +335 +00:16:09,602 --> 00:16:11,838 +Going forward, +discover, diagnose, + +336 +00:16:11,838 --> 00:16:14,240 +and fix hangs +in the earliest possible phase + +337 +00:16:14,240 --> 00:16:16,075 +of the development process. + +338 +00:16:16,075 --> 00:16:18,211 +Use the tools available to help, + +339 +00:16:18,211 --> 00:16:22,815 +including proactively profiling +new features using Instruments. + +340 +00:16:22,815 --> 00:16:25,284 +Be sure to enable +the Thread Performance Checker + +341 +00:16:25,284 --> 00:16:28,287 +and on-device hang detection. + +342 +00:16:28,287 --> 00:16:31,157 +After each release, +use the Xcode Organizer + +343 +00:16:31,157 --> 00:16:33,826 +to tackle hangs that are most +affecting your customers + +344 +00:16:33,826 --> 00:16:36,362 +and to validate that hangs +from previous app versions + +345 +00:16:36,362 --> 00:16:38,564 +have been resolved. + +346 +00:16:38,564 --> 00:16:41,734 +Enable regression notifications +to be proactively alerted + +347 +00:16:41,734 --> 00:16:43,803 +to regressed +performance metrics, + +348 +00:16:43,803 --> 00:16:48,007 +which can be the first sign of +power and performance problems. + +349 +00:16:48,007 --> 00:16:50,977 +And finally, build and submit +your app to the App Store + +350 +00:16:50,977 --> 00:16:52,478 +with symbol information + +351 +00:16:52,478 --> 00:16:55,815 +to improve the usefulness +of the Xcode Organizer. + +352 +00:16:55,815 --> 00:16:57,250 +By following these steps, + +353 +00:16:57,250 --> 00:16:59,352 +your apps will have +even better performance + +354 +00:16:59,352 --> 00:17:02,121 +to provide the best possible +user experience. + +355 +00:17:02,121 --> 00:17:05,591 +Thanks for hanging out at WWDC! + +356 +00:17:05,591 --> 00:17:09,028 +♪ + diff --git a/eng/2022 Session 10083 Power down - Improve battery consumption en.srt b/eng/2022 Session 10083 Power down - Improve battery consumption en.srt new file mode 100644 index 0000000..159f5be --- /dev/null +++ b/eng/2022 Session 10083 Power down - Improve battery consumption en.srt @@ -0,0 +1,1419 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,645 +Vaibhav Gautam: +Hi, I am Vaibhav Gautam, + +3 +00:00:11,678 --> 00:00:15,482 +and I am an engineer +on the Software Power team. + +4 +00:00:15,516 --> 00:00:17,251 +Apps enrich people's lives, + +5 +00:00:17,284 --> 00:00:22,222 +as they provide various critical +functionalities throughout a day of use. + +6 +00:00:22,256 --> 00:00:26,293 +But this use can come with a cost: +battery drain. + +7 +00:00:26,326 --> 00:00:30,230 +Therefore, it is important +that you take special attention + +8 +00:00:30,264 --> 00:00:32,432 +to improve your app's battery life + +9 +00:00:32,466 --> 00:00:37,437 +so that your users can use their devices +and your apps longer. + +10 +00:00:37,471 --> 00:00:39,573 +We deeply study +different system components + +11 +00:00:39,606 --> 00:00:41,775 +to understand power consumption, + +12 +00:00:41,808 --> 00:00:45,245 +and in this session, +I will go over four key actions + +13 +00:00:45,279 --> 00:00:47,281 +we have identified that you can take + +14 +00:00:47,314 --> 00:00:51,185 +to significantly improve +your app's battery life. + +15 +00:00:51,218 --> 00:00:54,454 +These are Dark Mode in your app, + +16 +00:00:54,488 --> 00:00:56,823 +auditing frame rates, + +17 +00:00:56,857 --> 00:01:00,060 +limiting background time,, + +18 +00:01:00,093 --> 00:01:02,196 +and deferring work in your app. + +19 +00:01:03,797 --> 00:01:05,899 +First, I'll talk about Dark Mode. + +20 +00:01:06,834 --> 00:01:09,703 +Dark Mode was introduced in iOS 13, + +21 +00:01:09,736 --> 00:01:14,942 +and allows someone to configure +their devices in a darker presentation. + +22 +00:01:14,975 --> 00:01:18,946 +You may be familiar with +the personalization benefits of Dark Mode, + +23 +00:01:18,979 --> 00:01:22,649 +but it can also +dramatically affect battery life. + +24 +00:01:24,051 --> 00:01:27,888 +This is because +on devices with OLED displays, + +25 +00:01:27,921 --> 00:01:31,191 +like iPhone 13 and 13 Pro, + +26 +00:01:31,225 --> 00:01:35,762 +darker content consumes +less power than lighter content. + +27 +00:01:35,796 --> 00:01:40,434 +On an OLED screen +each pixel requires individual power, + +28 +00:01:40,467 --> 00:01:45,539 +and for darker colors less power +is required to light up the pixels. + +29 +00:01:45,572 --> 00:01:48,609 +Among all the components in the system, + +30 +00:01:48,642 --> 00:01:52,779 +display is one of the major sources +of power consumption. + +31 +00:01:52,813 --> 00:01:58,051 +In fact, in typical use cases, +the display can be the leading contributor + +32 +00:01:58,085 --> 00:02:00,554 +to battery drain. + +33 +00:02:00,587 --> 00:02:04,725 +You have a way to influence +the power consumption of the display. + +34 +00:02:04,758 --> 00:02:07,961 +And one way is by adopting Dark Mode. + +35 +00:02:08,896 --> 00:02:12,933 +I'll use the Food Truck app +my team has been working on + +36 +00:02:12,966 --> 00:02:14,801 +as an example. + +37 +00:02:14,835 --> 00:02:18,071 +This app has a very prominent +background color + +38 +00:02:18,105 --> 00:02:21,008 +that takes up most of the screen. + +39 +00:02:21,041 --> 00:02:22,910 +When presenting in Dark Mode, + +40 +00:02:22,943 --> 00:02:27,047 +this background color becomes much darker +than its Light Mode version, + +41 +00:02:27,080 --> 00:02:29,583 +contributing heavily to battery savings. + +42 +00:02:29,616 --> 00:02:34,254 +In fact, for cases like this, +we expect up + +43 +00:02:34,288 --> 00:02:38,759 +to a 70% display power savings +as a result. + +44 +00:02:38,792 --> 00:02:41,495 +This is a huge savings! + +45 +00:02:41,528 --> 00:02:43,897 +And when screen brightness is high, + +46 +00:02:43,931 --> 00:02:46,667 +the battery savings are even higher. + +47 +00:02:46,700 --> 00:02:49,603 +For users who prefer Dark Mode, + +48 +00:02:49,636 --> 00:02:53,607 +this is a tremendous opportunity +to save them some battery drain + +49 +00:02:53,640 --> 00:02:56,009 +and it can reduce thermal load too. + +50 +00:02:56,844 --> 00:03:01,215 +To adopt Dark Mode, start by reviewing +how your app currently appears + +51 +00:03:01,248 --> 00:03:03,884 +when Dark Mode is enabled. + +52 +00:03:03,917 --> 00:03:07,154 +Figure out what components +of your app you should update + +53 +00:03:07,187 --> 00:03:10,257 +to fit in better with system UI. + +54 +00:03:10,290 --> 00:03:16,930 +Xcode makes this easy by using the +appearance feature when building your app. + +55 +00:03:18,165 --> 00:03:23,971 +Your app may have hard coded colors +because it only supports Light Mode. + +56 +00:03:24,004 --> 00:03:28,275 +Use dynamic colors in Xcode +to support background color, + +57 +00:03:28,308 --> 00:03:32,713 +images, and text +in Light and Dark Mode. + +58 +00:03:32,746 --> 00:03:36,350 +The system will automatically use +the correct color values, + +59 +00:03:36,383 --> 00:03:38,919 +and will update when the mode changes. + +60 +00:03:40,587 --> 00:03:44,558 +Your app should also support +alternate images for Light Mode + +61 +00:03:44,591 --> 00:03:46,593 +and Dark Mode. + +62 +00:03:46,627 --> 00:03:50,063 +To learn more about +customizing your app for Dark Mode, + +63 +00:03:50,097 --> 00:03:55,469 +check out "Implementing Dark Mode on iOS" +from WWDC 2019. + +64 +00:03:57,004 --> 00:04:00,507 +Now that you know +how to adopt Dark Mode in your app, + +65 +00:04:00,541 --> 00:04:06,113 +you should also think about how to adopt +Dark Mode for your web content. + +66 +00:04:06,146 --> 00:04:09,049 +Safari does not auto-darken +any web content, + +67 +00:04:09,082 --> 00:04:13,320 +so make sure you adopt Dark Mode +for your web content too. + +68 +00:04:14,321 --> 00:04:19,293 +To do so, implement the color-scheme +property in your website's stylesheet. + +69 +00:04:20,894 --> 00:04:25,399 +This enables the default text +and background colors of the web page + +70 +00:04:25,432 --> 00:04:27,768 +to match the current system appearance, + +71 +00:04:27,801 --> 00:04:31,405 +standard form controls, and scrollbars. + +72 +00:04:31,438 --> 00:04:34,374 +Other named system colors +change their look, + +73 +00:04:34,408 --> 00:04:38,011 +switching between Light and Dark Mode. + +74 +00:04:38,045 --> 00:04:40,414 +Start using stylesheet variables + +75 +00:04:40,447 --> 00:04:44,418 +wherever colors are referenced +in your stylesheet. + +76 +00:04:44,451 --> 00:04:47,821 +This allows your web content +to update its colors + +77 +00:04:47,855 --> 00:04:51,925 +as the device switches +between Light and Dark. + +78 +00:04:51,959 --> 00:04:56,997 +Apply the same logic to images +and other media assets on your web pages, + +79 +00:04:57,030 --> 00:05:00,501 +with different variants +for different modes. + +80 +00:05:00,534 --> 00:05:04,371 +To learn more about implementing Dark Mode +for web content, + +81 +00:05:04,404 --> 00:05:07,574 +refer to "Supporting Dark Mode +in Your Web Content" + +82 +00:05:07,608 --> 00:05:11,211 +from WWDC 2019. + +83 +00:05:11,245 --> 00:05:17,184 +Another way to reduce your app's +power usage is to audit your frame rates. + +84 +00:05:17,217 --> 00:05:19,953 +On devices with ProMotion displays, + +85 +00:05:19,987 --> 00:05:23,257 +the refresh rate +can affect power consumption. + +86 +00:05:23,290 --> 00:05:26,760 +Higher refresh rates use higher power. + +87 +00:05:26,793 --> 00:05:29,696 +The frame rates +of animations in your app + +88 +00:05:29,730 --> 00:05:32,499 +determine the display's refresh rate. + +89 +00:05:32,533 --> 00:05:35,669 +Be considerate +of the primary content in your app + +90 +00:05:35,702 --> 00:05:37,971 +and what frame rate it requires, + +91 +00:05:38,005 --> 00:05:42,176 +as not all content in your app +may require a high frame rate. + +92 +00:05:42,209 --> 00:05:45,712 +The display's refresh rate is determined +by the animation + +93 +00:05:45,746 --> 00:05:48,448 +with the highest frame rate in your app. + +94 +00:05:48,482 --> 00:05:52,619 +Your app may have secondary elements +refreshing at a higher rate + +95 +00:05:52,653 --> 00:05:54,454 +than necessary, + +96 +00:05:54,488 --> 00:05:58,792 +causing the app as a whole +to consume more battery than expected. + +97 +00:06:00,427 --> 00:06:03,797 +Here we have the food truck app again. + +98 +00:06:03,830 --> 00:06:08,836 +The primary truck scene at the top +is rendering at 30 frames per second. + +99 +00:06:08,869 --> 00:06:12,439 +Below the truck, +there is a text overlay "Food Truck," + +100 +00:06:12,472 --> 00:06:15,142 +which is horizontally scrolling. + +101 +00:06:15,175 --> 00:06:19,613 +This secondary text is rendering +at 60 frames per second. + +102 +00:06:19,646 --> 00:06:24,351 +As a result, the whole screen is +now rendering at 60 frames per second. + +103 +00:06:24,384 --> 00:06:27,588 +If we change the text animation to 30fps, + +104 +00:06:27,621 --> 00:06:30,691 +then the entire screen can render +at 30fps, + +105 +00:06:30,724 --> 00:06:33,961 +and we could save +up to 20% of the battery drain. + +106 +00:06:33,994 --> 00:06:35,462 +Amazing! + +107 +00:06:36,230 --> 00:06:40,701 +To debug and get more information +about the frame rates in your app, + +108 +00:06:40,734 --> 00:06:42,636 +use Instruments. + +109 +00:06:42,669 --> 00:06:45,572 +Use the CoreAnimation FPS instrument + +110 +00:06:45,606 --> 00:06:51,178 +to see a timeline showing the frame rate +of your app over time. + +111 +00:06:51,211 --> 00:06:54,414 +Start by auditing main user scenarios. + +112 +00:06:54,448 --> 00:06:59,052 +To identify if frames are being rendered +at the rate you expect, + +113 +00:06:59,086 --> 00:07:01,955 +determine if secondary elements +on the screen + +114 +00:07:01,989 --> 00:07:05,626 +have a higher frame rate +than your primary content. + +115 +00:07:06,693 --> 00:07:12,766 +Your app may be using CADisplayLink +offered by CoreAnimation on iOS + +116 +00:07:12,799 --> 00:07:16,603 +to drive custom animations +and custom render loop. + +117 +00:07:16,637 --> 00:07:21,542 +CADisplayLink is a timer synchronized +with the display refresh rate. + +118 +00:07:21,575 --> 00:07:25,145 +It provides necessary timing information +to your app + +119 +00:07:25,179 --> 00:07:30,384 +so that your custom drawing +can be aware of refresh events. + +120 +00:07:30,417 --> 00:07:34,288 +Your app can give hints +to a CADisplayLink object + +121 +00:07:34,321 --> 00:07:37,758 +about the desired screen refresh rate. + +122 +00:07:37,791 --> 00:07:41,461 +Set the preferredFrameRateRange +of a CADisplayLink + +123 +00:07:41,495 --> 00:07:46,466 +and specify your minimum, +maximum, and preferred frame rates. + +124 +00:07:47,868 --> 00:07:51,305 +Display link will then choose +the nearest available frame rate + +125 +00:07:51,338 --> 00:07:55,676 +to your preferred rate, +based on what the system can handle. + +126 +00:07:55,709 --> 00:07:57,978 +If it cannot provide that rate, + +127 +00:07:58,011 --> 00:08:02,382 +it will try to stay +within your specified range. + +128 +00:08:02,416 --> 00:08:04,384 +To configure your display link, + +129 +00:08:04,418 --> 00:08:07,888 +initialize it with a target and selector. + +130 +00:08:07,921 --> 00:08:11,825 +The provided selector is used +to perform custom animation + +131 +00:08:11,859 --> 00:08:16,163 +and calculate which video frame +to display next. + +132 +00:08:16,196 --> 00:08:18,298 +Once your display link is initialized, + +133 +00:08:18,332 --> 00:08:20,667 +set the preferred frame rate range. + +134 +00:08:20,701 --> 00:08:24,404 +In this example, +the preferred rate is 30, + +135 +00:08:24,438 --> 00:08:29,076 +but the range can handle +anything between 10 and 60. + +136 +00:08:29,109 --> 00:08:32,746 +Finally, add the display link +to the current run loop. + +137 +00:08:34,882 --> 00:08:39,820 +Keep refresh rates in mind when thinking +about your app's battery consumption. + +138 +00:08:39,853 --> 00:08:43,891 +This is especially important +for devices with ProMotion displays + +139 +00:08:43,924 --> 00:08:47,361 +that support very dynamic refresh rates. + +140 +00:08:47,394 --> 00:08:50,831 +Monitor your app's frame rates +with Instruments + +141 +00:08:50,864 --> 00:08:55,536 +to discover issues +before you release your app. + +142 +00:08:55,569 --> 00:09:00,240 +Lastly, provide information to the system, +using CADisplayLink + +143 +00:09:00,274 --> 00:09:03,810 +to limit refresh rate +for your app's content. + +144 +00:09:05,279 --> 00:09:08,215 +To learn more +about frame rate optimizations, + +145 +00:09:08,248 --> 00:09:11,852 +refer to "Optimize for variable refresh +rate displays" + +146 +00:09:11,885 --> 00:09:15,455 +from WWDC 2021. + +147 +00:09:15,489 --> 00:09:19,193 +Now, let's talk about +how you can power down your app + +148 +00:09:19,226 --> 00:09:21,295 +when it's running in the background. + +149 +00:09:21,328 --> 00:09:25,365 +When someone switches from your app +to a different app, + +150 +00:09:25,399 --> 00:09:28,902 +your app may rely +on background execution APIs + +151 +00:09:28,936 --> 00:09:31,438 +to continue to run in the background. + +152 +00:09:31,471 --> 00:09:33,106 +While running in the background, + +153 +00:09:33,140 --> 00:09:39,112 +your app may continue to use +common services like location and audio. + +154 +00:09:39,146 --> 00:09:43,550 +Running these services for long durations +will cause battery drain, + +155 +00:09:43,584 --> 00:09:46,887 +so when your app is using +these services in background, + +156 +00:09:46,920 --> 00:09:49,723 +you need to be especially careful! + +157 +00:09:49,756 --> 00:09:56,029 +So let's talk about how to avoid +excess drain when using these modes + +158 +00:09:56,063 --> 00:09:58,866 +Location services keep the device awake + +159 +00:09:58,899 --> 00:10:01,201 +to continuously stream location. + +160 +00:10:01,235 --> 00:10:04,304 +Even though the app is invisible +to the user, + +161 +00:10:04,338 --> 00:10:07,741 +it could be continuously streaming +location in the background, + +162 +00:10:07,774 --> 00:10:10,277 +and cause excess battery drain. + +163 +00:10:10,310 --> 00:10:15,616 +It is important to make sure, that you +are on top of background location session + +164 +00:10:15,649 --> 00:10:17,718 +runtime in your app. + +165 +00:10:17,751 --> 00:10:19,853 +When you no longer need the session, + +166 +00:10:19,887 --> 00:10:23,323 +make sure your app calls +stopUpdatingLocation() + +167 +00:10:23,357 --> 00:10:25,559 +to stop the session. + +168 +00:10:25,592 --> 00:10:28,161 +During different stages +of app development, + +169 +00:10:28,195 --> 00:10:31,765 +you can use different tools +to find out background location usage, + +170 +00:10:31,798 --> 00:10:34,168 +which may not be expected. + +171 +00:10:34,201 --> 00:10:36,403 +While building and testing the app, + +172 +00:10:36,436 --> 00:10:40,541 +Xcode gauges can be used +to find out system energy usage, + +173 +00:10:40,574 --> 00:10:43,210 +as well as background location usage. + +174 +00:10:43,243 --> 00:10:46,079 +When testing your app before release, + +175 +00:10:46,113 --> 00:10:51,685 +you can use MetricKit to collect +diagnostic information for a day of use. + +176 +00:10:51,718 --> 00:10:57,558 +New in iOS 16 is the location usage +in Control Center. + +177 +00:10:57,591 --> 00:11:00,594 +Xcode gauges provide information +about system usage + +178 +00:11:00,627 --> 00:11:05,265 +such as CPU, Network, and Location usage. + +179 +00:11:05,299 --> 00:11:09,069 +Xcode gauges will show a timeline +of the location usage + +180 +00:11:09,102 --> 00:11:11,772 +and energy impact of your app. + +181 +00:11:11,805 --> 00:11:15,442 +Looking at this timeline view +can be a great way to verify + +182 +00:11:15,475 --> 00:11:19,446 +that your location runtime stops +when you expect it to stop. + +183 +00:11:20,314 --> 00:11:24,818 +Another tool is to use Metric Kit +when testing your apps. + +184 +00:11:24,852 --> 00:11:28,055 +Use the +cumulativeBackgroundLocationTime property + +185 +00:11:28,088 --> 00:11:33,727 +to find out how long your app was actively +using location services in background. + +186 +00:11:34,995 --> 00:11:38,365 +New in iOS 16, +users can monitor apps + +187 +00:11:38,398 --> 00:11:43,070 +which are currently using location +services by navigating to Control Center. + +188 +00:11:43,103 --> 00:11:45,906 +They can tap the text at the top + +189 +00:11:45,939 --> 00:11:49,276 +for a detailed view +of apps using location. + +190 +00:11:49,309 --> 00:11:52,112 +Use this to get +an away-from-your-desk insight + +191 +00:11:52,145 --> 00:11:54,548 +into your location runtime. + +192 +00:11:54,581 --> 00:11:57,885 +If you see your app here +and you don't expect to, + +193 +00:11:57,918 --> 00:12:03,090 +it is an indicator that your app has +an active location streaming session. + +194 +00:12:03,123 --> 00:12:07,027 +We can apply the same principles +to audio sessions. + +195 +00:12:07,060 --> 00:12:11,331 +Let's say we have a music app +that is using the audio player + +196 +00:12:11,365 --> 00:12:15,102 +for playing back some file, +and the user stops playback. + +197 +00:12:15,936 --> 00:12:18,739 +The app should not only +pause or stop the sound, + +198 +00:12:18,772 --> 00:12:22,042 +but it should also pause or stop +the Audio Engine + +199 +00:12:22,075 --> 00:12:25,979 +in order to prevent it from running idle. + +200 +00:12:26,013 --> 00:12:28,448 +We recommend using auto shutdown mode + +201 +00:12:28,482 --> 00:12:32,619 +which can be enabled by setting +the autoShutdownEnabled property + +202 +00:12:32,653 --> 00:12:35,556 +of AVAudioEngine class. + +203 +00:12:35,589 --> 00:12:39,726 +In this mode, the audio engine +continuously monitors and detects + +204 +00:12:39,760 --> 00:12:42,496 +if it is idle for a certain duration. + +205 +00:12:42,529 --> 00:12:47,000 +When idle, the engine will shut down +the audio hardware. + +206 +00:12:47,034 --> 00:12:51,205 +And later on, +if any of the sources become active again, + +207 +00:12:51,238 --> 00:12:54,341 +it will start the audio hardware +dynamically. + +208 +00:12:54,374 --> 00:12:57,611 +And all of this happens under the hood. + +209 +00:12:57,644 --> 00:13:02,249 +On watchOS auto shutdown mode +is the enforced behavior. + +210 +00:13:02,282 --> 00:13:05,385 +Make sure to stop +the Audio Engine when not in use + +211 +00:13:05,419 --> 00:13:07,654 +in order to conserve power. + +212 +00:13:07,688 --> 00:13:09,923 +The key to limiting background runtime + +213 +00:13:09,957 --> 00:13:13,894 +is to remember to tell the system +when you are done. + +214 +00:13:13,927 --> 00:13:18,532 +The last action you can take +to improve battery life is to defer work. + +215 +00:13:18,565 --> 00:13:20,467 +Over the course of the day, + +216 +00:13:20,501 --> 00:13:24,838 +your app may process +lots of different tasks and data. + +217 +00:13:24,872 --> 00:13:28,876 +Some of this work needs to occur +immediately to service user actions, + +218 +00:13:28,909 --> 00:13:32,412 +like rendering content on the screen +or playing audio + +219 +00:13:32,446 --> 00:13:35,282 +or a video the user taps on. + +220 +00:13:37,584 --> 00:13:39,853 +Other work like machine learning tasks, + +221 +00:13:39,887 --> 00:13:45,292 +uploading analytics, +or backups is not as time-sensitive. + +222 +00:13:45,325 --> 00:13:49,129 +If we defer this time insensitive work +to a better time– + +223 +00:13:49,162 --> 00:13:51,031 +when the device is charging– + +224 +00:13:51,064 --> 00:13:52,299 +we can save battery + +225 +00:13:52,332 --> 00:13:56,537 +and avoid contending with +the user initiated and interactive work. + +226 +00:13:56,570 --> 00:14:01,008 +Let's talk about three APIs +you can use to accomplish this. + +227 +00:14:01,041 --> 00:14:07,648 +BGProcessingTask is a good choice to +defer tasks which run for long durations. + +228 +00:14:07,681 --> 00:14:10,617 +Discretionary URLSession +is the perfect choice + +229 +00:14:10,651 --> 00:14:13,053 +to schedule deferrable networking. + +230 +00:14:13,086 --> 00:14:16,456 +And leveraging the right push priority +can help servers + +231 +00:14:16,490 --> 00:14:19,059 +deliver pushes at the right time. + +232 +00:14:19,092 --> 00:14:21,995 +Let's go into detail for each. + +233 +00:14:22,029 --> 00:14:24,798 +First is BGProcessingTask. + +234 +00:14:24,831 --> 00:14:29,269 +BGProcessingTask lets you defer +long-running processing tasks + +235 +00:14:29,303 --> 00:14:34,241 +to a better time, +such as when the device is charging. + +236 +00:14:34,274 --> 00:14:37,010 +It's great for tasks +like database cleanup, + +237 +00:14:37,044 --> 00:14:41,415 +creating backups, +and running machine learning training. + +238 +00:14:41,448 --> 00:14:47,754 +To use it, you simply create a request by +using BGProcessingTaskRequest API, + +239 +00:14:47,788 --> 00:14:50,891 +and provide an application identifier. + +240 +00:14:50,924 --> 00:14:52,359 +Then provide more information, + +241 +00:14:52,392 --> 00:14:57,497 +such as if your task needs +external power or network. + +242 +00:14:57,531 --> 00:15:01,335 +Providing more information +will help the system schedule the task + +243 +00:15:01,368 --> 00:15:03,537 +at a better time window. + +244 +00:15:03,570 --> 00:15:08,242 +The system will then launch your app +in the background at an opportune time + +245 +00:15:08,275 --> 00:15:12,613 +and grant several minutes of runtime +to complete the deferrable work. + +246 +00:15:12,646 --> 00:15:16,116 +Next is discretionary URLSession. + +247 +00:15:16,149 --> 00:15:20,254 +Your app may already be using +Background URLSessions + +248 +00:15:20,287 --> 00:15:22,055 +for general networking. + +249 +00:15:22,089 --> 00:15:28,195 +Background URLSessions get even better +when you use the discretionary flag. + +250 +00:15:28,228 --> 00:15:32,833 +URLSessions with discretionary flag +are networking transactions + +251 +00:15:32,866 --> 00:15:35,335 +offloaded to the system + +252 +00:15:35,369 --> 00:15:38,605 +to perform networking +at a more optimal time, + +253 +00:15:38,639 --> 00:15:42,743 +such as when the device is plugged in +and connected to Wi-Fi. + +254 +00:15:42,776 --> 00:15:46,547 +The discretionary flag +is great for non-user initiated + +255 +00:15:46,580 --> 00:15:50,784 +long-running networking, +such as telemetry collection + +256 +00:15:50,817 --> 00:15:54,454 +or downloading +the next episode of a TV show. + +257 +00:15:54,488 --> 00:15:57,090 +And because the networking was offloaded, + +258 +00:15:57,124 --> 00:15:59,927 +it means +your app does not need to be running + +259 +00:15:59,960 --> 00:16:02,362 +while the network transaction completes. + +260 +00:16:03,230 --> 00:16:05,866 +To use discretionary URL sessions, + +261 +00:16:05,899 --> 00:16:09,970 +you simply set up a background URL +session + +262 +00:16:10,003 --> 00:16:14,074 +and set isDiscretionary to true. + +263 +00:16:14,107 --> 00:16:16,276 +You can provide additional information + +264 +00:16:16,310 --> 00:16:20,480 +to help the system schedule the download +at the right time. + +265 +00:16:20,514 --> 00:16:24,451 +Pass in timeout intervals +so that the system does not attempt + +266 +00:16:24,484 --> 00:16:27,120 +to download forever, +causing battery drain. + +267 +00:16:28,722 --> 00:16:31,291 +If you don't want to upload +or download data + +268 +00:16:31,325 --> 00:16:33,694 +until some point later in the future, + +269 +00:16:33,727 --> 00:16:36,864 +pass in an earliest begin date. + +270 +00:16:36,897 --> 00:16:40,033 +Finally, pass in an expected workload size + +271 +00:16:40,067 --> 00:16:43,270 +so that the system +can intelligently load balance + +272 +00:16:43,303 --> 00:16:45,873 +between your various download tasks. + +273 +00:16:48,175 --> 00:16:52,913 +Similar to how you can control +the immediacy of certain operations + +274 +00:16:52,946 --> 00:16:57,417 +with BGProcessingTask +and discretionary URL sessions, + +275 +00:16:57,451 --> 00:17:00,554 +you can influence the immediacy +of push delivery + +276 +00:17:00,587 --> 00:17:03,123 +by using different push priorities. + +277 +00:17:03,156 --> 00:17:07,327 +Push priority determines how urgently +a push needs to be delivered + +278 +00:17:07,361 --> 00:17:08,529 +to the device. + +279 +00:17:08,562 --> 00:17:10,197 +For a high priority push, + +280 +00:17:10,230 --> 00:17:13,867 +the server will send the push immediately +to the device, + +281 +00:17:13,901 --> 00:17:18,071 +potentially waking the device +and causing battery drain. + +282 +00:17:18,105 --> 00:17:19,806 +For low priority pushes, + +283 +00:17:19,840 --> 00:17:24,011 +the server will delay sending the push +until an opportune time, + +284 +00:17:24,044 --> 00:17:26,446 +such as when the device is awake + +285 +00:17:26,480 --> 00:17:29,449 +or a high priority push comes through. + +286 +00:17:29,483 --> 00:17:34,855 +High priority pushes are great for urgent +messages like a severe weather warning. + +287 +00:17:34,888 --> 00:17:39,059 +Low priority pushes are great +for more passive notifications + +288 +00:17:39,092 --> 00:17:42,496 +that aren't urgent and can be deferred. + +289 +00:17:42,529 --> 00:17:46,667 +Leveraging low priority push to delay +the delivery of deferrable messages + +290 +00:17:46,700 --> 00:17:52,840 +will save battery as the device will not +have to wake as frequently from sleep. + +291 +00:17:52,873 --> 00:17:55,108 +To configure low priority pushes, + +292 +00:17:55,142 --> 00:17:59,780 +simply set apns-priority to 5 +in the push payload. + +293 +00:17:59,813 --> 00:18:01,782 +The server will take care of the rest, + +294 +00:18:01,815 --> 00:18:05,686 +and your users will appreciate +the battery life savings. + +295 +00:18:06,553 --> 00:18:10,324 +So let's wrap it up +with some final thoughts and next steps. + +296 +00:18:10,357 --> 00:18:13,427 +Provide an option +of Dark Mode in your app. + +297 +00:18:13,460 --> 00:18:18,799 +If a user chooses Dark Mode, +respecting their intent can save battery. + +298 +00:18:18,832 --> 00:18:20,634 +Review your animations + +299 +00:18:20,667 --> 00:18:25,172 +and hunt for opportunities +to reduce frame rates to what's necessary. + +300 +00:18:25,205 --> 00:18:28,709 +One small animation can have a big impact. + +301 +00:18:28,742 --> 00:18:31,478 +Keep a close eye +on your background runtime + +302 +00:18:31,512 --> 00:18:34,715 +by letting the system know +when you are done. + +303 +00:18:34,748 --> 00:18:38,585 +Finally, consider deferring +long-running background work + +304 +00:18:38,619 --> 00:18:42,756 +to a better time, such as +when the device is connected to a charger. + +305 +00:18:42,789 --> 00:18:44,691 +If you do all this, + +306 +00:18:44,725 --> 00:18:47,594 +then you will have +truly powered down your app. + +307 +00:18:47,628 --> 00:18:49,463 +Thank you so much. + diff --git a/eng/2022 Session 10089 What's new in PDFKit en.srt b/eng/2022 Session 10089 What's new in PDFKit en.srt new file mode 100644 index 0000000..afb185b --- /dev/null +++ b/eng/2022 Session 10089 What's new in PDFKit en.srt @@ -0,0 +1,1130 @@ +1 +00:00:01,468 --> 00:00:07,474 +[spacey music] + +2 +00:00:09,877 --> 00:00:11,912 +Conrad: I'm Conrad Carlen, and today, + +3 +00:00:11,945 --> 00:00:14,481 +I'll be talking about what's new +in PDFKit. + +4 +00:00:14,515 --> 00:00:15,883 +Here's our agenda. + +5 +00:00:15,916 --> 00:00:19,520 +First a quick review of PDFKit, + +6 +00:00:19,553 --> 00:00:21,455 +and then, look at what's new, + +7 +00:00:21,488 --> 00:00:24,558 +including Live text and forms, + +8 +00:00:24,591 --> 00:00:27,628 +a new way to make PDF pages from images, + +9 +00:00:27,661 --> 00:00:30,831 +and, finally, overlay views. + +10 +00:00:30,864 --> 00:00:33,567 +Let's start with a quick refresher +on how PDFKit works. + +11 +00:00:34,501 --> 00:00:38,539 +PDFKit is a full-featured framework +that helps your app view, + +12 +00:00:38,572 --> 00:00:41,041 +edit, and write PDF files. + +13 +00:00:41,074 --> 00:00:44,978 +It's available on iOS, macOS, +and Mac Catalyst, + +14 +00:00:45,012 --> 00:00:49,616 +and it can also be used in SwiftUI +by way of UIViewRepresentable, + +15 +00:00:49,650 --> 00:00:54,888 +a wrapper that lets you integrate +UI views into your app. + +16 +00:00:54,922 --> 00:00:58,592 +PDFKit consists of 4 core classes +that cover most of the functionality + +17 +00:00:58,625 --> 00:01:00,694 +you'll need in your app. + +18 +00:01:00,727 --> 00:01:03,430 +PDFView is the widget +that you include in your layout + +19 +00:01:03,463 --> 00:01:05,966 +using SwiftUI or Interface Builder. + +20 +00:01:05,999 --> 00:01:09,670 +It displays the content of a PDF document +and lets people navigate, + +21 +00:01:09,703 --> 00:01:12,372 +set zoom level, +and copy text to the Pasteboard. + +22 +00:01:13,507 --> 00:01:16,710 +PDFDocument represents a PDF file. + +23 +00:01:16,743 --> 00:01:20,681 +It's not common to subclass PDFDocument, +but you will always use one. + +24 +00:01:20,714 --> 00:01:24,518 +It's the root of the PDF object graph, +or the trunk of the tree. + +25 +00:01:24,551 --> 00:01:26,720 +You can't have a tree +without one of these. + +26 +00:01:28,455 --> 00:01:32,192 +Each document contains one +or more PDFPage objects. + +27 +00:01:32,226 --> 00:01:36,263 +Pages render content and store resources +like fonts and images + +28 +00:01:36,296 --> 00:01:37,631 +that are unique to that page. + +29 +00:01:38,799 --> 00:01:41,969 +At the leaves of our object graph +are PDFAnnotations. + +30 +00:01:42,002 --> 00:01:43,537 +These are optional. + +31 +00:01:43,570 --> 00:01:46,306 +Whereas the content of a PDFPage +is not intended to be edited, + +32 +00:01:46,340 --> 00:01:49,610 +annotations are interactive +by nature and often editable. + +33 +00:01:50,177 --> 00:01:53,947 +Each of these objects will play a role +in what I'll cover today. + +34 +00:01:53,981 --> 00:01:55,949 +To learn more about the fundamentals +of PDFKit, + +35 +00:01:55,983 --> 00:01:59,953 +check out the great presentation +"Introducing PDFKit" in the link below. + +36 +00:02:01,989 --> 00:02:07,160 +Now, let's talk about new features +introduced in iOS 16 and macOS Ventura. + +37 +00:02:08,228 --> 00:02:11,131 +PDFKit now supports Live Text. + +38 +00:02:11,164 --> 00:02:13,000 +It's different than in Photos, +where the text + +39 +00:02:13,033 --> 00:02:16,703 +is often a small amount +that you can tap to copy. + +40 +00:02:16,737 --> 00:02:21,275 +Unlike with photos, with a PDF, +if you see text, it generally is text, + +41 +00:02:21,308 --> 00:02:25,312 +and people expect it to behave as such +without doing anything special. + +42 +00:02:25,345 --> 00:02:27,614 +Now, with Live Text, +you can select and search + +43 +00:02:27,648 --> 00:02:30,517 +text in a PDF document like this one. + +44 +00:02:30,551 --> 00:02:32,886 +It's just a scanned bitmap, +with no text at all. + +45 +00:02:34,188 --> 00:02:36,890 +Of course, PDFs can have many pages. + +46 +00:02:36,924 --> 00:02:40,594 +You wouldn't want to grind through OCR +on all the pages of a PDF document + +47 +00:02:40,627 --> 00:02:41,895 +when you open it, + +48 +00:02:41,929 --> 00:02:45,566 +so PDFKit does it on demand, +as you interact with each page, + +49 +00:02:45,599 --> 00:02:47,534 +if you interact with it. + +50 +00:02:47,568 --> 00:02:51,605 +OCR is done in place, so there's no need +to make a copy of the document. + +51 +00:02:52,472 --> 00:02:55,209 +And, if you choose to save text +for the whole document, + +52 +00:02:55,242 --> 00:02:56,877 +there's an option to do so when saving. + +53 +00:02:58,478 --> 00:03:02,783 +In addition to live text, +PDFKit has improved form handling. + +54 +00:03:02,816 --> 00:03:05,719 +Documents that contain form fields +are automatically recognized, + +55 +00:03:05,752 --> 00:03:08,522 +even if they don't contain +built-in text fields. + +56 +00:03:08,555 --> 00:03:11,191 +You can tab through these text fields +and enter text, + +57 +00:03:11,225 --> 00:03:12,526 +just like you would expect. + +58 +00:03:14,394 --> 00:03:18,365 +Next, let's talk about a new API +for creating PDF pages from images. + +59 +00:03:20,567 --> 00:03:24,605 +In iOS 16 and macOS Ventura, +there's a new, flexible API + +60 +00:03:24,638 --> 00:03:28,041 +that lets your app create PDF pages +using images as inputs. + +61 +00:03:28,809 --> 00:03:32,012 +Your app supplies images using CGImageRef. + +62 +00:03:32,045 --> 00:03:35,549 +PDFKit takes the CGImageRef +that you provide and compresses it + +63 +00:03:35,582 --> 00:03:37,518 +using high-quality JPEG encoding. + +64 +00:03:38,285 --> 00:03:41,455 +Because CGImageRef is +a native data type in CoreGraphics, + +65 +00:03:41,488 --> 00:03:43,657 +additional conversions are unnecessary. + +66 +00:03:44,725 --> 00:03:48,161 +There are several options that help you +handle the most common cases. + +67 +00:03:49,463 --> 00:03:52,599 +MediaBox specifies the size of the page. + +68 +00:03:52,633 --> 00:03:56,937 +You can choose to fit the image exactly, +or choose a paper size, like Letter. + +69 +00:03:58,372 --> 00:04:02,009 +Rotation lets you specify +portrait or landscape orientation. + +70 +00:04:03,210 --> 00:04:04,444 +UpscaleIfSmaller. + +71 +00:04:04,478 --> 00:04:07,080 +By default, +if the image is larger than the MediaBox, + +72 +00:04:07,114 --> 00:04:09,216 +the image is downscaled to fit. + +73 +00:04:09,249 --> 00:04:12,452 +If UpscaleIfSmaller is specified, +that still applies, + +74 +00:04:12,486 --> 00:04:16,456 +but, if the image is small, +it will be upscaled to fill the page. + +75 +00:04:18,292 --> 00:04:21,562 +And now, to answer a question +that many of you have asked-- + +76 +00:04:21,595 --> 00:04:24,865 +"How can I draw on PDF pages +using PencilKit?"-- + +77 +00:04:24,898 --> 00:04:27,301 +the answer is to use an overlay view. + +78 +00:04:28,535 --> 00:04:31,538 +In the past, the only way to do +additional drawing on PDFs + +79 +00:04:31,572 --> 00:04:34,842 +was to subclass PDFPage +and override the drawing method, + +80 +00:04:34,875 --> 00:04:37,678 +or by using custom PDF annotations. + +81 +00:04:37,711 --> 00:04:41,615 +But starting in iOS 16 and macOS Ventura, +it's now possible + +82 +00:04:41,648 --> 00:04:44,952 +to overlay your own view +on top of each PDF page. + +83 +00:04:44,985 --> 00:04:47,988 +This allows your app to create +live, fully interactive views + +84 +00:04:48,021 --> 00:04:49,756 +that appear on top of PDF pages. + +85 +00:04:50,557 --> 00:04:53,961 +Here are the 3 things you need +to know about overlaying views. + +86 +00:04:54,695 --> 00:04:58,265 +First, you'll use a new protocol +to install your overlay view + +87 +00:04:58,298 --> 00:04:59,733 +on a PDF page. + +88 +00:05:00,934 --> 00:05:05,339 +When it comes time to save, you'll need +to incorporate your content into the PDF. + +89 +00:05:06,306 --> 00:05:09,576 +And, speaking of saving, we'll cover +some best practices + +90 +00:05:09,610 --> 00:05:11,311 +when saving a PDF document. + +91 +00:05:13,280 --> 00:05:17,885 +Installing overlay views +on PDF Pages is straightforward. + +92 +00:05:17,918 --> 00:05:21,121 +Because PDFs can contain hundreds, +if not thousands, of pages, + +93 +00:05:21,154 --> 00:05:25,792 +there's no way you want to create views +for all of those pages when opening a PDF. + +94 +00:05:25,826 --> 00:05:28,562 +And what if the user scrolls +back and forth rapidly? + +95 +00:05:28,595 --> 00:05:30,464 +How do you know when to create your views? + +96 +00:05:31,732 --> 00:05:35,869 +Fortunately, PDFKit is already designed +to intelligently prepare content + +97 +00:05:35,903 --> 00:05:38,238 +before people scroll pages into view. + +98 +00:05:38,272 --> 00:05:41,341 +So it knows best +when to ask for an overlay view. + +99 +00:05:41,375 --> 00:05:43,777 +Your app just needs to respond +to its requests + +100 +00:05:43,810 --> 00:05:45,546 +that are made through the new protocol. + +101 +00:05:47,881 --> 00:05:51,685 +PDFPageOverlayViewProvider +is the new protocol. + +102 +00:05:51,718 --> 00:05:56,924 +By the way, PDFKitPlatformView is just +a define of UIView or NSView, + +103 +00:05:56,957 --> 00:05:59,593 +depending on the platform. + +104 +00:05:59,626 --> 00:06:03,730 +The most important method you need +to implement is overlayViewForPage. + +105 +00:06:04,398 --> 00:06:07,835 +Simply provide an instance +of your view, and PDFKit will size it + +106 +00:06:07,868 --> 00:06:10,070 +by applying the appropriate constraints. + +107 +00:06:10,103 --> 00:06:13,173 +It will also rotate it +if the page has a non-zero rotation. + +108 +00:06:14,641 --> 00:06:16,643 +The next 2 methods are optional. + +109 +00:06:16,677 --> 00:06:20,414 +willDisplayOverlayView can be used +to install your own gesture handlers, + +110 +00:06:20,447 --> 00:06:23,917 +or to set up failure relationships +with those of PDFKit. + +111 +00:06:25,619 --> 00:06:30,224 +willEndDisplayingOverlayView is called +when PDFKit is done with your view, + +112 +00:06:30,257 --> 00:06:32,559 +likely because +the page scrolled out of view. + +113 +00:06:32,593 --> 00:06:37,431 +You can release your view here, but there +is another important use for this method. + +114 +00:06:37,464 --> 00:06:40,968 +Assuming your view has some data +to represent what it's drawing, + +115 +00:06:41,001 --> 00:06:44,671 +you can use this method +to get that data and set it aside. + +116 +00:06:44,705 --> 00:06:47,474 +We'll do that in +our example with PencilKit, + +117 +00:06:47,508 --> 00:06:49,977 +but, if your view data lives +somewhere else, + +118 +00:06:50,010 --> 00:06:51,612 +you won't need to implement this. + +119 +00:06:53,380 --> 00:06:57,150 +In the example, this is the class +we're using as the provider. + +120 +00:06:57,184 --> 00:07:01,154 +It implements +the PDFPageOverlayViewProvider protocol. + +121 +00:07:01,188 --> 00:07:05,392 +This is iOS, so the PDFKitPlatformView +is a UIView. + +122 +00:07:05,425 --> 00:07:09,029 +It uses a map +to go from a PDFPage to a UIView. + +123 +00:07:09,930 --> 00:07:12,132 +Here are the placeholder protocol methods. + +124 +00:07:12,165 --> 00:07:15,169 +Next, lets look at the implementations. + +125 +00:07:15,202 --> 00:07:18,238 +overlayViewForPage checks +its pageToView map + +126 +00:07:18,272 --> 00:07:20,908 +to see if it's already made a view +for the given page. + +127 +00:07:20,941 --> 00:07:23,010 +If not, it creates a new view. + +128 +00:07:23,043 --> 00:07:25,679 +In either case, we get the drawing +from the page + +129 +00:07:25,712 --> 00:07:28,182 +and set that on the canvas view. + +130 +00:07:28,215 --> 00:07:32,252 +In these examples, +I'm using a subclass of PDFPage. + +131 +00:07:32,286 --> 00:07:34,521 +All it does is +to add a "drawing" property. + +132 +00:07:37,691 --> 00:07:42,196 +And now, let's focus on the next method: +WillEndDisplayingOverlay. + +133 +00:07:44,264 --> 00:07:46,466 +willEndDisplayingOverlayView is simple. + +134 +00:07:46,500 --> 00:07:51,939 +It gets the drawing from the view +and stores it on our custom page class. + +135 +00:07:51,972 --> 00:07:54,408 +Now that we've done that, +let's see it in action. + +136 +00:07:56,076 --> 00:07:59,713 +Normally, around this time off the year, +I would be in Maine, fishing, + +137 +00:07:59,746 --> 00:08:01,982 +but instead, I'm here at WWDC, + +138 +00:08:02,015 --> 00:08:04,618 +so another person is taking my place +on the trip, + +139 +00:08:04,651 --> 00:08:07,521 +and I'm going to show him +some of my favorite spots. + +140 +00:08:07,554 --> 00:08:09,723 +I'll be doing that with this app, + +141 +00:08:09,756 --> 00:08:12,159 +which uses PencilKit in an overlay view. + +142 +00:08:12,793 --> 00:08:16,129 +This app consists of the code we just saw +and little else. + +143 +00:08:16,163 --> 00:08:21,001 +The entirety of the code to get +overlay views onscreen is about 30 lines! + +144 +00:08:21,034 --> 00:08:24,972 +So, Grand Lake Stream. +This is the dam pool. + +145 +00:08:25,005 --> 00:08:28,675 +There are always lots of fish in there. +It's where most of the action is. + +146 +00:08:28,709 --> 00:08:30,811 +You can get to the dam pool + +147 +00:08:30,844 --> 00:08:33,647 +by taking this trail down +through the woods and then fishing. + +148 +00:08:33,680 --> 00:08:35,449 +You can fish all of that stuff, + +149 +00:08:35,482 --> 00:08:40,053 +or you can take this road, +go over the dam, + +150 +00:08:40,087 --> 00:08:41,522 +and down around through here. + +151 +00:08:41,555 --> 00:08:43,857 +From there, you can fish up into here, + +152 +00:08:43,891 --> 00:08:46,326 +go around the island, down into here, + +153 +00:08:46,360 --> 00:08:49,763 +but whatever you do, +don't go past here. + +154 +00:08:49,796 --> 00:08:52,599 +The water gets deep and fast. + +155 +00:08:52,633 --> 00:08:56,670 +Avoid that and come down here, + +156 +00:08:56,703 --> 00:08:58,572 +to the hatchery. + +157 +00:08:58,605 --> 00:09:02,009 +Walk down beside the hatchery + +158 +00:09:02,042 --> 00:09:04,578 +and enter this pool here. + +159 +00:09:04,611 --> 00:09:06,580 +You can cast all around through here + +160 +00:09:06,613 --> 00:09:09,283 +This is a great spot. +I always catch fish here. + +161 +00:09:10,317 --> 00:09:14,588 +All right, now that we have some marks +on the page, + +162 +00:09:14,621 --> 00:09:16,423 +let's exercise zooming and scrolling. + +163 +00:09:17,891 --> 00:09:19,059 +See how responsive it is? + +164 +00:09:25,299 --> 00:09:26,233 +And there we have it. + +165 +00:09:26,266 --> 00:09:28,235 +Overlay views in PDFKit. + +166 +00:09:28,268 --> 00:09:31,738 +So, now that you have these sketches, +how do you save them? + +167 +00:09:31,772 --> 00:09:34,441 +We'll use the PDFAnnotation class +to do this. + +168 +00:09:34,474 --> 00:09:37,144 +There are 2 things we want to achieve when +saving: + +169 +00:09:37,177 --> 00:09:39,379 +We want to match the onscreen appearance + +170 +00:09:39,413 --> 00:09:40,848 +with high fidelity, + +171 +00:09:40,881 --> 00:09:43,217 +and we want to do round-trip editing. + +172 +00:09:43,250 --> 00:09:47,554 +PDF annotations have some features +that will facilitate this. + +173 +00:09:47,588 --> 00:09:49,590 +PDF annotations can have an +"appearance stream," + +174 +00:09:49,623 --> 00:09:52,526 +which is a stream of PDF drawing commands. + +175 +00:09:52,559 --> 00:09:55,195 +Nearly anything that you can draw +using Quartz2D + +176 +00:09:55,229 --> 00:09:57,731 +can be recorded in an appearance stream. + +177 +00:09:57,764 --> 00:10:02,102 +Anything else can be rendered +into an image, and that can be recorded. + +178 +00:10:02,135 --> 00:10:03,904 +That's how we do it if we're using Metal. + +179 +00:10:04,771 --> 00:10:07,074 +And, since it's recorded as PDF drawing, + +180 +00:10:07,107 --> 00:10:10,043 +it will look identical in Adobe Reader, +Chrome, etc. + +181 +00:10:11,378 --> 00:10:14,915 +PDF annotations are stored +as dictionaries in a PDF document. + +182 +00:10:14,948 --> 00:10:18,685 +That means we can also store custom data +in private key/value pairs. + +183 +00:10:19,653 --> 00:10:22,756 +So let's see what the code looks like. + +184 +00:10:22,789 --> 00:10:26,360 +Start by creating a subclass +of PDFAnnotation. + +185 +00:10:26,393 --> 00:10:28,795 +We do this in order to override +the draw() method. + +186 +00:10:29,596 --> 00:10:32,766 +PDFKit will call this method when it saves +the appearance stream + +187 +00:10:32,799 --> 00:10:34,434 +that I mentioned on the last slide. + +188 +00:10:36,336 --> 00:10:39,640 +To save our document, we override +UIDocument's contents(). + +189 +00:10:39,673 --> 00:10:43,610 +Here is an overview of the function, +for context later. + +190 +00:10:43,644 --> 00:10:47,147 +We loop through all pages +of the PDFDocument. + +191 +00:10:47,181 --> 00:10:49,316 +We'll flesh out the loop next. + +192 +00:10:51,084 --> 00:10:53,253 +We do the following for each page: + +193 +00:10:53,287 --> 00:10:57,090 +create an annotation of our custom class; + +194 +00:10:57,124 --> 00:11:00,527 +encode our drawing into data; + +195 +00:11:00,561 --> 00:11:02,129 +add the data to our annotation. + +196 +00:11:02,162 --> 00:11:06,300 +Next time, when we open this document, +we can use value:forAnnotationKey + +197 +00:11:06,333 --> 00:11:08,235 +to read back the stored drawing data + +198 +00:11:08,268 --> 00:11:10,003 +and put that into our overlay view. + +199 +00:11:11,238 --> 00:11:14,174 +Finally, add the annotation to the page. + +200 +00:11:15,075 --> 00:11:16,944 +Back to our contents() override. + +201 +00:11:16,977 --> 00:11:20,881 +Now that we have added annotations +to our pages, + +202 +00:11:20,914 --> 00:11:25,018 +We use PDFDocument's dataRepresentation() +and return the result. + +203 +00:11:26,153 --> 00:11:28,589 +When your content +is saved as an annotation, + +204 +00:11:28,622 --> 00:11:31,892 +a recipient of the document can move it, +resize it, or delete it. + +205 +00:11:31,925 --> 00:11:33,260 +Often, that's what you want. + +206 +00:11:33,293 --> 00:11:38,232 +But sometimes, you want your annotations +to be "burned in," as part of the page. + +207 +00:11:38,265 --> 00:11:42,970 +There is a new PDFDocumentWriteOption +in iOS 16 and macOS Ventura + +208 +00:11:43,003 --> 00:11:44,404 +that makes this easy. + +209 +00:11:44,438 --> 00:11:47,274 +Just add burnInAnnotationsOption = true + +210 +00:11:47,307 --> 00:11:49,142 +to the save options, and that does it. + +211 +00:11:50,544 --> 00:11:52,779 +Speaking of PDF writing options, +there are a few + +212 +00:11:52,813 --> 00:11:56,717 +that have been made available +in iOS 16 and macOS Ventura. + +213 +00:11:56,750 --> 00:11:58,418 +Let's look at them. + +214 +00:11:58,452 --> 00:12:01,488 +CoreGraphics has always strived +to save images in PDFs + +215 +00:12:01,522 --> 00:12:03,423 +with maximum fidelity, + +216 +00:12:03,457 --> 00:12:07,294 +so images are saved at full resolution, +with lossless compression. + +217 +00:12:07,327 --> 00:12:10,898 +That's great if the PDF will be printed +on a large-format printer. + +218 +00:12:10,931 --> 00:12:13,634 +More likely, though, +it will be displayed on screen, + +219 +00:12:13,667 --> 00:12:18,005 +and all of that high-fidelity image data +will result in a file that's very large. + +220 +00:12:18,038 --> 00:12:20,807 +To address that, I introduce +the next two options. + +221 +00:12:21,942 --> 00:12:25,379 +saveAllImagesAsJPEG +does just what it says. + +222 +00:12:25,412 --> 00:12:28,015 +No matter how the image was created, +it will be saved + +223 +00:12:28,048 --> 00:12:30,150 +with JPEG encoding in the PDF. + +224 +00:12:31,351 --> 00:12:34,154 +optimizeImagesForScreen +will downsample images + +225 +00:12:34,188 --> 00:12:37,057 +to a maximum of HiDPI screen resolution. + +226 +00:12:37,090 --> 00:12:39,159 +These two options may be used together. + +227 +00:12:41,428 --> 00:12:44,665 +createLinearizedPDF will create +a special kind of PDF + +228 +00:12:44,698 --> 00:12:46,600 +that's optimized for the internet. + +229 +00:12:46,633 --> 00:12:50,971 +The PDF format, as originally designed +right before the Internet came along, + +230 +00:12:51,004 --> 00:12:53,207 +is read from the end of the file. + +231 +00:12:53,240 --> 00:12:55,876 +That means the entirety of it needs +to be downloaded first + +232 +00:12:55,909 --> 00:12:57,911 +before anything is displayed. + +233 +00:12:57,945 --> 00:13:01,415 +A linearized PDF has everything +needed to display the first page + +234 +00:13:01,448 --> 00:13:03,450 +at the beginning of the file, + +235 +00:13:03,483 --> 00:13:05,419 +so a web browser can show that quickly + +236 +00:13:05,452 --> 00:13:06,920 +while the rest of it is loaded. + +237 +00:13:08,722 --> 00:13:12,059 +You can pass these options +to PDFDocument's dataRepresentation + +238 +00:13:12,092 --> 00:13:14,394 +or writeToURL methods. + +239 +00:13:14,428 --> 00:13:18,131 +And there we have it. +PDFKit is powerful, yet easy to use, + +240 +00:13:18,165 --> 00:13:21,902 +used by many of your apps today on iOS +and macOS, + +241 +00:13:21,935 --> 00:13:25,239 +and now with new features for iOS 16 and +macOS Ventura. + +242 +00:13:25,272 --> 00:13:27,307 +I'm stoked to see what you do with them! + +243 +00:13:28,642 --> 00:13:30,511 +To learn more, +check out the sessions below + +244 +00:13:30,544 --> 00:13:32,012 +for additional information. + +245 +00:13:32,045 --> 00:13:33,146 +Thank you for watching! + +246 +00:13:33,180 --> 00:13:36,183 +[spacey music] + diff --git a/eng/2022 Session 10090 What's new in TextKit and text views en.srt b/eng/2022 Session 10090 What's new in TextKit and text views en.srt new file mode 100644 index 0000000..0d9aecd --- /dev/null +++ b/eng/2022 Session 10090 What's new in TextKit and text views en.srt @@ -0,0 +1,1648 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:13,714 +- Hi, and welcome to What's New +in TextKit and text views! + +3 +00:00:14,681 --> 00:00:17,384 +I'm Donna Tom, and I'm a TextKit engineer. + +4 +00:00:18,752 --> 00:00:23,924 +In iOS 15 and macOS Monterey, +we introduced TextKit 2, + +5 +00:00:23,957 --> 00:00:27,094 +a powerful new text engine +with improved performance, + +6 +00:00:27,127 --> 00:00:29,263 +correctness, and safety. + +7 +00:00:30,264 --> 00:00:33,100 +TextKit 2's viewport-based layout +architecture + +8 +00:00:33,133 --> 00:00:35,469 +delivers high performance text layout, + +9 +00:00:35,502 --> 00:00:38,705 +especially for documents +with large contents. + +10 +00:00:40,174 --> 00:00:44,711 +TextKit 2 provides a better text +experience for international audiences + +11 +00:00:44,745 --> 00:00:49,049 +by removing the unnecessary complexity +of working with glyphs, + +12 +00:00:49,082 --> 00:00:52,219 +and it has full support +for modern font technologies + +13 +00:00:52,252 --> 00:00:54,621 +like OpenType and Variable Fonts. + +14 +00:00:56,356 --> 00:00:59,359 +And TextKit 2's focus on working with +higher level objects + +15 +00:00:59,393 --> 00:01:01,261 +to control text layout + +16 +00:01:01,295 --> 00:01:04,665 +makes it easier for you +to customize the layout of your text + +17 +00:01:04,698 --> 00:01:07,768 +so you can +build cooler stuff with less code. + +18 +00:01:10,504 --> 00:01:12,773 +Moving forward, the TextKit 2 engine + +19 +00:01:12,806 --> 00:01:18,145 +forms the foundation of text layout +and rendering on all of Apple's platforms. + +20 +00:01:19,713 --> 00:01:24,051 +Future performance enhancements, +updates, and improvements + +21 +00:01:24,084 --> 00:01:27,387 +will all be focused +on the TextKit 2 engine. + +22 +00:01:28,188 --> 00:01:31,258 +By updating to TextKit 2, +your app can get the benefits + +23 +00:01:31,291 --> 00:01:33,560 +of these improvements +as we roll them out. + +24 +00:01:34,394 --> 00:01:36,563 +For an in-depth introduction +to TextKit2, + +25 +00:01:36,597 --> 00:01:39,900 +watch the Meet TextKit2 video. + +26 +00:01:39,933 --> 00:01:42,669 +That video covers the fundamentals + +27 +00:01:42,703 --> 00:01:46,807 +and how to build your own text +layout components using TextKit 2. + +28 +00:01:47,641 --> 00:01:52,579 +In contrast, this video covers +the latest advancements in TextKit 2 + +29 +00:01:52,613 --> 00:01:56,683 +and how to get the most out +of TextKit 2-backed text views. + +30 +00:01:56,717 --> 00:02:02,155 +That's right, I said text views, +plural, because now, + +31 +00:02:02,189 --> 00:02:05,626 +as of iOS 16 and macOS Ventura, + +32 +00:02:05,659 --> 00:02:09,963 +all text controls in UIKit +and AppKit are using TextKit 2, + +33 +00:02:09,997 --> 00:02:12,766 +including UITextView. + +34 +00:02:12,799 --> 00:02:17,938 +So we're using TextKit 2 for layout +and rendering all throughout the system. + +35 +00:02:17,971 --> 00:02:22,876 +It's important that all apps transition +to TextKit 2 as soon as possible, + +36 +00:02:22,910 --> 00:02:27,481 +and we've added a number of tools +to make the transition easier for you. + +37 +00:02:27,514 --> 00:02:31,251 +For many apps, +this can be a zero code transition. + +38 +00:02:31,285 --> 00:02:33,153 +And we expect this to be true + +39 +00:02:33,187 --> 00:02:36,890 +for apps that don't make any special +modifications to their text views. + +40 +00:02:36,924 --> 00:02:39,092 +I'll tell you a bit more about that later. + +41 +00:02:40,294 --> 00:02:44,031 +But first, I'll start by going +over what's new in TextKit 2, + +42 +00:02:44,064 --> 00:02:46,700 +including some +of those tools I just mentioned. + +43 +00:02:48,001 --> 00:02:50,270 +After that, +I'll dive deep into the details + +44 +00:02:50,304 --> 00:02:53,774 +of the TextKit 1 +compatibility mode for text views. + +45 +00:02:54,942 --> 00:02:58,979 +Then I'll finish with a discussion +of modernization strategies you can use + +46 +00:02:59,012 --> 00:03:02,182 +when preparing to transition +your code to TextKit 2. + +47 +00:03:03,550 --> 00:03:07,187 +So first up is what's new in TextKit 2. + +48 +00:03:08,322 --> 00:03:12,059 +TextKit 2 first came to UIKit in iOS 15 + +49 +00:03:12,092 --> 00:03:14,928 +where UITextField was upgraded to use it. + +50 +00:03:15,529 --> 00:03:20,234 +In iOS 16, the UIKit transition +to TextKit 2 is complete, + +51 +00:03:20,267 --> 00:03:26,340 +with all text controls using TextKit 2 +by default, including UITextView. + +52 +00:03:26,373 --> 00:03:30,010 +Most text views will be +automatically opted in to TextKit 2, + +53 +00:03:30,043 --> 00:03:32,846 +requiring zero adoption on your part. + +54 +00:03:32,880 --> 00:03:36,884 +There are just a few situations +where text views might not get opted in, + +55 +00:03:36,917 --> 00:03:40,521 +and I'll cover that in the +compatibility segment of this video. + +56 +00:03:42,155 --> 00:03:44,458 +And it's a similar story for AppKit. + +57 +00:03:44,491 --> 00:03:49,329 +TextKit 2 first came to AppKit +in macOS Big Sur. + +58 +00:03:49,363 --> 00:03:53,867 +In macOS Monterey, NSTextField +was upgraded to use it by default, + +59 +00:03:53,901 --> 00:03:57,671 +and it was available for +NSTextView by opting in. + +60 +00:03:58,939 --> 00:04:03,944 +In macOS Ventura, all text controls +use TextKit 2 by default. + +61 +00:04:03,977 --> 00:04:09,583 +Just like UITextView, most NSTextViews +get automatic opt in to TextKit 2 + +62 +00:04:09,616 --> 00:04:11,985 +and require zero adoption on your part. + +63 +00:04:14,221 --> 00:04:17,858 +TextEdit, which is a thin wrapper +around NSTextView, + +64 +00:04:17,891 --> 00:04:22,029 +uses TextKit 2 everywhere +in macOS Ventura. + +65 +00:04:22,062 --> 00:04:27,768 +TextEdit has been using TextKit 2 +in plain text mode since macOS Big Sur. + +66 +00:04:27,801 --> 00:04:32,272 +In macOS Ventura, +rich text mode uses TextKit 2 as well. + +67 +00:04:34,708 --> 00:04:37,277 +Since TextKit 2 is the new standard, + +68 +00:04:37,311 --> 00:04:42,683 +we've added some convenience constructors +for both UITextView and NSTextView. + +69 +00:04:42,716 --> 00:04:46,453 +Use these new constructors +to choose at initialization time + +70 +00:04:46,486 --> 00:04:48,689 +which text engine to use. + +71 +00:04:49,957 --> 00:04:52,326 +To create a text view that uses TextKit 2, + +72 +00:04:52,359 --> 00:04:55,095 +use the new constructor and pass true + +73 +00:04:55,128 --> 00:04:58,332 +for the "UsingTextLayoutManager" +parameter. + +74 +00:04:58,365 --> 00:05:04,371 +If the text view needs to use TextKit 1 +for compatibility, pass "false" instead. + +75 +00:05:07,207 --> 00:05:12,045 +And there's a new Text Layout option for +text views created in Interface Builder. + +76 +00:05:12,079 --> 00:05:16,149 +This new option gives you control +of which layout system to use + +77 +00:05:16,183 --> 00:05:18,519 +on a per-instance basis. + +78 +00:05:18,552 --> 00:05:22,756 +The default setting is the system default, +which is TextKit 2. + +79 +00:05:23,757 --> 00:05:28,762 +You can also choose to explicitly +use TextKit 2 or TextKit 1. + +80 +00:05:30,664 --> 00:05:34,468 +TextKit 2 now supports +non-simple text containers. + +81 +00:05:34,501 --> 00:05:39,606 +Non-simple text containers +may have holes or gaps in them. + +82 +00:05:39,640 --> 00:05:43,810 +This allows text to wrap around +images or other inline content. + +83 +00:05:45,145 --> 00:05:47,414 +To create a non-simple text container, + +84 +00:05:47,447 --> 00:05:51,552 +use the exclusionPaths property +on NSTextContainer + +85 +00:05:51,585 --> 00:05:55,656 +to define the areas +where text should not be laid out. + +86 +00:05:55,689 --> 00:06:00,460 +For an example of how to do this, check +out the TextKitAndTextView sample code + +87 +00:06:00,494 --> 00:06:03,497 +from the resources +associated with this video. + +88 +00:06:03,530 --> 00:06:07,534 +You can find the related example +on the "exclusion path" tab. + +89 +00:06:10,137 --> 00:06:13,207 +We've enhanced the line breaking engine +in TextKit 2 + +90 +00:06:13,240 --> 00:06:17,477 +to choose more even line breaks +for justified paragraphs. + +91 +00:06:17,511 --> 00:06:22,382 +This is a subtle change that's easier +to notice on longer paragraphs of text. + +92 +00:06:23,684 --> 00:06:27,688 +Here we have two versions of the +same text, laid out in the same area. + +93 +00:06:28,689 --> 00:06:32,526 +Notice the stretched out lines +and large interword spacing + +94 +00:06:32,559 --> 00:06:34,528 +with traditional line breaking. + +95 +00:06:36,029 --> 00:06:37,698 +There's much less of that going on + +96 +00:06:37,731 --> 00:06:40,033 +with the new even line breaking. + +97 +00:06:40,067 --> 00:06:42,503 +This makes the text easier to read, + +98 +00:06:42,536 --> 00:06:45,272 +and you get it for free with TextKit 2. + +99 +00:06:45,305 --> 00:06:47,107 +There's no adoption required. + +100 +00:06:48,642 --> 00:06:53,680 +And finally, we've added text list support +in TextKit 2 for all platforms. + +101 +00:06:53,714 --> 00:06:56,750 +With text lists, +you can programmatically create + +102 +00:06:56,783 --> 00:07:00,387 +numbered or bulleted lists +for display in a text view. + +103 +00:07:00,420 --> 00:07:06,994 +TextKit 2 uses NSTextList to represent +text lists, just like TextKit 1. + +104 +00:07:07,027 --> 00:07:10,197 +NSTextList +used to be available in AppKit only, + +105 +00:07:10,230 --> 00:07:13,767 +but in iOS 16, +it's available in UIKit too. + +106 +00:07:15,669 --> 00:07:20,774 +Use NSTextList together with +NSmutableParagraphStyle to specify + +107 +00:07:20,807 --> 00:07:25,712 +that a paragraph in your text storage +should be formatted as a list for display. + +108 +00:07:25,746 --> 00:07:29,283 +The text view is responsible +for picking up these attributes + +109 +00:07:29,316 --> 00:07:34,221 +from the text storage and reformatting +the paragraph content to look like a list. + +110 +00:07:35,856 --> 00:07:41,795 +While NSTextList itself isn't new, +there are a few new TextKit 2 additions. + +111 +00:07:41,828 --> 00:07:44,164 +Since lists can have nested items, + +112 +00:07:44,198 --> 00:07:47,267 +it's natural to represent them +as a tree structure. + +113 +00:07:47,301 --> 00:07:52,973 +In TextKit 2, we've enhanced NSTextElement +to support structuring them as trees + +114 +00:07:53,006 --> 00:07:56,443 +with properties for accessing +child and parent elements. + +115 +00:07:58,011 --> 00:08:02,616 +And we've added a new element subclass +called NSTextListElement. + +116 +00:08:02,649 --> 00:08:07,487 +When the content manager comes across +a NSTextList in the text content, + +117 +00:08:07,521 --> 00:08:12,192 +it will generate NSTextListElements +to represent the items in the list. + +118 +00:08:14,394 --> 00:08:18,565 +To get a more in-depth view of +how to create text lists and add items, + +119 +00:08:18,599 --> 00:08:22,302 +refer to the +TextKitAndTextView sample code. + +120 +00:08:22,336 --> 00:08:25,205 +You can find the related example +on the "list" tab. + +121 +00:08:27,474 --> 00:08:29,443 +And while +you're exploring the sample code, + +122 +00:08:29,476 --> 00:08:31,879 +don't miss the text attachment example + +123 +00:08:31,912 --> 00:08:37,084 +which shows how to use the text attachment +view provider APIs in TextKit 2. + +124 +00:08:38,852 --> 00:08:43,223 +These APIs let you use a UI +or NSView as the text attachment, + +125 +00:08:43,257 --> 00:08:47,294 +and events can be handled directly +by the attachment view. + +126 +00:08:47,327 --> 00:08:50,998 +This makes event handling with +text attachments a whole lot easier, + +127 +00:08:51,031 --> 00:08:54,268 +and it's only possible with TextKit 2. + +128 +00:08:54,301 --> 00:08:57,838 +All right, +that's it for what's new in TextKit 2. + +129 +00:08:57,871 --> 00:09:03,443 +Next, I'll get into the details of +the TextKit 1 compatibility mode. + +130 +00:09:03,477 --> 00:09:08,015 +Since TextKit 2 is such a radical +departure from the design of TextKit 1, + +131 +00:09:08,048 --> 00:09:12,553 +we understand that full adoption of +TextKit 2 may take some time + +132 +00:09:12,586 --> 00:09:17,157 +for apps that are heavily invested +in the TextKit 1 architecture. + +133 +00:09:17,191 --> 00:09:21,662 +We want these apps to continue to work +well until the transition can be made, + +134 +00:09:21,695 --> 00:09:25,399 +and that's why we've added +a special TextKit 1 compatibility mode + +135 +00:09:25,432 --> 00:09:28,468 +for UITextView and NSTextView. + +136 +00:09:28,502 --> 00:09:32,172 +When you explicitly +call an NSLayoutManager API, + +137 +00:09:32,206 --> 00:09:35,943 +the text view replaces its +NSTextLayoutManager + +138 +00:09:35,976 --> 00:09:41,215 +with an NSLayoutManager +and reconfigures itself to use TextKit 1. + +139 +00:09:41,248 --> 00:09:45,052 +This can also happen +if the text view encounters attributes + +140 +00:09:45,085 --> 00:09:50,557 +not yet supported by TextKit 2, +such as tables, or when printing. + +141 +00:09:52,826 --> 00:09:57,865 +If you encounter an unexpected runtime +fallback to TextKit 1 in UITextView, + +142 +00:09:57,898 --> 00:10:01,802 +check the log for a message +warning about the switch. + +143 +00:10:01,835 --> 00:10:07,274 +Set a breakpoint on the symbol underscore +UITextViewEnablingCompatibilityMode + +144 +00:10:07,307 --> 00:10:11,044 +to capture a stack trace +and other useful debugging information. + +145 +00:10:13,046 --> 00:10:15,549 +For NSTextView, +you can get more information + +146 +00:10:15,582 --> 00:10:19,953 +about unexpected runtime fallbacks +by subscribing to the willSwitch + +147 +00:10:19,987 --> 00:10:23,223 +or didSwitchToNSLayoutManager +notifications. + +148 +00:10:25,292 --> 00:10:27,694 +If you must drop back to TextKit 1, + +149 +00:10:27,728 --> 00:10:31,064 +it's best to opt out +at initialization time + +150 +00:10:31,098 --> 00:10:34,501 +with programmatically +initialized text views. + +151 +00:10:34,535 --> 00:10:39,039 +Do this by using your own text container +and a TextKit 1 layout manager. + +152 +00:10:40,707 --> 00:10:43,610 +Another option is to use the +new convenience constructor + +153 +00:10:43,644 --> 00:10:49,316 +to initialize a TextKit 1 text view +and pass false as the parameter. + +154 +00:10:49,349 --> 00:10:52,152 +This will +make your text view use TextKit 1. + +155 +00:10:54,354 --> 00:10:57,057 +And a third option +is to use Interface Builder + +156 +00:10:57,090 --> 00:11:01,862 +and set the new Text Layout option +to TextKit 1 on your text view. + +157 +00:11:03,230 --> 00:11:05,432 +Here's something to watch out for. + +158 +00:11:05,465 --> 00:11:08,836 +If you're swapping out your +text container's layout manager + +159 +00:11:08,869 --> 00:11:11,672 +during or after initialization, + +160 +00:11:11,705 --> 00:11:16,643 +then your text view +will fall back to TextKit 1 as designed. + +161 +00:11:16,677 --> 00:11:21,381 +It's inefficient to create all the TextKit +2 objects during initialization + +162 +00:11:21,415 --> 00:11:24,351 +only to throw them away moments later. + +163 +00:11:24,384 --> 00:11:28,922 +There's also potential user side effects, +depending on the timing. + +164 +00:11:28,956 --> 00:11:32,559 +If it happens during typing, +the text view could lose its focus + +165 +00:11:32,593 --> 00:11:38,899 +and interrupt input, requiring the text +view to be selected again to resume. + +166 +00:11:38,932 --> 00:11:44,705 +Avoid this by opting the text view out +at initialization time. + +167 +00:11:44,738 --> 00:11:47,708 +Now that you know +all about compatibility mode, + +168 +00:11:47,741 --> 00:11:52,212 +it's time to talk about how to avoid it +altogether by modernizing your app + +169 +00:11:52,246 --> 00:11:54,281 +and adopting TextKit 2. + +170 +00:11:54,314 --> 00:11:57,718 +And there's one really important +thing I want you to remember. + +171 +00:11:59,553 --> 00:12:03,023 +There can be only one +layout manager per text view. + +172 +00:12:03,056 --> 00:12:06,393 +A text view can't have both +an NSTextLayoutManager + +173 +00:12:06,426 --> 00:12:10,030 +and an NSLayoutManager at the same time. + +174 +00:12:11,698 --> 00:12:17,004 +Once a text view switches to TextKit 1, +there's no automatic way of going back. + +175 +00:12:17,037 --> 00:12:20,073 +The process of switching +layout systems is expensive, + +176 +00:12:20,107 --> 00:12:25,345 +and you will lose any UI state that +was present at the time of the switch. + +177 +00:12:25,379 --> 00:12:28,348 +So for optimum performance and usability, + +178 +00:12:28,382 --> 00:12:33,554 +the system will never switch a text view +back to TextKit 2 from TextKit 1. + +179 +00:12:33,587 --> 00:12:35,389 +It's a one-way operation. + +180 +00:12:36,790 --> 00:12:42,429 +This means it's really important +to avoid compatibility mode. + +181 +00:12:42,462 --> 00:12:46,934 +And there's a few different reasons +a text view will enter compatibility mode. + +182 +00:12:46,967 --> 00:12:50,838 +The number one reason for a +text view to enter compatibility mode + +183 +00:12:50,871 --> 00:12:55,342 +is accessing the text view's +layoutManager property. + +184 +00:12:55,375 --> 00:12:57,778 +The other reasons are much less common. + +185 +00:12:59,746 --> 00:13:01,181 +So an important strategy + +186 +00:13:01,215 --> 00:13:05,219 +is to avoid accessing +the text view's layout manager property. + +187 +00:13:05,252 --> 00:13:10,991 +Also avoid accessing the layout manager +through the text view's text container. + +188 +00:13:11,024 --> 00:13:14,194 +Audit your code +for uses of these properties, + +189 +00:13:14,228 --> 00:13:18,432 +and remove them or replace them +with TextKit 2 equivalents. + +190 +00:13:20,334 --> 00:13:24,705 +If you're deploying your app to older +OS versions that don't have TextKit 2, + +191 +00:13:24,738 --> 00:13:28,809 +you might not be able to entirely +remove your layoutManager code. + +192 +00:13:29,977 --> 00:13:35,048 +In that case, you should first check +for the text view's NSTextLayoutManager. + +193 +00:13:36,216 --> 00:13:39,086 +Put your TextKit 2 code in the if clause + +194 +00:13:39,119 --> 00:13:42,389 +and put the TextKit 1 code +in the else clause, + +195 +00:13:42,422 --> 00:13:45,392 +including the layoutManager access. + +196 +00:13:45,425 --> 00:13:50,531 +This way, the TextKit 1 code only runs +when TextKit 2 is not available, + +197 +00:13:50,564 --> 00:13:55,469 +and your layoutManager query won't cause +an unintended fallback to TextKit 1. + +198 +00:13:57,771 --> 00:14:00,641 +If you've followed all this +advice and you still encounter + +199 +00:14:00,674 --> 00:14:04,411 +an unexpected fallback to TextKit 1 +coming from the system, + +200 +00:14:04,444 --> 00:14:09,483 +that's our problem, so please report +the issue with Feedback Assistant. + +201 +00:14:09,516 --> 00:14:12,519 +Include a capture of the stack trace +at the time of fallback, + +202 +00:14:12,553 --> 00:14:13,921 +which you can get from breaking on + +203 +00:14:13,954 --> 00:14:17,791 +underscore +UITextViewEnablingCompatibilityMode + +204 +00:14:17,824 --> 00:14:22,229 +in UIKit, or +willSwitchToNSLayoutManagerNotification + +205 +00:14:22,262 --> 00:14:24,231 +in AppKit. + +206 +00:14:25,632 --> 00:14:29,336 +Okay, now I'll get into the specifics +of updating code + +207 +00:14:29,369 --> 00:14:33,974 +related to TextKit 1 types, +starting with NSLayoutManager. + +208 +00:14:34,007 --> 00:14:37,444 +Once you've audited your code +for NSLayoutManager queries, + +209 +00:14:37,477 --> 00:14:41,982 +you'll need to figure out the TextKit 2 +equivalents with NSTextLayoutManager. + +210 +00:14:44,418 --> 00:14:49,122 +Some layout manager APIs have +similar names between TextKit 1 and 2, + +211 +00:14:49,156 --> 00:14:51,892 +and the substitutions are straightforward. + +212 +00:14:51,925 --> 00:14:53,894 +Here's a few examples. + +213 +00:14:53,927 --> 00:14:59,032 +In TextKit 1, you call usedRect(for: +textContainer) on NSLayoutManager + +214 +00:14:59,066 --> 00:15:04,805 +to get the bounding rectangle for +the text inside a text container. + +215 +00:15:04,838 --> 00:15:09,643 +In TextKit 2, you get this from the +usageBoundsForTextContainer property + +216 +00:15:09,676 --> 00:15:11,712 +on NSTextLayoutManager. + +217 +00:15:12,779 --> 00:15:16,316 +In TextKit 1, +we used the name "temporary attributes" + +218 +00:15:16,350 --> 00:15:21,455 +for attributes that affected +only the rendering, and not the layout. + +219 +00:15:21,488 --> 00:15:25,792 +In TextKit 2, we more accurately +call those "rendering attributes." + +220 +00:15:27,861 --> 00:15:30,030 +But there are some TextKit 1 APIs that + +221 +00:15:30,063 --> 00:15:33,634 +have no direct equivalents in TextKit 2. + +222 +00:15:33,667 --> 00:15:36,436 +To understand why, +you need to understand + +223 +00:15:36,470 --> 00:15:39,640 +there is no correct +character to glyph mapping + +224 +00:15:39,673 --> 00:15:42,743 +for many words in Indic scripts +like Kannada. + +225 +00:15:43,644 --> 00:15:46,079 +In these scripts, glyphs can be split up, + +226 +00:15:46,113 --> 00:15:49,349 +reordered, recombined, or even deleted. + +227 +00:15:50,751 --> 00:15:53,787 +The glyph-based APIs +on NSLayoutManager + +228 +00:15:53,820 --> 00:15:58,392 +assume you can directly associate +a contiguous range of characters + +229 +00:15:58,425 --> 00:16:03,630 +with a contiguous range of glyphs, +and that's just not true for all scripts. + +230 +00:16:03,664 --> 00:16:07,134 +Using these APIs can result +in broken layout and rendering + +231 +00:16:07,167 --> 00:16:09,937 +for text written in scripts like Kannada. + +232 +00:16:09,970 --> 00:16:14,508 +That's why there are +zero glyph APIs in TextKit 2. + +233 +00:16:14,541 --> 00:16:19,413 +You can't just substitute a single +TextKit 2 API for a TextKit 1 glyph API. + +234 +00:16:19,446 --> 00:16:23,016 +Replacing these APIs +requires a different approach. + +235 +00:16:24,818 --> 00:16:28,422 +So here's how to update glyph-based code. + +236 +00:16:28,455 --> 00:16:33,060 +The first step is to identify +which glyph APIs you're using. + +237 +00:16:33,093 --> 00:16:36,597 +Next, look at how you're using those APIs + +238 +00:16:36,630 --> 00:16:40,701 +and define what you are trying to do +at a high level. + +239 +00:16:40,734 --> 00:16:44,171 +Glyph-based code is very low level, +and there are many details + +240 +00:16:44,204 --> 00:16:47,107 +that aren't relevant +to your high-level task. + +241 +00:16:48,342 --> 00:16:50,110 +Once you've defined the high-level task, + +242 +00:16:50,143 --> 00:16:54,448 +then examine the structures +available to you in TextKit 2 + +243 +00:16:54,481 --> 00:16:58,952 +such as layout fragments, +line fragments, and text selections. + +244 +00:16:58,986 --> 00:17:02,155 +These can help you accomplish your task. + +245 +00:17:02,189 --> 00:17:06,126 +For example, consider this TextKit 1 code. + +246 +00:17:06,159 --> 00:17:09,296 +There's two glyph APIs used here: + +247 +00:17:09,329 --> 00:17:10,964 +numberOfGlyphs, + +248 +00:17:10,998 --> 00:17:15,836 +and lineFragmentRect(forGlyphAt: index) . + +249 +00:17:15,869 --> 00:17:19,973 +This TextKit 1 code is iterating +over all of the glyphs in the document + +250 +00:17:20,007 --> 00:17:22,743 +and counting the line fragment rects. + +251 +00:17:22,776 --> 00:17:27,581 +The high-level task is counting the number +of lines of wrapped text + +252 +00:17:27,614 --> 00:17:29,816 +in the text view. + +253 +00:17:29,850 --> 00:17:32,953 +Since this code is working with +line fragment rects, + +254 +00:17:32,986 --> 00:17:37,124 +the TextKit 2 structures to use +are NSTextLineFragment + +255 +00:17:37,157 --> 00:17:39,693 +and NSTextLayoutFragment. + +256 +00:17:40,594 --> 00:17:43,964 +And here's the code +rewritten to use TextKit 2. + +257 +00:17:43,997 --> 00:17:46,400 +Instead of iterating over glyphs, + +258 +00:17:46,433 --> 00:17:49,837 +it's enumerating the text layout +fragments in the document + +259 +00:17:49,870 --> 00:17:53,740 +and supplying a closure that counts +all of the text line fragments + +260 +00:17:53,774 --> 00:17:55,909 +within each layout fragment. + +261 +00:17:57,578 --> 00:18:01,648 +Keep that example in mind when +updating your own code for TextKit 2. + +262 +00:18:01,682 --> 00:18:06,587 +Now I'm going to shift gears and discuss +updating code that's based on NSRange. + +263 +00:18:09,756 --> 00:18:14,561 +TextKit 1 uses NSRange +to index into text content, + +264 +00:18:14,595 --> 00:18:19,099 +and NSRange +is a linear index into a string. + +265 +00:18:19,132 --> 00:18:22,703 +For the text "Hello TextKit 2!" +exclamation point, + +266 +00:18:22,736 --> 00:18:27,674 +the NSRange that represents +the "TextKit 2 exclamation point" + +267 +00:18:27,708 --> 00:18:31,278 +is location 6 and length 10, + +268 +00:18:31,311 --> 00:18:36,016 +since it begins at the 6th character +and it's 10 characters long. + +269 +00:18:36,049 --> 00:18:38,785 +This linear model is easy to understand, + +270 +00:18:38,819 --> 00:18:41,755 +and it works great +for indexing into strings. + +271 +00:18:43,557 --> 00:18:47,394 +But the linear model doesn't +work for indexing into any content + +272 +00:18:47,427 --> 00:18:50,297 +that has more structure than a string. + +273 +00:18:50,330 --> 00:18:52,833 +Here's an example. + +274 +00:18:52,866 --> 00:18:56,336 +HTML documents +are represented as a tree structure, + +275 +00:18:56,370 --> 00:18:58,872 +where each tag is a node in the tree. + +276 +00:18:58,906 --> 00:19:01,341 +If our Hello TextKit 2! text + +277 +00:19:01,375 --> 00:19:03,710 +is part of an HTML document, + +278 +00:19:03,744 --> 00:19:08,215 +there's no way for our NSRange to tell us +that the text is inside the span tag, + +279 +00:19:08,248 --> 00:19:10,851 +nested 3 levels deep. + +280 +00:19:10,884 --> 00:19:14,488 +The linear model isn't expressive +enough to store that information, + +281 +00:19:14,521 --> 00:19:19,426 +so we can't use it to index +into a nested structure like this one. + +282 +00:19:19,459 --> 00:19:26,466 +This is why TextKit 2 added new types for +representing ranges in the text content. + +283 +00:19:26,500 --> 00:19:31,271 +NSTextLocation is an object +that represents a single location + +284 +00:19:31,305 --> 00:19:33,574 +inside the text content. + +285 +00:19:33,607 --> 00:19:38,245 +NSTextRange +consists of a start and end location. + +286 +00:19:38,278 --> 00:19:42,449 +The end location +is excluded from the range. + +287 +00:19:42,482 --> 00:19:46,954 +These new types can represent the +nested structure of this HTML document + +288 +00:19:46,987 --> 00:19:52,426 +by defining a location as the +DOM node plus a character offset. + +289 +00:19:53,594 --> 00:19:58,532 +Since NSTextLocation is a protocol, +any custom object can be a location + +290 +00:19:58,565 --> 00:20:03,437 +as long as it implements +the NSTextLocation protocol methods. + +291 +00:20:03,470 --> 00:20:07,407 +This is crucial infrastructure for working +with different types of backing stores + +292 +00:20:07,441 --> 00:20:10,344 +that support structured data +in their models. + +293 +00:20:11,912 --> 00:20:15,983 +But text views are built on +NSAttributedString backing stores + +294 +00:20:16,016 --> 00:20:19,019 +that don't have this structure, +and we can't change that + +295 +00:20:19,052 --> 00:20:23,123 +without breaking lots of apps, +including yours. + +296 +00:20:23,156 --> 00:20:26,994 +So you'll continue to use NSRange +when using text view APIs + +297 +00:20:27,027 --> 00:20:30,731 +like selectedRange +or scrollRangeToVisible. + +298 +00:20:30,764 --> 00:20:34,968 +And you'll need to convert between +NSRange and NSTextRange + +299 +00:20:35,002 --> 00:20:39,473 +when communicating with the TextKit 2 +layout manager or content manager. + +300 +00:20:40,941 --> 00:20:44,778 +To convert a text view's NSRange +to an NSTextRange, + +301 +00:20:44,811 --> 00:20:49,716 +define the location as the integer index +into the attributed string. + +302 +00:20:50,851 --> 00:20:55,589 +Use the NSRange location as +the start location for NSTextRange. + +303 +00:20:56,790 --> 00:21:02,596 +Use the NSRange location plus the length +as the end location of the NSTextRange. + +304 +00:21:02,629 --> 00:21:07,234 +Conceptually, that's how to map +from NSRange to NSTextRange. + +305 +00:21:09,069 --> 00:21:11,705 +In practice, +the code looks a little different + +306 +00:21:11,738 --> 00:21:15,442 +because NSTextLocations must be objects. + +307 +00:21:17,110 --> 00:21:20,547 +You need to go through the content +manager to compute the locations. + +308 +00:21:21,815 --> 00:21:24,785 +For the start location, +ask the content manager + +309 +00:21:24,818 --> 00:21:27,354 +for the location +of the beginning of the document, + +310 +00:21:27,387 --> 00:21:31,091 +then offset it by the NSRange's location. + +311 +00:21:31,124 --> 00:21:36,530 +Then offset the start location by the +NSRange's length to get the end location. + +312 +00:21:38,899 --> 00:21:42,369 +To go in the other direction, +use the text content manager + +313 +00:21:42,402 --> 00:21:44,204 +to get two different offsets. + +314 +00:21:45,772 --> 00:21:49,676 +The NSRange's location is the offset +between the beginning of the document + +315 +00:21:49,710 --> 00:21:53,347 +and the NSTextRange's location. + +316 +00:21:53,380 --> 00:21:57,651 +And the NSRange's length is the offset +between the start and end locations + +317 +00:21:57,684 --> 00:21:59,686 +of the NSTextRange. + +318 +00:22:01,455 --> 00:22:06,193 +UITextViews and UITextFields +conform to the UITextInput protocol, + +319 +00:22:06,226 --> 00:22:09,963 +which uses UITextPosition and range. + +320 +00:22:09,997 --> 00:22:12,399 +Most of the time, +you won't need to convert + +321 +00:22:12,432 --> 00:22:16,003 +a UITextRange +directly to an NSTextRange + +322 +00:22:16,036 --> 00:22:19,540 +when using UITextView or UITextField. + +323 +00:22:19,573 --> 00:22:22,009 +But if you do, use the integer offsets + +324 +00:22:22,042 --> 00:22:24,611 +as the intermediary +between the two range types. + +325 +00:22:26,713 --> 00:22:30,851 +On the other hand, if you're using +a custom view with UITextInput, + +326 +00:22:30,884 --> 00:22:34,288 +you have direct control +over the UITextPosition + +327 +00:22:34,321 --> 00:22:38,592 +and UITextRange subclasses +used with your view. + +328 +00:22:38,625 --> 00:22:41,495 +You can make your +UITextPosition subclass + +329 +00:22:41,528 --> 00:22:45,399 +conform to NSTextLocation, +implement the required method, + +330 +00:22:45,432 --> 00:22:49,703 +and use your subclass +to create NSTextRanges directly. + +331 +00:22:51,138 --> 00:22:56,076 +Finally, here's a reminder to avoid +reusing UITextPosition objects + +332 +00:22:56,109 --> 00:23:02,416 +across different views, even if the +content in both views is similar. + +333 +00:23:02,449 --> 00:23:07,721 +A UITextPosition is only valid +for the view used to create it. + +334 +00:23:09,223 --> 00:23:12,526 +All right, now you've got lots of +strategies at your disposal + +335 +00:23:12,559 --> 00:23:14,561 +for modernizing your code. + +336 +00:23:14,595 --> 00:23:17,297 +Apply these strategies, +and your app will be ready + +337 +00:23:17,331 --> 00:23:19,666 +to reap the benefits of TextKit 2. + +338 +00:23:21,568 --> 00:23:24,805 +And that's what's new +in TextKit and text views. + +339 +00:23:24,838 --> 00:23:27,875 +I covered a lot of great improvements +in TextKit 2 + +340 +00:23:27,908 --> 00:23:30,344 +and shared some strategies +for updating your apps + +341 +00:23:30,377 --> 00:23:33,814 +while maintaining compatibility +for older OS versions. + +342 +00:23:33,847 --> 00:23:39,052 +Use TextKit 2 in your apps today to take +full advantage of the new improvements. + +343 +00:23:39,086 --> 00:23:40,954 +Check your text views to make sure + +344 +00:23:40,988 --> 00:23:44,391 +they aren't unintentionally +falling back to TextKit 1. + +345 +00:23:44,424 --> 00:23:47,127 +And finally, +employ the modernization strategies + +346 +00:23:47,160 --> 00:23:50,464 +to get your app on TextKit 2. + +347 +00:23:50,497 --> 00:23:55,169 +We can't wait to read what you'll create +with TextKit 2 and text views. + +348 +00:23:55,202 --> 00:23:57,171 +Thanks for watching! + diff --git a/eng/2022 Session 10092 Meet passkeys en.srt b/eng/2022 Session 10092 Meet passkeys en.srt new file mode 100644 index 0000000..4367e98 --- /dev/null +++ b/eng/2022 Session 10092 Meet passkeys en.srt @@ -0,0 +1,2974 @@ +1 +00:00:00,067 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,776 +♪ + +3 +00:00:09,776 --> 00:00:11,912 +Hi, I'm Garrett, + +4 +00:00:11,912 --> 00:00:15,148 +an engineer on the +Authentication Experience team. + +5 +00:00:15,148 --> 00:00:19,386 +And in this video, I'm excited +to talk about passkeys, + +6 +00:00:19,386 --> 00:00:23,490 +a next-generation +authentication technology. + +7 +00:00:23,490 --> 00:00:25,359 +But first, I need to talk about + +8 +00:00:25,359 --> 00:00:28,896 +today's authentication +technology: passwords. + +9 +00:00:28,896 --> 00:00:30,397 +You're probably used +to signing in + +10 +00:00:30,397 --> 00:00:33,600 +to nearly every app +and website with them. + +11 +00:00:33,600 --> 00:00:37,137 +Passwords are really hard +to use securely. + +12 +00:00:37,137 --> 00:00:39,940 +All of us know +we're supposed to create strong, + +13 +00:00:39,940 --> 00:00:42,709 +unique passwords +for every account, + +14 +00:00:42,709 --> 00:00:45,779 +but not many people actually do. + +15 +00:00:45,779 --> 00:00:48,782 +As you're designing +your apps and websites, + +16 +00:00:48,782 --> 00:00:52,019 +there's this constant tradeoff +between keeping accounts secure + +17 +00:00:52,019 --> 00:00:55,322 +and designing a good experience. + +18 +00:00:55,322 --> 00:00:59,760 +And even if your apps and +websites do everything right, + +19 +00:00:59,760 --> 00:01:02,996 +issues like phishing +and password reuse + +20 +00:01:02,996 --> 00:01:06,433 +can still lead +to account compromise. + +21 +00:01:06,433 --> 00:01:09,436 +In macOS Monterey +and iOS 15, + +22 +00:01:09,436 --> 00:01:12,606 +we announced a developer +preview of the solution -- + +23 +00:01:12,606 --> 00:01:16,310 +passkeys -- and got so much +great feedback. + +24 +00:01:16,310 --> 00:01:19,313 +In macOS Ventura and iOS 16, + +25 +00:01:19,313 --> 00:01:23,083 +we're excited to make passkeys +available to everyone. + +26 +00:01:23,083 --> 00:01:26,086 +Now is the time to adopt them. + +27 +00:01:26,086 --> 00:01:27,387 +With passkeys, + +28 +00:01:27,387 --> 00:01:31,358 +not only is the user experience +better than a password, + +29 +00:01:31,358 --> 00:01:35,329 +but also entire categories +of security problems, + +30 +00:01:35,329 --> 00:01:37,564 +like weak and reused +credentials, + +31 +00:01:37,564 --> 00:01:42,769 +credential leaks, and phishing, +are just not possible anymore. + +32 +00:01:42,769 --> 00:01:45,272 +And they're so easy to use. + +33 +00:01:45,272 --> 00:01:47,975 +Let me show you. + +34 +00:01:47,975 --> 00:01:51,378 +Let's start with our favorite +demo app, Shiny. + +35 +00:01:51,378 --> 00:01:54,781 +This app lets me see +one cute picture a day + +36 +00:01:54,781 --> 00:01:59,386 +and has a typical +password-based sign-in flow. + +37 +00:01:59,386 --> 00:02:01,755 +I can tap in the +user name field + +38 +00:02:01,755 --> 00:02:04,958 +and see an AutoFill suggestion +for my account. + +39 +00:02:04,958 --> 00:02:08,428 +I'll select that, sign in. + +40 +00:02:08,428 --> 00:02:10,664 +Then, I can fill in my password. + +41 +00:02:12,933 --> 00:02:14,968 +Then, I wait around +for a little bit + +42 +00:02:14,968 --> 00:02:18,005 +until an SMS message comes in +with my one-time code. + +43 +00:02:20,374 --> 00:02:22,142 +There it is. + +44 +00:02:22,142 --> 00:02:25,212 +And eventually, I'm signed in. + +45 +00:02:25,212 --> 00:02:28,348 +It took a few steps, +but with the help of AutoFill + +46 +00:02:28,348 --> 00:02:31,051 +and my password manager, +I was able to get there. + +47 +00:02:33,320 --> 00:02:37,524 +Now that I'm signed in, I'll add +a passkey to this account. + +48 +00:02:37,524 --> 00:02:41,228 +Account Management, Add passkey. + +49 +00:02:41,228 --> 00:02:45,332 +Here, I get the system sheet +for creating a passkey. + +50 +00:02:45,332 --> 00:02:47,000 +Continue. + +51 +00:02:47,000 --> 00:02:48,802 +Done! + +52 +00:02:48,802 --> 00:02:52,172 +In just a few taps, +my device has generated + +53 +00:02:52,172 --> 00:02:56,109 +a unique, cryptographically +strong key pair for my account + +54 +00:02:56,109 --> 00:02:58,879 +and stored it +in my iCloud Keychain, + +55 +00:02:58,879 --> 00:03:02,049 +so it will sync and work +across all of my devices + +56 +00:03:02,049 --> 00:03:04,584 +running macOS Ventura +and iOS 16. + +57 +00:03:06,987 --> 00:03:08,688 +Now that I have a passkey, + +58 +00:03:08,688 --> 00:03:11,458 +let me show you +how easy it is to use. + +59 +00:03:11,458 --> 00:03:13,627 +I'm going to sign out, + +60 +00:03:13,627 --> 00:03:18,398 +and I'm back at the same +sign-in form I used earlier. + +61 +00:03:18,398 --> 00:03:21,902 +I'm going to focus +the user name field like before. + +62 +00:03:21,902 --> 00:03:25,572 +Now that I have a passkey +saved for my account, + +63 +00:03:25,572 --> 00:03:28,208 +it shows up +in the QuickType bar. + +64 +00:03:28,208 --> 00:03:31,978 +All I have to do is tap it +and I'm signed in. + +65 +00:03:31,978 --> 00:03:34,247 +One step. + +66 +00:03:34,247 --> 00:03:35,882 +When saving the passkey, + +67 +00:03:35,882 --> 00:03:38,652 +I didn't have to come up +with a new password + +68 +00:03:38,652 --> 00:03:42,689 +or try to satisfy +any complexity requirements. + +69 +00:03:42,689 --> 00:03:45,926 +Each passkey +is generated by the system + +70 +00:03:45,926 --> 00:03:49,663 +and guaranteed to be strong +and only ever used + +71 +00:03:49,663 --> 00:03:52,332 +for a single account. + +72 +00:03:52,332 --> 00:03:54,968 +And when I'm signing in with it, +it can be shown + +73 +00:03:54,968 --> 00:03:57,938 +in the existing sign-in flows +I'm used to, + +74 +00:03:57,938 --> 00:04:00,774 +and it's a single tap to use. + +75 +00:04:00,774 --> 00:04:04,511 +And the system will take care +of only letting me use it + +76 +00:04:04,511 --> 00:04:07,013 +in the correct app or website, + +77 +00:04:07,013 --> 00:04:10,784 +with strong built-in +phishing resistance. + +78 +00:04:10,784 --> 00:04:14,354 +Of course, passkeys +work on the web too. + +79 +00:04:14,354 --> 00:04:17,924 +Here I am on Shiny's +website in Safari. + +80 +00:04:17,924 --> 00:04:21,828 +Just like on my phone, when I +focus the user name field, + +81 +00:04:21,828 --> 00:04:25,298 +my passkey is already there +and ready to use, + +82 +00:04:25,298 --> 00:04:27,334 +thanks to iCloud Keychain. + +83 +00:04:27,334 --> 00:04:30,837 +All I have to do is Touch ID +and I'm signed in. + +84 +00:04:30,837 --> 00:04:32,973 +That's it. + +85 +00:04:32,973 --> 00:04:37,010 +Apple's passkey implementation +is built on open standards. + +86 +00:04:37,010 --> 00:04:39,279 +We've been working +with other platform vendors + +87 +00:04:39,279 --> 00:04:42,182 +within the FIDO Alliance +to make sure + +88 +00:04:42,182 --> 00:04:45,886 +that passkey implementations +are compatible cross-platform + +89 +00:04:45,886 --> 00:04:49,923 +and can work on as many +devices as possible. + +90 +00:04:49,923 --> 00:04:52,893 +After upgrading my account +to use a passkey, + +91 +00:04:52,893 --> 00:04:57,297 +I'm still able to sign in to it +on my friend's PC. + +92 +00:04:57,297 --> 00:05:00,600 +Of course, my friend's PC +doesn't have the passkey + +93 +00:05:00,600 --> 00:05:05,805 +saved locally, but I can still +type my user name here. + +94 +00:05:05,805 --> 00:05:07,307 +When I press Sign In, + +95 +00:05:07,307 --> 00:05:11,077 +I get a sheet that's offering +to let me use my phone. + +96 +00:05:11,077 --> 00:05:14,581 +Then I get a QR code. +Let me scan that. + +97 +00:05:16,883 --> 00:05:19,419 +My phone recognizes +that this QR code + +98 +00:05:19,419 --> 00:05:22,656 +is for signing in +with a passkey. + +99 +00:05:22,656 --> 00:05:24,457 +When I select this option, + +100 +00:05:24,457 --> 00:05:29,062 +my phone and the browser +securely connect to each other. + +101 +00:05:29,062 --> 00:05:34,668 +Now I can just Continue, +and I'm signed in. + +102 +00:05:34,668 --> 00:05:37,270 +This cross-platform +sign-in experience + +103 +00:05:37,270 --> 00:05:39,973 +is a first-class system feature + +104 +00:05:39,973 --> 00:05:43,176 +that's part of the standards +behind passkeys. + +105 +00:05:43,176 --> 00:05:46,680 +On the surface, +it appears incredibly simple, + +106 +00:05:46,680 --> 00:05:49,516 +but this is not just a QR code. + +107 +00:05:49,516 --> 00:05:50,684 +Behind the scenes, + +108 +00:05:50,684 --> 00:05:53,587 +the devices are performing +a local key agreement, + +109 +00:05:53,587 --> 00:05:55,288 +proving proximity, + +110 +00:05:55,288 --> 00:05:59,259 +establishing an end-to-end +encrypted communication channel, + +111 +00:05:59,259 --> 00:06:02,796 +all to let you sign in +in a way that's easy + +112 +00:06:02,796 --> 00:06:07,033 +but maintains the strong +phishing resistance of passkeys. + +113 +00:06:07,033 --> 00:06:10,136 +It works great for allowing me +to sign in securely + +114 +00:06:10,136 --> 00:06:14,541 +to my account on any device. + +115 +00:06:14,541 --> 00:06:17,877 +Another important feature +for a password replacement + +116 +00:06:17,877 --> 00:06:22,382 +is the ability to share accounts +between two or more people. + +117 +00:06:22,382 --> 00:06:24,551 +To share a passkey +with someone else, + +118 +00:06:24,551 --> 00:06:26,386 +I can use AirDrop. + +119 +00:06:28,521 --> 00:06:32,626 +My partner and I also have an +account for Shiny that we share, + +120 +00:06:32,626 --> 00:06:35,962 +which I've already upgraded +to use a passkey. + +121 +00:06:35,962 --> 00:06:39,833 +With a passkey, the credential +isn't something I could type, + +122 +00:06:39,833 --> 00:06:43,169 +but I'm still able to share it +with people I trust. + +123 +00:06:43,169 --> 00:06:46,006 +On my phone, I'll open up +the account details. + +124 +00:06:48,942 --> 00:06:50,644 +Here are all of my accounts, + +125 +00:06:50,644 --> 00:06:54,648 +which use both passwords +and passkeys. + +126 +00:06:54,648 --> 00:06:58,852 +I can tap on our shared account +to pull up more details. + +127 +00:06:58,852 --> 00:07:02,455 +Here, I can get some information +about my saved passkey + +128 +00:07:02,455 --> 00:07:04,824 +or add a note to this account. + +129 +00:07:04,824 --> 00:07:08,428 +I can also share my passkey. + +130 +00:07:08,428 --> 00:07:09,996 +There's my partner's phone. + +131 +00:07:09,996 --> 00:07:11,698 +I'll go ahead and select that. + +132 +00:07:14,868 --> 00:07:17,604 +Now my partner +has the passkey too. + +133 +00:07:19,839 --> 00:07:25,512 +And that's how easy it is +to use passkeys everywhere. + +134 +00:07:25,512 --> 00:07:29,316 +I've just gone over the +experience of using a passkey. + +135 +00:07:29,316 --> 00:07:32,118 +Next, I'll talk about +what a passkey is + +136 +00:07:32,118 --> 00:07:36,323 +and some interface guidelines +when using them. + +137 +00:07:36,323 --> 00:07:39,359 +Then I'll show you +how you can integrate passkeys + +138 +00:07:39,359 --> 00:07:42,929 +into your existing sign-in flows +in your apps and websites, + +139 +00:07:42,929 --> 00:07:45,832 +by taking advantage of AutoFill, + +140 +00:07:45,832 --> 00:07:48,201 +followed by some +additional options + +141 +00:07:48,201 --> 00:07:52,539 +that can further streamline +your sign-in process. + +142 +00:07:52,539 --> 00:07:55,475 +After that, I'll go into +some more technical detail + +143 +00:07:55,475 --> 00:07:58,278 +about how passkeys work + +144 +00:07:58,278 --> 00:08:04,284 +and finally, discuss passkeys +and multifactor authentication. + +145 +00:08:04,284 --> 00:08:07,921 +First up, +designing for passkeys. + +146 +00:08:07,921 --> 00:08:10,490 +When it comes to talking +about passkeys, + +147 +00:08:10,490 --> 00:08:15,862 +first and foremost, passkeys are +replacements for passwords. + +148 +00:08:15,862 --> 00:08:19,399 +They're faster to sign in with, +easier to use, + +149 +00:08:19,399 --> 00:08:23,370 +and so much more secure. + +150 +00:08:23,370 --> 00:08:26,473 +Here are some guidelines +for how to refer to passkeys + +151 +00:08:26,473 --> 00:08:29,342 +in your apps and websites. + +152 +00:08:29,342 --> 00:08:33,246 +"Passkey" is a generic, +user-visible term. + +153 +00:08:33,246 --> 00:08:37,217 +This video focuses on +Apple's implementation, + +154 +00:08:37,217 --> 00:08:40,220 +but as I've just shown you, +other major platforms + +155 +00:08:40,220 --> 00:08:45,091 +have already started building +their own support for passkeys. + +156 +00:08:45,091 --> 00:08:49,195 +"Passkey" is also a common noun, +like "password." + +157 +00:08:49,195 --> 00:08:51,965 +In English, this means +it's lowercase + +158 +00:08:51,965 --> 00:08:54,968 +and gets pluralized +like "password" would. + +159 +00:08:54,968 --> 00:08:59,439 +I have a passkey for my account, +and I can go to Settings + +160 +00:08:59,439 --> 00:09:03,610 +to view all of my accounts +with passkeys. + +161 +00:09:03,610 --> 00:09:07,580 +On Apple platforms, +you can also use the SF Symbol + +162 +00:09:07,580 --> 00:09:11,484 +person.key.badge +and the .fill variant + +163 +00:09:11,484 --> 00:09:15,722 +to provide iconography +consistent with the system. + +164 +00:09:15,722 --> 00:09:18,124 +When it comes to offering +passkeys in your apps + +165 +00:09:18,124 --> 00:09:24,030 +and websites, you don't need +to design entire new interfaces. + +166 +00:09:24,030 --> 00:09:27,534 +The user name field is +the center point for most app + +167 +00:09:27,534 --> 00:09:30,136 +and website sign-in today. + +168 +00:09:30,136 --> 00:09:32,639 +Pretty much everyone +knows how to use it, + +169 +00:09:32,639 --> 00:09:36,376 +and many apps and websites +already take advantage of it + +170 +00:09:36,376 --> 00:09:40,613 +to tailor the sign-in experience +per account. + +171 +00:09:40,613 --> 00:09:45,084 +Now the user name field +has another big feature. + +172 +00:09:45,084 --> 00:09:47,554 +While passkeys +bring new paradigms + +173 +00:09:47,554 --> 00:09:51,858 +for how signing in works, the +transition away from passwords + +174 +00:09:51,858 --> 00:09:55,061 +needs to be smooth +and easy too. + +175 +00:09:55,061 --> 00:09:58,531 +You can now present passkeys +using AutoFill + +176 +00:09:58,531 --> 00:10:01,901 +as a first-class feature, +letting you drop them + +177 +00:10:01,901 --> 00:10:05,238 +right in to your existing +sign-in flows, + +178 +00:10:05,238 --> 00:10:10,276 +in an interface that's familiar +and people know how to use. + +179 +00:10:10,276 --> 00:10:12,145 +Presenting passkeys +with AutoFill + +180 +00:10:12,145 --> 00:10:15,281 +is the primary way +you should use them. + +181 +00:10:15,281 --> 00:10:18,284 +For more advanced uses though, +Apple platforms + +182 +00:10:18,284 --> 00:10:21,921 +also have a wide range +of additional UI options + +183 +00:10:21,921 --> 00:10:25,358 +for signing in with passkeys. + +184 +00:10:25,358 --> 00:10:27,660 +Here's how to get started +using passkeys + +185 +00:10:27,660 --> 00:10:30,530 +and presenting them +with AutoFill. + +186 +00:10:30,530 --> 00:10:33,099 +Passkeys are built +on the WebAuthentication -- + +187 +00:10:33,099 --> 00:10:37,670 +or WebAuthn standard -- +and use public-key cryptography. + +188 +00:10:37,670 --> 00:10:41,508 +Rather than having +a typable word or string, + +189 +00:10:41,508 --> 00:10:46,679 +unique cryptographic key pairs +are generated for every account. + +190 +00:10:46,679 --> 00:10:49,949 +You'll need to adopt WebAuthn +on your server back end + +191 +00:10:49,949 --> 00:10:52,952 +in order to perform +passkey sign-in. + +192 +00:10:52,952 --> 00:10:55,955 +Any standard WebAuthn +server implementation + +193 +00:10:55,955 --> 00:10:59,859 +should work with passkeys. + +194 +00:10:59,859 --> 00:11:01,861 +In apps on Apple platforms, + +195 +00:11:01,861 --> 00:11:05,598 +passkeys are part of the +ASAuthorization API family + +196 +00:11:05,598 --> 00:11:08,468 +in the AuthenticationServices +framework. + +197 +00:11:08,468 --> 00:11:11,004 +This is our API +for working with all kinds + +198 +00:11:11,004 --> 00:11:14,140 +of different credentials, +including passwords, + +199 +00:11:14,140 --> 00:11:17,744 +security keys, +and Sign in with Apple. + +200 +00:11:17,744 --> 00:11:20,513 +We've also added a few +new methods you can use, + +201 +00:11:20,513 --> 00:11:25,318 +like AutoFill support, to make +this API even more flexible + +202 +00:11:25,318 --> 00:11:30,423 +and let you fit it in seamlessly +in your existing sign-in flows. + +203 +00:11:30,423 --> 00:11:33,726 +To get started using +passkeys in your apps, + +204 +00:11:33,726 --> 00:11:36,496 +first, you'll need to set up +associated domains, + +205 +00:11:36,496 --> 00:11:39,165 +using the webcredentials +service. + +206 +00:11:39,165 --> 00:11:41,301 +You can find more +details about that + +207 +00:11:41,301 --> 00:11:44,237 +in the "Introducing Password +AutoFill for Apps" + +208 +00:11:44,237 --> 00:11:47,607 +and "What's new +in Universal Links" videos. + +209 +00:11:47,607 --> 00:11:51,277 +In your app's interface, +make sure your user name field + +210 +00:11:51,277 --> 00:11:54,914 +is using the user name +textContentTtype. + +211 +00:11:54,914 --> 00:11:59,586 +This lets the system know where +to offer passkey suggestions. + +212 +00:11:59,586 --> 00:12:02,088 +Once that's configured, +here's the code needed + +213 +00:12:02,088 --> 00:12:05,592 +to start an AutoFill-assisted +passkey request. + +214 +00:12:05,592 --> 00:12:09,529 +It's just a few simple steps +when you break it down. + +215 +00:12:09,529 --> 00:12:12,665 +As with any WebAuthn request, +you'll first need + +216 +00:12:12,665 --> 00:12:16,102 +to fetch a challenge +from your server. + +217 +00:12:16,102 --> 00:12:19,472 +Then create the provider +and the request. + +218 +00:12:19,472 --> 00:12:23,142 +ASAuthorizationPlatformPublicKey +CredentialProvider + +219 +00:12:23,142 --> 00:12:25,178 +is the +ASAuthorizationProvider + +220 +00:12:25,178 --> 00:12:28,247 +for working +with passkey requests. + +221 +00:12:28,247 --> 00:12:32,452 +In WebAuthn terms, assertions +are used when signing in, + +222 +00:12:32,452 --> 00:12:35,054 +so here, I'm creating +an assertion request + +223 +00:12:35,054 --> 00:12:38,191 +to sign in with +an existing passkey. + +224 +00:12:38,191 --> 00:12:39,993 +ASAuthorizationController + +225 +00:12:39,993 --> 00:12:42,695 +is what actually handles +the request. + +226 +00:12:42,695 --> 00:12:45,365 +Create an instance +with the passkey request + +227 +00:12:45,365 --> 00:12:50,536 +and configure its delegate and +presentationContextProvider. + +228 +00:12:50,536 --> 00:12:54,273 +And finally, call +performAutoFillAssistedRequests + +229 +00:12:54,273 --> 00:12:56,509 +to start the request. + +230 +00:12:56,509 --> 00:12:59,045 +While this request +is running in your app, + +231 +00:12:59,045 --> 00:13:01,714 +whenever a user name field +is focused, + +232 +00:13:01,714 --> 00:13:05,918 +the system will offer available +passkeys in the QuickType bar. + +233 +00:13:05,918 --> 00:13:09,756 +Make sure to start this request +early in your view lifetime + +234 +00:13:09,756 --> 00:13:11,858 +before a user name field +gets focused, + +235 +00:13:11,858 --> 00:13:15,628 +so passkeys are ready +when the keyboard appears. + +236 +00:13:15,628 --> 00:13:18,464 +When an item from +the QuickType bar is selected, + +237 +00:13:18,464 --> 00:13:21,467 +Face ID gets invoked, +then you'll receive + +238 +00:13:21,467 --> 00:13:24,470 +an ASAuthorizationController +Delegate callback + +239 +00:13:24,470 --> 00:13:26,472 +to complete the sign-in. + +240 +00:13:26,472 --> 00:13:29,575 +Nothing actually gets filled +in your text field. + +241 +00:13:29,575 --> 00:13:32,912 +When an authorization succeeds +for any credential type, + +242 +00:13:32,912 --> 00:13:35,048 +you'll get the +didCompleteWithAuthorization + +243 +00:13:35,048 --> 00:13:37,216 +callback. + +244 +00:13:37,216 --> 00:13:38,785 +The first thing you should do + +245 +00:13:38,785 --> 00:13:41,821 +is check the type +of the credential that you got. + +246 +00:13:41,821 --> 00:13:44,657 +In the case of passkey sign-in, +it will be an + +247 +00:13:44,657 --> 00:13:50,029 +ASAuthorizationPlatformPublicKey +CredentialAssertion. + +248 +00:13:50,029 --> 00:13:52,465 +The assertion object will +contain the fields needed + +249 +00:13:52,465 --> 00:13:55,368 +to verify the sign-in +on your back end. + +250 +00:13:55,368 --> 00:13:59,005 +You should read the values, +verify them with your server, + +251 +00:13:59,005 --> 00:14:01,140 +and complete the sign-in. + +252 +00:14:01,140 --> 00:14:04,977 +AutoFill-assisted passkey +requests are powerful. + +253 +00:14:04,977 --> 00:14:06,813 +With that small code change, + +254 +00:14:06,813 --> 00:14:11,284 +your app's sign-in flow +now offers a lot of flexibility. + +255 +00:14:11,284 --> 00:14:14,153 +The primary case, of course, +is to select + +256 +00:14:14,153 --> 00:14:16,823 +the passkey suggestion +from the QuickType bar + +257 +00:14:16,823 --> 00:14:19,959 +to sign in quickly +with that passkey. + +258 +00:14:19,959 --> 00:14:24,030 +This is what you should expect +to happen most often. + +259 +00:14:24,030 --> 00:14:26,499 +There are other options though. + +260 +00:14:26,499 --> 00:14:29,769 +The code I just showed you +also allows passkey sign-in + +261 +00:14:29,769 --> 00:14:33,439 +from nearby devices +with no additional changes. + +262 +00:14:33,439 --> 00:14:36,175 +You can tap the key icon +to bring up a view + +263 +00:14:36,175 --> 00:14:40,079 +that lists all available +passkeys and passwords + +264 +00:14:40,079 --> 00:14:43,683 +and get to the option to sign in +with a nearby device. + +265 +00:14:43,683 --> 00:14:47,687 +Then you can perform a +cross-device passkey sign-in. + +266 +00:14:47,687 --> 00:14:50,723 +In both cases, +if a passkey is used, + +267 +00:14:50,723 --> 00:14:52,024 +you'll receive the same + +268 +00:14:52,024 --> 00:14:55,061 +ASAuthorizationController +Delegate callback. + +269 +00:14:55,061 --> 00:14:59,165 +There's nothing special you need +to do to support this. + +270 +00:14:59,165 --> 00:15:01,868 +If a user doesn't have +any passkeys yet, + +271 +00:15:01,868 --> 00:15:05,772 +they can just use your login +form like they're used to. + +272 +00:15:05,772 --> 00:15:09,142 +They'll get password suggestions +in the QuickType bar, + +273 +00:15:09,142 --> 00:15:11,978 +or they can just type +in the fields. + +274 +00:15:11,978 --> 00:15:14,347 +If a password item is selected, + +275 +00:15:14,347 --> 00:15:17,750 +the credential will still be +filled in to your text fields, + +276 +00:15:17,750 --> 00:15:21,154 +and you can cancel +the running request. + +277 +00:15:21,154 --> 00:15:23,289 +We designed this API +to let you drop it + +278 +00:15:23,289 --> 00:15:25,992 +right in to your existing +sign-in flows + +279 +00:15:25,992 --> 00:15:30,630 +and make it super easy +for your users. + +280 +00:15:30,630 --> 00:15:34,233 +If someone that has already +upgraded to using a passkey + +281 +00:15:34,233 --> 00:15:36,869 +decides to enter +their user name anyway + +282 +00:15:36,869 --> 00:15:39,305 +instead of using +the AutoFill suggestion, + +283 +00:15:39,305 --> 00:15:41,207 +you should cancel +the AutoFill request + +284 +00:15:41,207 --> 00:15:43,342 +and use +ASAuthorizationController + +285 +00:15:43,342 --> 00:15:46,279 +to present a modal passkey +sign-in sheet. + +286 +00:15:46,279 --> 00:15:48,815 +From here, +it's still a single tap, + +287 +00:15:48,815 --> 00:15:50,316 +and you'll receive the same + +288 +00:15:50,316 --> 00:15:53,920 +ASAuthorizationController +Delegate callback. + +289 +00:15:53,920 --> 00:15:56,122 +Here's the code from before. + +290 +00:15:56,122 --> 00:15:59,792 +To switch this from an AutoFill +request to a modal request, + +291 +00:15:59,792 --> 00:16:03,229 +just swap this +performAutoFillAssistedRequests + +292 +00:16:03,229 --> 00:16:07,600 +method call +with a performRequests() call. + +293 +00:16:07,600 --> 00:16:11,571 +This will present a modal sheet +with all available passkeys + +294 +00:16:11,571 --> 00:16:16,008 +as well as the option to use +a passkey from a nearby device. + +295 +00:16:16,008 --> 00:16:19,278 +Those are the only code changes +you need in your app + +296 +00:16:19,278 --> 00:16:22,081 +to support passkeys. + +297 +00:16:22,081 --> 00:16:25,618 +The web platform also supports +both AutoFill-assisted + +298 +00:16:25,618 --> 00:16:28,287 +and modal passkey requests. + +299 +00:16:28,287 --> 00:16:33,292 +On the web, passkeys are used +via standard WebAuthn API, + +300 +00:16:33,292 --> 00:16:36,696 +which is also used +for security keys. + +301 +00:16:36,696 --> 00:16:40,333 +Just like in apps, adopting +AutoFill-assisted requests + +302 +00:16:40,333 --> 00:16:43,803 +allows signing in quickly +with just a Touch ID, + +303 +00:16:43,803 --> 00:16:47,673 +getting to all of your available +passkeys and passwords + +304 +00:16:47,673 --> 00:16:50,877 +or using a passkey +from a nearby device, + +305 +00:16:50,877 --> 00:16:54,080 +all with very little code. + +306 +00:16:54,080 --> 00:16:57,884 +First off, make sure to annotate +your user name field + +307 +00:16:57,884 --> 00:17:01,020 +on your webpage +with both the username + +308 +00:17:01,020 --> 00:17:04,657 +and webauthn +autocomplete detail tokens, + +309 +00:17:04,657 --> 00:17:07,860 +so that both password +and passkey suggestions + +310 +00:17:07,860 --> 00:17:10,129 +are shown in the right place. + +311 +00:17:10,129 --> 00:17:13,666 +Once that's done, here's +a typical WebAuthn sign-in, + +312 +00:17:13,666 --> 00:17:16,302 +in JavaScript. + +313 +00:17:16,302 --> 00:17:19,472 +In WebAuthn, +AutoFill-style requests + +314 +00:17:19,472 --> 00:17:22,975 +are invoked using +conditional mediation. + +315 +00:17:22,975 --> 00:17:24,210 +You should start by using + +316 +00:17:24,210 --> 00:17:26,545 +standard JavaScript +feature detection + +317 +00:17:26,545 --> 00:17:29,248 +to check if it is available. + +318 +00:17:29,248 --> 00:17:33,319 +If it is, you can proceed +making your request. + +319 +00:17:33,319 --> 00:17:37,056 +Just like with a native API, +you'll start by making a request + +320 +00:17:37,056 --> 00:17:40,693 +using a challenge +fetched from your server. + +321 +00:17:40,693 --> 00:17:42,862 +To make it +an AutoFill-assisted request, + +322 +00:17:42,862 --> 00:17:47,900 +add the mediation: "conditional" +parameter to your options. + +323 +00:17:47,900 --> 00:17:53,406 +Then, use navigator.credentials +.get to start the request. + +324 +00:17:53,406 --> 00:17:56,275 +The .get call +returns a promise. + +325 +00:17:56,275 --> 00:17:59,845 +If it resolves, you'll +receive an assertion object, + +326 +00:17:59,845 --> 00:18:02,682 +which you can send back +to your server to verify, + +327 +00:18:02,682 --> 00:18:04,850 +then complete the sign-in. + +328 +00:18:04,850 --> 00:18:08,354 +Like in apps, if someone +manually enters a user name + +329 +00:18:08,354 --> 00:18:12,058 +for an account with a passkey, +you should use the API + +330 +00:18:12,058 --> 00:18:15,061 +to present a modal +sign-in sheet. + +331 +00:18:15,061 --> 00:18:19,131 +To switch to a modal request, +all you need to do is remove + +332 +00:18:19,131 --> 00:18:22,668 +the mediation: "conditional" +parameter. + +333 +00:18:22,668 --> 00:18:24,837 +One thing to note +when using WebAuthn + +334 +00:18:24,837 --> 00:18:29,809 +is how Apple platforms handle +user verification -- or UV. + +335 +00:18:29,809 --> 00:18:33,546 +UV is a Boolean field +in a WebAuthn response + +336 +00:18:33,546 --> 00:18:35,581 +that indicates whether +the authenticator + +337 +00:18:35,581 --> 00:18:37,883 +attempted to verify +that the current user + +338 +00:18:37,883 --> 00:18:39,952 +is the owner of the device. + +339 +00:18:39,952 --> 00:18:44,557 +On Apple devices, a value of +one indicates that biometrics, + +340 +00:18:44,557 --> 00:18:47,493 +or a password +or passcode were used. + +341 +00:18:47,493 --> 00:18:50,963 +Apple platforms will always +require UV for passkeys + +342 +00:18:50,963 --> 00:18:52,932 +when biometrics are available, + +343 +00:18:52,932 --> 00:18:55,768 +so you don't have +to worry about that. + +344 +00:18:55,768 --> 00:18:57,770 +When making WebAuthn requests, + +345 +00:18:57,770 --> 00:19:02,108 +there's an option to specify +a user-verification requirement. + +346 +00:19:02,108 --> 00:19:06,145 +The default value is +userVerification: "preferred". + +347 +00:19:06,145 --> 00:19:10,483 +Always use the default value to +avoid creating a bad experience + +348 +00:19:10,483 --> 00:19:14,286 +on devices without biometrics. + +349 +00:19:14,286 --> 00:19:18,557 +Here are some additional notes +for using passkeys on the web. + +350 +00:19:18,557 --> 00:19:20,960 +When you make +AutoFill-assisted requests, + +351 +00:19:20,960 --> 00:19:23,396 +you should make them +early in the page lifetime, + +352 +00:19:23,396 --> 00:19:25,731 +just like in apps. + +353 +00:19:25,731 --> 00:19:28,768 +For modal WebAuthn requests, +you should trigger them + +354 +00:19:28,768 --> 00:19:32,905 +from a user gesture event, +such as a button click. + +355 +00:19:32,905 --> 00:19:36,108 +A modal request can be +triggered once per page load + +356 +00:19:36,108 --> 00:19:40,012 +outside of a user gesture +event, but WebKit may limit + +357 +00:19:40,012 --> 00:19:44,083 +subsequent calls +on that page if you do so. + +358 +00:19:44,083 --> 00:19:46,719 +AutoFill requests +are not modal, + +359 +00:19:46,719 --> 00:19:49,088 +so they don't require +a user gesture + +360 +00:19:49,088 --> 00:19:52,358 +and have a much longer timeout. + +361 +00:19:52,358 --> 00:19:55,995 +Finally, passkeys are +replacing Safari's legacy + +362 +00:19:55,995 --> 00:19:58,397 +platform authenticator. + +363 +00:19:58,397 --> 00:20:01,033 +Existing credentials +will still work + +364 +00:20:01,033 --> 00:20:04,270 +and still be bound to the +device they were created on, + +365 +00:20:04,270 --> 00:20:08,841 +but new platform credentials +will be created as passkeys. + +366 +00:20:08,841 --> 00:20:11,310 +They can be differentiated +from legacy credentials + +367 +00:20:11,310 --> 00:20:12,912 +during registration, + +368 +00:20:12,912 --> 00:20:17,316 +as passkeys will not provide +an attestation statement. + +369 +00:20:17,316 --> 00:20:19,985 +That's passkeys and AutoFill. + +370 +00:20:19,985 --> 00:20:23,389 +Next up, I'll go over some +additional platform features + +371 +00:20:23,389 --> 00:20:26,892 +that can further streamline +your sign-in experience. + +372 +00:20:26,892 --> 00:20:29,695 +In addition to the +AutoFill-assisted sign-in, + +373 +00:20:29,695 --> 00:20:31,630 +the ASAuthorization API + +374 +00:20:31,630 --> 00:20:34,467 +provides many more +useful features. + +375 +00:20:34,467 --> 00:20:37,570 +I'm going to cover three +additional features of the API + +376 +00:20:37,570 --> 00:20:39,772 +and when you might +want to use them. + +377 +00:20:39,772 --> 00:20:43,075 +Starting with passkey +allow lists. + +378 +00:20:43,075 --> 00:20:45,478 +When presenting +a modal passkey sheet + +379 +00:20:45,478 --> 00:20:47,513 +after a user +name is entered, + +380 +00:20:47,513 --> 00:20:50,616 +it's possible that there are +passkeys for multiple accounts + +381 +00:20:50,616 --> 00:20:52,184 +saved on the device. + +382 +00:20:52,184 --> 00:20:56,355 +All available passkeys will be +shown in the sheet by default. + +383 +00:20:56,355 --> 00:20:58,491 +You can use +a passkey allow list + +384 +00:20:58,491 --> 00:21:01,660 +to restrict which passkeys +are shown in the sheet, + +385 +00:21:01,660 --> 00:21:05,131 +so that only the matching +account is offered. + +386 +00:21:05,131 --> 00:21:07,933 +To add an allow list +to a modal request, + +387 +00:21:07,933 --> 00:21:10,603 +you'll first need +the user name. + +388 +00:21:10,603 --> 00:21:13,038 +You can use that user name +to fetch a list + +389 +00:21:13,038 --> 00:21:17,776 +of matching credential IDs +and turn it into an allow list. + +390 +00:21:17,776 --> 00:21:22,014 +A credential ID is a unique +identifier for a passkey. + +391 +00:21:22,014 --> 00:21:25,718 +A Webauthn server should have +a way to look up credential IDs + +392 +00:21:25,718 --> 00:21:28,154 +for a given user name. + +393 +00:21:28,154 --> 00:21:32,658 +From here, just proceed +with your request like before. + +394 +00:21:32,658 --> 00:21:35,961 +Now, on my device which +has three Shiny accounts + +395 +00:21:35,961 --> 00:21:38,931 +using passkeys, +the sheet only offers + +396 +00:21:38,931 --> 00:21:42,134 +the single account +I'm trying to use. + +397 +00:21:42,134 --> 00:21:45,571 +When making modal requests, +you should use an allow list + +398 +00:21:45,571 --> 00:21:48,507 +when you have additional +context about which account + +399 +00:21:48,507 --> 00:21:50,809 +the user is trying +to sign in with, + +400 +00:21:50,809 --> 00:21:54,213 +such as if they've already +typed in their user name. + +401 +00:21:54,213 --> 00:21:58,551 +Next up, I'll cover what happens +you make a modal passkey request + +402 +00:21:58,551 --> 00:22:02,555 +if there are no passkeys +saved on the current device. + +403 +00:22:02,555 --> 00:22:05,224 +This also applies +if you use an allow list + +404 +00:22:05,224 --> 00:22:08,561 +and none of the saved +passkeys match that list. + +405 +00:22:08,561 --> 00:22:12,364 +By default, when you make +a modal passkey request, + +406 +00:22:12,364 --> 00:22:14,733 +if there are no matching +passkeys available, + +407 +00:22:14,733 --> 00:22:17,069 +the modal sheet +will be displayed + +408 +00:22:17,069 --> 00:22:19,405 +and will immediately show +the QR code + +409 +00:22:19,405 --> 00:22:23,108 +for signing in with a passkey +from a nearby device. + +410 +00:22:23,108 --> 00:22:26,745 +This provides most +flexibility when signing in + +411 +00:22:26,745 --> 00:22:30,316 +and is the best option when you +know a passkey is being used. + +412 +00:22:30,316 --> 00:22:34,019 +But there's a new option +in the API to prefer credentials + +413 +00:22:34,019 --> 00:22:37,256 +that are immediately available +and fall back silently + +414 +00:22:37,256 --> 00:22:39,925 +with a delegate callback +if there aren't any. + +415 +00:22:39,925 --> 00:22:43,028 +This can be used to quickly +offer up existing credentials + +416 +00:22:43,028 --> 00:22:44,363 +when possible, + +417 +00:22:44,363 --> 00:22:47,700 +before even showing +a traditional sign-in form. + +418 +00:22:47,700 --> 00:22:50,569 +This modal request +using the default options + +419 +00:22:50,569 --> 00:22:52,905 +will fall back +to showing a QR code + +420 +00:22:52,905 --> 00:22:56,742 +if there are no matching +passkeys on the current device. + +421 +00:22:56,742 --> 00:23:00,879 +If you use the preferImmediately +AvailableCredentials option, + +422 +00:23:00,879 --> 00:23:02,915 +instead of +getting a QR code, + +423 +00:23:02,915 --> 00:23:07,453 +you'll receive a delegate +callback with an error. + +424 +00:23:07,453 --> 00:23:10,155 +If you receive +an ASAuthorizationError + +425 +00:23:10,155 --> 00:23:12,691 +with a code of canceled, +that means + +426 +00:23:12,691 --> 00:23:16,729 +either the user saw the sheet +and manually dismissed it, + +427 +00:23:16,729 --> 00:23:20,633 +or you passed preferImmediately +AvailableCredentials + +428 +00:23:20,633 --> 00:23:23,936 +and no credentials +were immediately available. + +429 +00:23:23,936 --> 00:23:26,405 +What you do from here +depends on the context + +430 +00:23:26,405 --> 00:23:28,707 +where you were +calling this from. + +431 +00:23:28,707 --> 00:23:31,443 +For example, if you were +using this option + +432 +00:23:31,443 --> 00:23:33,879 +as a way to test +for local credentials + +433 +00:23:33,879 --> 00:23:36,649 +before showing +your normal sign-in form, + +434 +00:23:36,649 --> 00:23:40,219 +this is where would trigger +showing your form. + +435 +00:23:40,219 --> 00:23:43,455 +If there's at least one matching +credential on the device, + +436 +00:23:43,455 --> 00:23:45,958 +the full modal sheet +will be displayed + +437 +00:23:45,958 --> 00:23:48,894 +regardless +of the options used. + +438 +00:23:48,894 --> 00:23:50,863 +Make sure you're also using + +439 +00:23:50,863 --> 00:23:54,700 +either AutoFill-assisted +requests or modal requests + +440 +00:23:54,700 --> 00:23:57,936 +with the default fallback +somewhere in your app, + +441 +00:23:57,936 --> 00:24:00,806 +so that the option to sign in +with a nearby device + +442 +00:24:00,806 --> 00:24:03,542 +is still reachable +if there are no passkeys + +443 +00:24:03,542 --> 00:24:06,211 +on the current device. + +444 +00:24:06,211 --> 00:24:09,148 +The last feature +of the ASAuthorization API + +445 +00:24:09,148 --> 00:24:13,385 +that I'll cover is making +combined credential requests. + +446 +00:24:13,385 --> 00:24:16,255 +In this example, +the app made a request + +447 +00:24:16,255 --> 00:24:21,193 +for passkeys, passwords, +and Sign in with Apple. + +448 +00:24:21,193 --> 00:24:24,263 +My device happens to have +three different credentials + +449 +00:24:24,263 --> 00:24:26,298 +for three different +accounts saved, + +450 +00:24:26,298 --> 00:24:29,101 +so they're all +presented here. + +451 +00:24:29,101 --> 00:24:30,736 +But a more likely scenario + +452 +00:24:30,736 --> 00:24:34,006 +is that someone would only +have a single account. + +453 +00:24:34,006 --> 00:24:37,576 +In that case, this same +combined credential request + +454 +00:24:37,576 --> 00:24:40,913 +will only offer one account +in the sheet. + +455 +00:24:40,913 --> 00:24:42,948 +Adding additional +credential types + +456 +00:24:42,948 --> 00:24:47,853 +to an existing ASAuthorization +request is really easy. + +457 +00:24:47,853 --> 00:24:50,689 +You just need to create +providers and requests + +458 +00:24:50,689 --> 00:24:52,491 +for the additional +request types, + +459 +00:24:52,491 --> 00:24:56,061 +then pass those new requests +to your controller. + +460 +00:24:56,061 --> 00:24:58,797 +Now, the modal sheet will +offer whatever credentials + +461 +00:24:58,797 --> 00:25:02,935 +are available from any +of these credential types. + +462 +00:25:02,935 --> 00:25:04,570 +You'll get the same +delegate callback + +463 +00:25:04,570 --> 00:25:07,940 +regardless of which +credential type is used. + +464 +00:25:07,940 --> 00:25:10,976 +You should check the type +of the credential you received + +465 +00:25:10,976 --> 00:25:13,078 +and finish the sign-in +as appropriate + +466 +00:25:13,078 --> 00:25:15,314 +for that credential type. + +467 +00:25:15,314 --> 00:25:18,016 +So that covers a few +of the more advanced features + +468 +00:25:18,016 --> 00:25:21,387 +of the ASAuthorization +API family. + +469 +00:25:21,387 --> 00:25:24,056 +Now, I'm going to dig in +to some more technical details + +470 +00:25:24,056 --> 00:25:29,027 +about how passkeys actually work +and what makes them so secure. + +471 +00:25:29,027 --> 00:25:31,764 +When you sign in +with a password today, + +472 +00:25:31,764 --> 00:25:34,233 +generally what's actually +happening + +473 +00:25:34,233 --> 00:25:36,535 +is after you enter +that password, + +474 +00:25:36,535 --> 00:25:40,839 +it gets hashed and salted, and +the resulting obfuscated value + +475 +00:25:40,839 --> 00:25:44,076 +is sent to the server, +which stores it. + +476 +00:25:44,076 --> 00:25:48,714 +Later, if you can produce +the same hashed salted value, + +477 +00:25:48,714 --> 00:25:51,183 +you're allowed into the account. + +478 +00:25:51,183 --> 00:25:53,852 +This means the server +is responsible + +479 +00:25:53,852 --> 00:25:56,922 +for storing this derivation +of your password, + +480 +00:25:56,922 --> 00:25:59,858 +which is highly valuable +to attackers. + +481 +00:25:59,858 --> 00:26:02,528 +If they can get it, +it's possible to figure out + +482 +00:26:02,528 --> 00:26:06,498 +what your password is +and gain access to your account. + +483 +00:26:06,498 --> 00:26:10,269 +Passkeys, however, +work very differently. + +484 +00:26:10,269 --> 00:26:13,205 +Rather than having a single, +typeable string, + +485 +00:26:13,205 --> 00:26:16,575 +a passkey is actually +a pair of related keys. + +486 +00:26:16,575 --> 00:26:19,411 +These keys are generated +by your devices, + +487 +00:26:19,411 --> 00:26:23,549 +securely and uniquely, +for every account. + +488 +00:26:23,549 --> 00:26:27,586 +One is public +and is stored on the server. + +489 +00:26:27,586 --> 00:26:31,490 +The other is private +and stays on your devices + +490 +00:26:31,490 --> 00:26:33,725 +even when signing in. + +491 +00:26:33,725 --> 00:26:36,728 +The public key +is not a secret. + +492 +00:26:36,728 --> 00:26:40,432 +It's just as public +as your user name. + +493 +00:26:40,432 --> 00:26:44,169 +The private key is what is +needed to actually sign in. + +494 +00:26:44,169 --> 00:26:47,339 +The server never learns +what your private key is, + +495 +00:26:47,339 --> 00:26:50,976 +and your devices keep it safe. + +496 +00:26:50,976 --> 00:26:54,446 +When you go to sign in, +the server sends your device + +497 +00:26:54,446 --> 00:26:57,182 +a single-use challenge. + +498 +00:26:57,182 --> 00:27:01,386 +WebAuthn allows many different +challenge-response algorithms, + +499 +00:27:01,386 --> 00:27:07,326 +but passkeys on Apple +platforms use standard ES256. + +500 +00:27:07,326 --> 00:27:10,462 +Only your private key +is capable of producing + +501 +00:27:10,462 --> 00:27:14,933 +a valid solution to +the challenge for your account. + +502 +00:27:14,933 --> 00:27:17,102 +Your device produces +this solution -- + +503 +00:27:17,102 --> 00:27:19,872 +called a signature -- locally, + +504 +00:27:19,872 --> 00:27:23,742 +and only sends the solution +back to the server. + +505 +00:27:23,742 --> 00:27:28,947 +Your private key stays secret +and only on your devices. + +506 +00:27:28,947 --> 00:27:34,152 +The server then validates the +solution using your public key. + +507 +00:27:34,152 --> 00:27:37,022 +If the solution your device +provided is valid, + +508 +00:27:37,022 --> 00:27:39,324 +you're signed in! + +509 +00:27:39,324 --> 00:27:43,829 +A public key can be used +to check if a solution is valid + +510 +00:27:43,829 --> 00:27:47,566 +but is not able to produce +a solution itself. + +511 +00:27:47,566 --> 00:27:49,801 +This means +the server can be sure + +512 +00:27:49,801 --> 00:27:51,970 +that you have +the right private key, + +513 +00:27:51,970 --> 00:27:55,974 +without knowing what +the private key actually is. + +514 +00:27:55,974 --> 00:27:59,511 +And since the server doesn't +know any private keys, + +515 +00:27:59,511 --> 00:28:02,014 +it's a less valuable target +for attackers, + +516 +00:28:02,014 --> 00:28:06,084 +because there are +no user credentials to leak. + +517 +00:28:06,084 --> 00:28:08,754 +All of this cryptography +and key protection + +518 +00:28:08,754 --> 00:28:13,191 +is totally transparent +and performed by the devices. + +519 +00:28:13,191 --> 00:28:17,296 +Your customers never have +to know or think about it. + +520 +00:28:17,296 --> 00:28:20,566 +From their perspective, +passkeys are super simple + +521 +00:28:20,566 --> 00:28:23,569 +and just work, everywhere. + +522 +00:28:23,569 --> 00:28:27,639 +Passkeys can also be used +to sign in across devices + +523 +00:28:27,639 --> 00:28:30,709 +in a secure, +phishing-resistant manner. + +524 +00:28:30,709 --> 00:28:33,011 +Here's how that works. + +525 +00:28:33,011 --> 00:28:34,813 +There are two devices here. + +526 +00:28:34,813 --> 00:28:37,649 +The client, which is the +device or web browser + +527 +00:28:37,649 --> 00:28:40,619 +where I'm signing in, +and the authenticator, + +528 +00:28:40,619 --> 00:28:44,289 +which is the device +which has my passkey. + +529 +00:28:44,289 --> 00:28:46,892 +First, the client +shows a QR code, + +530 +00:28:46,892 --> 00:28:49,294 +which the +authenticator scans. + +531 +00:28:49,294 --> 00:28:51,797 +This QR code contains a URL + +532 +00:28:51,797 --> 00:28:56,468 +that encodes a pair +of single-use encryption keys. + +533 +00:28:56,468 --> 00:29:00,238 +Then, the authenticator produces +a Bluetooth advertisement + +534 +00:29:00,238 --> 00:29:04,443 +containing routing information +for a network relay server. + +535 +00:29:04,443 --> 00:29:07,179 +This local exchange +allows selecting a server + +536 +00:29:07,179 --> 00:29:09,214 +and sharing routing +information, + +537 +00:29:09,214 --> 00:29:13,085 +but also serves +two additional functions. + +538 +00:29:13,085 --> 00:29:15,621 +It performs an out-of-band +key agreement + +539 +00:29:15,621 --> 00:29:18,857 +that the server can't see, +so everything + +540 +00:29:18,857 --> 00:29:21,960 +going over the network +is end-to-end encrypted + +541 +00:29:21,960 --> 00:29:25,163 +and the server +can't read anything. + +542 +00:29:25,163 --> 00:29:27,699 +It also provides a strong claim + +543 +00:29:27,699 --> 00:29:31,536 +that these two devices +are in physical proximity. + +544 +00:29:31,536 --> 00:29:35,273 +That means a QR code +sent in an email + +545 +00:29:35,273 --> 00:29:39,511 +or generated on a fake +website won't work, + +546 +00:29:39,511 --> 00:29:42,714 +because a remote attacker +won't be able receive + +547 +00:29:42,714 --> 00:29:47,052 +the Bluetooth advertisement +and complete the local exchange. + +548 +00:29:47,052 --> 00:29:49,121 +So that's the local part. + +549 +00:29:49,121 --> 00:29:52,457 +Once the local exchange +and key agreement have happened, + +550 +00:29:52,457 --> 00:29:54,993 +the two devices connect +to a relay server + +551 +00:29:54,993 --> 00:29:57,029 +picked by the phone. + +552 +00:29:57,029 --> 00:30:01,466 +From there, they perform +a standard FIDO CTAP operation, + +553 +00:30:01,466 --> 00:30:04,202 +which is encrypted using +the keys from earlier, + +554 +00:30:04,202 --> 00:30:07,539 +so the relay server can't see +anything that's going on. + +555 +00:30:09,408 --> 00:30:12,444 +This whole process is +performed by the device + +556 +00:30:12,444 --> 00:30:14,179 +and the web browser. + +557 +00:30:14,179 --> 00:30:17,416 +The website is not involved +at any point + +558 +00:30:17,416 --> 00:30:19,618 +in the cross-device +communication. + +559 +00:30:19,618 --> 00:30:23,655 +Cross-device cross-platform +sign-in is a system feature + +560 +00:30:23,655 --> 00:30:28,260 +that just works anywhere +passkeys can be used. + +561 +00:30:28,260 --> 00:30:31,663 +So that's a more technical +look into how passkeys work + +562 +00:30:31,663 --> 00:30:35,033 +and how they can make such +strong security guarantees, + +563 +00:30:35,033 --> 00:30:37,102 +even across devices. + +564 +00:30:37,102 --> 00:30:40,706 +Next up, +multifactor authentication. + +565 +00:30:40,706 --> 00:30:44,042 +A common way to think about +authentication today + +566 +00:30:44,042 --> 00:30:46,411 +is in terms of factors. + +567 +00:30:46,411 --> 00:30:48,714 +Different factors +are strong or weak + +568 +00:30:48,714 --> 00:30:51,083 +against different kinds +of attacks, + +569 +00:30:51,083 --> 00:30:52,517 +and combining factors + +570 +00:30:52,517 --> 00:30:55,353 +can provide better +collective coverage. + +571 +00:30:55,353 --> 00:30:59,925 +But with passkeys, you don't +need to think like that anymore. + +572 +00:30:59,925 --> 00:31:02,027 +Here are some +of the most common methods + +573 +00:31:02,027 --> 00:31:04,262 +used to sign in today. + +574 +00:31:04,262 --> 00:31:05,630 +Passwords in your head + +575 +00:31:05,630 --> 00:31:08,467 +are vulnerable +to pretty much everything. + +576 +00:31:08,467 --> 00:31:09,801 +Password managers + +577 +00:31:09,801 --> 00:31:14,106 +are good at generating unique, +high-entropy strings, + +578 +00:31:14,106 --> 00:31:17,042 +may have local protections +against device theft, + +579 +00:31:17,042 --> 00:31:20,779 +and offer some hints +about phishing. + +580 +00:31:20,779 --> 00:31:23,715 +Adding an SMS +or time-based code + +581 +00:31:23,715 --> 00:31:27,152 +can help with theft or phishing +in some circumstances + +582 +00:31:27,152 --> 00:31:29,788 +but doesn't really +solve either. + +583 +00:31:29,788 --> 00:31:31,223 +With passkeys though, + +584 +00:31:31,223 --> 00:31:36,161 +every passkey is a unique, +device-generated key pair. + +585 +00:31:36,161 --> 00:31:39,698 +On Apple devices, they're +built on a strong foundation + +586 +00:31:39,698 --> 00:31:42,467 +of local device protections. + +587 +00:31:42,467 --> 00:31:45,771 +Passkeys also +completely eliminate + +588 +00:31:45,771 --> 00:31:48,073 +the human factor +from phishing. + +589 +00:31:48,073 --> 00:31:51,443 +And they can't be leaked +by an app or website server, + +590 +00:31:51,443 --> 00:31:55,447 +because the servers +don't have the private keys. + +591 +00:31:55,447 --> 00:31:58,617 +Adding factors to +a password-based sign-in flow + +592 +00:31:58,617 --> 00:32:01,653 +makes sense, +as together they can protect + +593 +00:32:01,653 --> 00:32:05,223 +against more types of attacks +than passwords alone. + +594 +00:32:05,223 --> 00:32:08,827 +But a passkey alone protects +against so much more + +595 +00:32:08,827 --> 00:32:12,464 +that it doesn't need +additional factors. + +596 +00:32:12,464 --> 00:32:16,568 +I'm looking forward to +a future without passwords. + +597 +00:32:16,568 --> 00:32:20,705 +Here's how you can get +started making that happen. + +598 +00:32:20,705 --> 00:32:24,242 +First off, you'll need to adopt +WebAuthn on your server, + +599 +00:32:24,242 --> 00:32:26,711 +if you haven't already done so. + +600 +00:32:26,711 --> 00:32:27,979 +Passkeys should work + +601 +00:32:27,979 --> 00:32:32,517 +with any standard WebAuthn +server implementation. + +602 +00:32:32,517 --> 00:32:34,452 +Once your server +is ready to go, + +603 +00:32:34,452 --> 00:32:38,223 +adopt our new API +in your apps and websites. + +604 +00:32:38,223 --> 00:32:41,193 +AutoFill-assisted passkey +requests can be dropped + +605 +00:32:41,193 --> 00:32:44,529 +right in to your existing +sign-in flows, + +606 +00:32:44,529 --> 00:32:47,999 +plus we have a range of more +advanced UI options as well, + +607 +00:32:47,999 --> 00:32:50,235 +if you need them. + +608 +00:32:50,235 --> 00:32:56,174 +And finally, transition your +users away from passwords. + +609 +00:32:56,174 --> 00:32:59,511 +Passkeys are +an industry-standard solution + +610 +00:32:59,511 --> 00:33:01,713 +to the convenience +and security problem + +611 +00:33:01,713 --> 00:33:05,784 +of securely signing in +to apps and websites. + +612 +00:33:05,784 --> 00:33:08,486 +By guiding your customers +to passkeys + +613 +00:33:08,486 --> 00:33:11,456 +and away from passwords, +you can give them + +614 +00:33:11,456 --> 00:33:15,327 +an incredibly quick and +convenient sign-in experience + +615 +00:33:15,327 --> 00:33:18,496 +while raising the security bar +for everyone. + +616 +00:33:18,496 --> 00:33:19,464 +Thank you. + +617 +00:33:19,464 --> 00:33:23,301 +♪ + diff --git a/eng/2022 Session 10093 Integrate your custom collaboration app with Messages en.srt b/eng/2022 Session 10093 Integrate your custom collaboration app with Messages en.srt new file mode 100644 index 0000000..ee435b7 --- /dev/null +++ b/eng/2022 Session 10093 Integrate your custom collaboration app with Messages en.srt @@ -0,0 +1,2621 @@ +1 +00:00:00,167 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,977 +♪ + +3 +00:00:09,977 --> 00:00:14,281 +Devin Clary: Hi. I'm Devin, an +engineer on the Messages team. + +4 +00:00:14,281 --> 00:00:17,017 +Lance Parker: And I'm Lance, +also a Messages engineer. + +5 +00:00:17,017 --> 00:00:20,053 +Devin: Welcome to "Integrate +your custom collaboration app + +6 +00:00:20,053 --> 00:00:22,222 +with Messages." + +7 +00:00:22,222 --> 00:00:24,625 +Collaboration starts +with a conversation, + +8 +00:00:24,625 --> 00:00:27,227 +and in iOS 16 +and macOS Ventura, + +9 +00:00:27,227 --> 00:00:29,897 +you can bring your app's +custom collaboration experience + +10 +00:00:29,897 --> 00:00:32,466 +right into the fabric +of the conversation. + +11 +00:00:32,466 --> 00:00:36,870 +In this video, we'll go over the +life cycle of a collaboration. + +12 +00:00:36,870 --> 00:00:38,438 +Then, we'll show you +how to prepare + +13 +00:00:38,438 --> 00:00:42,809 +your app's collaborative content +to be shared through Messages. + +14 +00:00:42,809 --> 00:00:45,879 +Next, we'll give you everything +you need to instantly verify + +15 +00:00:45,879 --> 00:00:49,650 +recipient access, and respond +to participant changes, + +16 +00:00:49,650 --> 00:00:52,619 +all without +compromising privacy. + +17 +00:00:52,619 --> 00:00:55,355 +Finally, we'll show you +how your app can post notices + +18 +00:00:55,355 --> 00:00:59,293 +about the content right +to the Messages conversation. + +19 +00:00:59,293 --> 00:01:00,928 +This video assumes your app + +20 +00:01:00,928 --> 00:01:03,263 +has existing collaboration +infrastructure, + +21 +00:01:03,263 --> 00:01:06,066 +and has already adopted +universal links. + +22 +00:01:06,066 --> 00:01:08,268 +We'll also build on some +concepts introduced in + +23 +00:01:08,268 --> 00:01:09,870 +"Add Shared with You +to your app" + +24 +00:01:09,870 --> 00:01:14,574 +and "Enhance collaboration +experiences with Messages." + +25 +00:01:14,574 --> 00:01:16,677 +First up, +I'll go over the life cycle + +26 +00:01:16,677 --> 00:01:18,979 +of a custom +collaboration message + +27 +00:01:18,979 --> 00:01:21,515 +to illustrate how this API +allows your users + +28 +00:01:21,515 --> 00:01:24,818 +to start collaborating +faster than ever. + +29 +00:01:24,818 --> 00:01:26,987 +When a user decides +to share a collaboration + +30 +00:01:26,987 --> 00:01:29,756 +from your app through Messages, + +31 +00:01:29,756 --> 00:01:33,427 +you first create metadata +to represent the content. + +32 +00:01:33,427 --> 00:01:36,463 +The metadata includes share +options the user can configure + +33 +00:01:36,463 --> 00:01:37,965 +prior to sending the message, + +34 +00:01:37,965 --> 00:01:41,301 +and a number of other properties +you can customize. + +35 +00:01:41,301 --> 00:01:43,971 +Next, you provide that metadata +to the share sheet, + +36 +00:01:43,971 --> 00:01:46,039 +or to drag and drop. + +37 +00:01:46,039 --> 00:01:48,375 +This allows a draft +of the content to be staged + +38 +00:01:48,375 --> 00:01:51,144 +in the Messages compose field. + +39 +00:01:51,144 --> 00:01:55,215 +The collaboration needs to be +represented by a universal link. + +40 +00:01:55,215 --> 00:01:57,918 +That can be created immediately, +but it's best deferred + +41 +00:01:57,918 --> 00:02:00,721 +until right before +the message is sent. + +42 +00:02:00,721 --> 00:02:02,422 +This is useful +if your app's link creation + +43 +00:02:02,422 --> 00:02:05,325 +depends on the selected +share options, or recipients, + +44 +00:02:05,325 --> 00:02:08,395 +as configured in +the Messages compose field. + +45 +00:02:08,395 --> 00:02:11,365 +The user chooses the recipients +and share options + +46 +00:02:11,365 --> 00:02:14,201 +and taps the send button. + +47 +00:02:14,201 --> 00:02:15,569 +Before the message is sent, + +48 +00:02:15,569 --> 00:02:17,971 +Messages asks your app +for the universal link + +49 +00:02:17,971 --> 00:02:21,575 +and a device independent +identifier for the content. + +50 +00:02:21,575 --> 00:02:23,877 +Using that identifier, +Messages provides a set of + +51 +00:02:23,877 --> 00:02:26,747 +cryptographic identities +representing the recipients + +52 +00:02:26,747 --> 00:02:29,916 +of that specific +collaboration message. + +53 +00:02:29,916 --> 00:02:31,685 +Your app will +use these identities later + +54 +00:02:31,685 --> 00:02:34,087 +to allow the recipients +to immediately open the link + +55 +00:02:34,087 --> 00:02:38,725 +on any of their devices. + +56 +00:02:38,725 --> 00:02:41,261 +Your app stores those +identities on its servers + +57 +00:02:41,261 --> 00:02:44,364 +and associates them +with the shared content. + +58 +00:02:44,364 --> 00:02:46,033 +Once your app +finishes this step, + +59 +00:02:46,033 --> 00:02:50,470 +the message is sent +to the recipients. + +60 +00:02:50,470 --> 00:02:53,440 +Now, here's what happens +on the receiving device. + +61 +00:02:53,440 --> 00:02:56,009 +The goal is to instantly +verify access, + +62 +00:02:56,009 --> 00:02:59,913 +pairing a recipient identity +with an account on your server. + +63 +00:02:59,913 --> 00:03:01,681 +When the recipient +opens the link, + +64 +00:03:01,681 --> 00:03:04,351 +your app receives +a call to open the URL, + +65 +00:03:04,351 --> 00:03:08,388 +just like it does +for any other link. + +66 +00:03:08,388 --> 00:03:10,090 +When your app detects +that a user account + +67 +00:03:10,090 --> 00:03:12,325 +doesn't yet have access +to the document, + +68 +00:03:12,325 --> 00:03:15,262 +it queries the system +for a proof of user identity + +69 +00:03:15,262 --> 00:03:18,865 +cryptographically signed +by the recipient device. + +70 +00:03:18,865 --> 00:03:20,901 +Your app sends +that signed identity proof + +71 +00:03:20,901 --> 00:03:23,870 +to your server for validation. + +72 +00:03:23,870 --> 00:03:26,373 +If the signature is valid, +the server compares the proof + +73 +00:03:26,373 --> 00:03:28,308 +against the identities +previously provided + +74 +00:03:28,308 --> 00:03:30,310 +by the sending device. + +75 +00:03:30,310 --> 00:03:33,213 +If there's a match, +your server grants access + +76 +00:03:33,213 --> 00:03:35,215 +to the user's account. + +77 +00:03:35,215 --> 00:03:36,716 +And with that, +the recipient has gained + +78 +00:03:36,716 --> 00:03:39,419 +instant and secure access +to the content, + +79 +00:03:39,419 --> 00:03:42,022 +all without exchanging +account information! + +80 +00:03:42,022 --> 00:03:45,826 +And that's the life cycle +of a collaboration message! + +81 +00:03:45,826 --> 00:03:47,561 +Next, +let's look more closely + +82 +00:03:47,561 --> 00:03:51,198 +at the API +for starting a collaboration. + +83 +00:03:51,198 --> 00:03:55,168 +The system needs some metadata +about the collaboration. + +84 +00:03:55,168 --> 00:03:56,970 +And for that, +you use a new class + +85 +00:03:56,970 --> 00:04:01,575 +in the Shared with You framework +called SWCollaborationMetadata. + +86 +00:04:01,575 --> 00:04:05,011 +This class has a few properties +for you to configure: + +87 +00:04:05,011 --> 00:04:08,148 +the content's title, +a local identifier + +88 +00:04:08,148 --> 00:04:10,851 +to reference the content +before its been shared; + +89 +00:04:10,851 --> 00:04:13,220 +the initiator name +and account handle, + +90 +00:04:13,220 --> 00:04:14,788 +to provide transparency +to the user + +91 +00:04:14,788 --> 00:04:16,890 +about the account +they're sharing from; + +92 +00:04:16,890 --> 00:04:21,528 +and the default share options, +for the user to configure. + +93 +00:04:21,528 --> 00:04:23,330 +Here's how to create +a metadata object + +94 +00:04:23,330 --> 00:04:27,033 +and configure its properties. + +95 +00:04:27,033 --> 00:04:31,104 +Create a local identifier using +SWLocalCollaborationIdentifier + +96 +00:04:31,104 --> 00:04:33,874 +initialized with a string. + +97 +00:04:33,874 --> 00:04:36,143 +The string only needs +to be sufficient for your app + +98 +00:04:36,143 --> 00:04:41,114 +to identify the content locally, +not across devices. + +99 +00:04:41,114 --> 00:04:43,183 +Initialize +a new metadata instance + +100 +00:04:43,183 --> 00:04:47,020 +using the local identifier. + +101 +00:04:47,020 --> 00:04:52,025 +Set the content title, +the initiator's account handle, + +102 +00:04:52,025 --> 00:04:54,628 +and their name +using PersonNameComponents + +103 +00:04:54,628 --> 00:04:57,164 +from the foundation framework. + +104 +00:04:57,164 --> 00:04:59,566 +The handle and name +are only displayed locally + +105 +00:04:59,566 --> 00:05:04,371 +so the user can confirm the +account they're sharing from. + +106 +00:05:04,371 --> 00:05:07,474 +Next, +set the defaultShareOptions. + +107 +00:05:07,474 --> 00:05:08,942 +Before I show you +how to do that, + +108 +00:05:08,942 --> 00:05:12,012 +I'll first describe +how options work. + +109 +00:05:12,012 --> 00:05:14,548 +Share options are +the settings a user configures + +110 +00:05:14,548 --> 00:05:18,251 +on the collaboration +in Messages or the share sheet. + +111 +00:05:18,251 --> 00:05:20,620 +The options selected by the user +are provided to you + +112 +00:05:20,620 --> 00:05:23,323 +before the message is sent. + +113 +00:05:23,323 --> 00:05:25,325 +Share options +might include settings like + +114 +00:05:25,325 --> 00:05:27,694 +who can make edits +to a collaboration + +115 +00:05:27,694 --> 00:05:32,199 +or who should have access +to the content. + +116 +00:05:32,199 --> 00:05:34,768 +You use a few classes +to define options, + +117 +00:05:34,768 --> 00:05:38,238 +starting with +SWCollaborationOption. + +118 +00:05:38,238 --> 00:05:39,639 +Depending on how +they're grouped, + +119 +00:05:39,639 --> 00:05:42,209 +options represent +individual switches, + +120 +00:05:42,209 --> 00:05:46,279 +or mutually exclusive values +for a setting. + +121 +00:05:46,279 --> 00:05:48,715 +Options have a title +and an identifier, + +122 +00:05:48,715 --> 00:05:53,920 +and they are either +selected or unselected. + +123 +00:05:53,920 --> 00:05:58,091 +There are two classes +to represent a group of options: + +124 +00:05:58,091 --> 00:06:00,060 +SWCollaborationOptionsGroup + +125 +00:06:00,060 --> 00:06:04,197 +and SWCollaborationOptions +PickerGroup. + +126 +00:06:04,197 --> 00:06:06,766 +You use +SWCollaborationOptionsGroup + +127 +00:06:06,766 --> 00:06:09,269 +to represent +a collection of switches, + +128 +00:06:09,269 --> 00:06:11,905 +while SWCollaborationOptions +PickerGroup + +129 +00:06:11,905 --> 00:06:16,610 +represents mutually exclusive +values for a setting. + +130 +00:06:16,610 --> 00:06:19,246 +Finally, +SWCollaborationShareOptions + +131 +00:06:19,246 --> 00:06:21,681 +defines the full set +of option groups, + +132 +00:06:21,681 --> 00:06:25,252 +to be set on the metadata's +defaultShareOptions property. + +133 +00:06:25,252 --> 00:06:29,489 +You can also provide a summary +string to describe the options. + +134 +00:06:29,489 --> 00:06:31,658 +Now that I've described +the option classes, + +135 +00:06:31,658 --> 00:06:34,661 +here's an example +showing how to use them. + +136 +00:06:34,661 --> 00:06:38,765 +This code defines +two option groups. + +137 +00:06:38,765 --> 00:06:41,368 +The first group is initialized +with an identifier + +138 +00:06:41,368 --> 00:06:43,937 +and two possible options. + +139 +00:06:43,937 --> 00:06:45,972 +The identifier is +an arbitrary string + +140 +00:06:45,972 --> 00:06:50,710 +you later use to identify which +option was selected by the user. + +141 +00:06:50,710 --> 00:06:52,145 +Since this is a picker group, + +142 +00:06:52,145 --> 00:06:54,914 +the options are +mutually exclusive. + +143 +00:06:54,914 --> 00:06:56,883 +This group represents +the permission settings + +144 +00:06:56,883 --> 00:07:00,787 +for the content: +readwrite or readonly. + +145 +00:07:00,787 --> 00:07:06,092 +Then, the first option in that +group is selected by default. + +146 +00:07:06,092 --> 00:07:11,231 +And the title is set to a string +describing this group. + +147 +00:07:11,231 --> 00:07:13,667 +The second option group +is initialized the same way, + +148 +00:07:13,667 --> 00:07:16,202 +and also contains two options. + +149 +00:07:16,202 --> 00:07:18,305 +But since this is +a generic option group, + +150 +00:07:18,305 --> 00:07:19,673 +the user will be able +to configure + +151 +00:07:19,673 --> 00:07:23,710 +whether to allow mentions +and comments independently. + +152 +00:07:23,710 --> 00:07:26,546 +Finally, the two option groups +are used to initialize + +153 +00:07:26,546 --> 00:07:30,183 +an instance of +SWCollaborationShareOptions, + +154 +00:07:30,183 --> 00:07:34,454 +which is then set +on the metadata. + +155 +00:07:34,454 --> 00:07:37,090 +Next, the metadata is provided +to the share sheet + +156 +00:07:37,090 --> 00:07:39,626 +or drag and drop, +depending on how the user + +157 +00:07:39,626 --> 00:07:42,362 +decides to share the content. + +158 +00:07:42,362 --> 00:07:46,032 +If your app uses SwiftUI, +SWCollaborationMetadata + +159 +00:07:46,032 --> 00:07:49,536 +is compatible +with the new ShareLink API. + +160 +00:07:49,536 --> 00:07:52,105 +Watch "Meet Transferable" +and "What's new in SwiftUI" + +161 +00:07:52,105 --> 00:07:58,244 +to learn more about +Transferable and ShareLink. + +162 +00:07:58,244 --> 00:08:00,814 +Here's how easy it is +to support collaboration + +163 +00:08:00,814 --> 00:08:05,552 +on a proxy representation +in SwiftUI! + +164 +00:08:05,552 --> 00:08:08,288 +From within +a Transferable model object, + +165 +00:08:08,288 --> 00:08:10,090 +set up a ProxyRepresentation + +166 +00:08:10,090 --> 00:08:15,395 +to return a collaboration +metadata instance. + +167 +00:08:15,395 --> 00:08:16,629 +Then, from a view, + +168 +00:08:16,629 --> 00:08:22,836 +initialize ShareLink +with that model object. + +169 +00:08:22,836 --> 00:08:26,673 +For UIKit and AppKit apps, +you use NSItemProvider + +170 +00:08:26,673 --> 00:08:28,408 +to support sharing. + +171 +00:08:28,408 --> 00:08:31,010 +And SWCollaborationMetadata +conforms to + +172 +00:08:31,010 --> 00:08:33,780 +NSItemProviderReading +and writing. + +173 +00:08:33,780 --> 00:08:36,216 +So you simply register +a metadata instance + +174 +00:08:36,216 --> 00:08:39,853 +with an item provider, +to support collaboration. + +175 +00:08:39,853 --> 00:08:41,254 +It's also good practice + +176 +00:08:41,254 --> 00:08:43,757 +to register multiple +representations of the content + +177 +00:08:43,757 --> 00:08:47,527 +to support sharing through +as many channels as possible. + +178 +00:08:47,527 --> 00:08:49,596 +For example, +Messages automatically + +179 +00:08:49,596 --> 00:08:52,232 +offers an option to send +the content as a copy + +180 +00:08:52,232 --> 00:08:55,969 +if you provide +a file representation. + +181 +00:08:55,969 --> 00:08:58,671 +You'll use +the NSItemProvider API with + +182 +00:08:58,671 --> 00:09:03,476 +UIActivityViewController and +UIDragItem on iOS and iPadOS + +183 +00:09:03,476 --> 00:09:08,281 +and NSSharingServicePicker +on macOS. + +184 +00:09:08,281 --> 00:09:12,519 +Here's how to set that up +with the share sheet on iOS. + +185 +00:09:12,519 --> 00:09:16,189 +Create an NSItemProvider +instance. + +186 +00:09:16,189 --> 00:09:18,091 +Register +the collaboration metadata + +187 +00:09:18,091 --> 00:09:19,826 +created in the previous example, + +188 +00:09:19,826 --> 00:09:25,031 +with visibility set +to all processes on the system. + +189 +00:09:25,031 --> 00:09:27,734 +Initialize +UIActivityItemsConfiguration + +190 +00:09:27,734 --> 00:09:30,603 +with the item provider, + +191 +00:09:30,603 --> 00:09:32,972 +then initialize +UIActivityViewController + +192 +00:09:32,972 --> 00:09:36,042 +with that configuration. + +193 +00:09:36,042 --> 00:09:40,947 +And finally, +present the view controller. + +194 +00:09:40,947 --> 00:09:44,918 +It's just as easy +to support drag and drop. + +195 +00:09:44,918 --> 00:09:46,186 +Initialize NSItemProvider + +196 +00:09:46,186 --> 00:09:50,590 +and register the metadata +the same way, + +197 +00:09:50,590 --> 00:09:53,359 +then create a UIDragItem +with the item provider + +198 +00:09:53,359 --> 00:09:58,565 +to use +with the drag and drop APIs. + +199 +00:09:58,565 --> 00:10:03,236 +The API is similar on macOS +for the sharing popover. + +200 +00:10:03,236 --> 00:10:06,573 +Again, set up the item provider. + +201 +00:10:06,573 --> 00:10:08,241 +And this time, +use it to initialize + +202 +00:10:08,241 --> 00:10:11,878 +NSSharingServicePicker. + +203 +00:10:11,878 --> 00:10:17,183 +And then show the picker +relative to a target view. + +204 +00:10:17,183 --> 00:10:20,620 +Drag and drop on macOS +utilizes NSPasteboardItem + +205 +00:10:20,620 --> 00:10:22,822 +rather than NSItemProvider. + +206 +00:10:22,822 --> 00:10:24,624 +To support this, +SharedWithYou exports + +207 +00:10:24,624 --> 00:10:29,329 +an NSPasteboardItem extension. + +208 +00:10:29,329 --> 00:10:31,865 +Using that extension, +set the collaboration metadata + +209 +00:10:31,865 --> 00:10:34,834 +directly on a new +NSPasteboardItem instance + +210 +00:10:34,834 --> 00:10:39,105 +in order to support +drag and drop. + +211 +00:10:39,105 --> 00:10:40,640 +And that's all you need +for a draft + +212 +00:10:40,640 --> 00:10:45,745 +of your collaborative content +to be staged in Messages! + +213 +00:10:45,745 --> 00:10:47,981 +Next, when the user +taps the send button, + +214 +00:10:47,981 --> 00:10:52,552 +the system coordinates with +your app to set up the share. + +215 +00:10:52,552 --> 00:10:54,387 +It does this through +a new class called + +216 +00:10:54,387 --> 00:10:57,390 +SWCollaborationCoordinator. + +217 +00:10:57,390 --> 00:11:00,193 +SWCollaborationCoordinator +is a singleton, + +218 +00:11:00,193 --> 00:11:03,229 +meaning there is +a global shared instance. + +219 +00:11:03,229 --> 00:11:05,498 +That shared instance +coordinates the collaboration + +220 +00:11:05,498 --> 00:11:09,335 +through a delegate you define +called an actionHandler. + +221 +00:11:09,335 --> 00:11:11,137 +To ensure your app +is always available + +222 +00:11:11,137 --> 00:11:12,805 +to coordinate collaborations, + +223 +00:11:12,805 --> 00:11:15,174 +it will be launched +in the background when needed. + +224 +00:11:15,174 --> 00:11:17,677 +So you should register +the delegate soon after launch + +225 +00:11:17,677 --> 00:11:22,181 +and handle actions +immediately to avoid timeouts. + +226 +00:11:22,181 --> 00:11:24,250 +Here's how to set up +the collaboration coordinator + +227 +00:11:24,250 --> 00:11:28,021 +after your app +finishes launching. + +228 +00:11:28,021 --> 00:11:30,256 +Access the singleton +coordinator instance + +229 +00:11:30,256 --> 00:11:33,593 +through the shared property. + +230 +00:11:33,593 --> 00:11:34,827 +Then, in the app delegate's + +231 +00:11:34,827 --> 00:11:36,963 +didFinishLaunchingWithOptions +method, + +232 +00:11:36,963 --> 00:11:38,264 +set the actionHandler property + +233 +00:11:38,264 --> 00:11:39,799 +to an object +that conforms to the + +234 +00:11:39,799 --> 00:11:44,370 +SWCollaborationActionHandler +protocol. + +235 +00:11:44,370 --> 00:11:46,873 +The action handler +protocol uses a new class + +236 +00:11:46,873 --> 00:11:49,175 +called SWAction. + +237 +00:11:49,175 --> 00:11:53,813 +SWActions represent work +your app is expected to perform. + +238 +00:11:53,813 --> 00:11:56,115 +You fulfill actions +to mark them as complete, + +239 +00:11:56,115 --> 00:11:59,018 +and fail them otherwise. + +240 +00:11:59,018 --> 00:12:01,087 +The first action your app +needs to handle + +241 +00:12:01,087 --> 00:12:03,923 +is the start collaboration +action. + +242 +00:12:03,923 --> 00:12:05,558 +SWStartCollaborationAction + +243 +00:12:05,558 --> 00:12:08,595 +contains the collaboration +metadata you set up earlier, + +244 +00:12:08,595 --> 00:12:12,298 +updated with the user's +selected share options. + +245 +00:12:12,298 --> 00:12:14,233 +Once you've performed +the necessary setup, + +246 +00:12:14,233 --> 00:12:17,036 +you fulfill the start action +with the universal link + +247 +00:12:17,036 --> 00:12:18,972 +and a device-independent +identifier + +248 +00:12:18,972 --> 00:12:20,974 +for the collaboration. + +249 +00:12:20,974 --> 00:12:23,276 +If you explicitly +fail the start action, + +250 +00:12:23,276 --> 00:12:26,012 +the message is canceled. + +251 +00:12:26,012 --> 00:12:28,281 +Here's an implementation +to handle the start action + +252 +00:12:28,281 --> 00:12:32,218 +using an example +server request. + +253 +00:12:32,218 --> 00:12:34,287 +First, +retrieve the local identifier, + +254 +00:12:34,287 --> 00:12:36,022 +and user-selected share options + +255 +00:12:36,022 --> 00:12:40,226 +from the action's +metadata property. + +256 +00:12:40,226 --> 00:12:42,795 +Set up a server request +to prepare the collaboration + +257 +00:12:42,795 --> 00:12:47,333 +using the identifier +and options. + +258 +00:12:47,333 --> 00:12:50,169 +Then, send the request +to the server. + +259 +00:12:50,169 --> 00:12:54,507 +This example uses async await. + +260 +00:12:54,507 --> 00:12:56,976 +Finally, fulfill the action +with the universal link + +261 +00:12:56,976 --> 00:13:01,714 +and the device independent +identifier from the response. + +262 +00:13:01,714 --> 00:13:03,116 +Or, if there was an error, + +263 +00:13:03,116 --> 00:13:05,952 +fail the action +to cancel the message. + +264 +00:13:05,952 --> 00:13:07,954 +If the start action +was successful, + +265 +00:13:07,954 --> 00:13:10,223 +the system sends your app +a second action + +266 +00:13:10,223 --> 00:13:13,092 +to update +the collaboration participants. + +267 +00:13:13,092 --> 00:13:15,461 +The SWUpdateCollaboration +ParticipantsAction + +268 +00:13:15,461 --> 00:13:17,664 +contains +the cryptographic identities + +269 +00:13:17,664 --> 00:13:19,866 +for the participants. + +270 +00:13:19,866 --> 00:13:22,468 +The identities are derived from +the collaboration identifier + +271 +00:13:22,468 --> 00:13:26,839 +fulfilled by the start action +in the previous step. + +272 +00:13:26,839 --> 00:13:28,474 +Store the identities +on your server + +273 +00:13:28,474 --> 00:13:30,877 +associated with the content. + +274 +00:13:30,877 --> 00:13:32,879 +You'll use this data +for verifying access + +275 +00:13:32,879 --> 00:13:35,381 +on the recipient devices. + +276 +00:13:35,381 --> 00:13:36,949 +Finally, +fulfilling this action + +277 +00:13:36,949 --> 00:13:40,586 +will send the universal link +in Messages. + +278 +00:13:40,586 --> 00:13:42,088 +This example shows +how to handle + +279 +00:13:42,088 --> 00:13:45,024 +the update participants action. + +280 +00:13:45,024 --> 00:13:46,592 +Retrieve +the collaboration identifier + +281 +00:13:46,592 --> 00:13:49,162 +from the action's metadata. + +282 +00:13:49,162 --> 00:13:50,730 +This is the identifier +you fulfilled + +283 +00:13:50,730 --> 00:13:53,999 +while handling the start action. + +284 +00:13:53,999 --> 00:13:57,203 +Next, retrieve the participant +data to store on your servers + +285 +00:13:57,203 --> 00:14:01,074 +using the action's +addedIdentities property. + +286 +00:14:01,074 --> 00:14:05,111 +Each identity has a Data +property called a root hash. + +287 +00:14:05,111 --> 00:14:06,713 +This is the data you should +store on your server + +288 +00:14:06,713 --> 00:14:09,315 +for later use. + +289 +00:14:09,315 --> 00:14:10,783 +Lance will go over +more of the details + +290 +00:14:10,783 --> 00:14:16,189 +about this property in +the Verifying Access section. + +291 +00:14:16,189 --> 00:14:18,424 +Set up another server +request, this time to add + +292 +00:14:18,424 --> 00:14:20,393 +the participants +to the collaboration + +293 +00:14:20,393 --> 00:14:23,930 +with the target identifier. + +294 +00:14:23,930 --> 00:14:26,666 +And just like before, +send the request to your server, + +295 +00:14:26,666 --> 00:14:29,502 +and fulfill or fail the action. + +296 +00:14:29,502 --> 00:14:34,474 +This time, the fulfill method +does not take any parameters. + +297 +00:14:34,474 --> 00:14:36,442 +Now that you've set up +the collaboration, + +298 +00:14:36,442 --> 00:14:39,545 +your app has everything it needs +to grant immediate access + +299 +00:14:39,545 --> 00:14:42,048 +to the recipients +of the message. + +300 +00:14:42,048 --> 00:14:44,817 +I'll hand it over to Lance +to show you how to do that! + +301 +00:14:44,817 --> 00:14:47,253 +Lance: Thanks, Devin. +In this section, + +302 +00:14:47,253 --> 00:14:49,689 +I'll show how to provide +immediate access + +303 +00:14:49,689 --> 00:14:52,125 +to the recipients using +the identity data + +304 +00:14:52,125 --> 00:14:56,329 +you stored on your server +in the previous step. + +305 +00:14:56,329 --> 00:14:58,998 +The rootHash property +on SWPersonIdentity + +306 +00:14:58,998 --> 00:15:02,135 +is used to do this verification. + +307 +00:15:02,135 --> 00:15:05,938 +A rootHash is a secure value +used to uniquely identify + +308 +00:15:05,938 --> 00:15:08,941 +a participant on their devices. + +309 +00:15:08,941 --> 00:15:11,043 +In order to perform +verification, + +310 +00:15:11,043 --> 00:15:14,213 +you'll need to understand +how to compute a root hash. + +311 +00:15:14,213 --> 00:15:16,883 +I'll take you through that now. + +312 +00:15:16,883 --> 00:15:18,951 +When a collaboration +message is sent, + +313 +00:15:18,951 --> 00:15:24,590 +it's actually sent individually +to each of a person's devices. + +314 +00:15:24,590 --> 00:15:26,492 +Messages identifies each device + +315 +00:15:26,492 --> 00:15:29,562 +using a cryptographic +public key. + +316 +00:15:29,562 --> 00:15:31,464 +Since the goal +is to allow access + +317 +00:15:31,464 --> 00:15:35,134 +only on this set of devices, +the root hash is derived + +318 +00:15:35,134 --> 00:15:39,972 +from the set of public keys +registered to each recipient. + +319 +00:15:39,972 --> 00:15:42,575 +The root hash is the root node +of a data structure + +320 +00:15:42,575 --> 00:15:45,077 +called a Merkle tree. + +321 +00:15:45,077 --> 00:15:47,580 +A Merkle tree is +a binary tree that is built + +322 +00:15:47,580 --> 00:15:51,784 +by performing a sequence +of hashing operations. + +323 +00:15:51,784 --> 00:15:54,620 +In order to derive an identity +for the user based on their + +324 +00:15:54,620 --> 00:15:59,458 +public keys, the keys are used +as the leaves of this tree. + +325 +00:15:59,458 --> 00:16:02,628 +The hashing algorithm used +in the Merkle tree ensures + +326 +00:16:02,628 --> 00:16:07,900 +that the root node can only be +computed from that set of keys. + +327 +00:16:07,900 --> 00:16:11,838 +In this example, +this user has three devices + +328 +00:16:11,838 --> 00:16:15,174 +and three public keys. + +329 +00:16:15,174 --> 00:16:18,277 +The keys will be unique for +each collaboration identifier + +330 +00:16:18,277 --> 00:16:19,846 +provided by your app, + +331 +00:16:19,846 --> 00:16:24,317 +using a process called +key diversification. + +332 +00:16:24,317 --> 00:16:28,054 +To prevent tracking the number +of devices registered to a user, + +333 +00:16:28,054 --> 00:16:31,958 +the set is padded with +random keys up to a fixed size. + +334 +00:16:31,958 --> 00:16:34,627 +The leaf nodes of the tree +are created by hashing + +335 +00:16:34,627 --> 00:16:38,431 +the padded set +of diversified keys. + +336 +00:16:38,431 --> 00:16:41,968 +The SHA256 algorithm is used +for the hashing operations + +337 +00:16:41,968 --> 00:16:44,170 +in this tree. + +338 +00:16:44,170 --> 00:16:47,406 +Then, each pair of leaf nodes +are concatenated + +339 +00:16:47,406 --> 00:16:51,944 +and then hashed to derive +their parent nodes. + +340 +00:16:51,944 --> 00:16:56,048 +This process is repeated +with the parent nodes + +341 +00:16:56,048 --> 00:17:00,653 +and repeated again until +a single root node remains. + +342 +00:17:00,653 --> 00:17:03,789 +This is the root hash +used to uniquely represent + +343 +00:17:03,789 --> 00:17:09,896 +this recipient's identity +across their devices. + +344 +00:17:09,896 --> 00:17:12,565 +Notice that it's possible +to generate a root hash + +345 +00:17:12,565 --> 00:17:17,203 +using a subset of the nodes +from a complete Merkle tree. + +346 +00:17:17,203 --> 00:17:20,740 +The root hash in this tree +can be reproduced using just + +347 +00:17:20,740 --> 00:17:25,144 +the hashes H4, 7, and 11, + +348 +00:17:25,144 --> 00:17:29,315 +along with the diversified +public key P3. + +349 +00:17:29,315 --> 00:17:35,688 +First, hash the public key +to get the missing leaf node H3. + +350 +00:17:35,688 --> 00:17:39,892 +Use H3 and H4 to generate H8. + +351 +00:17:39,892 --> 00:17:44,697 +Use the given H7 node +with H8 to generate H10. + +352 +00:17:44,697 --> 00:17:49,969 +And finally, H10 and H11 +produce the root hash. + +353 +00:17:49,969 --> 00:17:53,205 +It's important to note that you +can prove the public key P3 + +354 +00:17:53,205 --> 00:17:55,775 +was used to generate +a given root hash, + +355 +00:17:55,775 --> 00:17:59,645 +without needing +to reconstruct the entire tree. + +356 +00:17:59,645 --> 00:18:01,781 +The subset of nodes +needed to do this is called + +357 +00:18:01,781 --> 00:18:05,351 +a proof of inclusion. + +358 +00:18:05,351 --> 00:18:08,254 +Verification begins +when a universal link + +359 +00:18:08,254 --> 00:18:10,489 +is opened in your app. + +360 +00:18:10,489 --> 00:18:12,091 +To do this, +you first need to check + +361 +00:18:12,091 --> 00:18:15,027 +that the link is collaborative. + +362 +00:18:15,027 --> 00:18:18,497 +SWCollaborationHighlight +represents a collaborative link + +363 +00:18:18,497 --> 00:18:22,501 +and is retrieved +from SWHighlightCenter. + +364 +00:18:22,501 --> 00:18:24,070 +Use that collaboration highlight + +365 +00:18:24,070 --> 00:18:28,741 +to generate +the proof of inclusion. + +366 +00:18:28,741 --> 00:18:31,310 +To represent +a proof of inclusion, + +367 +00:18:31,310 --> 00:18:35,348 +use a class called +SWPersonIdentityProof. + +368 +00:18:35,348 --> 00:18:37,550 +To perform verification, +you'll first generate + +369 +00:18:37,550 --> 00:18:40,553 +this object along +with a cryptographic signature + +370 +00:18:40,553 --> 00:18:43,089 +to send to your server. + +371 +00:18:43,089 --> 00:18:46,325 +Retrieve the proof using the +getSignedIdentityProof method + +372 +00:18:46,325 --> 00:18:48,861 +on SWHighlightCenter. + +373 +00:18:48,861 --> 00:18:51,731 +It takes an +SWCollaborationHighlight + +374 +00:18:51,731 --> 00:18:57,403 +and some arbitrary data +to be signed by the device. + +375 +00:18:57,403 --> 00:19:00,840 +Use the signature to ensure +the request cannot be replayed + +376 +00:19:00,840 --> 00:19:05,911 +by a bad actor to gain access +to your collaboration. + +377 +00:19:05,911 --> 00:19:09,148 +The data could be a challenge +you request from your server, + +378 +00:19:09,148 --> 00:19:13,185 +or a nonce generated +on the device. + +379 +00:19:13,185 --> 00:19:16,555 +This example uses +the challenge approach. + +380 +00:19:16,555 --> 00:19:19,458 +The URL is passed +to this method on your app's + +381 +00:19:19,458 --> 00:19:22,194 +UIApplicationDelegate. + +382 +00:19:22,194 --> 00:19:24,063 +This URL is the universal link + +383 +00:19:24,063 --> 00:19:28,234 +associated +with the collaboration. + +384 +00:19:28,234 --> 00:19:31,003 +The URL is used +to fetch the associated + +385 +00:19:31,003 --> 00:19:36,909 +SWCollaborationHighlight +from the SWHighlightCenter. + +386 +00:19:36,909 --> 00:19:39,912 +Next, I'll request the challenge +from my server, + +387 +00:19:39,912 --> 00:19:41,914 +and pass the data I get back + +388 +00:19:41,914 --> 00:19:46,719 +to the getSignedIdentityProof +method on SWHighlightCenter, + +389 +00:19:46,719 --> 00:19:50,189 +along with the highlight. + +390 +00:19:50,189 --> 00:19:53,626 +This method returns +a signed identity proof. + +391 +00:19:53,626 --> 00:19:55,428 +I'll discuss +what your server should do + +392 +00:19:55,428 --> 00:19:58,531 +to validate this data later on. + +393 +00:19:58,531 --> 00:20:03,035 +Now I can send the signed proof +to my server for verification. + +394 +00:20:03,035 --> 00:20:09,175 +Finally, I update my user +interface with the result. + +395 +00:20:09,175 --> 00:20:11,544 +The app sends +the proof to the server, + +396 +00:20:11,544 --> 00:20:16,716 +along with the public key +and the signed data. + +397 +00:20:16,716 --> 00:20:18,517 +The data is signed +using the elliptic curve + +398 +00:20:18,517 --> 00:20:23,055 +digital signature algorithm +over the P-256 elliptic curve, + +399 +00:20:23,055 --> 00:20:27,293 +using SHA256 +as a hash function. + +400 +00:20:27,293 --> 00:20:29,495 +Verify the signature on the data + +401 +00:20:29,495 --> 00:20:32,932 +using the public key +in the identity proof. + +402 +00:20:32,932 --> 00:20:35,034 +You can do this +with most commonly used + +403 +00:20:35,034 --> 00:20:38,571 +encryption libraries. + +404 +00:20:38,571 --> 00:20:40,573 +Once you have verified +the signature, + +405 +00:20:40,573 --> 00:20:43,409 +you can trust +that the identity proof was sent + +406 +00:20:43,409 --> 00:20:46,979 +from the device associated +with that public key. + +407 +00:20:46,979 --> 00:20:52,251 +Next, you use the identity proof +to recompute the root hash. + +408 +00:20:52,251 --> 00:20:55,321 +Here is an example of what +an SWPersonIdentityProof + +409 +00:20:55,321 --> 00:20:59,825 +would contain using the example +tree we looked at before. + +410 +00:20:59,825 --> 00:21:04,029 +Use it to reconstruct +the root hash of a Merkle tree. + +411 +00:21:04,029 --> 00:21:06,932 +The public key is P3. + +412 +00:21:06,932 --> 00:21:12,004 +The inclusion hashes are +H4, 7, and 11. + +413 +00:21:12,004 --> 00:21:15,007 +A local key index of 2 +indicates the position + +414 +00:21:15,007 --> 00:21:19,111 +of the public key in the tree. + +415 +00:21:19,111 --> 00:21:21,580 +Here is an example +implementation that reconstructs + +416 +00:21:21,580 --> 00:21:26,285 +a root hash from the properties +on the proof. + +417 +00:21:26,285 --> 00:21:28,320 +A recursive algorithm +works nicely when working + +418 +00:21:28,320 --> 00:21:32,758 +with tree data structures, +so that's what I've done here. + +419 +00:21:32,758 --> 00:21:37,830 +On the initial invocation, pass +in the hash of the public key, + +420 +00:21:37,830 --> 00:21:44,537 +the set of inclusion hashes, +and the public key index. + +421 +00:21:44,537 --> 00:21:49,008 +Next, the first inclusion hash +is pulled out. + +422 +00:21:49,008 --> 00:21:51,710 +The public key index +is checked to see if the key + +423 +00:21:51,710 --> 00:21:56,482 +is on the left or the right +of its sibling. + +424 +00:21:56,482 --> 00:21:58,784 +The selected hashes +are concatenated + +425 +00:21:58,784 --> 00:22:03,088 +in the correct order, +and then hashed. + +426 +00:22:03,088 --> 00:22:06,525 +Next, the consumed node +in the inclusionHashes array + +427 +00:22:06,525 --> 00:22:09,028 +is removed, +and the rest are passed + +428 +00:22:09,028 --> 00:22:14,133 +to a recursive call +to this same function. + +429 +00:22:14,133 --> 00:22:16,235 +The public key index +is also updated + +430 +00:22:16,235 --> 00:22:20,806 +so that it's ready +for the next node in the tree. + +431 +00:22:20,806 --> 00:22:23,409 +With this simple function, +you can quickly compute + +432 +00:22:23,409 --> 00:22:28,547 +a root hash given +an identity proof. + +433 +00:22:28,547 --> 00:22:31,183 +The server can now check +that this generated root hash + +434 +00:22:31,183 --> 00:22:33,986 +is in the list of root hashes +the owner of the document + +435 +00:22:33,986 --> 00:22:36,388 +uploaded during sending. + +436 +00:22:36,388 --> 00:22:38,924 +The hash is present +in the list of known hashes, + +437 +00:22:38,924 --> 00:22:42,561 +so the server can grant access +to the document. + +438 +00:22:42,561 --> 00:22:46,265 +Now you can grant access +to the document with confidence! + +439 +00:22:46,265 --> 00:22:49,735 +To recap the steps you'll follow +to verify an identity: + +440 +00:22:49,735 --> 00:22:51,971 +first, look up +the collaboration highlight + +441 +00:22:51,971 --> 00:22:55,441 +for your content while handling +its universal link. + +442 +00:22:55,441 --> 00:22:59,745 +Next, sign some data and +retrieve the proof of inclusion. + +443 +00:22:59,745 --> 00:23:04,016 +Send the signed data +and proof to your server. + +444 +00:23:04,016 --> 00:23:07,586 +Verify the signature +on the data. + +445 +00:23:07,586 --> 00:23:11,523 +Using the proof of inclusion, +generate the root hash. + +446 +00:23:11,523 --> 00:23:14,860 +Finally, compare the root hash +to the list of known identities + +447 +00:23:14,860 --> 00:23:19,832 +associated with that content. + +448 +00:23:19,832 --> 00:23:22,134 +Now that you know +all about verifying access + +449 +00:23:22,134 --> 00:23:24,503 +to your collaboration links, +I'll talk about + +450 +00:23:24,503 --> 00:23:28,307 +how to coordinate participant +changes with Messages. + +451 +00:23:28,307 --> 00:23:31,143 +When the participants +in a Messages group change, + +452 +00:23:31,143 --> 00:23:33,746 +and that group +is collaborating together, + +453 +00:23:33,746 --> 00:23:37,283 +a user can choose to propagate +those changes to your app, + +454 +00:23:37,283 --> 00:23:40,352 +right from a banner +in the Messages thread. + +455 +00:23:40,352 --> 00:23:42,855 +In this scenario, +your app receives another + +456 +00:23:42,855 --> 00:23:46,191 +SWUpdateCollaboration +ParticipantsAction + +457 +00:23:46,191 --> 00:23:50,562 +containing the added +and removed identities. + +458 +00:23:50,562 --> 00:23:53,565 +You'll use the same code +you wrote to handle this action + +459 +00:23:53,565 --> 00:23:55,434 +when setting up +a collaboration, + +460 +00:23:55,434 --> 00:24:00,039 +but you'll also need +to handle removed participants. + +461 +00:24:00,039 --> 00:24:02,741 +For removal, simply look up +any account associated + +462 +00:24:02,741 --> 00:24:06,946 +with a removed identity +and revoke their access. + +463 +00:24:06,946 --> 00:24:09,248 +If no account is yet associated, + +464 +00:24:09,248 --> 00:24:12,885 +simply delete the root hash +from your database. + +465 +00:24:12,885 --> 00:24:14,186 +Here's +the implementation for + +466 +00:24:14,186 --> 00:24:19,224 +the update participants action +that Devin went over earlier. + +467 +00:24:19,224 --> 00:24:20,392 +This example uses + +468 +00:24:20,392 --> 00:24:24,964 +the removed identities property +on the action + +469 +00:24:24,964 --> 00:24:28,834 +and passes them to +a similar removal API request. + +470 +00:24:28,834 --> 00:24:33,072 +Note that this code only shows +handling removed identities, + +471 +00:24:33,072 --> 00:24:34,907 +but a complete implementation +should handle + +472 +00:24:34,907 --> 00:24:38,577 +both added +and removed identities. + +473 +00:24:38,577 --> 00:24:42,481 +And that's all you need +to handle participant changes! + +474 +00:24:42,481 --> 00:24:45,818 +Lastly, when changes +are made to a collaboration, + +475 +00:24:45,818 --> 00:24:48,320 +your app posts notices +about those changes + +476 +00:24:48,320 --> 00:24:50,923 +to be shown +directly in Messages. + +477 +00:24:50,923 --> 00:24:53,125 +There are a few types +of supported notices + +478 +00:24:53,125 --> 00:24:55,294 +I'll go over in this section. + +479 +00:24:55,294 --> 00:24:57,129 +Notices are displayed +as a banner + +480 +00:24:57,129 --> 00:25:01,133 +right in the conversation +where the link was shared. + +481 +00:25:01,133 --> 00:25:03,936 +The banner includes +a description of what changed, + +482 +00:25:03,936 --> 00:25:07,573 +as well as who made the change. + +483 +00:25:07,573 --> 00:25:09,808 +In this conversation, +Charlie made edits + +484 +00:25:09,808 --> 00:25:12,778 +to the Baking Recipes document. + +485 +00:25:12,778 --> 00:25:14,580 +Tapping the show button +connects them + +486 +00:25:14,580 --> 00:25:17,516 +right back to the content. + +487 +00:25:17,516 --> 00:25:19,885 +To represent a notice, +the SharedWithYou framework + +488 +00:25:19,885 --> 00:25:24,423 +has a protocol named +SWHighlightEvent. + +489 +00:25:24,423 --> 00:25:27,226 +Highlight events are +initialized with SWHighlights + +490 +00:25:27,226 --> 00:25:31,730 +retrieved +from the SWHighlightCenter API. + +491 +00:25:31,730 --> 00:25:36,201 +Messages supports +several categories of events. + +492 +00:25:36,201 --> 00:25:40,239 +A change event +for content updates or comments, + +493 +00:25:40,239 --> 00:25:44,243 +a membership event when +a participant joins or leaves, + +494 +00:25:44,243 --> 00:25:49,448 +a mention event when a user +is mentioned in a collaboration, + +495 +00:25:49,448 --> 00:25:54,019 +and a persistence event when +content is moved or deleted. + +496 +00:25:54,019 --> 00:25:56,655 +Here's an example showing +how to post a change event + +497 +00:25:56,655 --> 00:25:59,758 +for an edit to a collaboration. + +498 +00:25:59,758 --> 00:26:02,161 +Using the highlight center API, + +499 +00:26:02,161 --> 00:26:03,929 +retrieve +a collaboration highlight + +500 +00:26:03,929 --> 00:26:06,098 +for the target identifier. + +501 +00:26:06,098 --> 00:26:09,768 +Remember, this identifier +is one you defined during + +502 +00:26:09,768 --> 00:26:13,172 +the collaboration initiation, +so your app should have this + +503 +00:26:13,172 --> 00:26:17,109 +available for use when +a content change is made. + +504 +00:26:17,109 --> 00:26:21,747 +Next, create a highlight change +event instance. + +505 +00:26:21,747 --> 00:26:23,749 +The initializer +takes a highlight, + +506 +00:26:23,749 --> 00:26:25,951 +and a trigger enum value; + +507 +00:26:25,951 --> 00:26:30,122 +in this case, +set it to the edit type. + +508 +00:26:30,122 --> 00:26:32,858 +Finally, again using +the highlight center, + +509 +00:26:32,858 --> 00:26:36,195 +post the notice for that event. + +510 +00:26:36,195 --> 00:26:39,298 +Similarly, +for membership changes, + +511 +00:26:39,298 --> 00:26:41,100 +post a membership event, + +512 +00:26:41,100 --> 00:26:44,203 +this time passing +the addedCollaborator + +513 +00:26:44,203 --> 00:26:49,174 +or removedCollaborator +trigger type. + +514 +00:26:49,174 --> 00:26:52,344 +Next, if your app +supports user mentions, + +515 +00:26:52,344 --> 00:26:55,180 +you can post a mention event. + +516 +00:26:55,180 --> 00:26:56,815 +Initialize a person identity + +517 +00:26:56,815 --> 00:27:00,085 +with the root hash +of the mentioned user. + +518 +00:27:00,085 --> 00:27:02,621 +Recall that you associated +a person identity + +519 +00:27:02,621 --> 00:27:07,759 +with a user account in your app +while verifying access. + +520 +00:27:07,759 --> 00:27:11,430 +Then, post the mention event +in the same way, + +521 +00:27:11,430 --> 00:27:15,634 +this time passing the mentioned +identity as a parameter. + +522 +00:27:15,634 --> 00:27:17,803 +This notice will only be shown +in Messages + +523 +00:27:17,803 --> 00:27:21,840 +to the mentioned user. + +524 +00:27:21,840 --> 00:27:24,543 +Finally, +use the persistence event type + +525 +00:27:24,543 --> 00:27:29,748 +when content is moved, +renamed, or deleted. + +526 +00:27:29,748 --> 00:27:32,117 +Here, the renamed +trigger type is used, + +527 +00:27:32,117 --> 00:27:37,789 +to signify that the user +changed the name of the content. + +528 +00:27:37,789 --> 00:27:41,159 +And that is how your app +can notify collaborators, + +529 +00:27:41,159 --> 00:27:46,231 +and they will get those +updates right in Messages. + +530 +00:27:46,231 --> 00:27:48,100 +Devin: And with that, +you're ready to integrate + +531 +00:27:48,100 --> 00:27:50,669 +your app's collaboration +experience with messages + +532 +00:27:50,669 --> 00:27:52,771 +by following a few steps. + +533 +00:27:52,771 --> 00:27:55,674 +Set up your content +to be shared collaboratively, + +534 +00:27:55,674 --> 00:27:58,644 +cryptographically verify +participant access, + +535 +00:27:58,644 --> 00:28:00,779 +keep track +of participant changes, + +536 +00:28:00,779 --> 00:28:03,782 +and post notices in Messages +to connect your users + +537 +00:28:03,782 --> 00:28:06,552 +right back to the content. + +538 +00:28:06,552 --> 00:28:07,953 +Be sure to check out + +539 +00:28:07,953 --> 00:28:11,256 +the "Enhance collaboration +experiences with Messages" video + +540 +00:28:11,256 --> 00:28:13,191 +to learn more about +the new UI elements + +541 +00:28:13,191 --> 00:28:15,994 +you can display +for collaborations. + +542 +00:28:15,994 --> 00:28:18,297 +Lance: We can't wait to get +collaborating with your apps! + +543 +00:28:18,297 --> 00:28:20,966 +Devin and Lance, +cryptographically signing off. + +544 +00:28:20,966 --> 00:28:22,334 +Devin: Thanks for watching! + +545 +00:28:22,334 --> 00:28:25,938 +♪ + diff --git a/eng/2022 Session 10094 Add Shared with You to your app en.srt b/eng/2022 Session 10094 Add Shared with You to your app en.srt new file mode 100644 index 0000000..c89f02a --- /dev/null +++ b/eng/2022 Session 10094 Add Shared with You to your app en.srt @@ -0,0 +1,1209 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:10,010 --> 00:00:11,912 +Karthik: Hi, I'm Karthik, + +3 +00:00:11,945 --> 00:00:14,581 +an engineer on the Messages team. + +4 +00:00:14,615 --> 00:00:18,318 +In this video, +I'm going to introduce Shared with You, + +5 +00:00:18,352 --> 00:00:20,921 +and how to adopt it in your app. + +6 +00:00:22,523 --> 00:00:25,959 +Shared with You was announced in iOS 15. + +7 +00:00:25,993 --> 00:00:29,830 +It is designed to make it easier +for you to revisit links + +8 +00:00:29,863 --> 00:00:33,667 +that friends and family send you +in Messages. + +9 +00:00:33,700 --> 00:00:37,604 +System applications that have +adopted Shared with You are + +10 +00:00:37,638 --> 00:00:40,240 +Safari, News, + +11 +00:00:40,274 --> 00:00:42,910 +Music, Podcasts, + +12 +00:00:42,943 --> 00:00:46,213 +the TV app and Photos. + +13 +00:00:46,246 --> 00:00:51,351 +Shared with You meets you +where you're consuming content. + +14 +00:00:51,385 --> 00:00:56,390 +Many times content is received +when we are not ready to consume it. + +15 +00:00:56,423 --> 00:01:01,028 +For example, a friend sends +a recommendation for a TV show + +16 +00:01:01,061 --> 00:01:03,630 +while you are out grocery shopping, + +17 +00:01:03,664 --> 00:01:07,901 +and then it's forgotten +in the course of the day. + +18 +00:01:07,935 --> 00:01:12,840 +Shared with You makes it easier +to discover this TV show later on + +19 +00:01:12,873 --> 00:01:17,477 +when you are browsing +for TV shows to watch in the TV app + +20 +00:01:17,511 --> 00:01:22,816 +Shared with You makes it convenient to +pick up a Messages conversation + +21 +00:01:22,850 --> 00:01:25,252 +right from within the app, + +22 +00:01:25,285 --> 00:01:29,456 +allowing you to stay in context +of shared content + +23 +00:01:29,489 --> 00:01:31,792 +without leaving the app. + +24 +00:01:33,594 --> 00:01:36,964 +Your content can be pinned in Messages. + +25 +00:01:36,997 --> 00:01:42,436 +It elevates the content +in Shared with You and Search. + +26 +00:01:42,469 --> 00:01:45,405 +This is a signal +that content is important, + +27 +00:01:45,439 --> 00:01:48,809 +and starts the flow +of automatic sharing consent. + +28 +00:01:48,842 --> 00:01:51,211 +More on that later. + +29 +00:01:51,245 --> 00:01:57,618 +In iOS 16, Shared with You is extended +to include your apps, links, and content. + +30 +00:01:57,651 --> 00:02:01,822 +This takes the convenience of +Shared with You and puts it in your apps. + +31 +00:02:03,023 --> 00:02:06,927 +I'll first talk about the design +of Shared with You in your app, + +32 +00:02:06,960 --> 00:02:09,997 +then show you how it works, + +33 +00:02:10,030 --> 00:02:13,066 +and then walk you through +how to adopt Shared with You. + +34 +00:02:14,401 --> 00:02:16,203 +Starting with design. + +35 +00:02:16,236 --> 00:02:21,208 +There are two parts of Shared with You +that are part of your app experience: + +36 +00:02:21,241 --> 00:02:26,013 +The Shared with You shelf, +and the Shared with You attribution view. + +37 +00:02:26,046 --> 00:02:29,516 +The Shared with You shelf +is a dedicated space + +38 +00:02:29,550 --> 00:02:32,386 +in your app's browsing experience + +39 +00:02:32,419 --> 00:02:37,090 +that highlights content +that was shared in Messages. + +40 +00:02:37,124 --> 00:02:44,198 +For example, the TV app's "Watch Now" tab +has a Shared with You shelf. + +41 +00:02:44,231 --> 00:02:48,502 +So does the Listen Now tab +in Music and Podcasts. + +42 +00:02:49,937 --> 00:02:55,475 +The content provided by Shared with You +is a ranked and ordered list. + +43 +00:02:55,509 --> 00:02:58,879 +I'll go over that later in the video. + +44 +00:02:58,912 --> 00:03:01,949 +For each item +in the Shared with You shelf, + +45 +00:03:01,982 --> 00:03:07,087 +show a rich preview +along with an attribution view. + +46 +00:03:07,120 --> 00:03:11,391 +A rich preview has thumbnail, a title, + +47 +00:03:11,425 --> 00:03:14,361 +and a subtitle if applicable. + +48 +00:03:14,394 --> 00:03:19,166 +In this example, the rich preview +consists of podcast art, + +49 +00:03:19,199 --> 00:03:22,669 +episode name, and the show name. + +50 +00:03:22,703 --> 00:03:27,341 +There is also an attribution view +for each shared content + +51 +00:03:27,374 --> 00:03:29,610 +in the Shared with You shelf. + +52 +00:03:29,643 --> 00:03:32,479 +This is the all of the information +that allows context + +53 +00:03:32,513 --> 00:03:34,681 +to be gleaned at a glance. + +54 +00:03:36,083 --> 00:03:39,486 +Have a Show More element +that can expand the view + +55 +00:03:39,520 --> 00:03:44,024 +or navigate to showing all of your app's +Shared with You content. + +56 +00:03:44,057 --> 00:03:47,794 +The Attribution view +is an out of process view + +57 +00:03:47,828 --> 00:03:54,434 +that securely displays the names +and avatars of who shared it. + +58 +00:03:54,468 --> 00:03:59,907 +It also shows +if the content was pinned in Messages. + +59 +00:03:59,940 --> 00:04:05,012 +Show the attribution view +in the details view of your content. + +60 +00:04:05,045 --> 00:04:09,216 +This allows people to connect +content in your app + +61 +00:04:09,249 --> 00:04:12,886 +back to the Messages conversation +it was shared in. + +62 +00:04:12,920 --> 00:04:17,124 +For example, +when I'm browsing for a TV show to watch, + +63 +00:04:17,157 --> 00:04:23,497 +the attribution view helps me recall +that a friend had recommend it. + +64 +00:04:23,530 --> 00:04:28,202 +I can quickly tell my friend +that I'm about to watch the show. + +65 +00:04:28,235 --> 00:04:31,872 +All this happens right in app, + +66 +00:04:31,905 --> 00:04:36,376 +and after replying, +I'm right back at the content. + +67 +00:04:36,410 --> 00:04:39,179 +The attribution view is interactive. + +68 +00:04:39,213 --> 00:04:42,482 +Tapping on the view takes you +to the Messages conversation + +69 +00:04:42,516 --> 00:04:45,185 +right from within the application. + +70 +00:04:45,219 --> 00:04:50,123 +The attribution view also presents +contextual menu options + +71 +00:04:50,157 --> 00:04:53,994 +such as "Reply" and "Remove". + +72 +00:04:54,027 --> 00:05:00,968 +The "Reply" content menu option +functions similar to tapping the view. + +73 +00:05:01,001 --> 00:05:04,638 +The "Remove" context menu option +is a way to tell the Shared with You + +74 +00:05:04,671 --> 00:05:08,509 +not to surface a piece of content +going forward. + +75 +00:05:08,542 --> 00:05:12,346 +The same contextual menus +are made available to you + +76 +00:05:12,379 --> 00:05:16,717 +to add to your content's contextual menus. + +77 +00:05:16,750 --> 00:05:21,488 +The title for the Remove context menu +can be customized. + +78 +00:05:21,522 --> 00:05:24,358 +For example in Safari, + +79 +00:05:24,391 --> 00:05:29,396 +the contextual menu +for the web page shows "Remove Link". + +80 +00:05:29,429 --> 00:05:35,269 +I'll go over how you can do this +in your app later on in this video. + +81 +00:05:35,302 --> 00:05:38,872 +Now you know where to present +a Shared with You shelf + +82 +00:05:38,906 --> 00:05:42,242 +and where to show the Attribution View. + +83 +00:05:42,276 --> 00:05:45,045 +Let me show you how Shared with You works. + +84 +00:05:45,078 --> 00:05:52,386 +Links shared by friends and family in +Messages are surfaced in Shared with You. + +85 +00:05:52,419 --> 00:05:55,589 +Links in group conversations are surfaced + +86 +00:05:55,622 --> 00:05:59,826 +when at least one participant +is a contact. + +87 +00:05:59,860 --> 00:06:05,332 +Shared with You is based on the same +technology behind Universal Links. + +88 +00:06:05,365 --> 00:06:11,505 +Universal Links allows for +seamless linking to content in your app + +89 +00:06:11,538 --> 00:06:14,208 +or on your website. + +90 +00:06:14,241 --> 00:06:19,346 +Users have granular control +over Shared with You content. + +91 +00:06:20,414 --> 00:06:25,118 +They can choose what content is shared +outside of Messages + +92 +00:06:25,152 --> 00:06:27,988 +on a per conversation basis, + +93 +00:06:28,021 --> 00:06:31,124 +per app, or globally. + +94 +00:06:31,158 --> 00:06:36,396 +This permission doesn't need +to be requested in advance. + +95 +00:06:36,430 --> 00:06:39,399 +It happens organically. + +96 +00:06:39,433 --> 00:06:46,340 +Pinning links is implicit permission +to surface the content in Shared with You. + +97 +00:06:46,373 --> 00:06:52,012 +Pinned content is always available +to Shared with You in your app. + +98 +00:06:52,045 --> 00:06:57,951 +Based on heuristics when content +for your app is pinned in Messages, + +99 +00:06:57,985 --> 00:07:03,123 +an option to enable +automatic sharing is presented. + +100 +00:07:03,156 --> 00:07:06,527 +When automatic sharing is turned on, + +101 +00:07:06,560 --> 00:07:12,299 +further content is automatically available +to Shared with You in your app. + +102 +00:07:13,834 --> 00:07:17,304 +Content in Shared with You is ordered. + +103 +00:07:17,337 --> 00:07:22,910 +The first item in the Shared with You +shelf is curated by Siri Suggestions + +104 +00:07:22,943 --> 00:07:26,914 +based on various signals from the system. + +105 +00:07:26,947 --> 00:07:30,751 +This is followed by Pinned items + +106 +00:07:30,784 --> 00:07:34,788 +and the remainder of the list +is chronologically ordered. + +107 +00:07:36,790 --> 00:07:40,661 +Siri Suggestions uses signals +such as "Has the user viewed + +108 +00:07:40,694 --> 00:07:43,130 +or interacted with the content?" + +109 +00:07:43,163 --> 00:07:45,032 +Is the content Pinned? + +110 +00:07:45,065 --> 00:07:48,902 +In which context +is the content being presented? + +111 +00:07:48,936 --> 00:07:52,539 +Your app plays a part +in providing this feedback. + +112 +00:07:52,573 --> 00:07:56,443 +I'll go over in detail +a little later on in the video. + +113 +00:07:56,476 --> 00:08:02,649 +All this is done to ensure that content +is not too transient or stale. + +114 +00:08:02,683 --> 00:08:07,521 +In a conversation +when a link is shared multiple times, + +115 +00:08:07,554 --> 00:08:12,726 +Shared with You surfaces +only the most recent message. + +116 +00:08:12,759 --> 00:08:17,331 +When a link is shared in +multiple Messages conversations, + +117 +00:08:17,364 --> 00:08:23,470 +Shared with You visually represents +that via the attribution view. + +118 +00:08:23,504 --> 00:08:27,908 +For example, +both Enrique and Sarah have shared + +119 +00:08:27,941 --> 00:08:31,178 +the Top 25 Chicago playlist. + +120 +00:08:31,211 --> 00:08:36,984 +The attribution view shows +both their contact avatars. + +121 +00:08:37,017 --> 00:08:41,889 +Tapping on the attribution view +presents a disambiguation menu, + +122 +00:08:41,922 --> 00:08:47,861 +allowing you to choose which Messages +conversation to reply to. + +123 +00:08:47,895 --> 00:08:51,064 +Security and Privacy +was a primary consideration + +124 +00:08:51,098 --> 00:08:53,967 +and focus when designing Shared with You. + +125 +00:08:54,001 --> 00:08:58,105 +The attribution views +and disambiguation views + +126 +00:08:58,138 --> 00:09:02,309 +are drawn on your behalf out of process. + +127 +00:09:02,342 --> 00:09:05,279 +Shared with You protects +your app's content + +128 +00:09:05,312 --> 00:09:08,215 +via the Universal Links association + +129 +00:09:08,248 --> 00:09:12,219 +so it is accessible to only your app. + +130 +00:09:12,252 --> 00:09:17,958 +Apps do not have access to Messages +recipients and conversations. + +131 +00:09:19,426 --> 00:09:22,196 +Now you know how Shared with You works. + +132 +00:09:22,229 --> 00:09:24,131 +On to the most exciting part– + +133 +00:09:24,164 --> 00:09:27,568 +how to adopt Shared with You in your app. + +134 +00:09:27,601 --> 00:09:31,104 +First, adopt Universal Links. + +135 +00:09:31,138 --> 00:09:34,441 +Then, add the new Shared with You +Capability, + +136 +00:09:34,474 --> 00:09:37,411 +under the Capabilities tab in Xcode. + +137 +00:09:37,444 --> 00:09:41,682 +Then, put a Shared with You shelf +in your app, + +138 +00:09:41,715 --> 00:09:46,420 +and add attribution view +to your content. + +139 +00:09:46,453 --> 00:09:50,224 +Let me briefly talk about +adopting Universal Links. + +140 +00:09:51,491 --> 00:09:56,730 +Universal Links allows your users +to intelligently follow links to content + +141 +00:09:56,763 --> 00:09:59,633 +in your app or to your website. + +142 +00:09:59,666 --> 00:10:03,537 +Take the following steps +to support universal links. + +143 +00:10:03,570 --> 00:10:06,974 +First create a two-way association + +144 +00:10:07,007 --> 00:10:10,477 +between your app and your website + +145 +00:10:10,511 --> 00:10:14,715 +and specify the URLs +that your app handles. + +146 +00:10:14,748 --> 00:10:20,854 +You create them by adding the Associated +Domains entitlement to your app, + +147 +00:10:20,888 --> 00:10:26,126 +and by adding a JSON file +to your web server. + +148 +00:10:26,159 --> 00:10:28,929 +Next, update your app delegate + +149 +00:10:28,962 --> 00:10:32,966 +to respond to the user activity object +the system provides + +150 +00:10:33,000 --> 00:10:36,670 +when a universal link routes to your app. + +151 +00:10:36,703 --> 00:10:42,409 +For more information, please watch +the "What's new in Universal Links" video. + +152 +00:10:42,442 --> 00:10:47,614 +In iOS 16 we have introduced +a new Framework called Shared with You. + +153 +00:10:47,648 --> 00:10:51,752 +There are three main classes +in the Shared with You Framework: + +154 +00:10:51,785 --> 00:10:54,121 +SWHighlightCenter, + +155 +00:10:54,154 --> 00:10:59,026 +SWHighlight, and SWAttributionView. + +156 +00:10:59,059 --> 00:11:03,497 +SWHighlightCenter is the class that helps +you get Shared with You content + +157 +00:11:03,530 --> 00:11:04,831 +for your app. + +158 +00:11:04,865 --> 00:11:10,204 +SWHighlight is a model object +that wraps your app's shared content. + +159 +00:11:10,237 --> 00:11:15,042 +SWAttributionView is the view +that helps connect your content + +160 +00:11:15,075 --> 00:11:17,778 +back to a Messages conversation + +161 +00:11:17,811 --> 00:11:20,547 +and displays attribution information. + +162 +00:11:21,949 --> 00:11:25,953 +The highlight center is +a simple object that consists of: + +163 +00:11:25,986 --> 00:11:31,358 +Highlights, +which is an array of SWHighlight objects; + +164 +00:11:31,391 --> 00:11:35,462 +and a delegate property +by which apps get notified + +165 +00:11:35,495 --> 00:11:41,768 +when there is content added, +removed, or updated by Shared with You. + +166 +00:11:41,802 --> 00:11:46,373 +A highlight is represented +by the SWHighlight class. + +167 +00:11:46,406 --> 00:11:52,513 +It is used to pass the URL for your app's +content that was shared in Messages. + +168 +00:11:52,546 --> 00:11:57,317 +You use it to refer to your content, +render a rich preview, + +169 +00:11:57,351 --> 00:12:01,021 +and navigate to the content in your app. + +170 +00:12:01,054 --> 00:12:05,726 +Let me show you how to enumerate +Shared with You content in your app. + +171 +00:12:06,994 --> 00:12:12,299 +First create an instance +of SWHighlightCenter. + +172 +00:12:12,332 --> 00:12:16,670 +Then set the delegate property. + +173 +00:12:16,703 --> 00:12:22,109 +Implement the SWHighlightCenterDelegate +method. + +174 +00:12:22,142 --> 00:12:24,811 +Use the highlights property +on the highlight center + +175 +00:12:24,845 --> 00:12:29,149 +to access your app's Shared with You +content. + +176 +00:12:29,183 --> 00:12:32,786 +Apps can choose to keep +the previous list of highlights + +177 +00:12:32,819 --> 00:12:37,491 +in order to quickly diff that list +against the latest list. + +178 +00:12:39,193 --> 00:12:44,464 +Use the URL property on each highlight +to generate a Rich preview + +179 +00:12:44,498 --> 00:12:47,634 +of your app's content. + +180 +00:12:47,668 --> 00:12:53,674 +And it's as easy as that to enumerate +Shared with You content in your app. + +181 +00:12:53,707 --> 00:12:59,012 +Next, I'll show you how to add +and customize the Attribution View + +182 +00:12:59,046 --> 00:13:01,548 +to your app's Shared with You content. + +183 +00:13:01,582 --> 00:13:07,254 +SWAttributionView is the view +that shows the names and avatars + +184 +00:13:07,287 --> 00:13:10,090 +of who shared the content. + +185 +00:13:10,123 --> 00:13:14,595 +Each highlight has +a corresponding attribution view. + +186 +00:13:14,628 --> 00:13:17,831 +Setting the highlight Property +on the attribution view + +187 +00:13:17,865 --> 00:13:22,102 +triggers the out of process rendering +of the attribution information. + +188 +00:13:23,604 --> 00:13:28,275 +Specify a maximum width +the attribution view can take. + +189 +00:13:28,308 --> 00:13:34,281 +The attribution view will fill +or fit the space as needed. + +190 +00:13:34,314 --> 00:13:39,853 +Set the alignment of the attribution view +within the maximum space. + +191 +00:13:39,887 --> 00:13:41,321 +Let me show you an example. + +192 +00:13:42,923 --> 00:13:46,693 +Create an instance of SWAttributionView + +193 +00:13:46,727 --> 00:13:49,897 +and set the highlight property. + +194 +00:13:49,930 --> 00:13:52,699 +Set the preferredMaxLayoutWidth. + +195 +00:13:52,733 --> 00:13:57,905 +In this example it spans the bottom +of the shared content thumbnail. + +196 +00:13:57,938 --> 00:14:00,507 +Constrain this view's width anchor + +197 +00:14:00,541 --> 00:14:05,879 +or set its frame width to control +the maximum width of its contents. + +198 +00:14:05,913 --> 00:14:09,950 +Set the maximum AX content size category +for the view as necessary + +199 +00:14:09,983 --> 00:14:13,153 +using: +minimumContentSizeCategory + +200 +00:14:13,187 --> 00:14:18,392 +or maximumContentSizeCategory +properties on UIView. + +201 +00:14:18,425 --> 00:14:22,162 +Provide enough vertical space +around this view. + +202 +00:14:22,196 --> 00:14:26,166 +The view's height is dependent +on the preferredContentSizeCategory, + +203 +00:14:26,200 --> 00:14:28,836 +and the resulting font size. + +204 +00:14:28,869 --> 00:14:32,306 +If the view's height +is unnecessarily constrained, + +205 +00:14:32,339 --> 00:14:36,310 +then the view might be clipped +or not get drawn. + +206 +00:14:36,343 --> 00:14:40,747 +The horizontalAlignment is set +to leading in this case. + +207 +00:14:40,781 --> 00:14:44,084 +It can also be set to Center + +208 +00:14:44,117 --> 00:14:46,887 +or Trailing. + +209 +00:14:46,920 --> 00:14:51,725 +Next, let me show you how to customize +the Attribution view. + +210 +00:14:51,758 --> 00:14:54,561 +Setting a display context helps inform +the system + +211 +00:14:54,595 --> 00:14:58,866 +about how the user is consuming +the attributed content. + +212 +00:14:58,899 --> 00:15:03,303 +It also influences ranking of +Shared with You content for your app. + +213 +00:15:03,337 --> 00:15:06,106 +Set this before it's added to a window. + +214 +00:15:06,139 --> 00:15:10,077 +The background style of +the attribution view can be customized + +215 +00:15:10,110 --> 00:15:14,615 +based on the content's background +it is being used against. + +216 +00:15:14,648 --> 00:15:17,284 +Let me show you an example. + +217 +00:15:17,317 --> 00:15:22,990 +The default value for +displayContext property is summary. + +218 +00:15:23,023 --> 00:15:28,762 +This indicates that the content +is being shown for consumption. + +219 +00:15:28,795 --> 00:15:31,198 +Set the displayContext to detail + +220 +00:15:31,231 --> 00:15:34,401 +when the user is actively consuming +the content, + +221 +00:15:34,434 --> 00:15:39,173 +like watching a movie +or listening to a podcast. + +222 +00:15:39,206 --> 00:15:43,510 +Setting the displayContext +is the feedback your app can provide. + +223 +00:15:43,544 --> 00:15:47,548 +This'll help Siri Suggestions +rank content for you app. + +224 +00:15:47,581 --> 00:15:51,385 +Set the background style +for the attribution view to color + +225 +00:15:51,418 --> 00:15:55,355 +when placing the attribution view +over monochrome backgrounds + +226 +00:15:55,389 --> 00:15:57,558 +like in this example. + +227 +00:15:57,591 --> 00:16:03,964 +Use material when placing the attribution +view over multicolored backgrounds. + +228 +00:16:03,997 --> 00:16:08,669 +This sets a material background blur +for the view's contents. + +229 +00:16:08,702 --> 00:16:14,041 +In this example, Safari's landing page +has a background image. + +230 +00:16:14,074 --> 00:16:17,611 +The contents of the attribution view +are more visible + +231 +00:16:17,644 --> 00:16:21,582 +by setting the correct background style. + +232 +00:16:21,615 --> 00:16:26,086 +Next let me show you how to add Shared +with You Contextual Menus + +233 +00:16:26,119 --> 00:16:30,324 +to your app's content +and customize the title. + +234 +00:16:30,357 --> 00:16:34,127 +The existing contextual menu +attached to your app's content + +235 +00:16:34,161 --> 00:16:39,032 +can be supplemented +by the attribution view's menu. + +236 +00:16:39,066 --> 00:16:42,669 +This menu should be added inline with + +237 +00:16:42,703 --> 00:16:48,175 +or at the end of the contextual menu +it augments. + +238 +00:16:48,208 --> 00:16:53,814 +A custom title for the Remove contextual +menu option can be provided. + +239 +00:16:53,847 --> 00:16:59,052 +The string should include the word +"Remove", localized correctly. + +240 +00:16:59,086 --> 00:17:04,391 +In this example, Safari has customized +the Remove menu title to "Remove Link" + +241 +00:17:04,424 --> 00:17:08,762 +in its content's context menu at the end. + +242 +00:17:08,795 --> 00:17:12,533 +Let me show you how to do this +via an example. + +243 +00:17:12,566 --> 00:17:16,036 +First create an instance +of the attribution view + +244 +00:17:16,069 --> 00:17:18,505 +and set the highlight property. + +245 +00:17:18,539 --> 00:17:21,975 +Provide a custom title +for the Remove context menu + +246 +00:17:22,009 --> 00:17:24,945 +via the menuTitleForHideAction. + +247 +00:17:24,978 --> 00:17:28,081 +When configuring +your content's context menu, + +248 +00:17:28,115 --> 00:17:34,188 +get the menu from the attribution view +via the supplementalMenu property. + +249 +00:17:34,221 --> 00:17:38,692 +Then append them +to your content's context menu. + +250 +00:17:38,725 --> 00:17:43,363 +These easy steps allow your app +to add the context menu option + +251 +00:17:43,397 --> 00:17:49,336 +available on the attribution view +to your content's context menus. + +252 +00:17:49,369 --> 00:17:54,508 +Now you know all that is needed +to adopt Shared with You in your app. + +253 +00:17:54,541 --> 00:17:59,680 +Let's recap the three easy steps +to adopt Shared with You in your app. + +254 +00:17:59,713 --> 00:18:01,849 +Go adopt Universal Links. + +255 +00:18:01,882 --> 00:18:07,354 +Then add the new Shared with You +Capability in Xcode for your app. + +256 +00:18:07,387 --> 00:18:11,625 +Import and use the new +Shared with You framework. + +257 +00:18:11,658 --> 00:18:15,462 +I'm looking forward to sharing +your content in Messages + +258 +00:18:15,495 --> 00:18:18,498 +and using Shared with You in your app. + +259 +00:18:18,532 --> 00:18:21,869 +Thank you for your time and attention. + diff --git a/eng/2022 Session 10095 Enhance collaboration experiences with Messages en.srt b/eng/2022 Session 10095 Enhance collaboration experiences with Messages en.srt new file mode 100644 index 0000000..3d6b137 --- /dev/null +++ b/eng/2022 Session 10095 Enhance collaboration experiences with Messages en.srt @@ -0,0 +1,1951 @@ +1 +00:00:00,000 --> 00:00:02,836 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:02,836 --> 00:00:09,843 +♪ + +3 +00:00:09,843 --> 00:00:11,845 +Miranda Zhou: Hi, +my name is Miranda + +4 +00:00:11,845 --> 00:00:14,681 +and I'm an engineer +on the Sharing team. + +5 +00:00:14,681 --> 00:00:16,250 +Elana Stettin: I'm Elana +and I'm an engineer + +6 +00:00:16,250 --> 00:00:18,218 +on the Messages team. + +7 +00:00:18,218 --> 00:00:21,655 +Miranda: In this video, +Elana and I will be diving into + +8 +00:00:21,655 --> 00:00:26,193 +how to enhance collaboration +with Messages in your app. + +9 +00:00:26,193 --> 00:00:31,865 +I'll start with an introduction +of what the feature is. + +10 +00:00:31,865 --> 00:00:35,936 +You'll learn how to prepare +to adopt this feature, + +11 +00:00:35,936 --> 00:00:38,605 +and how to tie Messages +into the process + +12 +00:00:38,605 --> 00:00:42,676 +to start a collaboration. + +13 +00:00:42,676 --> 00:00:45,512 +Elana will explain +how to add collaboration + +14 +00:00:45,512 --> 00:00:49,616 +with Messages UI to your app, + +15 +00:00:49,616 --> 00:00:52,552 +and finally she will discuss +how to keep up to date + +16 +00:00:52,552 --> 00:00:56,523 +when the collaboration +is updated. + +17 +00:00:56,523 --> 00:01:02,296 +First, let me introduce +collaboration with Messages! + +18 +00:01:02,296 --> 00:01:07,768 +In iOS 16 and macOS Ventura, +we've added a new and easy way + +19 +00:01:07,768 --> 00:01:13,907 +to improve communication between +people who are collaborating. + +20 +00:01:13,907 --> 00:01:17,711 +Collaborators are able to tie +a document to conversations + +21 +00:01:17,711 --> 00:01:21,815 +by sharing via Messages. + +22 +00:01:21,815 --> 00:01:25,052 +Collaboration activity +is surfaced in Messages + +23 +00:01:25,052 --> 00:01:31,325 +conversations and in +ongoing FaceTime calls. + +24 +00:01:31,325 --> 00:01:34,227 +A customizable +Collaboration popover + +25 +00:01:34,227 --> 00:01:38,065 +is also provided to your app +to manage details + +26 +00:01:38,065 --> 00:01:44,638 +of the collaboration and connect +to the Messages conversation. + +27 +00:01:44,638 --> 00:01:47,874 +This builds on technologies +that you are already using, + +28 +00:01:47,874 --> 00:01:52,379 +such as the share sheet +and drag and drop. + +29 +00:01:52,379 --> 00:01:56,550 +Next, I'll go over the types of +collaboration infrastructures + +30 +00:01:56,550 --> 00:01:59,653 +we support, +and how to tie each of those + +31 +00:01:59,653 --> 00:02:02,856 +to Messages collaboration. + +32 +00:02:02,856 --> 00:02:07,561 +We support three types of +collaboration infrastructures: + +33 +00:02:07,561 --> 00:02:11,164 +CloudKit, iCloud Drive, +and whatever custom + +34 +00:02:11,164 --> 00:02:15,635 +collaboration infrastructure +you may be using today! + +35 +00:02:15,635 --> 00:02:18,138 +In this video, +I'll mainly focus + +36 +00:02:18,138 --> 00:02:22,142 +on the CloudKit +and iCloud Drive cases. + +37 +00:02:22,142 --> 00:02:25,278 +If you are using +a custom infrastructure, + +38 +00:02:25,278 --> 00:02:29,383 +watch the "Integrate your custom +collaboration app with Messages" + +39 +00:02:29,383 --> 00:02:33,286 +video for more details. + +40 +00:02:33,286 --> 00:02:36,490 +If you use CloudKit-based +collaboration, + +41 +00:02:36,490 --> 00:02:39,860 +we've provided a new API +to create an object + +42 +00:02:39,860 --> 00:02:44,264 +that the system +can recognize for collaboration. + +43 +00:02:44,264 --> 00:02:47,534 +This is based off +the macOS Sierra API + +44 +00:02:47,534 --> 00:02:52,639 +to start or manage a share +with NSSharingService. + +45 +00:02:52,639 --> 00:02:55,375 +Once you have +the collaboration object, + +46 +00:02:55,375 --> 00:02:58,612 +identify where in the app +you are showing UI + +47 +00:02:58,612 --> 00:03:02,149 +to start or manage the share. + +48 +00:03:02,149 --> 00:03:04,484 +You need to update +to the new API + +49 +00:03:04,484 --> 00:03:07,454 +to enhance your +collaboration with Messages, + +50 +00:03:07,454 --> 00:03:13,894 +as we will deprecate +the existing AppKit API. + +51 +00:03:13,894 --> 00:03:19,299 +The new collaboration object API +uses NSItemProvider. + +52 +00:03:19,299 --> 00:03:23,136 +NSItemProvider is used +by system services + +53 +00:03:23,136 --> 00:03:28,308 +to transport your app's data to +other processes on the system. + +54 +00:03:28,308 --> 00:03:31,044 +The provider requires +either the CKShare + +55 +00:03:31,044 --> 00:03:35,382 +for the collaboration item, +or a preparation handler + +56 +00:03:35,382 --> 00:03:39,920 +to create a CKShare +when collaboration starts. + +57 +00:03:39,920 --> 00:03:43,723 +Your app's CKContainer +is also required. + +58 +00:03:43,723 --> 00:03:48,328 +And finally, provide a +CKAllowedSharingOptions object + +59 +00:03:48,328 --> 00:03:51,698 +representing the access +and permissions options + +60 +00:03:51,698 --> 00:03:54,634 +for the collaboration. + +61 +00:03:54,634 --> 00:03:57,204 +The values of the options +are the same as the + +62 +00:03:57,204 --> 00:04:02,342 +NSCloudKitSharingServiceOptions +which were previously requested + +63 +00:04:02,342 --> 00:04:05,378 +from +NSCloudSharingServiceDelegate + +64 +00:04:05,378 --> 00:04:08,048 +methods. + +65 +00:04:08,048 --> 00:04:11,051 +Here's a brief overview +of what it looks like to create + +66 +00:04:11,051 --> 00:04:15,622 +a CloudKit collaboration object. + +67 +00:04:15,622 --> 00:04:19,126 +If the collaboration +is being started and you pass in + +68 +00:04:19,126 --> 00:04:21,995 +a preparation handler, +you need to both + +69 +00:04:21,995 --> 00:04:28,535 +create the share and save it +to the server in the handler. + +70 +00:04:28,535 --> 00:04:35,575 +If it's already started, just +pass in the associated share. + +71 +00:04:35,575 --> 00:04:39,446 +The CKAllowedSharingOptions +instance being registered + +72 +00:04:39,446 --> 00:04:42,782 +is using +a static standard property + +73 +00:04:42,782 --> 00:04:46,853 +which returns the default set +of allowed options. + +74 +00:04:46,853 --> 00:04:51,158 +CloudKit adopters can use that +or create a custom instance + +75 +00:04:51,158 --> 00:04:55,862 +of the class for a restricted +set of allowed options. + +76 +00:04:57,864 --> 00:05:02,235 +For those of you who might be +interested in adopting CloudKit, + +77 +00:05:02,235 --> 00:05:05,805 +CloudKit lets you use iCloud +as your app's database + +78 +00:05:05,805 --> 00:05:09,276 +without writing your own +server code. + +79 +00:05:09,276 --> 00:05:12,412 +You will also get a built-in +method of sharing parts + +80 +00:05:12,412 --> 00:05:15,916 +of that data +with other iCloud users. + +81 +00:05:15,916 --> 00:05:22,389 +For more details, watch the +"What's new in CloudKit" video. + +82 +00:05:22,389 --> 00:05:24,591 +If you're using iCloud Drive, + +83 +00:05:24,591 --> 00:05:29,062 +your object for collaboration +is simply your file URL -- + +84 +00:05:29,062 --> 00:05:33,166 +we'll do all the work +to recognize it! + +85 +00:05:33,166 --> 00:05:36,336 +Once you have that, +identify the entry points + +86 +00:05:36,336 --> 00:05:40,140 +to start or manage collaboration +in your app + +87 +00:05:40,140 --> 00:05:46,313 +and prepare to replace them with +the new and improved versions. + +88 +00:05:46,313 --> 00:05:49,349 +For custom collaboration +infrastructures, + +89 +00:05:49,349 --> 00:05:54,988 +your collaboration object is +called SWCollaborationMetadata, + +90 +00:05:54,988 --> 00:05:59,192 +wrapped in new +NSItemProvider API. + +91 +00:05:59,192 --> 00:06:02,929 +Watch the "Integrate your custom +collaboration app with Messages" + +92 +00:06:02,929 --> 00:06:06,733 +video for details on +how to use this API + +93 +00:06:06,733 --> 00:06:10,804 +to update your collaboration UI. + +94 +00:06:10,804 --> 00:06:13,039 +Now you're ready to go. + +95 +00:06:13,039 --> 00:06:16,409 +Next, initiating +a collaboration. + +96 +00:06:16,409 --> 00:06:19,279 +There are two different ways: +through the share sheet + +97 +00:06:19,279 --> 00:06:23,049 +in its new collaboration mode, +and through drag and drop + +98 +00:06:23,049 --> 00:06:25,852 +to Messages, +either from your app + +99 +00:06:25,852 --> 00:06:31,891 +or from the Files app on iOS +and Finder on macOS. + +100 +00:06:31,891 --> 00:06:34,661 +The new share sheet +collaboration mode + +101 +00:06:34,661 --> 00:06:38,498 +can be identified +by an indicator in the header, + +102 +00:06:38,498 --> 00:06:41,635 +which also provides +a choice between collaboration + +103 +00:06:41,635 --> 00:06:44,404 +and sending a copy. + +104 +00:06:44,404 --> 00:06:47,641 +In order to have collaboration +in the share sheet, + +105 +00:06:47,641 --> 00:06:50,543 +give the share sheet +the collaboration object + +106 +00:06:50,543 --> 00:06:54,481 +you prepared earlier. + +107 +00:06:54,481 --> 00:06:57,517 +On macOS, +this collaboration indicator + +108 +00:06:57,517 --> 00:07:01,788 +is shown in a beautiful +new share popover! + +109 +00:07:01,788 --> 00:07:05,825 +The share popover +also includes a title and image + +110 +00:07:05,825 --> 00:07:09,496 +in the header, plus a row +of conversation suggestions, + +111 +00:07:09,496 --> 00:07:13,600 +and all the transports +we offered already. + +112 +00:07:13,600 --> 00:07:15,502 +For more details about this, + +113 +00:07:15,502 --> 00:07:20,874 +watch the WWDC22 +"What's new in AppKit" video. + +114 +00:07:20,874 --> 00:07:24,978 +On iOS and Mac Catalyst, +show the share sheet using + +115 +00:07:24,978 --> 00:07:28,648 +the UIActivityViewController +class. + +116 +00:07:28,648 --> 00:07:36,456 +On macOS, show the share popover +using NSSharingServicePicker. + +117 +00:07:36,456 --> 00:07:40,694 +Pass the collaboration object +to UIActivityViewController + +118 +00:07:40,694 --> 00:07:47,300 +as an activity item to present +it with collaboration enabled. + +119 +00:07:47,300 --> 00:07:51,071 +And similarly, initialize +NSSharingServicePicker + +120 +00:07:51,071 --> 00:07:54,140 +with the collaboration object +to show it + +121 +00:07:54,140 --> 00:07:57,811 +with collaboration enabled. + +122 +00:07:57,811 --> 00:08:01,448 +CloudKit adopters +will need to take an extra step + +123 +00:08:01,448 --> 00:08:06,052 +to provide a title +and image for the headers. + +124 +00:08:06,052 --> 00:08:12,525 +On iOS, use existing API such +as UIActivityItemsConfiguration + +125 +00:08:12,525 --> 00:08:16,296 +or UIActivityItemSource +to provide + +126 +00:08:16,296 --> 00:08:23,670 +an LPLinkMetadata object +with a title and imageProvider. + +127 +00:08:23,670 --> 00:08:28,108 +Create your configuration +with your collaboration object, + +128 +00:08:28,108 --> 00:08:31,611 +then set the metadata provider +to return + +129 +00:08:31,611 --> 00:08:38,318 +your LPLinkMetadata object +for the item being shared. + +130 +00:08:38,318 --> 00:08:42,088 +Finally, initialize +UIActivityViewController + +131 +00:08:42,088 --> 00:08:45,692 +with that configuration. + +132 +00:08:45,692 --> 00:08:48,762 +On macOS, +we have a new API called + +133 +00:08:48,762 --> 00:08:52,332 +NSPreviewRepresenting +ActivityItem + +134 +00:08:52,332 --> 00:08:55,268 +for providing +the header metadata. + +135 +00:08:55,268 --> 00:08:56,336 +Refer to the + +136 +00:08:56,336 --> 00:09:00,340 +NSPreviewRepresenting +ActivityItem documentation + +137 +00:09:00,340 --> 00:09:04,244 +for more details. + +138 +00:09:04,244 --> 00:09:07,914 +To use NSPreviewRepresenting +ActivityItem, + +139 +00:09:07,914 --> 00:09:12,519 +first choose your title, +image, and icon. + +140 +00:09:12,519 --> 00:09:15,588 +The image represents +the item being shared, + +141 +00:09:15,588 --> 00:09:18,725 +while the icon represents +the source of the item + +142 +00:09:18,725 --> 00:09:24,831 +being shared -- +for example, an app icon. + +143 +00:09:24,831 --> 00:09:28,401 +Create an NSPreviewRepresenting +ActivityItem + +144 +00:09:28,401 --> 00:09:34,174 +with your collaboration object, +title, image, and icon, + +145 +00:09:34,174 --> 00:09:37,610 +and initialize +NSSharingServicePicker + +146 +00:09:37,610 --> 00:09:41,414 +with that preview item. + +147 +00:09:41,414 --> 00:09:45,652 +On an exciting note, +the new SwiftUI ShareLink API + +148 +00:09:45,652 --> 00:09:50,256 +for the share sheet will also +support collaboration mode! + +149 +00:09:50,256 --> 00:09:53,760 +To adopt, the item you are +sharing must be represented + +150 +00:09:53,760 --> 00:10:00,066 +by Transferable, a new protocol +for sharing and data transfer. + +151 +00:10:00,066 --> 00:10:03,770 +CloudKit adopters provide +the share, container, + +152 +00:10:03,770 --> 00:10:08,041 +and options through +a CKShareTransferRepresentation + +153 +00:10:08,041 --> 00:10:11,444 +returned by your +Transferable item. + +154 +00:10:11,444 --> 00:10:15,081 +For more details, +watch the "Meet Transferable" + +155 +00:10:15,081 --> 00:10:22,155 +and WWDC22 +"What's new in SwiftUI" videos. + +156 +00:10:22,155 --> 00:10:25,725 +Here's an example of how +a CloudKit adopter like Notes + +157 +00:10:25,725 --> 00:10:32,065 +would create a transferable +object to share with ShareLink. + +158 +00:10:32,065 --> 00:10:38,304 +The note provides a +CKShareTransferRepresentation, + +159 +00:10:38,304 --> 00:10:41,608 +which is constructed either +as its existing value + +160 +00:10:41,608 --> 00:10:45,712 +with an existing CKShare, +CKContainer, + +161 +00:10:45,712 --> 00:10:49,716 +and CKAllowedSharingOptions +value, + +162 +00:10:49,716 --> 00:10:54,120 +or as its prepareShare value +with a CKContainer, + +163 +00:10:54,120 --> 00:10:59,125 +CKAllowedSharingOptions value, +and a preparation handler + +164 +00:10:59,125 --> 00:11:06,399 +to create and save a CKShare +for the collaboration object. + +165 +00:11:06,399 --> 00:11:10,537 +For iCloud Drive adopters, +your file URL + +166 +00:11:10,537 --> 00:11:15,742 +is the Transferable object which +you share through ShareLink. + +167 +00:11:15,742 --> 00:11:19,279 +If you have a custom +collaboration infrastructure, + +168 +00:11:19,279 --> 00:11:23,483 +watch the "Integrate your custom +collaboration app with Messages" + +169 +00:11:23,483 --> 00:11:29,289 +video for how to return an +SWCollaborationMetadata object + +170 +00:11:29,289 --> 00:11:33,993 +from your transferable object. + +171 +00:11:33,993 --> 00:11:36,796 +Once you have +your Transferable object, + +172 +00:11:36,796 --> 00:11:38,998 +pass it +to the ShareLink initializer + +173 +00:11:38,998 --> 00:11:41,367 +as the item to share. + +174 +00:11:41,367 --> 00:11:44,003 +At the same time, +pass in a preview + +175 +00:11:44,003 --> 00:11:50,977 +with a title and image to +fill in the share sheet header. + +176 +00:11:50,977 --> 00:11:52,378 +One notable feature + +177 +00:11:52,378 --> 00:11:55,281 +of the share sheet +collaboration mode header + +178 +00:11:55,281 --> 00:11:59,986 +is the summary of the access +and permissions options. + +179 +00:11:59,986 --> 00:12:03,256 +Tapping this summary brings up +a view where collaborators + +180 +00:12:03,256 --> 00:12:06,459 +choose what access +and permissions options + +181 +00:12:06,459 --> 00:12:11,631 +to use when collaborating. + +182 +00:12:11,631 --> 00:12:14,534 +For CloudKit adopters, +this view shows + +183 +00:12:14,534 --> 00:12:19,339 +the set of options registered +in the collaboration object. + +184 +00:12:19,339 --> 00:12:21,074 +iCloud Drive adopters + +185 +00:12:21,074 --> 00:12:25,478 +show the standard set +of iCloud Drive options. + +186 +00:12:25,478 --> 00:12:27,880 +If you have a custom +infrastructure, + +187 +00:12:27,880 --> 00:12:32,085 +watch the "Integrate your custom +collaboration app with Messages" + +188 +00:12:32,085 --> 00:12:36,189 +video for how to specify +your custom options + +189 +00:12:36,189 --> 00:12:41,094 +and have them +show up in this view. + +190 +00:12:41,094 --> 00:12:44,097 +There's one more way +to start a collaboration, + +191 +00:12:44,097 --> 00:12:46,833 +and that's through +drag and drop. + +192 +00:12:46,833 --> 00:12:51,204 +A collaborator can simply drag +your document into Messages + +193 +00:12:51,204 --> 00:12:54,507 +and get the new +collaboration-enabled rich link + +194 +00:12:54,507 --> 00:12:57,543 +for the document in Messages. + +195 +00:12:57,543 --> 00:13:00,179 +This rich link +provides functionality + +196 +00:13:00,179 --> 00:13:03,983 +both for collaboration +and sending a copy, + +197 +00:13:03,983 --> 00:13:08,254 +and for selecting +collaboration options. + +198 +00:13:08,254 --> 00:13:11,324 +To adopt, provide your +collaboration object + +199 +00:13:11,324 --> 00:13:18,598 +through ShareLink on iOS 16 +and macOS Ventura. + +200 +00:13:18,598 --> 00:13:22,368 +And that's how you prepare for +and initiate collaborations + +201 +00:13:22,368 --> 00:13:24,037 +with Messages. + +202 +00:13:24,037 --> 00:13:27,040 +Next, I'll hand it +over to Elana, + +203 +00:13:27,040 --> 00:13:30,710 +who will talk about taking your +app's collaboration experience + +204 +00:13:30,710 --> 00:13:32,979 +to the next level. + +205 +00:13:32,979 --> 00:13:34,681 +Elana: Thanks, Miranda! + +206 +00:13:34,681 --> 00:13:36,516 +Now that you know +how to get started, + +207 +00:13:36,516 --> 00:13:38,484 +I'll talk about +how to further integrate + +208 +00:13:38,484 --> 00:13:42,422 +our collaboration UI +into your app. + +209 +00:13:42,422 --> 00:13:43,956 +We've added these new features + +210 +00:13:43,956 --> 00:13:47,193 +to amplify the collaboration +experience. + +211 +00:13:47,193 --> 00:13:50,763 +The collaboration button is +placed in your app's navigation + +212 +00:13:50,763 --> 00:13:52,031 +and will show the group photo + +213 +00:13:52,031 --> 00:13:54,367 +of the associated +messages group. + +214 +00:13:54,367 --> 00:13:56,703 +There is also an active +participant count + +215 +00:13:56,703 --> 00:13:59,072 +to the right of the button +that will show when others + +216 +00:13:59,072 --> 00:14:02,842 +are present in the document. + +217 +00:14:02,842 --> 00:14:05,044 +When you tap +the collaboration button, + +218 +00:14:05,044 --> 00:14:08,347 +the new collaboration +popover appears. + +219 +00:14:08,347 --> 00:14:09,782 +The customizable popover + +220 +00:14:09,782 --> 00:14:12,585 +shows the overview +of the collaboration. + +221 +00:14:12,585 --> 00:14:15,188 +It also allows users +to initiate communication + +222 +00:14:15,188 --> 00:14:18,324 +with other participants +with just one tap. + +223 +00:14:18,324 --> 00:14:21,961 +This provides them the ability +to work together in real time + +224 +00:14:21,961 --> 00:14:24,864 +via Messages and FaceTime. + +225 +00:14:26,866 --> 00:14:30,136 +These UI elements are all +powered by a single class + +226 +00:14:30,136 --> 00:14:34,240 +in the SharedWithYou framework: +SWCollaborationView. + +227 +00:14:34,240 --> 00:14:38,111 +This view represents the +button view in the navigation. + +228 +00:14:38,111 --> 00:14:39,679 +All you need to do +is initialize + +229 +00:14:39,679 --> 00:14:42,381 +the SWCollaborationView +and we will take care + +230 +00:14:42,381 --> 00:14:46,919 +of the popover layout +and presentation for you. + +231 +00:14:46,919 --> 00:14:49,422 +To show custom content, +you'll provide a view + +232 +00:14:49,422 --> 00:14:52,658 +which will be added +to the popover. + +233 +00:14:52,658 --> 00:14:58,865 +Now, I'll go over the code to +create the SWCollaborationView. + +234 +00:14:58,865 --> 00:15:01,367 +Initialize +the SWCollaborationView + +235 +00:15:01,367 --> 00:15:03,269 +with an itemProvider. + +236 +00:15:03,269 --> 00:15:04,504 +The itemProvider contains + +237 +00:15:04,504 --> 00:15:07,607 +the CKShare +for CloudKit-based apps, + +238 +00:15:07,607 --> 00:15:10,143 +the fileURL +for iCloud Drive-based apps, + +239 +00:15:10,143 --> 00:15:12,512 +or the SW Collaboration +metadata + +240 +00:15:12,512 --> 00:15:16,015 +for custom collaboration +infrastructures. + +241 +00:15:18,017 --> 00:15:20,520 +Set the activeParticipantCount +property + +242 +00:15:20,520 --> 00:15:23,189 +on the collaboration view +to show the number + +243 +00:15:23,189 --> 00:15:27,860 +of active participants +on the document. + +244 +00:15:27,860 --> 00:15:30,163 +Then set the contentView +property + +245 +00:15:30,163 --> 00:15:32,765 +on the collaborationView +to provide the popover + +246 +00:15:32,765 --> 00:15:35,535 +with custom content. + +247 +00:15:35,535 --> 00:15:37,470 +The ContentView +is what makes the popover + +248 +00:15:37,470 --> 00:15:39,939 +completely customizable. + +249 +00:15:39,939 --> 00:15:42,642 +This is where you'll add +your own content to give users + +250 +00:15:42,642 --> 00:15:47,013 +a unique view of what is +going on in the collaboration. + +251 +00:15:47,013 --> 00:15:49,916 +For example, in Pages, +the ContentView contains + +252 +00:15:49,916 --> 00:15:51,951 +the Current Participants list + +253 +00:15:51,951 --> 00:15:55,822 +and the Participant Cursors +toggle. + +254 +00:15:55,822 --> 00:15:58,591 +Now, let's look at +the "manage" button. + +255 +00:15:58,591 --> 00:16:00,927 +For CloudKit +and iCloud Drive adopters, + +256 +00:16:00,927 --> 00:16:04,030 +this manage button +will bring up the manage UI, + +257 +00:16:04,030 --> 00:16:05,965 +where you can add +and remove participants + +258 +00:16:05,965 --> 00:16:08,768 +or change the share settings. + +259 +00:16:08,768 --> 00:16:12,505 +I'll talk more +about this shortly. + +260 +00:16:12,505 --> 00:16:15,174 +Customize the provided +button title by setting + +261 +00:16:15,174 --> 00:16:19,011 +the manageButtonTitle property +on the collaboration view. + +262 +00:16:19,011 --> 00:16:21,080 +If you do not set +this property, + +263 +00:16:21,080 --> 00:16:24,383 +the title will default +to "Manage Share." + +264 +00:16:24,383 --> 00:16:27,954 +If your app uses a custom +collaboration infrastructure, + +265 +00:16:27,954 --> 00:16:31,858 +include your own manage button +in the contentView. + +266 +00:16:31,858 --> 00:16:34,694 +One will not be +provided for you. + +267 +00:16:34,694 --> 00:16:38,231 +On Mac, make sure the button +has a transparent background + +268 +00:16:38,231 --> 00:16:43,302 +to adhere to Apple design +recommendations. + +269 +00:16:43,302 --> 00:16:47,073 +Finally, +create a UIBarButtonItem on iOS + +270 +00:16:47,073 --> 00:16:49,642 +as shown here, +using the collaborationView + +271 +00:16:49,642 --> 00:16:51,711 +as the custom view. + +272 +00:16:51,711 --> 00:16:54,513 +On Mac, +initialize an NSToolbarItem + +273 +00:16:54,513 --> 00:16:57,183 +using a UIBarButtonItem. + +274 +00:16:57,183 --> 00:17:00,553 +Then, add the bar button item +or toolbar item + +275 +00:17:00,553 --> 00:17:04,590 +to the ViewController's +navigationItem. + +276 +00:17:04,590 --> 00:17:06,626 +As I mentioned earlier, + +277 +00:17:06,626 --> 00:17:09,128 +CloudKit and iCloud Drive +adopters are provided + +278 +00:17:09,128 --> 00:17:12,131 +with a button +in the collaboration popover. + +279 +00:17:12,131 --> 00:17:14,667 +This enables you to manage +the share in the same way + +280 +00:17:14,667 --> 00:17:17,169 +you've always been able to. + +281 +00:17:17,169 --> 00:17:19,772 +Changes in the provided +manage UI are observable + +282 +00:17:19,772 --> 00:17:22,341 +by adhering to +the same delegate protocols + +283 +00:17:22,341 --> 00:17:24,911 +already used to observe changes: + +284 +00:17:24,911 --> 00:17:27,046 +UICloudSharing +ControllerDelegate + +285 +00:17:27,046 --> 00:17:30,917 +and NSCloudSharing +ServiceDelegate. + +286 +00:17:30,917 --> 00:17:33,753 +Now you have an understanding +of how to integrate + +287 +00:17:33,753 --> 00:17:38,524 +the new collaboration UI +into your app. + +288 +00:17:38,524 --> 00:17:41,127 +Next, I'll go over how +to observe and handle + +289 +00:17:41,127 --> 00:17:44,997 +updates to collaborations. + +290 +00:17:44,997 --> 00:17:48,968 +It is critical to know when +a share starts or stops. + +291 +00:17:48,968 --> 00:17:52,838 +For CloudKit adopters, +we've added a new protocol + +292 +00:17:52,838 --> 00:17:55,808 +called CKSystemSharing +UIObserver + +293 +00:17:55,808 --> 00:17:58,444 +to notify you of just that. + +294 +00:17:58,444 --> 00:18:01,147 +With this protocol, +you get callbacks corresponding + +295 +00:18:01,147 --> 00:18:04,350 +to when your CKShare +is saved or removed + +296 +00:18:04,350 --> 00:18:07,753 +without needing +the CloudKit Sharing UI. + +297 +00:18:07,753 --> 00:18:11,824 +I'll take you +through some code now. + +298 +00:18:11,824 --> 00:18:17,163 +Initialize an observer +using the CKContainer. + +299 +00:18:17,163 --> 00:18:19,999 +On the observer, +define a closure to be executed + +300 +00:18:19,999 --> 00:18:22,768 +when the CKShare +is saved and assign it + +301 +00:18:22,768 --> 00:18:28,474 +to the systemSharingUI +DidSaveShareBlock. + +302 +00:18:28,474 --> 00:18:31,444 +In the closure, if the Share +was saved correctly, + +303 +00:18:31,444 --> 00:18:33,212 +you'll get a success result -- + +304 +00:18:33,212 --> 00:18:35,448 +indicating the share +was started -- + +305 +00:18:35,448 --> 00:18:39,952 +and an associated +CKShare to work with. + +306 +00:18:39,952 --> 00:18:42,288 +If the save was unsuccessful, + +307 +00:18:42,288 --> 00:18:47,593 +you receive a failure result +and the associated error. + +308 +00:18:47,593 --> 00:18:49,628 +Here is the implementation +of the closure + +309 +00:18:49,628 --> 00:18:53,699 +for when the document owner +stops sharing. + +310 +00:18:53,699 --> 00:18:55,368 +In the success case, + +311 +00:18:55,368 --> 00:18:59,372 +the CKShare +has successfully been deleted. + +312 +00:18:59,372 --> 00:19:05,177 +In the failure case, you will +also get the associated error. + +313 +00:19:05,177 --> 00:19:06,746 +Starting and stopping shares + +314 +00:19:06,746 --> 00:19:09,882 +are not the only changes +you may care about. + +315 +00:19:09,882 --> 00:19:14,120 +Some changes, you might even +want to bubble up to users. + +316 +00:19:14,120 --> 00:19:17,156 +We've added API to enable you +to post notices + +317 +00:19:17,156 --> 00:19:19,959 +summarizing updates +to a collaboration, + +318 +00:19:19,959 --> 00:19:23,496 +right at the top +of the relevant Messages thread. + +319 +00:19:23,496 --> 00:19:26,198 +Collaborators are shown +what the update was + +320 +00:19:26,198 --> 00:19:30,436 +and who made the change. + +321 +00:19:30,436 --> 00:19:34,373 +To post a notice, retrieve +the SWCollaborationHighlight, + +322 +00:19:34,373 --> 00:19:36,208 +which is a +collaboration-specific + +323 +00:19:36,208 --> 00:19:39,278 +type of highlight +in Shared with You. + +324 +00:19:39,278 --> 00:19:43,115 +Use it to create an +SWCollaborationHighlight event. + +325 +00:19:43,115 --> 00:19:47,253 +Learn more about SWHighlights +and other SharedWithYou APIs + +326 +00:19:47,253 --> 00:19:51,290 +in the "Add Shared with You +to your app" video. + +327 +00:19:51,290 --> 00:19:53,559 +Watch this video before +beginning your work + +328 +00:19:53,559 --> 00:19:57,696 +to adopt notices. + +329 +00:19:57,696 --> 00:19:59,665 +I'll talk through posting +different notices + +330 +00:19:59,665 --> 00:20:02,668 +using a CloudKit app +as an example. + +331 +00:20:02,668 --> 00:20:06,138 +If your app uses a custom +collaboration infrastructure, + +332 +00:20:06,138 --> 00:20:08,674 +view the "Integrate your +custom collaboration app + +333 +00:20:08,674 --> 00:20:12,278 +with Messages" video +for detailed instructions. + +334 +00:20:12,278 --> 00:20:15,714 +To represent a notice, +we've introduced a protocol + +335 +00:20:15,714 --> 00:20:18,217 +called SWHighlightEvent. + +336 +00:20:18,217 --> 00:20:21,587 +Highlight events are +initialized with SWHighlights + +337 +00:20:21,587 --> 00:20:24,857 +retrieved from +the SWHighlightCenter API. + +338 +00:20:24,857 --> 00:20:28,060 +The supported event types +include a change event + +339 +00:20:28,060 --> 00:20:30,463 +for content updates or comments; + +340 +00:20:30,463 --> 00:20:34,600 +a mention event when a user +is mentioned in a collaboration; + +341 +00:20:34,600 --> 00:20:39,238 +a persistence event when content +is moved, renamed, or deleted; + +342 +00:20:39,238 --> 00:20:40,706 +and a membership event + +343 +00:20:40,706 --> 00:20:45,277 +when a participant is added +or removed from a document. + +344 +00:20:45,277 --> 00:20:48,214 +Here's an example showing +how to post a change event + +345 +00:20:48,214 --> 00:20:52,318 +when a collaboration +has been edited. + +346 +00:20:52,318 --> 00:20:54,386 +Using the highlight center API, + +347 +00:20:54,386 --> 00:20:58,324 +retrieve a collaboration +highlight using the CKShare URL. + +348 +00:20:58,324 --> 00:21:01,660 +Remember, this CKShare +is one you defined during + +349 +00:21:01,660 --> 00:21:04,997 +the collaboration initiation, +so your app should have + +350 +00:21:04,997 --> 00:21:09,535 +this available when +a content change is made. + +351 +00:21:09,535 --> 00:21:12,972 +Next, create a +HighlightChangeEvent instance. + +352 +00:21:12,972 --> 00:21:14,640 +The initializer +takes a highlight, + +353 +00:21:14,640 --> 00:21:17,343 +and a trigger enum value. + +354 +00:21:17,343 --> 00:21:22,148 +In this case, +we set the trigger type to edit. + +355 +00:21:22,148 --> 00:21:24,617 +Lastly, +post the notice for that event + +356 +00:21:24,617 --> 00:21:27,987 +to the highlightCenter. + +357 +00:21:27,987 --> 00:21:30,356 +The rest of the events +follow a similar format + +358 +00:21:30,356 --> 00:21:33,058 +with the sole exception +being the mentionEvent, + +359 +00:21:33,058 --> 00:21:34,560 +as it requires more information + +360 +00:21:34,560 --> 00:21:38,330 +to indicate which +user was mentioned. + +361 +00:21:38,330 --> 00:21:40,432 +You are able to post +this type of event + +362 +00:21:40,432 --> 00:21:44,803 +only if your app +supports user mentions. + +363 +00:21:44,803 --> 00:21:47,339 +Create the mentionEvent +by passing in the retrieved + +364 +00:21:47,339 --> 00:21:52,845 +highlight and the handle of the +mentioned CKShare participant. + +365 +00:21:52,845 --> 00:21:57,650 +This notice will only be shown +to the mentioned user. + +366 +00:21:57,650 --> 00:22:00,653 +Use the persistence event type +when content is moved, + +367 +00:22:00,653 --> 00:22:02,955 +renamed, or deleted. + +368 +00:22:02,955 --> 00:22:05,991 +Here, the renamed trigger type +is used to signify + +369 +00:22:05,991 --> 00:22:10,262 +the document name +has been changed. + +370 +00:22:10,262 --> 00:22:12,831 +Finally, +here is a membershipEvent. + +371 +00:22:12,831 --> 00:22:15,467 +For a membershipEvent, +use the addedCollaborator + +372 +00:22:15,467 --> 00:22:20,439 +or removedCollaborator +trigger type instead. + +373 +00:22:20,439 --> 00:22:23,108 +With mentionevents, +notices are posted to show + +374 +00:22:23,108 --> 00:22:26,212 +how the document +membership has changed. + +375 +00:22:26,212 --> 00:22:29,582 +However, we've also made it +possible to keep collaborators + +376 +00:22:29,582 --> 00:22:31,850 +on your shared documents +in sync + +377 +00:22:31,850 --> 00:22:35,621 +when the Messages group +membership changes. + +378 +00:22:35,621 --> 00:22:38,857 +For CloudKit and iCloud Drive, +this is simple: + +379 +00:22:38,857 --> 00:22:42,394 +we do the work for you. + +380 +00:22:42,394 --> 00:22:45,097 +When someone new is added +to the Messages group + +381 +00:22:45,097 --> 00:22:47,566 +with a collaboration, +the document owner + +382 +00:22:47,566 --> 00:22:51,637 +is prompted via a notice +to add them to the share. + +383 +00:22:51,637 --> 00:22:54,807 +The same goes for when +someone is removed. + +384 +00:22:57,476 --> 00:23:00,179 +For apps with custom +collaboration infrastructures, + +385 +00:23:00,179 --> 00:23:02,381 +there's a little more work +to be done. + +386 +00:23:02,381 --> 00:23:03,816 +You will need to adopt the + +387 +00:23:03,816 --> 00:23:07,119 +SWCollaborationActionHandler +API, + +388 +00:23:07,119 --> 00:23:09,722 +which you can learn more about +in "Integrate your custom + +389 +00:23:09,722 --> 00:23:12,758 +collaboration app +with Messages." + +390 +00:23:14,760 --> 00:23:18,163 +Now you know how to get started +with collaboration in Messages + +391 +00:23:18,163 --> 00:23:22,134 +and integrate it into your app, +whether you're using CloudKit, + +392 +00:23:22,134 --> 00:23:26,305 +iCloud Drive, or your own +collaboration infrastructure. + +393 +00:23:26,305 --> 00:23:29,708 +Prepare your app to initiate +collaborations by adopting + +394 +00:23:29,708 --> 00:23:33,712 +the new share sheet +and drag and drop APIs. + +395 +00:23:33,712 --> 00:23:36,215 +Integrate the new +collaboration UI + +396 +00:23:36,215 --> 00:23:38,117 +to provide a customized overview + +397 +00:23:38,117 --> 00:23:41,687 +of what is happening +in the shared document. + +398 +00:23:41,687 --> 00:23:43,155 +Once you have that set up, + +399 +00:23:43,155 --> 00:23:47,326 +go even further and adopt +notices to display changes + +400 +00:23:47,326 --> 00:23:51,063 +to the collaboration +from within the Messages thread. + +401 +00:23:51,063 --> 00:23:54,199 +Miranda and I can't wait to see +how your app takes advantage + +402 +00:23:54,199 --> 00:23:57,569 +of these new collaboration +features in Messages! + +403 +00:23:57,569 --> 00:23:59,738 +Both: Thank you for watching! + +404 +00:23:59,738 --> 00:24:05,077 +♪ + diff --git "a/eng/2022 Session 10096 What\342\200\231s new in privacy en.srt" "b/eng/2022 Session 10096 What\342\200\231s new in privacy en.srt" new file mode 100644 index 0000000..ca5804c --- /dev/null +++ "b/eng/2022 Session 10096 What\342\200\231s new in privacy en.srt" @@ -0,0 +1,1453 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,676 --> 00:00:12,546 +Hey there, +I'm Justin from Privacy Engineering, + +3 +00:00:12,579 --> 00:00:15,415 +and welcome to +"What's new in privacy." + +4 +00:00:15,449 --> 00:00:19,686 +At Apple, we believe +that privacy is a fundamental human right, + +5 +00:00:19,720 --> 00:00:24,758 +and protecting people's privacy +is at the center of everything we do. + +6 +00:00:24,791 --> 00:00:29,029 +You want people to love your app +and make it part of their life. + +7 +00:00:29,062 --> 00:00:32,699 +Building a great feature +is how you get their attention, + +8 +00:00:32,733 --> 00:00:36,436 +and building with privacy +is how you maintain their trust + +9 +00:00:36,470 --> 00:00:39,239 +and your place in their life. + +10 +00:00:39,273 --> 00:00:44,111 +When people understand what data is +collected about them and how it's used, + +11 +00:00:44,144 --> 00:00:46,513 +they're more likely to fully engage + +12 +00:00:46,547 --> 00:00:50,450 +with your app or service +instead of choosing an alternative. + +13 +00:00:51,618 --> 00:00:55,856 +At Apple, we've prioritized +great features and great privacy + +14 +00:00:55,889 --> 00:00:57,891 +for the people who use our products, + +15 +00:00:57,925 --> 00:01:01,161 +and we want to help you deliver +this to your customers too. + +16 +00:01:01,195 --> 00:01:06,567 +At Apple, we approach this goal +of great features and great privacy + +17 +00:01:06,600 --> 00:01:10,971 +with a set of actionable patterns, +the privacy pillars. + +18 +00:01:11,004 --> 00:01:15,742 +These are a great guide for you as well +on how build privacy into your app. + +19 +00:01:15,776 --> 00:01:18,912 +The first is data minimization. + +20 +00:01:18,946 --> 00:01:23,083 +Use only the data needed +to build the feature. + +21 +00:01:23,116 --> 00:01:25,719 +Next is on-device processing. + +22 +00:01:25,752 --> 00:01:28,889 +If the feature requires sensitive data, + +23 +00:01:28,922 --> 00:01:33,260 +use the power of the device +to avoid sharing it with the server. + +24 +00:01:33,293 --> 00:01:36,964 +Third is transparency and control. + +25 +00:01:36,997 --> 00:01:39,800 +If sensitive data is sent off device, + +26 +00:01:39,833 --> 00:01:43,670 +make sure people understand what is sent, +how it's used, + +27 +00:01:43,704 --> 00:01:46,206 +and give them control. + +28 +00:01:46,240 --> 00:01:48,976 +Finally, security protections. + +29 +00:01:49,009 --> 00:01:52,579 +Protect sensitive data +in transit and at rest, + +30 +00:01:52,613 --> 00:01:55,682 +both on and off the device. + +31 +00:01:56,683 --> 00:02:01,989 +In this video, I'll talk about some +platform changes that will impact you, + +32 +00:02:02,022 --> 00:02:06,093 +some new privacy-enhancing features +that you should adopt, + +33 +00:02:06,126 --> 00:02:10,430 +and an important new feature +with privacy impact. + +34 +00:02:12,199 --> 00:02:17,571 +First, I'll talk about some changes coming +to the platform that will impact your app. + +35 +00:02:17,604 --> 00:02:23,744 +iOS 16 and macOS Ventura +include several important changes: + +36 +00:02:23,777 --> 00:02:28,115 +a new device name entitlement +that restricts access to the device name; + +37 +00:02:29,349 --> 00:02:34,221 +the location indicator now +shows app attribution in Control Center; + +38 +00:02:35,789 --> 00:02:39,159 +improvements to Gatekeeper +that verify the integrity + +39 +00:02:39,193 --> 00:02:42,262 +of notarized apps in more places; + +40 +00:02:43,764 --> 00:02:47,301 +launching Mac apps at login +now notifies people of additions + +41 +00:02:47,334 --> 00:02:49,803 +and has a simplified API; + +42 +00:02:51,371 --> 00:02:55,042 +legacy Pasteboard access +now requires permission. + +43 +00:02:55,976 --> 00:03:00,581 +I'll start with changes +to device name access. + +44 +00:03:00,614 --> 00:03:03,016 +To make it easier to identify devices, + +45 +00:03:03,050 --> 00:03:05,919 +the user's name +from their Apple ID account + +46 +00:03:05,953 --> 00:03:11,225 +is included by default as part +of the device name on iOS. + +47 +00:03:11,258 --> 00:03:15,495 +Before iOS 16, +the UIDevice API allowed apps + +48 +00:03:15,529 --> 00:03:18,832 +to access the user-assigned device name. + +49 +00:03:18,866 --> 00:03:23,604 +To better align app access +of this value with user expectations, + +50 +00:03:23,637 --> 00:03:27,741 +the UIDevice.name API will +return the model of the device + +51 +00:03:27,774 --> 00:03:32,980 +rather than the user-assigned name, +regardless of how people customize it. + +52 +00:03:34,915 --> 00:03:38,852 +We understand that some apps +have multi-device experiences + +53 +00:03:38,886 --> 00:03:40,687 +that utilize the device name, + +54 +00:03:40,721 --> 00:03:44,324 +like making it clear to people +where a document was last edited. + +55 +00:03:45,492 --> 00:03:51,331 +If your app uses multi-device features +and makes this visible in your app's UI, + +56 +00:03:51,365 --> 00:03:55,469 +you can request the entitlement +to access the device name. + +57 +00:03:55,502 --> 00:04:00,073 +Even with this entitlement, +sharing the device name with third parties + +58 +00:04:00,107 --> 00:04:04,411 +other than cloud-hosting service providers +is not permitted. + +59 +00:04:06,113 --> 00:04:10,417 +Next is updates +to attribution for location use. + +60 +00:04:11,485 --> 00:04:16,390 +When apps use location, +iOS displays an arrow in the status bar. + +61 +00:04:17,824 --> 00:04:21,361 +In iOS 16, +swiping down from the Control Center + +62 +00:04:21,395 --> 00:04:25,065 +indicates which app is using location. + +63 +00:04:25,098 --> 00:04:28,202 +Make sure that your app +only uses location when expected + +64 +00:04:28,235 --> 00:04:30,504 +to avoid surprising people. + +65 +00:04:32,873 --> 00:04:34,942 +Now I'd like to talk about the Mac. + +66 +00:04:34,975 --> 00:04:38,679 +Gatekeeper checks the integrity +of newly-downloaded apps. + +67 +00:04:38,712 --> 00:04:41,481 +In macOS Ventura, +Gatekeeper will now check + +68 +00:04:41,515 --> 00:04:45,853 +the integrity of all notarized apps, +not just quarantined apps. + +69 +00:04:46,920 --> 00:04:49,756 +First, apps need to be properly signed. + +70 +00:04:49,790 --> 00:04:51,525 +Starting with macOS Ventura, + +71 +00:04:51,558 --> 00:04:54,761 +if your notarized app is +no longer validly signed, + +72 +00:04:54,795 --> 00:04:58,298 +it will be blocked +by Gatekeeper on first launch. + +73 +00:04:58,332 --> 00:05:01,001 +You should sign +all your executables and bundles + +74 +00:05:01,034 --> 00:05:05,706 +and ensure that their signatures stay +valid when you make changes to your apps. + +75 +00:05:05,739 --> 00:05:09,943 +In addition to an integrity check, +Gatekeeper will also prevent + +76 +00:05:09,977 --> 00:05:12,713 +your app from being modified +in certain ways. + +77 +00:05:14,181 --> 00:05:17,885 +The most common way +apps are modified is for updates. + +78 +00:05:17,918 --> 00:05:23,090 +Apps validly signed by the same +developer account or team will continue + +79 +00:05:23,123 --> 00:05:24,424 +to be able to update each other. + +80 +00:05:24,458 --> 00:05:26,159 +This will just work. + +81 +00:05:26,960 --> 00:05:29,563 +To allow another development team +to update your app + +82 +00:05:29,596 --> 00:05:32,833 +or restrict updates to only your updater, + +83 +00:05:32,866 --> 00:05:35,936 +you can update your info-plist. + +84 +00:05:35,969 --> 00:05:39,540 +For example, here, +Unrelated App can allow Pal About + +85 +00:05:39,573 --> 00:05:42,376 +to update it with just a plist change. + +86 +00:05:44,745 --> 00:05:49,850 +Simply add the NSUpdateSecurityPolicy +you want to allow. + +87 +00:05:49,883 --> 00:05:55,489 +Within NSUpdateSecurityPolicy, +add “AllowProcesses”, a dictionary + +88 +00:05:55,522 --> 00:06:00,194 +mapping team identifiers +to an array of signing identifiers. + +89 +00:06:00,227 --> 00:06:03,664 +In this example, +the policy allows any process + +90 +00:06:03,697 --> 00:06:09,036 +with the signing identifier +com.example.pal.about + +91 +00:06:09,069 --> 00:06:13,774 +signed by Pal About's team identifier +to update your app. + +92 +00:06:13,807 --> 00:06:16,710 +If an app is modified +by something that isn't signed + +93 +00:06:16,743 --> 00:06:19,246 +by the same development team +and isn't allowed + +94 +00:06:19,279 --> 00:06:23,617 +by an NSUpdateSecurityPolicy, + +95 +00:06:23,650 --> 00:06:26,119 +macOS will block the modification + +96 +00:06:26,153 --> 00:06:31,058 +and notify the user that an app wants +to manage other apps. + +97 +00:06:31,091 --> 00:06:35,062 +Clicking on the notification +sends people to System Settings, + +98 +00:06:35,095 --> 00:06:39,499 +where they can allow an app +to update and modify other apps. + +99 +00:06:41,235 --> 00:06:44,805 +To prepare for macOS Ventura, +you should sign the executables + +100 +00:06:44,838 --> 00:06:47,307 +and bundles for your app and make sure + +101 +00:06:47,341 --> 00:06:50,777 +those signatures remain valid +after updates. + +102 +00:06:50,811 --> 00:06:57,050 +Enumerate any updaters you use +and adopt an NSUpdateSecurityPolicy. + +103 +00:06:57,084 --> 00:07:02,523 +Remember that if your app tries +to modify other apps outside the policy, + +104 +00:07:02,556 --> 00:07:04,992 +users will need to allow this. + +105 +00:07:07,027 --> 00:07:11,765 +Next, I'll talk about launching apps +at login on the Mac. + +106 +00:07:11,798 --> 00:07:16,203 +In macOS Monterey and earlier, +when someone logs into their Mac, + +107 +00:07:16,236 --> 00:07:20,474 +apps can run at login +using launch agents or daemons. + +108 +00:07:20,507 --> 00:07:24,111 +This is convenient as it allows apps +to run menu helpers, + +109 +00:07:24,144 --> 00:07:26,547 +check for software updates +in the background + +110 +00:07:26,580 --> 00:07:29,950 +or synchronize data across multiple apps. + +111 +00:07:31,051 --> 00:07:33,921 +Sometimes when a user logs into their Mac, + +112 +00:07:33,954 --> 00:07:37,424 +apps that aren't relevant open +and may be in their way. + +113 +00:07:37,457 --> 00:07:43,463 +In addition, apps or other software +can access sensors or data like location. + +114 +00:07:43,497 --> 00:07:46,166 +It is not always clear to people +that this is happening, + +115 +00:07:46,200 --> 00:07:49,236 +as what's running may not be visible. + +116 +00:07:49,269 --> 00:07:50,737 +And for developers, + +117 +00:07:50,771 --> 00:07:54,174 +it's not always clear which mechanism +to use to launch at login: + +118 +00:07:54,208 --> 00:07:58,045 +daemons, agents, service management…? + +119 +00:07:58,078 --> 00:08:01,815 +With macOS Ventura, this is much simpler. + +120 +00:08:03,250 --> 00:08:08,255 +In macOS Ventura, you can use +a new, single API to launch your app, + +121 +00:08:08,288 --> 00:08:11,191 +launch agent or daemon at login. + +122 +00:08:11,225 --> 00:08:14,361 +Your app will be allowed +to launch at login by default, + +123 +00:08:14,394 --> 00:08:16,763 +and users will be notified. + +124 +00:08:16,797 --> 00:08:19,867 +If your app requires a daemon +with elevated permissions, + +125 +00:08:19,900 --> 00:08:22,736 +it will require admin approval to enable. + +126 +00:08:25,339 --> 00:08:28,709 +Clicking on the notification +opens the Login Items pane + +127 +00:08:28,742 --> 00:08:33,280 +in System Settings, where people can +manage apps that launch on their system. + +128 +00:08:33,313 --> 00:08:36,550 +The top portion +controls apps that run at login, + +129 +00:08:36,583 --> 00:08:40,487 +and the bottom portion controls +other items that run at login. + +130 +00:08:40,521 --> 00:08:44,324 +This pane now controls different ways +apps can run at login, + +131 +00:08:44,358 --> 00:08:48,362 +including agents, +daemons, SMLoginItems, + +132 +00:08:48,395 --> 00:08:51,331 +and apps that add themselves +to open at login. + +133 +00:08:51,365 --> 00:08:53,967 +Here's how you can use this new API. + +134 +00:08:55,569 --> 00:08:58,605 +The service management framework +makes it easy for you + +135 +00:08:58,639 --> 00:09:00,574 +to launch resources at login. + +136 +00:09:00,607 --> 00:09:04,444 +Since all agents and daemons +live inside your app bundle, + +137 +00:09:04,478 --> 00:09:07,481 +you don't need to use an installer +to write launch agents + +138 +00:09:07,514 --> 00:09:12,986 +or create cleanup scripts anymore, +and this works in Mac App Store apps too. + +139 +00:09:13,020 --> 00:09:17,891 +Call the SMAppService API +from your app to control when people + +140 +00:09:17,925 --> 00:09:22,229 +get the notification and what +your app icon looks like in Settings. + +141 +00:09:24,398 --> 00:09:27,634 +Next is pasteboard access. + +142 +00:09:29,736 --> 00:09:34,107 +Previously, a transparency notice +let people know when apps accessed + +143 +00:09:34,141 --> 00:09:38,045 +the pasteboard when they had +not pressed paste in the edit options. + +144 +00:09:39,613 --> 00:09:44,985 +In iOS 16, the system confirms intent +for all access to pasteboard items + +145 +00:09:45,018 --> 00:09:47,688 +written by other apps. + +146 +00:09:47,721 --> 00:09:51,425 +If your app continues +to access values for pasteboard items + +147 +00:09:51,458 --> 00:09:57,130 +using the UIPasteboard API, +the system will display a modal prompt. + +148 +00:09:57,164 --> 00:10:00,801 +There are three ways to avoid +this prompt appearing in your app. + +149 +00:10:00,834 --> 00:10:03,904 +The first is to use the edit options menu. + +150 +00:10:03,937 --> 00:10:07,074 +The second is +to use the keyboard shortcut. + +151 +00:10:07,107 --> 00:10:10,244 +In the “Features to adopt” section, +I'll talk about the third option, + +152 +00:10:10,277 --> 00:10:13,180 +the new UIKit paste controls. + +153 +00:10:14,448 --> 00:10:16,984 +Those are the privacy changes +coming to the platform + +154 +00:10:17,017 --> 00:10:18,886 +that you need to know about. + +155 +00:10:18,919 --> 00:10:22,856 +In addition, there are a few new +privacy-enhancing technologies + +156 +00:10:22,890 --> 00:10:26,159 +that make it easier for you +to build seamless experiences + +157 +00:10:26,193 --> 00:10:28,595 +with privacy into your app. + +158 +00:10:29,730 --> 00:10:33,767 +I'll start by talking +about UIKit paste controls. + +159 +00:10:35,169 --> 00:10:38,605 +Add UIKit paste controls +to your app experience + +160 +00:10:38,639 --> 00:10:44,144 +so people can intuitively provide access +to their pasteboard by pressing a button. + +161 +00:10:44,178 --> 00:10:48,549 +Adopting UIKit paste controls +allows pasting without an edit menu, + +162 +00:10:48,582 --> 00:10:50,984 +keyboard shortcut or system prompt. + +163 +00:10:51,018 --> 00:10:54,821 +The system confirms intent +by verifying the button was + +164 +00:10:54,855 --> 00:10:57,958 +visibly displayed and tapped. + +165 +00:10:57,991 --> 00:11:00,594 +Customize these buttons +to fit with your app's interface. + +166 +00:11:00,627 --> 00:11:02,362 +Change the rounded corners + +167 +00:11:02,396 --> 00:11:06,500 +or change the color of the text, +icon or background. + +168 +00:11:06,533 --> 00:11:09,536 +Just be sure +the button has enough contrast + +169 +00:11:09,570 --> 00:11:11,872 +and isn't hidden behind other elements + +170 +00:11:11,905 --> 00:11:13,740 +or it won't work. + +171 +00:11:13,774 --> 00:11:17,678 +Make sure to test that your paste buttons +work as you expect. + +172 +00:11:19,680 --> 00:11:22,416 +Another tool to create +a seamless experience + +173 +00:11:22,449 --> 00:11:27,421 +while minimizing data access +is media device discovery. + +174 +00:11:27,454 --> 00:11:30,557 +Apps today use a wide range +of streaming protocols, + +175 +00:11:30,591 --> 00:11:33,994 +with custom discovery +and communication logic. + +176 +00:11:35,295 --> 00:11:38,165 +To stream media using one +of those protocols before, + +177 +00:11:38,198 --> 00:11:41,101 +apps needed permission +to access the local network, + +178 +00:11:41,134 --> 00:11:43,770 +and often Bluetooth. + +179 +00:11:43,804 --> 00:11:47,174 +This permission is needed +because knowledge of all devices + +180 +00:11:47,207 --> 00:11:49,676 +is required to manage device selection + +181 +00:11:49,710 --> 00:11:53,380 +but this provides access +to more information than is necessary + +182 +00:11:53,413 --> 00:11:56,817 +and poses a fingerprinting risk. + +183 +00:11:56,850 --> 00:12:01,388 +Media device discovery lets +your app stream to selected devices + +184 +00:12:01,421 --> 00:12:05,659 +without having to present network +or Bluetooth access prompts. + +185 +00:12:05,692 --> 00:12:09,263 +Streaming devices appear right +in the same picker as AirPlay, + +186 +00:12:09,296 --> 00:12:13,767 +and apps only see +the device picked to stream to. + +187 +00:12:13,800 --> 00:12:17,638 +This works thanks +to DeviceDiscovery extensions. + +188 +00:12:19,239 --> 00:12:23,143 +This extension can search +for local network and Bluetooth devices + +189 +00:12:23,177 --> 00:12:28,549 +but is sandboxed separately from the app, +so it can't send scan results back. + +190 +00:12:28,582 --> 00:12:33,120 +This means no broad permission is needed +for the app to access the local network + +191 +00:12:33,153 --> 00:12:38,125 +or Bluetooth since the app +doesn't see the whole network. + +192 +00:12:38,158 --> 00:12:42,129 +Instead, the extension can +only send the discovered accessories + +193 +00:12:42,162 --> 00:12:45,666 +to the DeviceDiscoveryExtension framework. + +194 +00:12:46,633 --> 00:12:50,304 +The DeviceDiscoveryExtension +framework presents the list + +195 +00:12:50,337 --> 00:12:54,508 +of discovered devices in the picker, +and after a selection is made, + +196 +00:12:54,541 --> 00:12:58,011 +the system enables communication +with the selected device. + +197 +00:12:58,045 --> 00:13:01,081 +No other permissions are needed. + +198 +00:13:02,549 --> 00:13:06,019 +Protocol providers should +create an app extension + +199 +00:13:06,053 --> 00:13:08,889 +with DeviceDiscoveryExtension, + +200 +00:13:08,922 --> 00:13:14,194 +extend AVRoutePickerView +to handle user selection callbacks, + +201 +00:13:14,228 --> 00:13:18,432 +handle user-selected networking devices +in your protocol, + +202 +00:13:18,465 --> 00:13:22,569 +download the sample +app and extension to learn more. + +203 +00:13:22,603 --> 00:13:26,306 +App developers should contact +their streaming protocol provider + +204 +00:13:26,340 --> 00:13:30,310 +to implement a DeviceDiscoveryExtension. + +205 +00:13:30,344 --> 00:13:34,414 +Media device discovery is +an opportunity to build great features + +206 +00:13:34,448 --> 00:13:36,250 +with great privacy. + +207 +00:13:36,283 --> 00:13:40,254 +Your app gets to access exactly +the data it needs to stream - + +208 +00:13:40,287 --> 00:13:45,292 +discovery is simple and prompt-free, +and people get great network privacy. + +209 +00:13:45,325 --> 00:13:48,328 +This is a win for everyone. + +210 +00:13:48,362 --> 00:13:51,932 +Just like media device discovery +provides permission to access only + +211 +00:13:51,965 --> 00:13:54,334 +the devices needed without prompts, + +212 +00:13:54,368 --> 00:13:57,137 +the PHPicker API provides permission + +213 +00:13:57,171 --> 00:14:01,408 +to access only the photos needed +without prompts. + +214 +00:14:01,441 --> 00:14:04,978 +PHPicker is on the Mac now +with macOS Ventura + +215 +00:14:05,012 --> 00:14:08,282 +and the watch with watchOS 9. + +216 +00:14:08,315 --> 00:14:12,219 +Update your Mac and watch apps +to access photos without prompting users + +217 +00:14:12,252 --> 00:14:14,922 +for access to all photos. + +218 +00:14:17,224 --> 00:14:21,962 +Next is private access tokens, +which are a powerful tool to prevent fraud + +219 +00:14:21,995 --> 00:14:24,398 +without CAPTCHAs getting in the way. + +220 +00:14:26,166 --> 00:14:30,704 +Private Access Tokens replace CAPTCHAs +and are built using blinded tokens + +221 +00:14:30,737 --> 00:14:34,241 +that allow website +or API developers to recognize + +222 +00:14:34,274 --> 00:14:38,679 +legitimate devices without being able +to track those devices. + +223 +00:14:38,712 --> 00:14:42,783 +Apple doesn't know what websites +a device fetches tokens for, + +224 +00:14:42,816 --> 00:14:46,119 +and the servers receiving the tokens +don't learn the identity + +225 +00:14:46,153 --> 00:14:49,423 +of the device sending the token. + +226 +00:14:49,456 --> 00:14:54,428 +Private Access Tokens are part +of the Privacy Pass IETF open standard + +227 +00:14:54,461 --> 00:14:58,398 +and are the same technology we use +to verify the authenticity + +228 +00:14:58,432 --> 00:15:00,934 +of Private Relay users. + +229 +00:15:00,968 --> 00:15:03,470 +To learn more, check out the video, + +230 +00:15:03,504 --> 00:15:06,907 +"Replace CAPTCHAs +with Private Access Tokens." + +231 +00:15:08,809 --> 00:15:11,778 +Now I'll go over Passkeys. + +232 +00:15:12,713 --> 00:15:15,949 +Passwords are at the center +of authenticating accounts + +233 +00:15:15,983 --> 00:15:20,687 +and keeping personal data secure +but they have serious issues. + +234 +00:15:20,721 --> 00:15:24,491 +They're hard to remember, +so people make them simpler + +235 +00:15:24,525 --> 00:15:27,227 +or reuse them across multiple sites. + +236 +00:15:27,261 --> 00:15:29,029 +And they can be phished. + +237 +00:15:30,464 --> 00:15:34,001 +Passkeys enable +a more robust authentication solution + +238 +00:15:34,034 --> 00:15:37,271 +and use the same +instantly familiar UI style + +239 +00:15:37,304 --> 00:15:41,275 +and biometric verification +as password autofill. + +240 +00:15:45,179 --> 00:15:48,882 +Passkeys are +built on public key cryptography, + +241 +00:15:48,916 --> 00:15:51,952 +so the value the server stores +can't be weak, + +242 +00:15:51,985 --> 00:15:53,453 +and if it's revealed, + +243 +00:15:53,487 --> 00:15:57,524 +it doesn't matter +because it's truly public. + +244 +00:15:57,558 --> 00:16:01,862 +Because passkeys are inherently linked +to the website they correspond to, + +245 +00:16:01,895 --> 00:16:04,231 +they can't be phished. + +246 +00:16:04,264 --> 00:16:07,034 +For more information +on implementing passkeys, + +247 +00:16:07,067 --> 00:16:10,370 +there's a full video, "Meet Passkeys.” + +248 +00:16:12,339 --> 00:16:17,044 +Those are the exciting and important +privacy updates for you to adopt! + +249 +00:16:18,412 --> 00:16:22,649 +One more important thing I want +to talk about is a new privacy tool + +250 +00:16:22,683 --> 00:16:26,286 +in iOS 16 called Safety Check. + +251 +00:16:26,320 --> 00:16:29,890 +Safety Check is designed +to help people in domestic + +252 +00:16:29,923 --> 00:16:35,095 +or intimate partner violence situations +review and reset access + +253 +00:16:35,128 --> 00:16:38,332 +they may have granted +to others previously. + +254 +00:16:39,533 --> 00:16:42,569 +Safety Check helps in the following ways: + +255 +00:16:42,603 --> 00:16:47,808 +It stops sharing data with people by +turning off location sharing in FindMy + +256 +00:16:47,841 --> 00:16:52,412 +and stopping sharing in Photos, +Notes and Calendar. + +257 +00:16:52,446 --> 00:16:56,884 +It stops sharing data with apps +by resetting system privacy permission + +258 +00:16:56,917 --> 00:16:59,186 +for all third-party apps. + +259 +00:16:59,219 --> 00:17:04,191 +It helps you sign out of FaceTime +and iMessage on your other iCloud devices + +260 +00:17:04,224 --> 00:17:09,530 +to ensure Messages and calls are +sent only to the device in your hand. + +261 +00:17:09,563 --> 00:17:13,700 +It walks you through signing +out of other iCloud devices to ensure + +262 +00:17:13,734 --> 00:17:18,639 +other devices can't receive updates +on location from FindMy, Photos, + +263 +00:17:18,672 --> 00:17:21,475 +calendars, etc. + +264 +00:17:21,508 --> 00:17:24,778 +It helps you change passwords +for both the device + +265 +00:17:24,811 --> 00:17:26,813 +and iCloud account. + +266 +00:17:26,847 --> 00:17:30,918 +And trusted phone numbers +for iCloud two-factor auth. + +267 +00:17:30,951 --> 00:17:36,256 +And it helps you manage emergency contacts +to make any changes as needed. + +268 +00:17:36,290 --> 00:17:40,727 +All these things together help people +with threats to their personal safety + +269 +00:17:40,761 --> 00:17:43,630 +control access to their data. + +270 +00:17:44,698 --> 00:17:47,334 +There are two ways to use Safety Check. + +271 +00:17:47,367 --> 00:17:52,339 +The first is Emergency Reset, +for crisis situations when you need + +272 +00:17:52,372 --> 00:17:56,777 +to immediately reset access +across people and apps. + +273 +00:17:57,778 --> 00:18:01,215 +The second is Manage Sharing & Access, + +274 +00:18:01,248 --> 00:18:06,653 +which provides more fine-grained control +over each capability of Safety Check. + +275 +00:18:08,288 --> 00:18:14,795 +Emergency Reset lets you quickly +take action across all people and all apps + +276 +00:18:14,828 --> 00:18:20,133 +and disables access to FaceTime +and iMessage on your other devices. + +277 +00:18:20,167 --> 00:18:24,638 +Then you are walked through securing +your iCloud account to the device + +278 +00:18:24,671 --> 00:18:28,375 +in your hand +and reviewing your emergency contacts + +279 +00:18:28,408 --> 00:18:30,811 +and trusted devices. + +280 +00:18:30,844 --> 00:18:34,681 +Manage Sharing & Access +lets you review person by person + +281 +00:18:34,715 --> 00:18:39,119 +and app by app +to review who you're sharing with. + +282 +00:18:39,152 --> 00:18:43,724 +You can sort this by name or the type +of information you're sharing. + +283 +00:18:43,757 --> 00:18:47,528 +This is a great tool +to understand and control your sharing + +284 +00:18:47,561 --> 00:18:49,563 +with a particular person. + +285 +00:18:49,596 --> 00:18:52,499 +It also can help you find an app +with sensitive permissions + +286 +00:18:52,533 --> 00:18:55,669 +installed by someone +with access to your device. + +287 +00:18:58,238 --> 00:19:01,642 +Quick Exit is available +in all Safety Check flows + +288 +00:19:01,675 --> 00:19:03,877 +and quickly lets people exit the flow + +289 +00:19:03,911 --> 00:19:07,147 +if they're concerned +someone might see what they're doing. + +290 +00:19:07,181 --> 00:19:09,716 +Pressing it returns them +to the home screen, + +291 +00:19:09,750 --> 00:19:11,852 +and the next time they enter settings, + +292 +00:19:11,885 --> 00:19:13,754 +they'll be back +on the main settings page, + +293 +00:19:13,787 --> 00:19:16,523 +not in Safety Check. + +294 +00:19:16,557 --> 00:19:21,461 +Safety Check helps people in domestic +or intimate partner violence situations + +295 +00:19:21,495 --> 00:19:24,932 +take back control of their private data. + +296 +00:19:24,965 --> 00:19:29,436 +Privacy isn't just about making a decision +in the moment you share data, + +297 +00:19:29,469 --> 00:19:35,209 +but also being able to understand +and change that decision at any time. + +298 +00:19:35,242 --> 00:19:40,214 +This is just one example +of how Apple designs for privacy. + +299 +00:19:40,247 --> 00:19:44,918 +Designing for privacy in your app +will help you build and maintain trust + +300 +00:19:44,952 --> 00:19:46,887 +with your users. + +301 +00:19:46,920 --> 00:19:53,427 +iOS 16 and macOS Ventura bring many new +technologies to help you on this journey. + +302 +00:19:53,460 --> 00:19:57,297 +Prepare for new platform updates +like UI device name changes + +303 +00:19:57,331 --> 00:19:59,833 +and Gatekeeper improvements. + +304 +00:19:59,867 --> 00:20:02,569 +Adopt media device discovery extensions, + +305 +00:20:02,603 --> 00:20:05,038 +UIKit paste controls, + +306 +00:20:05,072 --> 00:20:09,243 +Private Access Tokens, +and Passkeys. + +307 +00:20:09,276 --> 00:20:10,811 +Thanks for joining me today. + +308 +00:20:10,844 --> 00:20:13,380 +We can't wait to see how +you build great features + +309 +00:20:13,413 --> 00:20:15,516 +and great privacy into your app. + +310 +00:20:15,549 --> 00:20:19,987 +[upbeat music] + diff --git a/eng/2022 Session 10097 What's new in App Clips en.srt b/eng/2022 Session 10097 What's new in App Clips en.srt new file mode 100644 index 0000000..d7ee707 --- /dev/null +++ b/eng/2022 Session 10097 What's new in App Clips en.srt @@ -0,0 +1,719 @@ +1 +00:00:00,701 --> 00:00:08,709 +♪ ♪ + +2 +00:00:09,643 --> 00:00:11,745 +Hi. I'm Charles Ying. + +3 +00:00:11,778 --> 00:00:14,715 +Welcome to What's new in App Clips. + +4 +00:00:14,748 --> 00:00:17,518 +An App Clip is a small part of an app. + +5 +00:00:17,551 --> 00:00:20,187 +They're light and fast, +and easy to discover, + +6 +00:00:20,220 --> 00:00:23,790 +so you can quickly get what you need +right when you need it. + +7 +00:00:23,824 --> 00:00:27,427 +They can be found in Messages, +Maps, Spotlight, and Safari, + +8 +00:00:27,461 --> 00:00:31,198 +or out in the world with QR codes +and App Clip Codes. + +9 +00:00:31,231 --> 00:00:33,567 +One really cool example is Toast. + +10 +00:00:33,600 --> 00:00:39,706 +Toast's app clip lets you scan a QR code +to pay for your food right at your table! + +11 +00:00:39,740 --> 00:00:43,377 +Toast configured their App Clip to open +from an existing QR code + +12 +00:00:43,410 --> 00:00:45,112 +printed on a receipt. + +13 +00:00:45,145 --> 00:00:48,515 +The App Clip has resulted +in users checking out faster, + +14 +00:00:48,549 --> 00:00:51,818 +and more users +choosing to install the full app. + +15 +00:00:51,852 --> 00:00:55,556 +Building an App Clip into +an existing flow is a great way + +16 +00:00:55,589 --> 00:00:57,791 +to streamline your experience. + +17 +00:00:58,492 --> 00:01:02,696 +Today you'll learn about new features +that let your users do more + +18 +00:01:02,729 --> 00:01:07,768 +with your App Clip, +and make developing App Clips even easier. + +19 +00:01:07,801 --> 00:01:11,238 +Let's start with the App Clip size limit. + +20 +00:01:11,939 --> 00:01:16,076 +App Clips are light and fast, +and designed for speed. + +21 +00:01:16,109 --> 00:01:19,947 +To make App Clips feel instant, +they need to be small. + +22 +00:01:19,980 --> 00:01:23,851 +And since wireless networks +are faster than they were two years ago, + +23 +00:01:23,884 --> 00:01:27,721 +I'm happy to say that new in iOS 16, + +24 +00:01:27,754 --> 00:01:32,559 +App Clips can now be up to 15 MB in size. + +25 +00:01:32,593 --> 00:01:36,496 +This gives you more room +to build your ideal experience. + +26 +00:01:36,530 --> 00:01:40,734 +Set your App Clip's minimum version +to iOS 16 for the new limit, + +27 +00:01:40,767 --> 00:01:45,739 +or stay under 10 MB to be compatible +with iOS 15 and earlier. + +28 +00:01:45,772 --> 00:01:49,176 +Either way, you can always download +additional resources + +29 +00:01:49,209 --> 00:01:51,879 +after your App Clip launches. + +30 +00:01:51,912 --> 00:01:54,882 +Speed is still key to a great experience. + +31 +00:01:54,915 --> 00:01:58,585 +Your users won't always be +in locations with a fast network, + +32 +00:01:58,619 --> 00:02:03,357 +so optimizing your App Clip +is still as critical as ever. + +33 +00:02:03,390 --> 00:02:05,759 +To learn more +about optimizing your App Clip, + +34 +00:02:05,792 --> 00:02:10,063 +please watch, +"Build light and fast App Clips”. + +35 +00:02:10,097 --> 00:02:13,634 +Next, I'm really excited to show you +a simple new tool + +36 +00:02:13,667 --> 00:02:19,106 +that makes sure your App Clip experience +or universal link is set up correctly. + +37 +00:02:19,139 --> 00:02:21,008 +Here's how it works. + +38 +00:02:21,041 --> 00:02:24,311 +On your iPhone or iPad, +go to Developer Settings + +39 +00:02:24,344 --> 00:02:28,782 +and under App Clips Testing, +select Diagnostics. + +40 +00:02:28,815 --> 00:02:31,351 +Now, enter your URL. + +41 +00:02:31,385 --> 00:02:36,857 +You can turn on Developer Settings +by connecting your device to Xcode. + +42 +00:02:36,890 --> 00:02:40,060 +iOS will check your URL's configuration. + +43 +00:02:40,093 --> 00:02:43,864 +If everything is set up right, +you'll see these green checkboxes. + +44 +00:02:44,998 --> 00:02:48,302 +But, if there's something wrong, +you'll see a screen like this one + +45 +00:02:48,335 --> 00:02:51,538 +that tells you exactly what's happening. + +46 +00:02:51,572 --> 00:02:55,409 +This will help you fix problems +like Safari's banner not showing, + +47 +00:02:55,442 --> 00:02:58,946 +or navigating to a web page +instead of your App Clip. + +48 +00:02:58,979 --> 00:03:01,915 +Each diagnostic has a link +to documentation + +49 +00:03:01,949 --> 00:03:04,852 +to explain the configuration step further. + +50 +00:03:04,885 --> 00:03:08,021 +Now you'll be able +to see exactly what's wrong. + +51 +00:03:09,289 --> 00:03:14,194 +App Clip diagnostics checks App Clip +experiences that use physical codes, + +52 +00:03:14,228 --> 00:03:15,629 +Safari and iMessage, + +53 +00:03:15,662 --> 00:03:20,634 +and it will also check your universal link +associated domains configuration. + +54 +00:03:20,667 --> 00:03:26,106 +This simple new tool makes it so much +easier to get your configuration right. + +55 +00:03:27,307 --> 00:03:29,643 +Next, CloudKit. + +56 +00:03:30,777 --> 00:03:35,682 +CloudKit is a framework that lets your app +access data stored on iCloud. + +57 +00:03:35,716 --> 00:03:40,220 +Up until now, CloudKit has +not been available for App Clips. + +58 +00:03:40,254 --> 00:03:44,958 +Your App Clip might be using a server +to read data or resources. + +59 +00:03:46,226 --> 00:03:49,930 +New in iOS 16, +App Clips can also read data + +60 +00:03:49,963 --> 00:03:53,400 +and resources stored +in a CloudKit public database. + +61 +00:03:53,433 --> 00:03:57,237 +You can now share the same code, +access the same data + +62 +00:03:57,271 --> 00:03:59,573 +in both your app and App Clip + +63 +00:03:59,606 --> 00:04:03,644 +with all the power and scale +CloudKit provides. + +64 +00:04:03,677 --> 00:04:07,648 +An important difference between apps +and App Clips is that App Clips can read + +65 +00:04:07,681 --> 00:04:11,885 +from a public database +but can't write data to CloudKit. + +66 +00:04:11,919 --> 00:04:16,123 +App Clips also can't use cloud documents +and the key-value store. + +67 +00:04:16,156 --> 00:04:20,294 +This keeps the promise to users +that when an App Clip isn't used anymore, + +68 +00:04:20,327 --> 00:04:23,263 +iOS will delete the App Clip and its data. + +69 +00:04:24,264 --> 00:04:27,067 +To enable CloudKit +for your App Clip in Xcode, + +70 +00:04:27,100 --> 00:04:31,238 +open your App Clip target's +Signing and Capabilities tab, + +71 +00:04:31,271 --> 00:04:35,542 +and select the CloudKit container +you want your App Clip to use. + +72 +00:04:35,576 --> 00:04:39,112 +This CloudKit container can be a new +or existing container + +73 +00:04:39,146 --> 00:04:41,181 +shared with your full app. + +74 +00:04:41,949 --> 00:04:47,120 +Here's an example of how to read +CloudKit public data from your App Clip. + +75 +00:04:47,154 --> 00:04:50,224 +Create a CKContainer +with the container's identifier + +76 +00:04:50,257 --> 00:04:53,827 +and access +the publicCloudDatabase property. + +77 +00:04:53,861 --> 00:04:57,464 +Then fetch the record you want +from the public database. + +78 +00:04:59,867 --> 00:05:02,769 +Next, keychain migration. + +79 +00:05:03,537 --> 00:05:06,273 +Today, when you want to transfer +sensitive information, + +80 +00:05:06,306 --> 00:05:09,243 +like authentication tokens +and payment information + +81 +00:05:09,276 --> 00:05:11,645 +from your App Clip to your full app, + +82 +00:05:11,678 --> 00:05:16,016 +your App Clip would store this data +in a shared app group container. + +83 +00:05:16,049 --> 00:05:21,421 +iOS saves this data when a user upgrades +from your App Clip to your full app. + +84 +00:05:22,723 --> 00:05:24,958 +Your full app reads +the app group container + +85 +00:05:24,992 --> 00:05:27,561 +and stores that information +in the keychain. + +86 +00:05:28,862 --> 00:05:32,566 +However, the keychain is the ideal place +to securely store + +87 +00:05:32,599 --> 00:05:35,369 +this type of information. + +88 +00:05:35,402 --> 00:05:38,772 +New this year, +when a user installs your app, + +89 +00:05:38,805 --> 00:05:41,842 +items stored in your App Clip's keychain +are transferred + +90 +00:05:41,875 --> 00:05:44,411 +from your App Clip to your app. + +91 +00:05:44,444 --> 00:05:48,081 +Now your App Clip can simply store +secure items in the keychain + +92 +00:05:48,115 --> 00:05:51,251 +and they will be moved to your app +when it's installed. + +93 +00:05:51,952 --> 00:05:56,256 +Shared keychain groups +and iCloud Keychain are not supported. + +94 +00:05:56,290 --> 00:05:59,493 +This keeps the promise to users +that keychain information + +95 +00:05:59,526 --> 00:06:03,030 +won't stick around +when an App Clip isn't used anymore. + +96 +00:06:04,231 --> 00:06:08,702 +Here's an example of how to store and +read login information in the keychain. + +97 +00:06:08,735 --> 00:06:12,406 +The code is the same +for both app and App Clip. + +98 +00:06:12,439 --> 00:06:16,109 +You can add new items to +the keychain with SecItemAdd. + +99 +00:06:16,143 --> 00:06:20,380 +And fetch these items from the keychain +with SecItemCopyMatching. + +100 +00:06:20,414 --> 00:06:23,817 +And add a label to your items +to help your full app + +101 +00:06:23,851 --> 00:06:27,654 +identify that the items were saved +by your App Clip. + +102 +00:06:28,822 --> 00:06:32,125 +Finally, the App Clip experiences API. + +103 +00:06:32,159 --> 00:06:37,497 +As your app clip grows, you'll have more +and more advanced App Clip experiences, + +104 +00:06:37,531 --> 00:06:41,068 +each with their own information +or location. + +105 +00:06:41,101 --> 00:06:45,839 +You need an easy way to add +and update all of these experiences. + +106 +00:06:45,873 --> 00:06:49,376 +App Store Connect has +an App Clip experiences web API + +107 +00:06:49,409 --> 00:06:53,347 +that's designed to automate this workflow. + +108 +00:06:53,380 --> 00:06:57,084 +Let's look at an example +that uses this API + +109 +00:06:57,117 --> 00:06:59,920 +to add an advanced App Clip experience. + +110 +00:07:00,754 --> 00:07:03,857 +First, get the App Clip resource ID. + +111 +00:07:03,891 --> 00:07:06,793 +Next, upload your header image. + +112 +00:07:06,827 --> 00:07:10,831 +Last, create the advanced +App Clip experience. + +113 +00:07:10,864 --> 00:07:14,535 +First, let's find the resource ID +for the app clip. + +114 +00:07:14,568 --> 00:07:19,706 +Call the web API with your app's item ID +and App Clip bundle ID. + +115 +00:07:19,740 --> 00:07:24,311 +And from the response, +save the App Clip resource ID for later. + +116 +00:07:24,344 --> 00:07:28,749 +Next, upload a header image +for the advanced App Clip experience. + +117 +00:07:28,782 --> 00:07:33,253 +Save the header image's resource ID +for the next step. + +118 +00:07:33,287 --> 00:07:36,857 +Last, with your App Clip resource ID +and your header image ID, + +119 +00:07:36,890 --> 00:07:40,027 +we can now create +your advanced App Clip experience. + +120 +00:07:40,694 --> 00:07:43,063 +There are three dictionaries to fill in: + +121 +00:07:43,096 --> 00:07:46,700 +attributes, relationships, and included. + +122 +00:07:48,202 --> 00:07:52,773 +In the attributes dictionary, +add information like the action name, + +123 +00:07:52,806 --> 00:07:54,875 +which category and language, + +124 +00:07:54,908 --> 00:07:58,011 +and the link +for the advanced App Clip experience. + +125 +00:07:58,045 --> 00:08:03,550 +If your advanced experience is tied to +a Maps location, add a place dictionary. + +126 +00:08:03,584 --> 00:08:07,120 +In the place dictionary, +add the matching Maps business place name, + +127 +00:08:07,154 --> 00:08:10,991 +a map action, and the map coordinates. + +128 +00:08:11,024 --> 00:08:12,860 +In the relationships dictionary, + +129 +00:08:12,893 --> 00:08:17,331 +add the App Clip resource ID +and the header image ID. + +130 +00:08:17,364 --> 00:08:21,301 +And in the included dictionary, +add a localized title and subtitle + +131 +00:08:21,335 --> 00:08:23,704 +for the advanced App Clip experience. + +132 +00:08:23,737 --> 00:08:25,873 +And that's it! + +133 +00:08:25,906 --> 00:08:28,342 +With this App Store Connect API, + +134 +00:08:28,375 --> 00:08:32,346 +you can fully automate creating +advanced App Clip experiences. + +135 +00:08:32,379 --> 00:08:34,748 +To learn more about App Store Connect, + +136 +00:08:34,781 --> 00:08:37,317 +check out “Automating App Store Connect” + +137 +00:08:37,351 --> 00:08:39,219 +and “What's new in App Store Connect” + +138 +00:08:39,253 --> 00:08:42,222 +from WWDC 2020. + +139 +00:08:42,990 --> 00:08:47,394 +To wrap up, the new App Clip size limit +gives you more room + +140 +00:08:47,427 --> 00:08:50,230 +to build your ideal experience. + +141 +00:08:50,264 --> 00:08:54,701 +App Clip diagnostics tools are a great way +to understand your App Clip + +142 +00:08:54,735 --> 00:08:58,272 +and universal link configuration +end to end. + +143 +00:08:58,305 --> 00:09:01,441 +CloudKit and keychain can ease +your development + +144 +00:09:01,475 --> 00:09:04,344 +by sharing more code with your app. + +145 +00:09:04,378 --> 00:09:07,714 +And the App Clip experiences API +automates the workflow + +146 +00:09:07,748 --> 00:09:11,518 +for managing +your advanced App Clip experiences. + +147 +00:09:11,552 --> 00:09:14,188 +To learn more about developing App Clips, + +148 +00:09:14,221 --> 00:09:16,823 +please watch “What's new in App Clips” + +149 +00:09:16,857 --> 00:09:19,993 +from WWDC 2021 + +150 +00:09:20,027 --> 00:09:24,998 +and “Design great App Clips” from WWDC 2020. + +151 +00:09:25,699 --> 00:09:28,402 +You developers have made +such great App Clips. + +152 +00:09:28,435 --> 00:09:29,870 +They're so creative! + +153 +00:09:29,903 --> 00:09:35,175 +We hope these new features help you build +your next great App Clip. + +154 +00:09:35,209 --> 00:09:37,444 +Thanks for watching! ♪ ♪ + diff --git a/eng/2022 Session 10098 Meet Web Push for Safari en.srt b/eng/2022 Session 10098 Meet Web Push for Safari en.srt new file mode 100644 index 0000000..2e917f5 --- /dev/null +++ b/eng/2022 Session 10098 Meet Web Push for Safari en.srt @@ -0,0 +1,1238 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,877 --> 00:00:12,379 +Brady Eidson: Hello. +My name is Brady Eidson. + +3 +00:00:12,412 --> 00:00:15,082 +I'm an engineer +on the WebKit Architecture team. + +4 +00:00:15,115 --> 00:00:19,019 +I am thrilled to introduce you +to Web Push in Safari. + +5 +00:00:19,052 --> 00:00:21,955 +Web Push lets you +remotely send notifications + +6 +00:00:21,989 --> 00:00:24,358 +to your web application's users. + +7 +00:00:24,391 --> 00:00:28,161 +Here, a notification displays +from webkit.org + +8 +00:00:28,195 --> 00:00:30,364 +in the upper-right of the screen. + +9 +00:00:30,397 --> 00:00:34,601 +Clicking the notification opens +a WebKit blog post in a new window. + +10 +00:00:34,635 --> 00:00:37,971 +Before I get into other details +on how this works, + +11 +00:00:38,005 --> 00:00:41,708 +I want to answer a few questions +up front I know many of you will have. + +12 +00:00:43,043 --> 00:00:47,848 +Web Push is supported in Mac Safari +beginning with macOS Ventura. + +13 +00:00:47,881 --> 00:00:52,052 +And Web Push will be coming +to iOS and iPadOS next year. + +14 +00:00:53,320 --> 00:00:56,723 +Apple's Safari Push Notifications +have been an option + +15 +00:00:56,757 --> 00:00:59,660 +for reaching Mac Safari users +for quite a while. + +16 +00:00:59,693 --> 00:01:02,596 +While it will continue to work, +today I'm happy to announce + +17 +00:01:02,629 --> 00:01:04,631 +that we have added support for Web Push, + +18 +00:01:04,665 --> 00:01:08,402 +and this really is Web Push! + +19 +00:01:08,435 --> 00:01:11,238 +The same combination +of various web standards + +20 +00:01:11,271 --> 00:01:13,774 +as implemented in other browsers. + +21 +00:01:13,807 --> 00:01:16,343 +We'll go over those standards +more later, but… + +22 +00:01:16,376 --> 00:01:18,412 +the most important takeaway + +23 +00:01:18,445 --> 00:01:22,182 +is that if you've coded +your application to web standards, + +24 +00:01:22,216 --> 00:01:25,219 +you won't need to make any changes +for it to work in Safari. + +25 +00:01:25,252 --> 00:01:28,689 +Of course, if you exclude Safari +through browser detection, + +26 +00:01:28,722 --> 00:01:30,257 +then you have some work ahead of you. + +27 +00:01:30,290 --> 00:01:35,062 +Now would be a great time to switch from +browser detection to feature detection, + +28 +00:01:35,095 --> 00:01:37,731 +which has always been the best practice. + +29 +00:01:37,764 --> 00:01:40,133 +We're using the same +Apple Push Notification service + +30 +00:01:40,167 --> 00:01:44,071 +that powers native push +on all Macs and iOS devices, + +31 +00:01:44,104 --> 00:01:48,609 +but no Apple Developer account +is required to reach Safari users. + +32 +00:01:48,642 --> 00:01:52,145 +We are using new +end point URLs for Web Push, + +33 +00:01:52,179 --> 00:01:57,084 +which brings up another thing you might be +doing to unintentionally exclude Safari. + +34 +00:01:57,117 --> 00:02:00,053 +If you tightly manage +push end points on your server, + +35 +00:02:00,087 --> 00:02:05,058 +make sure you allow URLs +from any subdomain of push.apple.com. + +36 +00:02:05,092 --> 00:02:08,161 +Moving beyond answers +to those important questions, + +37 +00:02:08,195 --> 00:02:10,130 +let's get into more detail. + +38 +00:02:10,163 --> 00:02:13,233 +First, we'll look at +the Web Push experience in Safari + +39 +00:02:13,267 --> 00:02:15,869 +from a user's perspective. + +40 +00:02:15,903 --> 00:02:19,873 +Then we'll cover the entire Web Push flow, +from asking for permission + +41 +00:02:19,907 --> 00:02:23,710 +to handling a click on an entry +in Notification Center. + +42 +00:02:23,744 --> 00:02:29,216 +Finally, we'll see what it takes to add +Web Push to an existing web app. + +43 +00:02:29,249 --> 00:02:32,586 +But first, the Mac Safari user experience. + +44 +00:02:32,619 --> 00:02:36,757 +And I can think of no better way +to cover that than with a demo. + +45 +00:02:36,790 --> 00:02:39,793 +Here's Safari on macOS Ventura. + +46 +00:02:39,826 --> 00:02:43,363 +I have webkit.org +open in this browser tab. + +47 +00:02:43,397 --> 00:02:46,266 +I need to keep up-to-date +with the WebKit open source project, + +48 +00:02:46,300 --> 00:02:49,069 +and Web Push is a great way to do that. + +49 +00:02:49,102 --> 00:02:53,040 +webkit.org is not allowed +to request permission to push + +50 +00:02:53,073 --> 00:02:56,109 +without the user asking +with a user gesture. + +51 +00:02:56,143 --> 00:03:01,181 +So I'll click this bell-shaped button here +to subscribe for notifications. + +52 +00:03:01,215 --> 00:03:04,885 +What you see here is +the system notifications prompt– + +53 +00:03:04,918 --> 00:03:07,888 +the same one you'd see for +any other application. + +54 +00:03:07,921 --> 00:03:11,525 +In this case, +it's on behalf of webkit.org. + +55 +00:03:11,558 --> 00:03:15,162 +I will click "allow," and I'm all set. + +56 +00:03:15,195 --> 00:03:20,200 +webkit.org is giving me the option +to be notified about new blog posts + +57 +00:03:20,234 --> 00:03:23,070 +as well as new commits +to the source code repository. + +58 +00:03:23,103 --> 00:03:27,508 +I know being notified for every commit +will distract me from important work, + +59 +00:03:27,541 --> 00:03:31,545 +but I absolutely want to be +notified about new blog posts. + +60 +00:03:31,578 --> 00:03:33,614 +So I'll check that box now. + +61 +00:03:33,647 --> 00:03:37,417 +Coincidentally, somebody must've +just published the WebKit blog post + +62 +00:03:37,451 --> 00:03:39,152 +about Web Push. + +63 +00:03:39,186 --> 00:03:41,855 +This notification looks +just like any other + +64 +00:03:41,889 --> 00:03:44,525 +and is attributed to webkit.org. + +65 +00:03:44,558 --> 00:03:47,060 +I can click it to activate, + +66 +00:03:47,094 --> 00:03:50,163 +and there is the blog post, +open in Safari. + +67 +00:03:50,197 --> 00:03:53,367 +Once a user has +granted permissions to a website, + +68 +00:03:53,400 --> 00:03:56,270 +they maintain control +over that permission. + +69 +00:03:56,303 --> 00:03:59,873 +As a macOS user, I'm used +to managing Notification preferences + +70 +00:03:59,907 --> 00:04:03,243 +inside System Settings, +and that's where I can go + +71 +00:04:03,277 --> 00:04:07,281 +to configure webkit.org's notifications. + +72 +00:04:07,314 --> 00:04:12,486 +The same rich configuration +as I'd find for any other app or service. + +73 +00:04:12,519 --> 00:04:16,089 +As a Safari user, +I'm used to managing website settings + +74 +00:04:16,123 --> 00:04:18,225 +from inside Safari preferences. + +75 +00:04:18,258 --> 00:04:24,031 +I can also go there to turn +webkit.org's permissions on or off. + +76 +00:04:24,064 --> 00:04:28,035 +And that's how Web Push works +for users in Mac Safari. + +77 +00:04:28,068 --> 00:04:32,606 +Before we move on, I want to reiterate +a few things covered in that demo. + +78 +00:04:32,639 --> 00:04:36,243 +First, we don't want users +to be spammed by subscription requests + +79 +00:04:36,276 --> 00:04:37,978 +they haven't asked for. + +80 +00:04:38,011 --> 00:04:41,081 +So a website may only request +a push subscription + +81 +00:04:41,114 --> 00:04:44,651 +in response to a mouse click +or a keystroke. + +82 +00:04:44,685 --> 00:04:48,222 +Once a website has permission +to show notifications to the user, + +83 +00:04:48,255 --> 00:04:50,224 +the user controls that permission. + +84 +00:04:50,257 --> 00:04:54,328 +They can choose to manage it in +Safari's preferences or System Settings. + +85 +00:04:54,361 --> 00:04:57,998 +And the setting will stay in sync +if they happen to manage it in both. + +86 +00:04:58,031 --> 00:05:01,935 +Finally, if you provide notifications +for different types of events, + +87 +00:05:01,969 --> 00:05:05,372 +it is a best practice to provide +fine-grained controls + +88 +00:05:05,405 --> 00:05:07,808 +for notification types +within your web app, + +89 +00:05:07,841 --> 00:05:09,409 +just like other apps do. + +90 +00:05:09,443 --> 00:05:11,912 +Now that you've seen Web Push in action, + +91 +00:05:11,945 --> 00:05:14,815 +let's dig in to what's happening +at each step. + +92 +00:05:14,848 --> 00:05:17,951 +Some of you are already +intimately familiar with this. + +93 +00:05:17,985 --> 00:05:21,722 +But for those of you new to Web Push, +I'll go step by step, + +94 +00:05:21,755 --> 00:05:25,459 +referring you to the relevant standards +and documentation along the way. + +95 +00:05:25,492 --> 00:05:30,063 +The first thing that happens is a user +visits your website in a browser tab. + +96 +00:05:30,097 --> 00:05:33,133 +Here's webkit.org open in Safari. + +97 +00:05:33,166 --> 00:05:37,638 +Since it is open in a tab, +it can install a Service Worker. + +98 +00:05:37,671 --> 00:05:40,807 +A Service Worker is +a unit of JavaScript that operates + +99 +00:05:40,841 --> 00:05:45,913 +on behalf of an entire domain, separate +from a currently open browser tab. + +100 +00:05:45,946 --> 00:05:48,582 +Once the Service Worker script +is installed, + +101 +00:05:48,615 --> 00:05:52,152 +your web app is eligible +to request a push subscription. + +102 +00:05:52,186 --> 00:05:56,823 +As already mentioned, this request +must be tied to a user gesture. + +103 +00:05:56,857 --> 00:06:01,161 +webkit.org requests permission +when clicking this bell-shaped button, + +104 +00:06:01,195 --> 00:06:04,097 +which fulfills +the user gesture requirement. + +105 +00:06:04,131 --> 00:06:07,000 +When your site asks +for a push subscription, + +106 +00:06:07,034 --> 00:06:09,436 +the user sees this system prompt. + +107 +00:06:09,469 --> 00:06:11,338 +Here is where they can make the final call + +108 +00:06:11,371 --> 00:06:13,941 +on granting your website +this powerful ability. + +109 +00:06:15,142 --> 00:06:18,078 +It is possible +the user might deny the request. + +110 +00:06:18,111 --> 00:06:21,181 +Your JavaScript should be +prepared to handle that. + +111 +00:06:21,215 --> 00:06:23,483 +But assuming the user grants permission, + +112 +00:06:23,517 --> 00:06:26,854 +your JavaScript gets back +a PushSubscription object. + +113 +00:06:26,887 --> 00:06:30,324 +This includes everything your server needs +to send a push message + +114 +00:06:30,357 --> 00:06:32,459 +to this user in this browser. + +115 +00:06:32,492 --> 00:06:36,096 +Information like +the exact URL end point to use. + +116 +00:06:36,129 --> 00:06:39,833 +You send this PushSubscription +payload back to your server + +117 +00:06:39,867 --> 00:06:42,669 +in whatever manner +works best for your web app. + +118 +00:06:42,703 --> 00:06:47,541 +Many popular server packages have +Web Push support to manage subscriptions, + +119 +00:06:47,574 --> 00:06:49,543 +or you can roll your own. + +120 +00:06:49,576 --> 00:06:53,714 +The same pertains to how and when +to actually send a push message + +121 +00:06:53,747 --> 00:06:56,416 +to the URL end points +your server knows about. + +122 +00:06:56,450 --> 00:07:00,454 +I can't tell you when to do so. +That's up to you and your website. + +123 +00:07:00,487 --> 00:07:03,023 +But once you've decided +to send that push message, + +124 +00:07:03,056 --> 00:07:05,125 +I can help with what happens next. + +125 +00:07:05,158 --> 00:07:09,329 +Remember how push requires +an installed Service Worker? + +126 +00:07:09,363 --> 00:07:13,300 +Once your server has sent +a push message and Safari receives it, + +127 +00:07:13,333 --> 00:07:18,238 +Safari wakes up your Service Worker +and sends it a JavaScript push event. + +128 +00:07:18,272 --> 00:07:21,675 +Showing a notification to the user +in Notification Center + +129 +00:07:21,708 --> 00:07:25,379 +is a requirement +while handling the push event. + +130 +00:07:25,412 --> 00:07:29,183 +Receiving the push event +and displaying the notification happens + +131 +00:07:29,216 --> 00:07:32,319 +if your website is currently open +in a browser tab. + +132 +00:07:32,352 --> 00:07:36,557 +It also happens if your website is +not currently open in a browser tab. + +133 +00:07:36,590 --> 00:07:39,226 +In the case of Safari on macOS Ventura, + +134 +00:07:39,259 --> 00:07:43,263 +this happens even if Safari +is not currently running. + +135 +00:07:43,297 --> 00:07:47,234 +The final step: If your user clicks +on that notification, + +136 +00:07:47,267 --> 00:07:50,838 +a notificationclick event is sent +to your Service Worker + +137 +00:07:50,871 --> 00:07:53,106 +so it can respond appropriately. + +138 +00:07:53,140 --> 00:07:55,809 +For example, by opening a new window + +139 +00:07:55,843 --> 00:07:58,979 +to the URL associated +with that notification. + +140 +00:07:59,012 --> 00:08:03,016 +With that understanding +of the Web Push flow under our belt, + +141 +00:08:03,050 --> 00:08:05,252 +it's time to get into even more detail + +142 +00:08:05,285 --> 00:08:09,089 +by actually adding Web Push support +to an existing web app. + +143 +00:08:09,122 --> 00:08:13,126 +Besides webkit.org, +Browser Pets is the most + +144 +00:08:13,160 --> 00:08:17,097 +mission critical internal tool +for the Safari and WebKit teams. + +145 +00:08:17,130 --> 00:08:20,367 +Keeping everyone in the department +up-to-date on their favorite WebKittens + +146 +00:08:20,400 --> 00:08:24,404 +and Pups on Safari has always been +the mission statement of Browser Pets, + +147 +00:08:24,438 --> 00:08:27,207 +and Web Push has made that +easier than ever. + +148 +00:08:27,241 --> 00:08:31,178 +Our internal BrowserPets domain +already had a ServiceWorker script + +149 +00:08:31,211 --> 00:08:35,983 +registered to speed up page loads +and synchronize between multiple tabs. + +150 +00:08:36,016 --> 00:08:40,287 +At a high level, a ServiceWorker script +looks a lot like this. + +151 +00:08:40,320 --> 00:08:44,157 +When an engineer visits +the Browser Pets page in a tab, + +152 +00:08:44,191 --> 00:08:47,861 +this JavaScript excerpt either determines +if the Service Worker script + +153 +00:08:47,895 --> 00:08:51,932 +has already been registered, +or registers it if necessary. + +154 +00:08:51,965 --> 00:08:55,302 +Notice we're practicing +feature detection here, + +155 +00:08:55,335 --> 00:08:57,871 +previously mentioned as a best practice. + +156 +00:08:57,905 --> 00:09:01,341 +With the Service Worker prerequisite +taken care of, + +157 +00:09:01,375 --> 00:09:03,877 +we're ready to subscribe for push. + +158 +00:09:03,911 --> 00:09:07,314 +Remember, you cannot request +a push subscription + +159 +00:09:07,347 --> 00:09:10,250 +without an explicit user gesture. + +160 +00:09:10,284 --> 00:09:13,387 +Running this script in response +to a button's onclick handler + +161 +00:09:13,420 --> 00:09:16,490 +is one of many ways +to satisfy that requirement. + +162 +00:09:16,523 --> 00:09:18,926 +Once the user clicks that button, + +163 +00:09:18,959 --> 00:09:22,062 +here's code to request +a push subscription. + +164 +00:09:22,095 --> 00:09:24,831 +I'll go into each of these points +in more detail. + +165 +00:09:24,865 --> 00:09:29,236 +First, we need to configure +the request for a push subscription. + +166 +00:09:29,269 --> 00:09:32,673 +An important bit for that is +the public key our server uses + +167 +00:09:32,706 --> 00:09:36,510 +to identify themselves +to Apple's push servers. + +168 +00:09:36,543 --> 00:09:41,815 +Here we use the standard technology +called VAPID, the same as other browsers. + +169 +00:09:41,849 --> 00:09:45,485 +I won't go over the sometimes +complex details of VAPID here, + +170 +00:09:45,519 --> 00:09:47,654 +but there are resources on the web +to help you + +171 +00:09:47,688 --> 00:09:51,391 +with the best solution +for your server's setup. + +172 +00:09:51,425 --> 00:09:56,163 +With the VAPID key set, we're ready +to configure the subscription request. + +173 +00:09:56,196 --> 00:09:59,132 +Notice we are explicitly stating + +174 +00:09:59,166 --> 00:10:03,070 +that we promise to always +make pushes user visible. + +175 +00:10:03,103 --> 00:10:06,406 +While the standard +for the JavaScript Push API + +176 +00:10:06,440 --> 00:10:09,676 +optionally accommodates +silent JavaScript runtime + +177 +00:10:09,710 --> 00:10:13,914 +in response to a push, +most browsers do not support that. + +178 +00:10:13,947 --> 00:10:16,216 +Safari does not support that. + +179 +00:10:16,250 --> 00:10:20,821 +And like most websites, +Browser Pets does not need that. + +180 +00:10:20,854 --> 00:10:23,924 +Then we request permission to push. + +181 +00:10:23,957 --> 00:10:26,660 +This line of JavaScript results +in the permission prompt + +182 +00:10:26,693 --> 00:10:30,063 +for the user to either approve or reject. + +183 +00:10:30,097 --> 00:10:32,032 +Assuming the user grants permission– + +184 +00:10:32,065 --> 00:10:34,568 +which all Safari team members do +for Browser Pets– + +185 +00:10:34,601 --> 00:10:37,237 +this gives us a PushSubscription object + +186 +00:10:37,271 --> 00:10:40,374 +with the details on how to reach +this user in their browser. + +187 +00:10:40,407 --> 00:10:44,244 +Things like the URL end point +and the key used to encrypt + +188 +00:10:44,278 --> 00:10:47,114 +the push message for transit. + +189 +00:10:47,147 --> 00:10:51,218 +Finally, we need to send +all of those details to our server. + +190 +00:10:51,251 --> 00:10:54,955 +As mentioned before, +the specifics of this will vary + +191 +00:10:54,988 --> 00:10:57,391 +based on your exact application. + +192 +00:10:57,424 --> 00:11:00,961 +Our BrowserPets server uses WordPress, +which already has + +193 +00:11:00,994 --> 00:11:04,097 +a few plugins to support +standard Web Push. + +194 +00:11:04,131 --> 00:11:07,334 +It's likely you'll find the same is true +for your backend, + +195 +00:11:07,367 --> 00:11:10,337 +and there are resources on the web +to help find the right solution + +196 +00:11:10,370 --> 00:11:12,139 +for just about any setup. + +197 +00:11:12,172 --> 00:11:16,009 +Now we need to go back +to our Service Worker JavaScript code. + +198 +00:11:16,043 --> 00:11:20,681 +It will need to handle a few new events, +starting with the push event. + +199 +00:11:20,714 --> 00:11:25,118 +When a push message makes its way from +the Browser Pets server to this browser, + +200 +00:11:25,152 --> 00:11:28,655 +this Service Worker has +a push event sent to it. + +201 +00:11:28,689 --> 00:11:32,092 +That event contains +a PushMessageData object + +202 +00:11:32,125 --> 00:11:35,929 +which has multiple ways of accessing +the data sent by your server. + +203 +00:11:35,963 --> 00:11:39,066 +We use the JSON accessor here. + +204 +00:11:39,099 --> 00:11:41,468 +Remember how when we subscribed for push, + +205 +00:11:41,502 --> 00:11:45,005 +our JavaScript promised +they would always be user visible? + +206 +00:11:45,038 --> 00:11:48,408 +That means we must always +show a platform native notification + +207 +00:11:48,442 --> 00:11:50,744 +in response to each push. + +208 +00:11:50,777 --> 00:11:55,249 +It is best to do this as early as possible +in your push event handler. + +209 +00:11:55,282 --> 00:11:57,818 +We're pulling everything +we need out of that JSON blob + +210 +00:11:57,851 --> 00:12:02,656 +to configure the notification, +including setting up an action with a URL. + +211 +00:12:02,689 --> 00:12:05,325 +That will come in handy in just a moment. + +212 +00:12:05,359 --> 00:12:09,897 +After the notification is shown, +we need to handle the user clicking on it. + +213 +00:12:09,930 --> 00:12:13,333 +One more event +for our Service Worker script to handle. + +214 +00:12:13,367 --> 00:12:18,005 +In this notificationclick handler, +BrowserPets will take the URL + +215 +00:12:18,038 --> 00:12:21,408 +from the notification that was clicked +to open a new window. + +216 +00:12:21,441 --> 00:12:24,178 +Take note: This is a very common pattern. + +217 +00:12:24,211 --> 00:12:28,782 +That's all the JavaScript +we need to write to support Web Push. + +218 +00:12:28,815 --> 00:12:31,818 +Of course, it's best to have +some help while developing. + +219 +00:12:31,852 --> 00:12:35,556 +As usual, +that's where Web Inspector comes in. + +220 +00:12:35,589 --> 00:12:39,059 +In addition to helping debug +your website open in a browser tab, + +221 +00:12:39,092 --> 00:12:42,629 +Web Inspector can also inspect +Service Worker instances + +222 +00:12:42,663 --> 00:12:45,365 +and set breakpoints on event handlers. + +223 +00:12:45,399 --> 00:12:48,836 +All of this together will let you +inspect and debug the JavaScript + +224 +00:12:48,869 --> 00:12:52,206 +that subscribes for push +as well as the service worker code + +225 +00:12:52,239 --> 00:12:55,776 +that handles the push event +and notification events. + +226 +00:12:55,809 --> 00:12:59,012 +Additionally, +the Apple Push Notification servers + +227 +00:12:59,046 --> 00:13:01,682 +will give you human readable errors +if something goes wrong + +228 +00:13:01,715 --> 00:13:04,384 +when you attempt +to publish a push message. + +229 +00:13:04,418 --> 00:13:08,789 +Check out the links associated with +this session for further documentation. + +230 +00:13:08,822 --> 00:13:12,492 +I'd also like to get into more detail +on a few points that came up + +231 +00:13:12,526 --> 00:13:14,828 +while writing that code, +with direct regards + +232 +00:13:14,862 --> 00:13:17,931 +to user privacy and power usage. + +233 +00:13:17,965 --> 00:13:20,868 +Importantly–and this is +not the first time I've said this– + +234 +00:13:20,901 --> 00:13:24,538 +subscribing for push requires +a user gesture. + +235 +00:13:24,571 --> 00:13:28,041 +As with other privileged features +of the web platform, + +236 +00:13:28,075 --> 00:13:30,410 +it's the right thing for user trust + +237 +00:13:30,444 --> 00:13:34,481 +to require that the user actually asked +to enable Web Push. + +238 +00:13:34,515 --> 00:13:38,986 +As mentioned when I showed you the code +on how to request a push subscription, + +239 +00:13:39,019 --> 00:13:42,856 +you must promise +that pushes will be user visible. + +240 +00:13:42,890 --> 00:13:46,393 +Handling a push event is not +an invitation for your JavaScript + +241 +00:13:46,426 --> 00:13:48,662 +to get silent background runtime. + +242 +00:13:48,695 --> 00:13:53,534 +Doing so would violate both a user's trust +and a user's battery life. + +243 +00:13:53,567 --> 00:13:56,837 +When handling a push event, +you are in fact required + +244 +00:13:56,870 --> 00:13:59,773 +to post a notification +to Notification Center. + +245 +00:13:59,806 --> 00:14:03,744 +Other browsers all have countermeasures +against violating the promise + +246 +00:14:03,777 --> 00:14:07,181 +to make pushes user visible, +and so does Safari. + +247 +00:14:07,214 --> 00:14:10,984 +In the beta build of macOS Ventura, +after three push events + +248 +00:14:11,018 --> 00:14:14,188 +where you fail to post a notification +in a timely manner, + +249 +00:14:14,221 --> 00:14:16,857 +your site's push subscription +will be revoked. + +250 +00:14:16,890 --> 00:14:19,893 +You will need to go through +the permission workflow again. + +251 +00:14:19,927 --> 00:14:21,695 +That's all. + +252 +00:14:21,728 --> 00:14:26,466 +We're genuinely proud to support Web Push +and excited that any site can use it, + +253 +00:14:26,500 --> 00:14:29,203 +no Apple Developer account required. + +254 +00:14:29,236 --> 00:14:33,073 +As long as you've coded to the standards +and use feature detection, + +255 +00:14:33,106 --> 00:14:37,010 +so you don't unwittingly exclude Safari, +your users will already get + +256 +00:14:37,044 --> 00:14:41,448 +the benefit of Web Push +in Safari 16 on macOS Ventura. + +257 +00:14:41,481 --> 00:14:45,652 +As usual, we've added tons of other +new stuff to Safari and WebKit this year, + +258 +00:14:45,686 --> 00:14:48,455 +and I hope you'll check out +that session to learn more. + +259 +00:14:48,488 --> 00:14:50,090 +Thank you for watching. + +260 +00:14:50,123 --> 00:14:53,727 +I hope you have a great rest of WWDC 2022. + diff --git "a/eng/2022 Session 10099 What\342\200\231s new in Safari Web Extensions en.srt" "b/eng/2022 Session 10099 What\342\200\231s new in Safari Web Extensions en.srt" new file mode 100644 index 0000000..f971572 --- /dev/null +++ "b/eng/2022 Session 10099 What\342\200\231s new in Safari Web Extensions en.srt" @@ -0,0 +1,1679 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,810 --> 00:00:11,912 +Kiara Rose: Hi, my name is Kiara Rose, + +3 +00:00:11,945 --> 00:00:15,148 +and I work as a Safari Extensions +Engineer. + +4 +00:00:15,182 --> 00:00:19,353 +Today I'm very excited to be speaking +to you all about what's new + +5 +00:00:19,386 --> 00:00:22,823 +in Safari Web Extensions this year. + +6 +00:00:22,856 --> 00:00:25,259 +Before we dive into today's presentation, + +7 +00:00:25,292 --> 00:00:27,494 +I'd like to take a moment +to thank all of you + +8 +00:00:27,528 --> 00:00:33,634 +for submitting your iOS, iPadOS, +and macOS extensions to the App Store. + +9 +00:00:33,667 --> 00:00:38,272 +Moving forward, our goal is to continue +to implement new features and APIs + +10 +00:00:38,305 --> 00:00:42,142 +so that you can deliver +even better experiences for your users. + +11 +00:00:42,176 --> 00:00:45,812 +And today, I'm going to highlight +some of the exciting new features + +12 +00:00:45,846 --> 00:00:48,515 +that we've implemented over the past year, + +13 +00:00:48,549 --> 00:00:52,052 +such as a new Manifest version +for extensions, + +14 +00:00:52,085 --> 00:00:54,254 +updated APIs, + +15 +00:00:54,288 --> 00:00:58,559 +and syncing extensions +across multiple devices. + +16 +00:00:58,592 --> 00:01:01,261 +Let's get started with Manifest version 3. + +17 +00:01:02,062 --> 00:01:06,867 +Manifest version 3 is the next iteration +of the web extension platform. + +18 +00:01:06,900 --> 00:01:09,736 +It introduces performance +and security improvements + +19 +00:01:09,770 --> 00:01:13,874 +and consolidates popular extension APIs. + +20 +00:01:13,907 --> 00:01:18,478 +For those of who have already updated +your extension to use version 3, + +21 +00:01:18,512 --> 00:01:23,283 +your extension +will now work in Safari 15.4 and onwards. + +22 +00:01:23,317 --> 00:01:26,386 +For those of you who haven't, +there's no need to worry, + +23 +00:01:26,420 --> 00:01:29,156 +because we will continue +to support extensions + +24 +00:01:29,189 --> 00:01:32,559 +using Manifest version 2 in Safari. + +25 +00:01:32,593 --> 00:01:35,362 +One of the key new features +in Manifest version 3 + +26 +00:01:35,395 --> 00:01:39,533 +is that your extension can use a service +worker instead of a background page. + +27 +00:01:39,566 --> 00:01:43,470 +If you're a web developer, you're likely +familiar with service workers. + +28 +00:01:43,504 --> 00:01:46,707 +These are event driven pages +where you can register listeners + +29 +00:01:46,740 --> 00:01:50,110 +using the addEventListener API. + +30 +00:01:50,143 --> 00:01:52,846 +These pages are also compatible +with other browsers + +31 +00:01:52,880 --> 00:01:55,682 +that support Manifest version 3. + +32 +00:01:55,716 --> 00:01:59,353 +If you prefer to keep using +background pages for your extension, + +33 +00:01:59,386 --> 00:02:03,790 +you're more than welcome to do so, +but they must be non-persistent. + +34 +00:02:03,824 --> 00:02:08,795 +Another improvement in version 3 is +that the APIs for executing JavaScript + +35 +00:02:08,829 --> 00:02:14,301 +and styling on a web page have moved from +the tabs API to the new scripting API. + +36 +00:02:15,269 --> 00:02:18,238 +Most of the functionality of these methods +remain the same, + +37 +00:02:18,272 --> 00:02:22,242 +but there are some new, additional +features that scripting provides, + +38 +00:02:22,276 --> 00:02:26,313 +such as new ways to inject code +on a webpage, + +39 +00:02:26,346 --> 00:02:31,218 +more options for which frames on the page +the code should be executed in, + +40 +00:02:31,251 --> 00:02:36,924 +and the ability to decide which execution +environment the code should run in. + +41 +00:02:36,957 --> 00:02:39,860 +Let's take a look at how the code +for the new scripting API + +42 +00:02:39,893 --> 00:02:42,629 +differs from the tabs API. + +43 +00:02:42,663 --> 00:02:46,733 +In this code snippet, +I'm using the tabs.executeScript API + +44 +00:02:46,767 --> 00:02:51,138 +to change the background color +of a webpage to blue. + +45 +00:02:51,171 --> 00:02:55,576 +And with this API, I can only +inject code that's contained in a string, + +46 +00:02:55,609 --> 00:02:58,278 +by passing along the "code" property, + +47 +00:02:58,312 --> 00:03:01,515 +But now, with the new scripting API, + +48 +00:03:01,548 --> 00:03:05,519 +I can pass along a function object +containing this code. + +49 +00:03:05,552 --> 00:03:07,487 +And like any other function, + +50 +00:03:07,521 --> 00:03:10,390 +it can contain arguments +that can be passed in. + +51 +00:03:10,424 --> 00:03:13,293 +This is a much more improved way +of executing a script + +52 +00:03:13,327 --> 00:03:17,931 +because you're not confined +to writing code in a string. + +53 +00:03:17,965 --> 00:03:22,369 +And note that with scripting, +there's a new property called target. + +54 +00:03:22,402 --> 00:03:26,540 +This property is used to specify +where the script should run. + +55 +00:03:26,573 --> 00:03:28,509 +In order to execute a script, + +56 +00:03:28,542 --> 00:03:33,347 +you have to specify the ID of the tab +you want to script to execute in. + +57 +00:03:33,380 --> 00:03:37,651 +This API will return an error +if the tab ID isn't specified. + +58 +00:03:37,684 --> 00:03:41,321 +Then, if you'd like to select which frames +of the webpage + +59 +00:03:41,355 --> 00:03:45,726 +to inject the code, +you can specify the frame IDs. + +60 +00:03:45,759 --> 00:03:50,264 +Note that with the tabs API, +you can only specify one ID. + +61 +00:03:50,297 --> 00:03:55,035 +But with scripting, you can specify +multiple IDs. + +62 +00:03:55,068 --> 00:03:57,337 +But let's say I have a lot more code + +63 +00:03:57,371 --> 00:04:02,543 +and it would look much cleaner +if I could contain it in multiple files. + +64 +00:04:02,576 --> 00:04:05,379 +In the tabs.executeScript API, + +65 +00:04:05,412 --> 00:04:08,348 +I can only specify one file, + +66 +00:04:08,382 --> 00:04:13,654 +but in scripting.executeScript, +I can specify multiple files. + +67 +00:04:13,687 --> 00:04:17,224 +Similarly, the same can be done +for insertCSS, + +68 +00:04:17,257 --> 00:04:20,594 +where you can inject styling +on a webpage, + +69 +00:04:20,627 --> 00:04:24,531 +and the same for removeCSS +where you can remove injected styling + +70 +00:04:24,565 --> 00:04:26,066 +from a webpage. + +71 +00:04:26,099 --> 00:04:31,004 +These APIs are available to use +in both Manifest versions 2 and 3. + +72 +00:04:31,038 --> 00:04:37,044 +However, the tabs.executeScript APIs +are not available in version 3. + +73 +00:04:37,077 --> 00:04:39,513 +In addition to the new scripting API, + +74 +00:04:39,546 --> 00:04:43,717 +there have been some slight modifications +to a few other APIs as well. + +75 +00:04:43,750 --> 00:04:47,521 +One of these modifications +is for web_accessible_resources. + +76 +00:04:48,755 --> 00:04:52,893 +In Manifest version 2, +if you want to include resources, + +77 +00:04:52,926 --> 00:04:55,996 +you'd do so by passing along +an array of files + +78 +00:04:56,029 --> 00:04:58,599 +you'd like a webpage to have access to. + +79 +00:04:58,632 --> 00:05:02,536 +But this can be problematic +since it'd give any webpage access + +80 +00:05:02,569 --> 00:05:06,139 +to all the resources +you specify in the Manifest. + +81 +00:05:07,374 --> 00:05:09,977 +With the new format in version 3, + +82 +00:05:10,010 --> 00:05:14,948 +you're in control of which resources +are available on any given site. + +83 +00:05:14,982 --> 00:05:17,451 +Let's take a look at an example. + +84 +00:05:17,484 --> 00:05:20,654 +Previously, +the cookie and pie images were available + +85 +00:05:20,687 --> 00:05:23,757 +on every site the extension had access to. + +86 +00:05:23,790 --> 00:05:26,126 +But now, with version 3, + +87 +00:05:26,159 --> 00:05:30,397 +I can make the pie image available +only on apple.com URLs, + +88 +00:05:30,430 --> 00:05:34,501 +and the cookie image +just on webkit.org pages. + +89 +00:05:34,535 --> 00:05:37,104 +Now let's take a look +at the modifications + +90 +00:05:37,137 --> 00:05:40,274 +to the browser_action and page_action +APIs. + +91 +00:05:41,608 --> 00:05:46,713 +In Manifest version 2, the actions +were specified distinctly like this. + +92 +00:05:46,747 --> 00:05:49,750 +But since these +APIs fulfill similar roles, + +93 +00:05:49,783 --> 00:05:54,855 +they've been consolidated to use just +one API in version 3, which is action. + +94 +00:05:56,256 --> 00:05:59,860 +We've also made updates to how +you declare content security policies + +95 +00:05:59,893 --> 00:06:01,962 +for your extension. + +96 +00:06:01,995 --> 00:06:06,900 +In version 2, an extension's policy +was defined using a string. + +97 +00:06:06,934 --> 00:06:10,838 +However, in version 3, +the policy is defined + +98 +00:06:10,871 --> 00:06:15,242 +using an object +with the key "extension_pages". + +99 +00:06:15,275 --> 00:06:18,412 +It's important to note +that remote sources for scripts + +100 +00:06:18,445 --> 00:06:21,381 +are no longer allowed for version 3. + +101 +00:06:21,415 --> 00:06:24,518 +The final API change has been +to the deprecated + +102 +00:06:24,551 --> 00:06:28,388 +browser.extension.getURL API. + +103 +00:06:28,422 --> 00:06:31,658 +This API is no longer supported +in version 3. + +104 +00:06:31,692 --> 00:06:35,929 +Instead, use the equivalent API +in browser.runtime. + +105 +00:06:35,963 --> 00:06:40,133 +So I've talked about the new features +introduced in Manifest version 3, + +106 +00:06:40,167 --> 00:06:43,670 +now let's step through the process +of updating your extensions + +107 +00:06:43,704 --> 00:06:46,173 +so that you can use these new features. + +108 +00:06:47,774 --> 00:06:51,178 +I'll update the Sea Creator extension +from last year's presentation + +109 +00:06:51,211 --> 00:06:54,815 +to use Manifest version 3. + +110 +00:06:54,848 --> 00:07:00,020 +This extension replaces all occurrences +of the word fish with an emoji. + +111 +00:07:00,053 --> 00:07:04,491 +The first thing I'll do is change +the version number from 2 to 3. + +112 +00:07:06,660 --> 00:07:10,163 +And even though I can still use +a non-persistent background page + +113 +00:07:10,197 --> 00:07:13,967 +for version 3, +I'll update this to use a service worker + +114 +00:07:14,001 --> 00:07:17,104 +so that my extension +will be compatible with Chrome. + +115 +00:07:19,406 --> 00:07:23,510 +Lastly, +I'll change browser_action to action. + +116 +00:07:25,712 --> 00:07:28,315 +And in terms of the structure +of the Manifest, + +117 +00:07:28,348 --> 00:07:31,185 +these are the key changes I'll need to +make to have this extension + +118 +00:07:31,218 --> 00:07:34,521 +be compatible with the new +specifications in version 3. + +119 +00:07:34,555 --> 00:07:40,360 +So to test this out, +I'll build the extension, + +120 +00:07:40,394 --> 00:07:42,763 +and enable it in Safari. + +121 +00:07:47,835 --> 00:07:51,505 +Then I'll navigate to a webkit.org +blog page + +122 +00:07:51,538 --> 00:07:55,175 +where I'll use this extension to replace +every instance of the word fish + +123 +00:07:55,209 --> 00:07:57,511 +with a fish emoji. + +124 +00:07:59,213 --> 00:08:01,548 +But it looks like something went wrong. + +125 +00:08:01,582 --> 00:08:05,686 +As you can see, none of the words on this +page have been replaced with an emoji. + +126 +00:08:05,719 --> 00:08:08,822 +Let's inspect the popover +to see if there are any error messages. + +127 +00:08:15,429 --> 00:08:18,398 +In the console tab, +I see that there is an error message + +128 +00:08:18,432 --> 00:08:22,836 +stating that browser.tabs.executeScript +is undefined. + +129 +00:08:22,870 --> 00:08:26,907 +That's because this API +is no longer available in version 3, + +130 +00:08:26,940 --> 00:08:31,879 +so I should update my extension +to use the new scripting API instead. + +131 +00:08:31,912 --> 00:08:35,983 +In Xcode, I'll go back +to the popup.js file, + +132 +00:08:36,016 --> 00:08:39,453 +and then I'll change this line +to use scripting instead. + +133 +00:08:42,256 --> 00:08:44,291 +I'll add the target property, + +134 +00:08:44,324 --> 00:08:48,328 +which is used to specify +where the script should be injected into. + +135 +00:08:50,230 --> 00:08:52,232 +And with the new scripting API, + +136 +00:08:52,266 --> 00:08:54,735 +I'll have to specify the ID of the tab. + +137 +00:08:54,768 --> 00:08:57,838 +I can do this by using +the tabs.getCurrent API + +138 +00:08:57,871 --> 00:09:01,308 +to get an object containing +the information of the current tab. + +139 +00:09:08,048 --> 00:09:12,319 +Then I can use that object +to retrieve the tab ID. + +140 +00:09:15,556 --> 00:09:18,892 +Next, I'll add the file +containing the script to run. + +141 +00:09:21,094 --> 00:09:24,565 +Finally, the last change I'll make +is to add the scripting permission + +142 +00:09:24,598 --> 00:09:26,466 +in the Manifest. + +143 +00:09:29,803 --> 00:09:34,608 +I'll go ahead and build the extension + +144 +00:09:34,641 --> 00:09:38,278 +and use these changes in Safari. + +145 +00:09:38,312 --> 00:09:41,682 +And as you can see, +this extension now works in Safari, + +146 +00:09:41,715 --> 00:09:44,318 +using the new features +in Manifest version 3. + +147 +00:09:44,351 --> 00:09:47,221 +So that's how simple it is +to upgrade your extension. + +148 +00:09:47,254 --> 00:09:49,990 +But if you're not yet comfortable +with these new changes, + +149 +00:09:50,023 --> 00:09:53,193 +a lot of the features +such as scripting and services workers + +150 +00:09:53,227 --> 00:09:56,230 +are also available to use in version 2. + +151 +00:09:56,263 --> 00:10:00,834 +Now let's take a look at some of the APIs +we've updated this year, + +152 +00:10:00,868 --> 00:10:03,437 +starting with declarative net request. + +153 +00:10:03,470 --> 00:10:06,607 +Declarative net request is +a content blocking API + +154 +00:10:06,640 --> 00:10:10,878 +that provides web extensions with a fast +and privacy preserving way + +155 +00:10:10,911 --> 00:10:15,182 +to block or modify network requests +using rulesets. + +156 +00:10:15,215 --> 00:10:18,819 +This API allows you to delegate +all the work of intercepting + +157 +00:10:18,852 --> 00:10:22,723 +and modifying requests off to Safari +and all you have to do + +158 +00:10:22,756 --> 00:10:26,293 +is specify the content blocking rules +that should be applied. + +159 +00:10:26,326 --> 00:10:29,396 +You can specify a ruleset in the Manifest. + +160 +00:10:30,831 --> 00:10:34,468 +Here I've added the declarative +net request permission, + +161 +00:10:34,501 --> 00:10:37,204 +and I've used +the declarative_net_request key + +162 +00:10:37,237 --> 00:10:40,541 +to add one ruleset that should applied +to all pages. + +163 +00:10:40,574 --> 00:10:45,245 +Previously, I could only declare +up to 10 rulesets in the Manifest. + +164 +00:10:45,279 --> 00:10:48,282 +But now with the new updates +to this feature, + +165 +00:10:48,315 --> 00:10:50,617 +you can declare up to 50 rulesets, + +166 +00:10:50,651 --> 00:10:54,488 +which means your extension +can be more customizable. + +167 +00:10:54,521 --> 00:10:59,760 +But keep in mind that only 10 of these +rulesets can be enabled at once. + +168 +00:10:59,793 --> 00:11:03,297 +For more information +on how to create rulesets, + +169 +00:11:03,330 --> 00:11:06,700 +check out last year's presentation +on Safari Web Extensions, + +170 +00:11:06,733 --> 00:11:09,703 +where we go into more depth on this API. + +171 +00:11:09,736 --> 00:11:13,841 +Let's move on to some of the new features +for declarative net request. + +172 +00:11:13,874 --> 00:11:17,778 +Previously, you could only declare +rulesets in the Manifest, + +173 +00:11:17,811 --> 00:11:21,515 +but now we've implemented +the following two APIs + +174 +00:11:21,548 --> 00:11:25,118 +that will allow you +to update your rules dynamically. + +175 +00:11:25,152 --> 00:11:28,322 +The first API is updateSessionRules, + +176 +00:11:28,355 --> 00:11:31,959 +which will allow you to add or remove +rules for your extension. + +177 +00:11:31,992 --> 00:11:35,195 +But it's important to note +that these rules will not persist + +178 +00:11:35,229 --> 00:11:39,266 +across browser sessions +or extension updates. + +179 +00:11:39,299 --> 00:11:41,902 +If you would like to update rules +that will persist, + +180 +00:11:41,935 --> 00:11:45,839 +use the updateDynamicRules API instead. + +181 +00:11:45,873 --> 00:11:48,141 +This will allow you to update +your blocking rules + +182 +00:11:48,175 --> 00:11:50,777 +without updating your entire extension. + +183 +00:11:50,811 --> 00:11:53,547 +Let's take a look +at how we can use one of these APIs + +184 +00:11:53,580 --> 00:11:56,984 +to make modifications to our rulesets. + +185 +00:11:57,017 --> 00:12:01,788 +I'm going to block some content on +webpages using the sea creator extension, + +186 +00:12:01,822 --> 00:12:06,827 +and then, I'll use the new APIs +to unblock content on select pages. + +187 +00:12:06,860 --> 00:12:09,563 +In the extensions Manifest, +the first thing I'll do + +188 +00:12:09,596 --> 00:12:11,932 +is add the declarative net request +permission. + +189 +00:12:15,235 --> 00:12:20,040 +Then, I'll use the declarative net request +key to add a ruleset. + +190 +00:12:24,912 --> 00:12:30,517 +The rule that's being applied +is located in the rules.json file. + +191 +00:12:30,551 --> 00:12:36,356 +In this file, I've declared one rule +which blocks all images on all URLs. + +192 +00:12:36,390 --> 00:12:40,394 +Let's build the extension and +see how this rule is applied in Safari. + +193 +00:12:43,664 --> 00:12:47,267 +As you can see, +the image on this page has disappeared. + +194 +00:12:47,301 --> 00:12:49,736 +Which is exactly what we expected. + +195 +00:12:49,770 --> 00:12:54,608 +This shows that Safari has successfully +applied our content blocking rule. + +196 +00:12:54,641 --> 00:12:58,178 +And if I navigate +to this Wikipedia page on fish, + +197 +00:12:58,212 --> 00:13:01,682 +I'll see that the image on this site +has been blocked as well. + +198 +00:13:01,715 --> 00:13:05,352 +But let's say we want to update +our rules to block images on all pages + +199 +00:13:05,385 --> 00:13:09,022 +expect webkit.org blog pages. + +200 +00:13:09,056 --> 00:13:12,993 +Using one of the updated APIs +for declarative net request, + +201 +00:13:13,026 --> 00:13:16,496 +we can do just that. + +202 +00:13:16,530 --> 00:13:20,067 +Let's go back to Xcode +and make some changes. + +203 +00:13:20,100 --> 00:13:22,002 +In the popup.js file, + +204 +00:13:22,035 --> 00:13:25,806 +I'll declare a function +to update our content blocking rules. + +205 +00:13:28,275 --> 00:13:33,547 +I'll set the rule to allow images +on webkit.org/blog-files pages. + +206 +00:13:33,580 --> 00:13:39,987 +Then, I'll use the updateSessionRules API +to add this rule to our ruleset. + +207 +00:13:40,020 --> 00:13:44,725 +Lastly, I'll build the extension +and test our changes in Safari. + +208 +00:13:48,462 --> 00:13:52,099 +As you can see, +the image on this blog post has loaded, + +209 +00:13:52,132 --> 00:13:56,370 +showing that our new rule to allow images +on this site has worked. + +210 +00:13:56,403 --> 00:13:58,605 +And if I go to the Wikipedia site, + +211 +00:13:58,639 --> 00:14:01,942 +we'll see that the images on this page +are still blocked, + +212 +00:14:01,975 --> 00:14:05,579 +showing that the new rule +wasn't applied to this page. + +213 +00:14:05,612 --> 00:14:08,549 +So that's how you can use the new +declarative net request APIs + +214 +00:14:08,582 --> 00:14:11,018 +to update your content blocking rules. + +215 +00:14:12,219 --> 00:14:17,024 +Now, let's take a look at how your +extension can communicate with a webpage. + +216 +00:14:17,057 --> 00:14:20,994 +This awesome feature allows websites +to create custom behavior + +217 +00:14:21,028 --> 00:14:24,431 +if the user has your extension enabled. + +218 +00:14:24,464 --> 00:14:28,535 +The API is called externally_connectable. + +219 +00:14:28,569 --> 00:14:32,940 +To use it, you declare match patterns +in the Manifest. + +220 +00:14:32,973 --> 00:14:37,344 +These match patterns determine which pages +can communicate with your extension. + +221 +00:14:39,313 --> 00:14:43,250 +And an important thing to note +is that this feature only works + +222 +00:14:43,283 --> 00:14:45,986 +using the browser namespace. + +223 +00:14:46,019 --> 00:14:50,324 +And lastly, the user has to grant +your extension access to the page + +224 +00:14:50,357 --> 00:14:54,628 +before it can send or receive messages. + +225 +00:14:54,661 --> 00:14:59,433 +Let's take a look at the code you'd add +in the web page to use this feature. + +226 +00:14:59,466 --> 00:15:02,703 +First, you'll need to get the extensionID. + +227 +00:15:02,736 --> 00:15:04,905 +It's the bundle identifier +of the extension + +228 +00:15:04,938 --> 00:15:08,175 +and the team identifier in this format. + +229 +00:15:08,208 --> 00:15:11,979 +You can find your team identifier +on developer.apple.com, + +230 +00:15:12,012 --> 00:15:14,982 +in the membership tab +in your account settings. + +231 +00:15:15,015 --> 00:15:21,455 +Then you'll use the send message API +to post a message to the extension. + +232 +00:15:21,488 --> 00:15:24,391 +You can handle the response +you'll receive from the extension + +233 +00:15:24,424 --> 00:15:27,294 +by passing along a function. + +234 +00:15:27,327 --> 00:15:30,063 +Now let's take a look at the code +your extension will have + +235 +00:15:30,097 --> 00:15:32,199 +to receive messages. + +236 +00:15:32,232 --> 00:15:34,768 +Your extension can receive messages +from the webpage + +237 +00:15:34,801 --> 00:15:38,672 +by listening to the event +called onMessageExternal. + +238 +00:15:38,705 --> 00:15:41,675 +The extension can send a message +back to the web page + +239 +00:15:41,708 --> 00:15:45,045 +using the method passed +to the event listener. + +240 +00:15:45,078 --> 00:15:49,383 +Because there are different extension +web stores for different browsers, + +241 +00:15:49,416 --> 00:15:52,252 +extensions can have +many different identifiers. + +242 +00:15:52,286 --> 00:15:55,622 +So you'll need to determine +the correct one to use + +243 +00:15:55,656 --> 00:15:58,692 +to make sure you're messaging +a Safari web extension, + +244 +00:15:58,725 --> 00:16:01,528 +and not a Chrome or Edge extension. + +245 +00:16:01,562 --> 00:16:07,167 +To do this, you can use +the browser.runtime.sendMessage API + +246 +00:16:07,201 --> 00:16:10,704 +with a call to Promise.all. + +247 +00:16:10,737 --> 00:16:16,143 +Next, let's look at some example code +that will help you do this. + +248 +00:16:16,176 --> 00:16:19,379 +From the webpage, +you can broadcast multiple messages + +249 +00:16:19,413 --> 00:16:21,715 +using multiple extension IDs. + +250 +00:16:21,748 --> 00:16:24,751 +You'll get exactly one response +from an extension + +251 +00:16:24,785 --> 00:16:30,791 +and that'll let you know which extension +ID to use for further communication. + +252 +00:16:30,824 --> 00:16:36,363 +Here, I have a function +called determineExtensionID. + +253 +00:16:36,396 --> 00:16:39,066 +This function sends a message +to the extension + +254 +00:16:39,099 --> 00:16:43,370 +using the browser.runtime.sendMessage API. + +255 +00:16:43,403 --> 00:16:46,173 +If you have multiple IDs +and you want to determine + +256 +00:16:46,206 --> 00:16:50,077 +the correct one to use, +then you can use Promise.all + +257 +00:16:50,110 --> 00:16:55,249 +to make multiple calls +using the determineExtensionID function. + +258 +00:16:55,282 --> 00:16:59,386 +Promise.all takes an array of promises +and then returns a single promise + +259 +00:16:59,419 --> 00:17:03,223 +with an array +of all of the resolved values. + +260 +00:17:03,257 --> 00:17:08,195 +You can use this array to find +the extension that the user has installed. + +261 +00:17:08,228 --> 00:17:10,364 +In the extension's background page, + +262 +00:17:10,397 --> 00:17:14,334 +you'll need to listen for a message +from the web page. + +263 +00:17:14,368 --> 00:17:17,304 +When you receive the message, +you'll need to send one back + +264 +00:17:17,337 --> 00:17:20,874 +to tell the web page +that your extension is installed. + +265 +00:17:20,908 --> 00:17:24,244 +So that's how you can use +the new externally_connectable API + +266 +00:17:24,278 --> 00:17:28,081 +to allow your extension +to communicate with a web page. + +267 +00:17:28,115 --> 00:17:31,251 +The next feature we've updated +is a personal favorite of mine, + +268 +00:17:31,285 --> 00:17:33,420 +and that is unlimitedStorage. + +269 +00:17:33,453 --> 00:17:37,824 +And I'm so happy to announce that +unlimitedStorage is actually unlimited! + +270 +00:17:37,858 --> 00:17:41,328 +Given that this feature +was so highly requested by you, + +271 +00:17:41,361 --> 00:17:46,567 +we're excited to share that your extension +will no longer have a 10 MB quota. + +272 +00:17:46,600 --> 00:17:50,404 +You are free to use +as much data as you see fit. + +273 +00:17:50,437 --> 00:17:52,773 +Although, it's important to note + +274 +00:17:52,806 --> 00:17:55,976 +that users have the ability +to clear the data being used + +275 +00:17:56,009 --> 00:17:59,313 +by your extension at any given time. + +276 +00:17:59,346 --> 00:18:02,616 +So be sure to only store data +that's strictly necessary + +277 +00:18:02,649 --> 00:18:06,386 +so users don't feel inclined +to clear your data. + +278 +00:18:06,420 --> 00:18:10,557 +To use this feature, simply claim the +storage and unlimitedStorage permission + +279 +00:18:10,591 --> 00:18:14,361 +in the Manifest, and you're good to go. + +280 +00:18:14,394 --> 00:18:18,999 +So those were all of the APIs we've +updated for web extensions this past year. + +281 +00:18:19,032 --> 00:18:23,470 +Lastly, let's talk about a new feature +that will easily allow your users + +282 +00:18:23,504 --> 00:18:27,274 +to get your extension +on all of their devices. + +283 +00:18:27,307 --> 00:18:33,480 +In Safari 16, we've made the experience +of using extensions more seamless. + +284 +00:18:33,514 --> 00:18:37,184 +If a user turns on your extension +on one of their devices, + +285 +00:18:37,217 --> 00:18:40,821 +it'll be turned on +on all of their devices. + +286 +00:18:40,854 --> 00:18:46,927 +On top of this, we've made the process of +downloading your extension much simpler. + +287 +00:18:46,960 --> 00:18:49,129 +Let's take a look at how this works. + +288 +00:18:49,162 --> 00:18:53,901 +Let's say a user has one of +your extensions enabled on their Mac. + +289 +00:18:53,934 --> 00:18:57,404 +In Extension Settings +on any of their other devices, + +290 +00:18:57,437 --> 00:19:00,440 +they'll be given the option +to download your extension. + +291 +00:19:00,474 --> 00:19:03,510 +Once it's downloaded, +it'll automatically be enabled + +292 +00:19:03,544 --> 00:19:07,781 +on their device, +improving their user experience. + +293 +00:19:07,814 --> 00:19:10,851 +Now, let's dive into +how you can set this up + +294 +00:19:10,884 --> 00:19:13,487 +for web extensions and content blockers. + +295 +00:19:13,520 --> 00:19:17,925 +First, we recommend that you list +your extension for iOS, + +296 +00:19:17,958 --> 00:19:22,329 +iPadOS, and macOS +when submitting to the App Store. + +297 +00:19:22,362 --> 00:19:28,001 +This way, your extension will be available +across all of your users' devices. + +298 +00:19:28,035 --> 00:19:31,839 +Then, to allow your extension +to sync across their devices, + +299 +00:19:31,872 --> 00:19:35,375 +you'll need to use one of +the following two methods. + +300 +00:19:35,409 --> 00:19:37,945 +The simplest and recommended way, + +301 +00:19:37,978 --> 00:19:41,782 +is to adopt universal purchase. + +302 +00:19:41,815 --> 00:19:46,653 +Universal purchase allows your users to +enjoy your extension across all platforms, + +303 +00:19:46,687 --> 00:19:49,690 +by only purchasing it once. + +304 +00:19:49,723 --> 00:19:52,826 +If you use this method, you're all set. + +305 +00:19:52,860 --> 00:19:55,128 +Your users will get all of the features +I've shown + +306 +00:19:55,162 --> 00:19:58,999 +after they download your extension once. + +307 +00:19:59,032 --> 00:20:00,934 +To set up universal purchase, + +308 +00:20:00,968 --> 00:20:03,704 +you'll need to use +a single bundle identifier + +309 +00:20:03,737 --> 00:20:08,242 +across your extensions so that it can be +associated with the same app record + +310 +00:20:08,275 --> 00:20:11,411 +in App Store Connect. + +311 +00:20:11,445 --> 00:20:14,014 +For more information on how to do this, + +312 +00:20:14,047 --> 00:20:17,551 +check out our documentation +on how to set up universal purchase + +313 +00:20:17,584 --> 00:20:19,353 +for your extensions. + +314 +00:20:19,386 --> 00:20:22,022 +But if you choose not to set up +universal purchase, + +315 +00:20:22,055 --> 00:20:24,491 +you can manually link your apps. + +316 +00:20:24,525 --> 00:20:29,162 +To do this, you'll use Xcode to add +bundle identifiers in the info plist + +317 +00:20:29,196 --> 00:20:32,432 +for the apps +and extensions you'd like to sync. + +318 +00:20:32,466 --> 00:20:36,503 +To sync your iOS app +and extension with the macOS ones, + +319 +00:20:36,537 --> 00:20:40,407 +you'll need to use specific keys +in the info plist. + +320 +00:20:40,440 --> 00:20:44,178 +You'll put this key +in your macOS app plist, + +321 +00:20:44,211 --> 00:20:48,215 +and this key in +the macOS extension plist. + +322 +00:20:48,248 --> 00:20:53,720 +Similarly, you'll follow the same process +for syncing your macOS app. + +323 +00:20:53,754 --> 00:20:58,125 +By adding this key to the iOS app plist + +324 +00:20:58,158 --> 00:21:01,962 +and this key to the iOS extension plist. + +325 +00:21:01,995 --> 00:21:04,364 +Let's see how this works in Xcode. + +326 +00:21:04,398 --> 00:21:09,670 +In Xcode, the first thing we'll need to do +is update the settings for each target + +327 +00:21:09,703 --> 00:21:14,341 +to include the bundle identifiers of +the extensions and apps we want to sync. + +328 +00:21:14,374 --> 00:21:18,712 +I'll start by adding the bundle identifier +for the corresponding macOS app + +329 +00:21:18,745 --> 00:21:21,715 +in the info plist for the iOS app. + +330 +00:21:24,051 --> 00:21:28,021 +And as you can see, I've done the same +process for the macOS app + +331 +00:21:28,055 --> 00:21:30,991 +by adding the iOS app bundle identifier. + +332 +00:21:31,024 --> 00:21:37,497 +And similarly for iOS extension by adding +the macOS extension bundle identifier. + +333 +00:21:37,531 --> 00:21:40,133 +And lastly, for the macOS extension + +334 +00:21:40,167 --> 00:21:43,871 +by adding the iOS extension +bundle identifier. + +335 +00:21:43,904 --> 00:21:46,874 +And that's how simple it is +to link your apps and extensions + +336 +00:21:46,907 --> 00:21:50,043 +so that your users +can use them everywhere. + +337 +00:21:50,077 --> 00:21:54,314 +To recap, you can make +this feature available for your users + +338 +00:21:54,348 --> 00:21:57,384 +by either setting up universal purchase + +339 +00:21:57,417 --> 00:22:04,491 +or by adding bundle identifiers for each +iOS and macOS app and extension in Xcode. + +340 +00:22:04,525 --> 00:22:08,896 +Today we discussed Manifest version 3, + +341 +00:22:08,929 --> 00:22:10,664 +the APIs we've updated, + +342 +00:22:10,697 --> 00:22:14,434 +and syncing extensions +across multiple devices. + +343 +00:22:14,468 --> 00:22:17,838 +I hope you're as excited as I am +about these all these new features + +344 +00:22:17,871 --> 00:22:20,174 +for Safari Web Extensions. + +345 +00:22:20,207 --> 00:22:24,244 +Feel free to download the sample project +containing the code from today's session + +346 +00:22:24,278 --> 00:22:28,315 +and to play around +with some of the APIs we featured. + +347 +00:22:28,348 --> 00:22:30,951 +Next, we'd love to know what you think. + +348 +00:22:30,984 --> 00:22:34,021 +Use Feedback Assistant to file bugs +or chat with us + +349 +00:22:34,054 --> 00:22:37,057 +on the Safari Developer Forums +to provide feedback + +350 +00:22:37,090 --> 00:22:40,928 +on how we can make developing extensions +better for you. + +351 +00:22:40,961 --> 00:22:43,397 +No, really. +We want to know what you think! + +352 +00:22:43,430 --> 00:22:46,400 +Consider joining +the WebExtensions community group + +353 +00:22:46,433 --> 00:22:49,236 +to shape the future of web extensions. + +354 +00:22:49,269 --> 00:22:55,108 +Finally, check out our WWDC presentation +on creating web inspector extensions. + +355 +00:22:55,142 --> 00:22:59,913 +Thanks for tuning in to this session, +and have a great rest of your WWDC. + diff --git a/eng/2022 Session 10100 Create Safari Web Inspector Extensions en.srt b/eng/2022 Session 10100 Create Safari Web Inspector Extensions en.srt new file mode 100644 index 0000000..16ddb7e --- /dev/null +++ b/eng/2022 Session 10100 Create Safari Web Inspector Extensions en.srt @@ -0,0 +1,1347 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:13,080 +Hello, I'm Devin Rousso, +an engineer on the WebKit team, + +3 +00:00:13,113 --> 00:00:16,250 +and I'm here today to share with you +the exciting new opportunity + +4 +00:00:16,283 --> 00:00:19,186 +to create extensions +for Safari's Web Inspector. + +5 +00:00:19,219 --> 00:00:22,589 +Web Inspector is the primary +developer tool for debugging web content + +6 +00:00:22,623 --> 00:00:24,791 +on all of Apple's platforms. + +7 +00:00:24,825 --> 00:00:29,162 +It already has a ton of built-in +functionality for debugging websites, + +8 +00:00:29,196 --> 00:00:31,231 +but there are often areas +of web development + +9 +00:00:31,265 --> 00:00:35,102 +that are difficult to build into +a generalized developer tool. + +10 +00:00:35,135 --> 00:00:38,038 +Maybe you're debugging +a popular JavaScript library, + +11 +00:00:38,071 --> 00:00:41,375 +or you just need something more specific +to what you're working on. + +12 +00:00:41,408 --> 00:00:44,077 +Web Inspector extensions +are a fantastic solution + +13 +00:00:44,111 --> 00:00:46,313 +for these personal workflow scenarios. + +14 +00:00:47,381 --> 00:00:51,818 +By utilizing cross-browser web extensions +and the DevTools APIs, + +15 +00:00:51,852 --> 00:00:56,456 +you can now add your own tabs +in Web Inspector in Safari 16. + +16 +00:00:56,490 --> 00:00:59,793 +Let's take a quick look +at a Web Inspector extension in action, + +17 +00:00:59,826 --> 00:01:02,162 +and then I'll show you +how you can build your own. + +18 +00:01:02,196 --> 00:01:05,265 +First I'll open Safari's +extension preferences. + +19 +00:01:07,701 --> 00:01:10,070 +And enable a Web Inspector extension. + +20 +00:01:10,103 --> 00:01:13,807 +Then I'll close Safari's extension +preferences and inspect the page. + +21 +00:01:15,409 --> 00:01:19,046 +I'll do that from the Develop menu, +Show Web Inspector. + +22 +00:01:19,079 --> 00:01:22,416 +In Web Inspector, +not only can I see the many built-in tabs + +23 +00:01:22,449 --> 00:01:23,951 +like the Elements Tab, + +24 +00:01:23,984 --> 00:01:28,121 +but also the tab for the Web Inspector +extension I just enabled. + +25 +00:01:28,155 --> 00:01:30,591 +Because we've just enabled +this extension, however, + +26 +00:01:30,624 --> 00:01:34,628 +we first have to give it permission +to work with the currently inspected page. + +27 +00:01:34,661 --> 00:01:37,965 +I have the same permission +duration options as other extensions + +28 +00:01:37,998 --> 00:01:39,700 +outside of Web Inspector, + +29 +00:01:39,733 --> 00:01:42,336 +so for now +let's give it access for one day. + +30 +00:01:42,369 --> 00:01:45,839 +This Open Graph extension is +what I'll be building in this session. + +31 +00:01:45,873 --> 00:01:49,810 +It displays the common social media +metadata that most websites put + +32 +00:01:49,843 --> 00:01:52,012 +on their pages +for use by link previews + +33 +00:01:52,045 --> 00:01:54,882 +in Messages and other social media sites. + +34 +00:01:54,915 --> 00:01:58,552 +Now that we've gotten a quick look +at what a Web Inspector extension can do, + +35 +00:01:58,585 --> 00:02:00,687 +let's talk about how they're built. + +36 +00:02:00,721 --> 00:02:03,757 +Like other Safari Web Extensions, +Web Inspector extensions + +37 +00:02:03,790 --> 00:02:06,527 +are distributed via an app +in the App Store. + +38 +00:02:07,961 --> 00:02:11,798 +To build your own Web Inspector extension, +you will need to have Xcode, + +39 +00:02:11,832 --> 00:02:15,936 +Apple's app creation tool +for building Mac and iOS apps. + +40 +00:02:15,969 --> 00:02:19,273 +Xcode also comes with project templates +to help jumpstart + +41 +00:02:19,306 --> 00:02:21,408 +making a new Safari extension app. + +42 +00:02:21,441 --> 00:02:23,777 +If you already have +an existing web extension + +43 +00:02:23,810 --> 00:02:25,512 +you've created for another browser, + +44 +00:02:25,546 --> 00:02:28,815 +you can also use +Xcode's bundled conversion tool. + +45 +00:02:28,849 --> 00:02:32,219 +Simply run safari-web-extension-converter +from the Terminal, + +46 +00:02:32,252 --> 00:02:34,621 +passing a path +to your extension's directory + +47 +00:02:34,655 --> 00:02:37,491 +that contains the manifest.json file. + +48 +00:02:37,524 --> 00:02:40,527 +A full app project will then be created +for your extension + +49 +00:02:40,561 --> 00:02:42,029 +that is ready to build and run. + +50 +00:02:43,197 --> 00:02:44,932 +For more information on this tool, + +51 +00:02:44,965 --> 00:02:48,836 +check out Meet Safari Web Extensions +from WWDC 2020, + +52 +00:02:48,869 --> 00:02:50,971 +as well as the online documentation. + +53 +00:02:51,004 --> 00:02:56,476 +So today I'm going to go over the basic +structure of a Web Inspector extension, + +54 +00:02:56,510 --> 00:03:00,147 +cover how to best evaluate code +in that extension, + +55 +00:03:00,180 --> 00:03:03,650 +and discuss some good +best practices for your users. + +56 +00:03:03,684 --> 00:03:05,118 +Let's get started. + +57 +00:03:06,653 --> 00:03:11,024 +Web Inspector extensions are structured +just like other Safari Web Extensions, + +58 +00:03:11,058 --> 00:03:14,928 +with a toolbar icon, background page, +content scripts, and more, + +59 +00:03:14,962 --> 00:03:18,432 +but they also have +a dedicated devtools background page. + +60 +00:03:18,465 --> 00:03:21,068 +Let's take a look at how that works +in practice. + +61 +00:03:21,101 --> 00:03:23,403 +The structure +of a typical Safari Web Extension + +62 +00:03:23,437 --> 00:03:26,473 +begins with a manifest file +that declares the basic details + +63 +00:03:26,507 --> 00:03:30,944 +about the extension, like the name, +icons, descriptions, and more. + +64 +00:03:30,978 --> 00:03:34,882 +It can declare a background page +to handle all the behind-the-scenes logic + +65 +00:03:34,915 --> 00:03:36,450 +of your extension, + +66 +00:03:36,483 --> 00:03:40,287 +and can also declare any content scripts +used to inject functionality + +67 +00:03:40,320 --> 00:03:43,790 +into web pages visited by the user. + +68 +00:03:43,824 --> 00:03:47,794 +For Web Inspector extensions, +a couple other pages enter the mix. + +69 +00:03:47,828 --> 00:03:50,898 +First there is a required +devtools background page + +70 +00:03:50,931 --> 00:03:54,401 +for the behind-the-scenes logic +of your Web Inspector extension. + +71 +00:03:54,434 --> 00:03:58,338 +This page has access +to the unique devtools APIs + +72 +00:03:58,372 --> 00:04:01,775 +and only a limited set +of content script APIs. + +73 +00:04:01,808 --> 00:04:06,113 +From this devtools background page +you can create devtools tab pages + +74 +00:04:06,146 --> 00:04:08,215 +that will be shown in Web Inspector. + +75 +00:04:08,248 --> 00:04:11,485 +But all of that is just for +a single Web Inspector. + +76 +00:04:11,518 --> 00:04:15,422 +If there are multiple Web Inspectors, +each has its own instance + +77 +00:04:15,455 --> 00:04:17,758 +of this same devtools background page, + +78 +00:04:17,791 --> 00:04:21,762 +staying alive for the duration +that the related Web Inspector is open. + +79 +00:04:21,795 --> 00:04:24,932 +As such, +you might also have multiple instances + +80 +00:04:24,965 --> 00:04:27,701 +of each devtools tab page as well. + +81 +00:04:27,734 --> 00:04:30,103 +Let's take a look +at how this structure looks in practice, + +82 +00:04:30,137 --> 00:04:33,607 +and start building my Open Graph +Web Inspector extension. + +83 +00:04:34,708 --> 00:04:37,544 +I'll start by creating a new project +in Xcode. + +84 +00:04:41,081 --> 00:04:45,219 +The project type I'm going to create +is a Safari Extension App. + +85 +00:04:45,252 --> 00:04:48,755 +I only really need macOS, +but I'm going to leave it as is, + +86 +00:04:48,789 --> 00:04:52,392 +in multiplatform, in case I want to add +iOS functionality in the future. + +87 +00:04:52,426 --> 00:04:56,496 +I'm going to name this "Open Graph", +and keep the rest of the defaults. + +88 +00:04:56,530 --> 00:05:00,300 +Note that you need to pick a team +and bundle identifier + +89 +00:05:00,334 --> 00:05:02,970 +based on the Apple developer account +you're using. + +90 +00:05:03,003 --> 00:05:05,672 +And finally, +I'm going to save this on the Desktop. + +91 +00:05:07,474 --> 00:05:11,979 +Now I have a generic Safari Web Extension +project, ready for me to modify. + +92 +00:05:12,012 --> 00:05:14,681 +I am first taken +to the manifest.json file, + +93 +00:05:14,715 --> 00:05:18,151 +which is the root configuration file +for every web extension. + +94 +00:05:18,185 --> 00:05:22,422 +The manifest file references the other +resources that make up your extension– + +95 +00:05:22,456 --> 00:05:26,693 +localizations, images, +pages, scripts, styles, and more. + +96 +00:05:26,727 --> 00:05:30,330 +For my Web Inspector extension, +I won't need some of these files, + +97 +00:05:30,364 --> 00:05:33,867 +like the background page, +content scripts, or popup, + +98 +00:05:33,901 --> 00:05:36,303 +so I'm going to delete them from +the manifest + +99 +00:05:36,336 --> 00:05:38,772 +and from the entire project. + +100 +00:05:42,543 --> 00:05:46,313 +Okay, let's start making this +a Web Inspector extension. + +101 +00:05:46,346 --> 00:05:50,184 +To do that I need to add +a devtools background page to the manifest + +102 +00:05:50,217 --> 00:05:52,386 +and create a corresponding file for it, + +103 +00:05:52,419 --> 00:05:55,722 +as well as a JavaScript file +I'm going to use inside it. + +104 +00:05:55,756 --> 00:05:58,926 +I'm going to go to File - New - File... + +105 +00:06:01,762 --> 00:06:04,765 +And scroll down +to find the Empty file template. + +106 +00:06:12,773 --> 00:06:16,176 +I'm going to call this file +"devtools_background.html" + +107 +00:06:16,210 --> 00:06:18,679 +to match the name I used in the manifest. + +108 +00:06:20,848 --> 00:06:24,318 +The location is already set +to be right next to my other resources + +109 +00:06:24,351 --> 00:06:28,589 +and part of the correct targets, +so I don't need to change anything here. + +110 +00:06:31,325 --> 00:06:35,329 +I'll repeat the same steps again +with the JavaScript file... + +111 +00:06:40,968 --> 00:06:44,338 +…which I'll call "devtools_background.js". + +112 +00:06:48,375 --> 00:06:52,679 +Finally, I need to include the JavaScript +file in my devtools background page. + +113 +00:06:54,314 --> 00:06:57,985 +Remember, this page gets created +when Web Inspector opens, + +114 +00:06:58,018 --> 00:07:02,189 +and is responsible for creating the custom +tabs that appear in Web Inspector. + +115 +00:07:02,222 --> 00:07:04,191 +I almost always want to create a tab + +116 +00:07:04,224 --> 00:07:07,294 +so that, if needed, +the permissions I saw earlier + +117 +00:07:07,327 --> 00:07:09,997 +will be displayed to my users +right there in-line, + +118 +00:07:10,030 --> 00:07:11,598 +instead of in some other place. + +119 +00:07:11,632 --> 00:07:15,502 +This devtools panels create API +takes three simple arguments. + +120 +00:07:15,536 --> 00:07:18,539 +The first is the name of the tab. + +121 +00:07:18,572 --> 00:07:21,074 +For that, +I am using the localization method + +122 +00:07:21,108 --> 00:07:23,977 +to look up the localized name +of my extension. + +123 +00:07:24,011 --> 00:07:26,947 +The next argument is the icon path to use. + +124 +00:07:26,980 --> 00:07:31,018 +Note that this should be a vector image +to smoothly scale to any size + +125 +00:07:31,051 --> 00:07:33,921 +if the user chooses +to zoom their user interface. + +126 +00:07:33,954 --> 00:07:37,758 +But in order to use this icon, I need to +make sure it's part of my project, + +127 +00:07:37,791 --> 00:07:40,827 +along with all of the other icons +I need for my extension. + +128 +00:07:40,861 --> 00:07:44,965 +The icons in the "images" folder are still +the defaults from the project template. + +129 +00:07:44,998 --> 00:07:47,534 +So let's delete these from the project. + +130 +00:07:50,838 --> 00:07:53,373 +And replace them with some icons +I created earlier... + +131 +00:07:57,277 --> 00:08:02,115 +…including the "logo.svg" I am trying to +use when creating the devtools tab. + +132 +00:08:04,751 --> 00:08:08,522 +Now that this is here, I'll go back +to the devtools background script. + +133 +00:08:10,624 --> 00:08:14,595 +The third argument is the HTML used +by the tab in Web Inspector. + +134 +00:08:14,628 --> 00:08:16,496 +And just like +with the images a moment ago, + +135 +00:08:16,530 --> 00:08:19,566 +I need to create this page +before I can use it. + +136 +00:08:26,740 --> 00:08:29,643 +I'll name this "devtools_tab.html" + +137 +00:08:29,676 --> 00:08:32,179 +to match the name I gave the API. + +138 +00:08:36,450 --> 00:08:39,019 +Unlike the devtools background page, +however, + +139 +00:08:39,052 --> 00:08:42,756 +this devtools tab page +will actually be shown to the user, + +140 +00:08:42,789 --> 00:08:47,194 +so this time I'll create both +a JavaScript and CSS file. + +141 +00:08:55,836 --> 00:08:59,640 +I'll name the JavaScript file +"devtools_tab.js"... + +142 +00:09:03,443 --> 00:09:06,947 +…and the CSS file "devtools_tab.css". + +143 +00:09:08,615 --> 00:09:10,817 +It's great that I've already set up +this structure, + +144 +00:09:10,851 --> 00:09:13,787 +but for now I'll just add the usual +"Hello World" + +145 +00:09:13,820 --> 00:09:15,822 +to make sure that things +are working correctly. + +146 +00:09:15,856 --> 00:09:18,859 +But don't worry, we'll dive deeper +into this a little bit later, + +147 +00:09:18,892 --> 00:09:22,129 +because first, +we still have a few more icons to replace + +148 +00:09:22,162 --> 00:09:25,232 +to make sure this extension +has a consistent look. + +149 +00:09:25,265 --> 00:09:28,569 +First we need to replace +the default large icon... + +150 +00:09:31,238 --> 00:09:33,140 +…by deleting it... + +151 +00:09:35,275 --> 00:09:37,644 +…and dragging my large icon in its place. + +152 +00:09:41,348 --> 00:09:44,017 +Since this large icon +is part of the app, however, + +153 +00:09:44,051 --> 00:09:46,520 +I need to add it to the right target. + +154 +00:09:50,557 --> 00:09:53,727 +The remaining icons are all part +of the Assets catalog, + +155 +00:09:53,760 --> 00:09:56,530 +specifically the AppIcon set. + +156 +00:09:59,900 --> 00:10:03,670 +I've already prepared these icons, +so I'll just paste them in. + +157 +00:10:03,704 --> 00:10:07,241 +And with that, I think we've replaced +all of the default icons, + +158 +00:10:07,274 --> 00:10:08,976 +so I'm now going to run my extension. + +159 +00:10:09,009 --> 00:10:13,347 +Note that this may take a few seconds +the first time the project is built. + +160 +00:10:20,354 --> 00:10:21,622 +And there it is! + +161 +00:10:21,655 --> 00:10:24,091 +Most of this UI +is from the Xcode template, + +162 +00:10:24,124 --> 00:10:27,895 +but I do correctly see +my icon instead of the default one. + +163 +00:10:27,928 --> 00:10:30,964 +Once we've launched +the Safari extension app at least once, + +164 +00:10:30,998 --> 00:10:33,233 +we can close it, +as the app doesn't need to be running + +165 +00:10:33,267 --> 00:10:34,902 +anymore for Safari to pick it up. + +166 +00:10:39,373 --> 00:10:41,508 +But before I can see it in Safari, +however, + +167 +00:10:41,542 --> 00:10:44,611 +I have to Allow Unsigned Extensions +in the Develop menu + +168 +00:10:44,645 --> 00:10:47,514 +as this is a locally built unsigned app. + +169 +00:10:47,548 --> 00:10:49,950 +Now, in Safari's Extensions preferences, + +170 +00:10:49,983 --> 00:10:53,520 +I can see Open Graph. +I'll turn it on. + +171 +00:10:53,554 --> 00:10:56,790 +I'm going to open a Safari tab +and browse to apple.com + +172 +00:10:56,823 --> 00:10:59,026 +so that I can try my extension. + +173 +00:10:59,059 --> 00:11:02,596 +And already we can see +that my icon is in the toolbar. + +174 +00:11:04,031 --> 00:11:06,834 +And my extension tab +is now in the tab bar. + +175 +00:11:06,867 --> 00:11:09,369 +Switching to it, + +176 +00:11:09,403 --> 00:11:12,239 +we can see the same permission prompt we +saw earlier. + +177 +00:11:12,272 --> 00:11:14,508 +This permission prompt +is automatically shown + +178 +00:11:14,541 --> 00:11:16,944 +if permissions are needed +by the extension. + +179 +00:11:16,977 --> 00:11:20,414 +Just like earlier, +I'll allow it for one day. + +180 +00:11:21,682 --> 00:11:25,219 +And there's the "Hello World" +I added to the devtools tab page earlier. + +181 +00:11:25,252 --> 00:11:30,457 +Those are the basics of how to create +a Web Inspector extension for Safari 16. + +182 +00:11:30,490 --> 00:11:31,825 +Let's recap. + +183 +00:11:31,859 --> 00:11:36,063 +I declared the devtools background page +and added it to my Xcode project. + +184 +00:11:36,096 --> 00:11:39,733 +From there, I was able to create +a new tab in Web Inspector + +185 +00:11:39,766 --> 00:11:41,335 +to show my custom tooling. + +186 +00:11:41,368 --> 00:11:45,305 +And finally, I've started considering +the permissions my extension needs. + +187 +00:11:45,339 --> 00:11:49,443 +For a Web Inspector extension, +this often boils down to evaluating code + +188 +00:11:49,476 --> 00:11:52,446 +in the inspected page, +usually to extract some data + +189 +00:11:52,479 --> 00:11:54,448 +for display inside of Web Inspector. + +190 +00:11:54,481 --> 00:11:58,252 +Web Extensions already have +a number of ways to evaluate code. + +191 +00:11:58,285 --> 00:11:59,987 +For Web Inspector extensions, + +192 +00:12:00,020 --> 00:12:02,322 +there is another API +that is the preferred method + +193 +00:12:02,356 --> 00:12:05,392 +of evaluating scripts +inside the inspected page. + +194 +00:12:05,425 --> 00:12:10,631 +Let's take a look at this API and see how +I can use it for my OpenGraph extension. + +195 +00:12:10,664 --> 00:12:14,735 +This devtools extension API to evaluate +JavaScript in the inspected window + +196 +00:12:14,768 --> 00:12:17,171 +is the best way to get quick results. + +197 +00:12:17,204 --> 00:12:20,407 +It will automatically target the page +associated with the Web Inspector + +198 +00:12:20,440 --> 00:12:22,476 +your extension is running in. + +199 +00:12:22,509 --> 00:12:27,447 +Remember, the user could be inspecting +multiple pages at the same time. + +200 +00:12:27,481 --> 00:12:30,050 +There are also some useful options +for this API + +201 +00:12:30,083 --> 00:12:32,452 +that can help you get the right results. + +202 +00:12:32,486 --> 00:12:35,889 +By default the expression given +to this API is evaluated + +203 +00:12:35,923 --> 00:12:39,893 +in the context of the main frame +of the inspected page. + +204 +00:12:39,927 --> 00:12:42,162 +But you can use the frameURL option + +205 +00:12:42,196 --> 00:12:45,465 +to specify evaluation +inside a different frame. + +206 +00:12:45,499 --> 00:12:48,035 +This is needed when your extension +needs to extract data + +207 +00:12:48,068 --> 00:12:50,971 +from one of many possible sub-frames +in the page. + +208 +00:12:51,004 --> 00:12:54,842 +For my OpenGraph extension, +I only need to worry about the main frame, + +209 +00:12:54,875 --> 00:12:56,543 +but I recommend you keep this in mind + +210 +00:12:56,577 --> 00:12:59,880 +when evaluating scripts +for your Web Inspector extensions. + +211 +00:12:59,913 --> 00:13:02,983 +Let's take a look at how I can use +this function in my extension + +212 +00:13:03,016 --> 00:13:05,853 +to get and display data +from the inspected page. + +213 +00:13:05,886 --> 00:13:09,790 +I'll start by replacing the placeholder +"Hello World" I added earlier + +214 +00:13:09,823 --> 00:13:14,094 +with HTML that actually loads +my CSS and JavaScript files. + +215 +00:13:18,398 --> 00:13:22,503 +Then I'll add some basic CSS +to give my devtools tab a nice style. + +216 +00:13:24,071 --> 00:13:27,674 +I want to make sure that my devtools tab +fits in with the rest of Web Inspector, + +217 +00:13:27,708 --> 00:13:31,578 +so I've declared +a root `color-scheme` property + +218 +00:13:31,612 --> 00:13:36,083 +that will make my devtools tab match the +appearance of the rest of Web Inspector. + +219 +00:13:36,116 --> 00:13:41,054 +I am using a system font family +and inheriting the font size, + +220 +00:13:41,088 --> 00:13:44,725 +and matching the colors +of more important text. + +221 +00:13:44,758 --> 00:13:48,529 +As far as functionality, + +222 +00:13:48,562 --> 00:13:50,264 +I'll start by adding some text + +223 +00:13:50,297 --> 00:13:53,834 +in case the page doesn't have +any opengraph metadata. + +224 +00:13:53,867 --> 00:13:57,971 +Note that I can use web extension +localized strings in Web Inspector + +225 +00:13:58,005 --> 00:14:00,474 +just like I can anywhere else +in a web extension. + +226 +00:14:00,507 --> 00:14:04,478 +But in order to do so, I need to add +that same localized string identifier + +227 +00:14:04,511 --> 00:14:06,380 +to the localized strings file. + +228 +00:14:07,514 --> 00:14:09,783 +Next, + +229 +00:14:09,816 --> 00:14:13,687 +I'll create the JavaScript +I'll provide to the powerful devtools + +230 +00:14:13,720 --> 00:14:15,789 +inspectedWindow eval API, + +231 +00:14:15,822 --> 00:14:18,192 +which lets me evaluate it +in the inspected page. + +232 +00:14:18,225 --> 00:14:21,428 +In this case, +I want to query the inspected page's DOM + +233 +00:14:21,461 --> 00:14:24,097 +for some common opengraph metadata, + +234 +00:14:24,131 --> 00:14:28,836 +specifically the title, description... + +235 +00:14:30,737 --> 00:14:33,607 +…and image, + +236 +00:14:33,640 --> 00:14:37,344 +as well as the current ready state +of the inspected page's document, + +237 +00:14:37,377 --> 00:14:41,815 +bundling it all together to send back +to the devtools page via the return value. + +238 +00:14:41,849 --> 00:14:45,152 +Once that's done, +I can grab the HTML element + +239 +00:14:45,185 --> 00:14:48,488 +that corresponds +to each of these properties + +240 +00:14:48,522 --> 00:14:51,992 +and configure them so that they show +the data that was gathered. + +241 +00:14:54,261 --> 00:14:58,699 +And if the document isn't quite ready yet, +I can try again after a short delay. + +242 +00:15:00,434 --> 00:15:03,837 +I also want to redo all of this +every time the inspected page navigates, + +243 +00:15:03,871 --> 00:15:07,074 +so I'll add a listener +for devtools network onNavigated. + +244 +00:15:15,916 --> 00:15:19,653 +This all looks great, +so I'll build again to test it. + +245 +00:15:26,126 --> 00:15:30,297 +Now, whenever I open Web Inspector or +navigate with Web Inspector already open, + +246 +00:15:30,330 --> 00:15:34,968 +I can see the opengraph title, +description, and image of every page. + +247 +00:15:35,002 --> 00:15:38,305 +And that's a simple example of +how to use some of the many new + +248 +00:15:38,338 --> 00:15:41,275 +and powerful Web Inspector extension APIs. + +249 +00:15:41,308 --> 00:15:44,378 +My OpenGraph extension +is coming along very nicely. + +250 +00:15:44,411 --> 00:15:47,414 +My Web Inspector extension's +devtools tab page + +251 +00:15:47,447 --> 00:15:50,684 +is now able to evaluate +in the inspected page. + +252 +00:15:50,717 --> 00:15:54,221 +I'm able to take the result data +and process it + +253 +00:15:54,254 --> 00:15:57,491 +to display it in a user friendly format +for quick access. + +254 +00:15:57,524 --> 00:16:00,761 +So what are some great ways to make +great user experiences + +255 +00:16:00,794 --> 00:16:02,729 +when creating a Web Inspector extension? + +256 +00:16:02,763 --> 00:16:07,301 +Always create devtools tab pages +from the devtools background page. + +257 +00:16:07,334 --> 00:16:11,572 +This way, the user can see where +these tabs will appear in Web Inspector, + +258 +00:16:11,605 --> 00:16:15,075 +and any appropriate permission prompting +will be shown in-line. + +259 +00:16:15,108 --> 00:16:17,744 +Instead of asking +for specific host permissions, + +260 +00:16:17,778 --> 00:16:19,880 +try to use the activeTab permission + +261 +00:16:19,913 --> 00:16:22,816 +to keep Web Inspector extensions +as targeted as possible. + +262 +00:16:22,850 --> 00:16:25,719 +And make sure to use +the CSS color-scheme property + +263 +00:16:25,752 --> 00:16:28,422 +or the web extension devtools theme APIs + +264 +00:16:28,455 --> 00:16:30,657 +to match the overall theme +of Web Inspector. + +265 +00:16:30,691 --> 00:16:34,161 +So today I showed you at how to create +entirely new debugging tools + +266 +00:16:34,194 --> 00:16:36,096 +with Web Inspector extensions + +267 +00:16:36,129 --> 00:16:39,399 +and covered some great best practices +to keep in mind while doing so. + +268 +00:16:39,433 --> 00:16:42,603 +You're welcome to download +the OpenGraph Web Inspector extension + +269 +00:16:42,636 --> 00:16:44,271 +in this session's related resources + +270 +00:16:44,304 --> 00:16:46,473 +if you want to take a closer look +for yourself. + +271 +00:16:48,275 --> 00:16:50,444 +We're super interested +in hearing your thoughts, + +272 +00:16:50,477 --> 00:16:54,481 +so please file bugs and feature requests +using the Feedback Assistant, + +273 +00:16:54,515 --> 00:16:58,385 +or come chat with us +on the Safari developer forums. + +274 +00:16:58,418 --> 00:17:01,421 +And even consider joining +the WebExtensions community group + +275 +00:17:01,455 --> 00:17:04,057 +to help shape the future +of web extensions. + +276 +00:17:05,325 --> 00:17:09,096 +And also, make sure to check out +What's new in Safari Web Extensions, + +277 +00:17:09,129 --> 00:17:13,433 +and the documentation online, +to learn more about what's new this year. + +278 +00:17:13,467 --> 00:17:16,603 +I really hope you've enjoyed learning +about the amazing new ability + +279 +00:17:16,637 --> 00:17:19,072 +to create custom tooling +within Web Inspector. + +280 +00:17:19,106 --> 00:17:22,876 +We can't wait to see what awesome +Web Inspector extensions you'll create, + +281 +00:17:22,910 --> 00:17:26,280 +and look forward to all the ways +you'll push the bounds of what's possible. + +282 +00:17:26,313 --> 00:17:27,748 +Thank you so much for listening, + +283 +00:17:27,781 --> 00:17:30,751 +and I hope you have +a fantastic rest of your WWDC. ♪ ♪ + diff --git a/eng/2022 Session 10101 Go bindless with Metal 3 en.srt b/eng/2022 Session 10101 Go bindless with Metal 3 en.srt new file mode 100644 index 0000000..64b6f85 --- /dev/null +++ b/eng/2022 Session 10101 Go bindless with Metal 3 en.srt @@ -0,0 +1,2601 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,743 --> 00:00:10,911 +Hello and welcome. + +3 +00:00:10,944 --> 00:00:15,048 +My name is Alè Segovia Azapian +from the GPU Software team here at Apple. + +4 +00:00:15,082 --> 00:00:18,385 +And I’m Mayur, +also from the GPU Software team. + +5 +00:00:18,418 --> 00:00:21,755 +Alè: In this session we are going +to talk about bindless rendering. + +6 +00:00:21,788 --> 00:00:25,692 +The bindless binding model is a modern +way to provide resources to your shaders, + +7 +00:00:25,726 --> 00:00:29,096 +unlocking advanced rendering techniques, +such as ray tracing. + +8 +00:00:29,129 --> 00:00:34,201 +For today, I'll start with a brief recap +of how the bindless binding model works, + +9 +00:00:34,234 --> 00:00:39,339 +and how you can easily adopt bindless +in your games and apps with Metal 3. + +10 +00:00:40,440 --> 00:00:44,111 +Bindless rendering aggregates data, +which opens up new opportunities + +11 +00:00:44,144 --> 00:00:47,247 +to improve performance on the CPU and GPU. + +12 +00:00:47,281 --> 00:00:52,519 +I'll give you two specific tips today +to improve your CPU and GPU times. + +13 +00:00:53,387 --> 00:00:54,755 +Then I'll hand it over to Mayur, + +14 +00:00:54,788 --> 00:00:58,859 +and he’ll show you how the tools +can help you adopt a bindless model. + +15 +00:00:59,760 --> 00:01:02,129 +In the bindless model, +resources are aggregated + +16 +00:01:02,162 --> 00:01:04,998 +and linked together with argument buffers. + +17 +00:01:05,032 --> 00:01:07,501 +Conceptually, +this is what this looks like. + +18 +00:01:07,534 --> 00:01:12,005 +In this example, an array +aggregates all the meshes in a scene. + +19 +00:01:12,039 --> 00:01:15,676 +Unlike the traditional binding model, +where you bind each resource independently + +20 +00:01:15,709 --> 00:01:17,644 +to a specific slot in a pipeline, + +21 +00:01:17,678 --> 00:01:22,082 +in the bindless model, resources +are first linked together in memory. + +22 +00:01:22,115 --> 00:01:25,786 +This lets you bind a single buffer +that your shaders can freely navigate + +23 +00:01:25,819 --> 00:01:27,888 +and access the resources they need + +24 +00:01:27,921 --> 00:01:32,259 +to calculate elaborate surfaces +and lighting. + +25 +00:01:32,292 --> 00:01:34,261 +After the app goes bindless, + +26 +00:01:34,294 --> 00:01:36,997 +the ray tracing shaders can access +all the data they need + +27 +00:01:37,030 --> 00:01:39,800 +to beautifully shade the reflections. + +28 +00:01:39,833 --> 00:01:42,069 +The app makes the 3D models and textures, + +29 +00:01:42,102 --> 00:01:46,073 +including the floor, the trucks, +their materials, and even the sky, + +30 +00:01:46,106 --> 00:01:48,175 +available to the ray tracing shaders + +31 +00:01:48,208 --> 00:01:51,979 +by placing all its data +into argument buffers. + +32 +00:01:52,012 --> 00:01:54,815 +Even better, +when bindless rendering is paired with + +33 +00:01:54,848 --> 00:01:59,319 +other Metal features like Heaps, +apps and games enjoy better performance, + +34 +00:01:59,353 --> 00:02:02,489 +thanks to less pressure on the CPU. + +35 +00:02:02,523 --> 00:02:05,792 +I'll talk about four specific enhancements +in Metal 3 + +36 +00:02:05,826 --> 00:02:08,929 +that you may find useful +for bindless rendering. + +37 +00:02:09,530 --> 00:02:12,833 +Argument buffers +are the fundamental Metal construct + +38 +00:02:12,866 --> 00:02:15,702 +that allows you to link +your resources together. + +39 +00:02:15,736 --> 00:02:20,007 +They reference resources +such as textures and other buffers. + +40 +00:02:20,040 --> 00:02:24,278 +Metal 3 makes writing +argument buffers easier than ever + +41 +00:02:24,311 --> 00:02:28,315 +because now you no longer need +an Argument encoder object. + +42 +00:02:28,348 --> 00:02:31,785 +And the same is true for unbounded arrays. + +43 +00:02:31,818 --> 00:02:34,254 +You can now allocate acceleration +structures + +44 +00:02:34,288 --> 00:02:36,223 +from a Metal Heap + +45 +00:02:36,256 --> 00:02:39,092 +and the Shader Validation Layer alerts you + +46 +00:02:39,126 --> 00:02:42,496 +when resources aren’t resident +in GPU memory. + +47 +00:02:42,529 --> 00:02:47,467 +Together, these four features +make it easier than ever to go bindless. + +48 +00:02:48,735 --> 00:02:54,107 +In particular, writing argument buffers +in Metal 3 is a joy. + +49 +00:02:54,141 --> 00:02:56,343 +To encode a scene into argument buffers, + +50 +00:02:56,376 --> 00:02:58,512 +you write into these buffers +the scene data, + +51 +00:02:58,545 --> 00:03:03,150 +such as instances, meshes, +materials, and textures. + +52 +00:03:03,183 --> 00:03:07,621 +In Metal 2, this is accomplished +with an argument encoder. + +53 +00:03:07,654 --> 00:03:10,858 +So first, I will recap +how these objects work, + +54 +00:03:10,891 --> 00:03:14,928 +and then I will show you +how Metal 3 helps simplify your code. + +55 +00:03:14,962 --> 00:03:19,299 +With argument encoders, the first step +is to create the encoder instance. + +56 +00:03:19,333 --> 00:03:21,568 +You do this via shader function reflection + +57 +00:03:21,602 --> 00:03:25,105 +or by describing +the struct members to Metal. + +58 +00:03:25,138 --> 00:03:29,309 +With the encoder instance, +set its recording destination and offset + +59 +00:03:29,343 --> 00:03:32,145 +to the target argument buffer. + +60 +00:03:32,179 --> 00:03:36,083 +And use its methods to +write data into the buffer. + +61 +00:03:36,116 --> 00:03:38,151 +Please check out the bindless session +from last year + +62 +00:03:38,185 --> 00:03:42,856 +for a detailed refresher on argument +buffers and argument encoders. + +63 +00:03:42,890 --> 00:03:44,725 +Now this mechanism is great, + +64 +00:03:44,758 --> 00:03:48,562 +but the encoder objects +can sometimes be challenging to manage. + +65 +00:03:48,595 --> 00:03:51,798 +Metal provides two mechanisms +for creating argument encoders. + +66 +00:03:51,832 --> 00:03:55,369 +It might not be clear +which one is appropriate for your app. + +67 +00:03:55,402 --> 00:04:00,507 +Additionally, using an argument encoder +from multiple threads requires care. + +68 +00:04:00,541 --> 00:04:04,111 +Developers, intuitively understand +how to write a C struct, + +69 +00:04:04,144 --> 00:04:09,316 +and with Metal 3, you can now do just that +for your argument buffers. + +70 +00:04:09,349 --> 00:04:12,319 +Metal 3 simplifies writing argument +buffers by allowing you + +71 +00:04:12,352 --> 00:04:16,857 +to directly write into them +like any other CPU-side structure. + +72 +00:04:16,890 --> 00:04:20,060 +You now have access +to the virtual GPU address + +73 +00:04:20,093 --> 00:04:23,797 +and Resource IDs of your resources. + +74 +00:04:23,830 --> 00:04:26,366 +When you write these directly +into your argument buffer, + +75 +00:04:26,400 --> 00:04:29,937 +Metal now understands +what resources you are referencing. + +76 +00:04:29,970 --> 00:04:32,873 +It is functionally the same +as previously encoding the reference + +77 +00:04:32,906 --> 00:04:37,678 +using an argument encoder, +except an encoder isn't needed anymore. + +78 +00:04:37,711 --> 00:04:41,915 +This capability is supported all devices +with argument buffers tier 2 support. + +79 +00:04:41,949 --> 00:04:45,452 +That is, any Mac from 2016 or newer, + +80 +00:04:45,485 --> 00:04:50,123 +and any iOS device with +the A13 bionic chip or newer. + +81 +00:04:51,892 --> 00:04:54,962 +If you are unsure whether +a device supports argument buffers tier 2, + +82 +00:04:54,995 --> 00:05:00,267 +there is a convenient feature query +in the MTLDevice object that you can use. + +83 +00:05:00,300 --> 00:05:03,904 +This is what the process +now looks like in Metal 3. + +84 +00:05:03,937 --> 00:05:09,376 +First, define your CPU-visible structs, +using a 64-bit type for buffer addresses, + +85 +00:05:09,409 --> 00:05:13,680 +and MTLResourceID for textures. + +86 +00:05:13,714 --> 00:05:16,550 +Then, allocate the argument buffer. + +87 +00:05:16,583 --> 00:05:23,056 +You allocate buffers either directly +from the MTLDevice, or from a MTLHeap. + +88 +00:05:23,090 --> 00:05:29,062 +You get the buffer's contents and cast it +to the argument buffer struct type. + +89 +00:05:29,096 --> 00:05:34,001 +And finally, write the addresses +and resource IDs to the struct members. + +90 +00:05:34,034 --> 00:05:37,938 +Take a look at how this is done +in the hybrid rendering demo. + +91 +00:05:37,971 --> 00:05:41,542 +Here is the code. +Notice how simple it is. + +92 +00:05:41,575 --> 00:05:46,880 +The host-side struct directly stores +the GPU address of the normals buffer. + +93 +00:05:46,914 --> 00:05:52,653 +This is a 64-bit unsigned integer +so I used uint64_t. + +94 +00:05:52,686 --> 00:05:54,488 +Now that there is no encoder object, + +95 +00:05:54,521 --> 00:05:59,459 +you simply use the size of the struct +for your argument buffer. + +96 +00:05:59,493 --> 00:06:03,697 +Metal guarantees that the size +and alignment of the GPU and CPU structs + +97 +00:06:03,730 --> 00:06:07,100 +match across clang +and the Metal shader compiler. + +98 +00:06:08,769 --> 00:06:13,273 +Next, allocate the buffer as usual. + +99 +00:06:13,307 --> 00:06:16,143 +And if a buffer’s storage mode +is Managed or Shared, + +100 +00:06:16,176 --> 00:06:21,648 +get a direct pointer to the buffer +and cast it to the struct type. + +101 +00:06:21,682 --> 00:06:25,185 +And finally, set the normals member +to the gpuAddress, + +102 +00:06:25,219 --> 00:06:29,623 +and optionally, an offset that you must +align to the GPU’s memory requirements. + +103 +00:06:32,359 --> 00:06:35,963 +One thing I want to highlight +is how the structure declaration changes + +104 +00:06:35,996 --> 00:06:39,833 +between the Metal Shading Language +and the C declaration. + +105 +00:06:39,867 --> 00:06:42,135 +In this example, these are kept separate, + +106 +00:06:42,169 --> 00:06:46,573 +but if you prefer, you can have a single +struct declaration in a shared header + +107 +00:06:46,607 --> 00:06:49,243 +and use conditional compilation +to distinguish between + +108 +00:06:49,276 --> 00:06:52,412 +the shader compiler types and the C types. + +109 +00:06:53,146 --> 00:06:56,149 +Here’s a unified declaration in C. + +110 +00:06:56,183 --> 00:07:00,254 +The __METAL_VERSION__ macro is only +defined when compiling shader code. + +111 +00:07:00,287 --> 00:07:05,692 +Use it to separate GPU and CPU code +in header declarations. + +112 +00:07:05,726 --> 00:07:09,062 +If your app targets C++, +you can take this further + +113 +00:07:09,096 --> 00:07:12,866 +and use templates to make +the declarations even more uniform. + +114 +00:07:13,867 --> 00:07:18,005 +Check out the argument buffer +sample code for the best practices. + +115 +00:07:18,038 --> 00:07:20,374 +Now that's how you write one struct, + +116 +00:07:20,407 --> 00:07:24,378 +but you can also write many structs +using unbounded arrays. + +117 +00:07:24,411 --> 00:07:28,315 +You could already implement unbounded +arrays in Metal using argument encoders, + +118 +00:07:28,348 --> 00:07:31,251 +but Metal 3 simplifies the process +even further + +119 +00:07:31,285 --> 00:07:35,122 +by bringing it closer to just filling out +an array of structs. + +120 +00:07:36,223 --> 00:07:40,160 +Here's what's different compared +to just writing one struct. + +121 +00:07:40,194 --> 00:07:46,400 +You now need to allocate enough storage +for all structs you want to store. + +122 +00:07:46,433 --> 00:07:52,539 +And then, iterate over the array, +writing the data for each struct. + +123 +00:07:53,540 --> 00:07:57,244 +Back to the code sample, +first, expand the size of the buffer + +124 +00:07:57,277 --> 00:08:01,615 +to store as many structs +as meshes in the scene. + +125 +00:08:01,648 --> 00:08:05,385 +Notice how this is exactly the same +you do for a CPU buffer: + +126 +00:08:05,419 --> 00:08:10,524 +multiply the size of the struct +times the mesh count. + +127 +00:08:10,557 --> 00:08:13,827 +I want to take a moment to note +how powerful this is. + +128 +00:08:13,861 --> 00:08:17,631 +This single variable completely controls +the size of the array. + +129 +00:08:17,664 --> 00:08:19,600 +The shader does not need +to declare this size + +130 +00:08:19,633 --> 00:08:21,835 +to the Metal shader compiler at any point + +131 +00:08:21,869 --> 00:08:24,771 +and it's free to index into any position. + +132 +00:08:24,805 --> 00:08:28,475 +This is part of the reason the bindless +model is so flexible in Metal, + +133 +00:08:28,509 --> 00:08:32,579 +because you write shaders that access +arrays of any size with no constraints. + +134 +00:08:32,613 --> 00:08:34,448 +It just works! + +135 +00:08:36,416 --> 00:08:40,187 +Next, allocate the buffer of this size + +136 +00:08:40,220 --> 00:08:44,892 +and cast the pointer to the contents +to the correct mesh struct type. + +137 +00:08:46,860 --> 00:08:50,397 +Now that the buffer is large enough, +walk it with a simple for loop, + +138 +00:08:50,430 --> 00:08:55,102 +straddling the size of the mesh struct. + +139 +00:08:55,135 --> 00:08:59,706 +And finally, directly set the GPUAddress +of each struct in the array, + +140 +00:08:59,740 --> 00:09:02,309 +and optionally, an aligned offset. + +141 +00:09:04,144 --> 00:09:06,613 +From the GPU side in the shader, + +142 +00:09:06,647 --> 00:09:09,650 +this is one way +to represent the unbounded array. + +143 +00:09:09,683 --> 00:09:14,955 +Here, I declare it as a mesh pointer +parameter that I pass to the shader. + +144 +00:09:16,657 --> 00:09:19,893 +This makes it possible to freely access +the contents directly, + +145 +00:09:19,927 --> 00:09:22,529 +just as you would for any C array. + +146 +00:09:25,365 --> 00:09:30,370 +Another option is to pull +all the unbounded arrays into a struct. + +147 +00:09:30,404 --> 00:09:35,275 +This helps keep shaders neat +by aggregating data in a single place. + +148 +00:09:35,309 --> 00:09:37,377 +In this example, +all the meshes and materials + +149 +00:09:37,411 --> 00:09:40,347 +are brought together in a scene struct. + +150 +00:09:42,549 --> 00:09:45,719 +Using the scene struct, +the scene is passed directly to the shader + +151 +00:09:45,752 --> 00:09:47,421 +by binding a single buffer, + +152 +00:09:47,454 --> 00:09:50,691 +instead of passing every unbounded array +separately. + +153 +00:09:52,459 --> 00:09:54,394 +And access is just like before, + +154 +00:09:54,428 --> 00:09:58,832 +but now, the mesh array is reached +through the scene struct. + +155 +00:09:59,666 --> 00:10:04,371 +And that’s how to write argument buffers +and unbounded arrays in Metal 3. + +156 +00:10:04,404 --> 00:10:07,241 +The completely revamped API +now makes it more intuitive + +157 +00:10:07,274 --> 00:10:12,212 +and matches what you do for CPU structs, +or arrays of structs. + +158 +00:10:12,246 --> 00:10:14,114 +With this year's ray tracing update, + +159 +00:10:14,147 --> 00:10:17,284 +ray tracing acceleration structures +can be allocated from Metal Heaps, + +160 +00:10:17,317 --> 00:10:19,786 +along with your buffers and textures. + +161 +00:10:21,722 --> 00:10:24,491 +This means they can be aggregated +together amongst themselves + +162 +00:10:24,525 --> 00:10:26,960 +and with other resource types. + +163 +00:10:26,994 --> 00:10:30,864 +This is great, because when you aggregate +all acceleration structures into heaps, + +164 +00:10:30,898 --> 00:10:35,269 +you can flag them all resident +in a single call to useHeap. + +165 +00:10:35,302 --> 00:10:38,438 +This is a huge opportunity +for significant CPU savings + +166 +00:10:38,472 --> 00:10:41,041 +in your application's render thread. + +167 +00:10:42,476 --> 00:10:46,113 +Here are some tips for working with +acceleration structures in heaps. + +168 +00:10:46,146 --> 00:10:49,349 +First, when allocated from heaps, +acceleration structures have + +169 +00:10:49,383 --> 00:10:53,787 +alignment and size requirements +that vary per device. + +170 +00:10:53,820 --> 00:10:56,790 +There is a new query to check +for the size and alignment + +171 +00:10:56,823 --> 00:11:00,227 +of an acceleration structure +for heap allocation. + +172 +00:11:00,260 --> 00:11:03,864 +Use the heapAccelerationStructureSize +andAlignWithDescriptor method + +173 +00:11:03,897 --> 00:11:06,834 +of the MTLDevice to determine +the SizeAndAlignment + +174 +00:11:06,867 --> 00:11:09,803 +for structure descriptors. + +175 +00:11:09,837 --> 00:11:11,705 +Keep in mind, this is different + +176 +00:11:11,738 --> 00:11:14,408 +from the accelerationStructureSizes +WithDescriptor method + +177 +00:11:14,441 --> 00:11:16,443 +in MTLDevice. + +178 +00:11:18,612 --> 00:11:22,015 +Now that acceleration structures +are in a MTLHeap object, + +179 +00:11:22,049 --> 00:11:25,652 +call useHeap: to make them +all resident in a single call. + +180 +00:11:25,686 --> 00:11:31,758 +This is faster than calling +useResource on each individual resource. + +181 +00:11:31,792 --> 00:11:35,095 +And keep in mind that unless +you opt your heap into hazard tracking, + +182 +00:11:35,128 --> 00:11:38,999 +Metal does not prevent race conditions +for resources allocated from them, + +183 +00:11:39,032 --> 00:11:41,768 +so you will need to synchronize +the acceleration structure builds + +184 +00:11:41,802 --> 00:11:44,938 +between one another +and with ray tracing work. + +185 +00:11:44,972 --> 00:11:47,441 +Don't worry though, +I will talk more about this in a moment. + +186 +00:11:48,942 --> 00:11:50,310 +For more details on this + +187 +00:11:50,344 --> 00:11:53,180 +and other ray tracing performance +advances in Metal 3, + +188 +00:11:53,213 --> 00:11:54,081 +make sure to check out + +189 +00:11:54,114 --> 00:11:58,118 +the "Maximize your Metal ray tracing +performance" talk this year. + +190 +00:11:58,151 --> 00:12:02,122 +Using heap-allocated acceleration +structures provides an opportunity + +191 +00:12:02,155 --> 00:12:06,059 +to reduce your app's CPU usage +when it matters the most. + +192 +00:12:06,093 --> 00:12:09,763 +Last but not least, here's one of my +favorite features this year: + +193 +00:12:09,796 --> 00:12:12,866 +Shader validation enhancements. + +194 +00:12:13,867 --> 00:12:16,436 +On the topic of useResource and useHeap, + +195 +00:12:16,470 --> 00:12:18,972 +it is very important that +apps flag residency to Metal + +196 +00:12:19,006 --> 00:12:22,075 +for all indirectly accessed resources. + +197 +00:12:22,109 --> 00:12:25,712 +Forgetting to do it means that the memory +pages backing those resources + +198 +00:12:25,746 --> 00:12:28,582 +may not be present at rendering time. + +199 +00:12:28,615 --> 00:12:34,188 +This can cause command buffer failures, +GPU restarts, or even image corruption. + +200 +00:12:34,221 --> 00:12:37,457 +Unfortunately, it is very common +to run into these problems + +201 +00:12:37,491 --> 00:12:40,494 +when starting the bindless journey, +because in bindless, + +202 +00:12:40,527 --> 00:12:43,864 +the majority of scene resources +are accessed indirectly, + +203 +00:12:43,897 --> 00:12:48,769 +and shaders make pointer navigation +decisions at runtime. + +204 +00:12:48,802 --> 00:12:51,705 +This year, Metal 3 introduces +new functionality + +205 +00:12:51,738 --> 00:12:54,908 +in the shader validation layer +that will help you track down + +206 +00:12:54,942 --> 00:12:58,712 +missing residency of resources +during command buffer execution. + +207 +00:12:58,745 --> 00:13:01,181 +I'll show you a concrete example. + +208 +00:13:01,215 --> 00:13:03,951 +During the update process +of the Hybrid Rendering app, + +209 +00:13:03,984 --> 00:13:09,356 +we encountered a real problem where +reflections sometimes looked incorrect. + +210 +00:13:09,389 --> 00:13:14,127 +I'll show you how the validation layer +helped diagnose and fix this problem. + +211 +00:13:15,362 --> 00:13:19,533 +To flag residency to Metal, the app stores +all individual resources + +212 +00:13:19,566 --> 00:13:24,538 +not backed by heaps +into a mutable set at load time. + +213 +00:13:24,571 --> 00:13:28,809 +The app adds buffers and it adds textures. + +214 +00:13:28,842 --> 00:13:32,779 +At rendering time, before the app +dispatches the ray tracing kernel, + +215 +00:13:32,813 --> 00:13:37,050 +it indicates to Metal that it uses +all resources in the set. + +216 +00:13:37,084 --> 00:13:39,953 +This is a simple process where +the app iterates over the set + +217 +00:13:39,987 --> 00:13:43,190 +and calls useResource on each element. + +218 +00:13:43,223 --> 00:13:46,026 +Metal then makes +all these resources resident + +219 +00:13:46,059 --> 00:13:49,263 +before starting the ray tracing work. + +220 +00:13:49,296 --> 00:13:53,800 +Here's part of the code where the app +collects the resources into this set. + +221 +00:13:53,834 --> 00:13:58,772 +The app does this as part of +its argument buffer writing process. + +222 +00:13:58,805 --> 00:14:03,810 +The app's loading function +iterates over each submesh. + +223 +00:14:03,844 --> 00:14:06,346 +It nabs the data it needs +to write to the argument buffer-- + +224 +00:14:06,380 --> 00:14:11,151 +that is, index data and +texture data for materials-- + +225 +00:14:11,185 --> 00:14:16,123 +then it stores the index buffer's +address in the argument buffer. + +226 +00:14:16,156 --> 00:14:18,959 +For materials, +it then loops over the texture array, + +227 +00:14:18,992 --> 00:14:24,865 +writing the texture GPU resource IDs +into the argument buffer. + +228 +00:14:24,898 --> 00:14:27,935 +And at the end, +it adds all individual textures + +229 +00:14:27,968 --> 00:14:31,138 +from the submesh materials +to the sceneResources set, + +230 +00:14:31,171 --> 00:14:34,174 +so it can flag them resident +at dispatch time. + +231 +00:14:35,976 --> 00:14:39,713 +Unfortunately, there is a subtle bug here. + +232 +00:14:39,746 --> 00:14:41,648 +The app would run the command buffer, + +233 +00:14:41,682 --> 00:14:45,285 +and in some cases, +reflections would be missing. + +234 +00:14:45,319 --> 00:14:49,556 +Previously, +it was hard to track this down. + +235 +00:14:49,590 --> 00:14:53,827 +Now in Metal 3, the shader +validation layer comes to the rescue. + +236 +00:14:53,861 --> 00:14:57,865 +These kinds of problems now produce +an error during command buffer execution, + +237 +00:14:57,898 --> 00:15:01,768 +indicating what the problem is. + +238 +00:15:01,802 --> 00:15:03,604 +The error message indicates + +239 +00:15:03,637 --> 00:15:07,007 +the name of the shader function +that triggered the problem, + +240 +00:15:07,040 --> 00:15:09,409 +the name of the pass, + +241 +00:15:09,443 --> 00:15:14,181 +the metal file and line of code where it +detected the access, + +242 +00:15:14,214 --> 00:15:17,317 +and even the label +of the buffer, its size, + +243 +00:15:17,351 --> 00:15:20,554 +and the fact that it was not resident. + +244 +00:15:20,587 --> 00:15:24,825 +As a pro tip, this is why it's always +a good practice to label Metal objects. + +245 +00:15:24,858 --> 00:15:27,694 +The tools use the labels, which is helpful +when trying to identify + +246 +00:15:27,728 --> 00:15:30,831 +which object is which +while debugging your app. + +247 +00:15:30,864 --> 00:15:32,799 +With all this +detailed information in hand, + +248 +00:15:32,833 --> 00:15:36,770 +it's now easy to find the missing resource +in the shader code. + +249 +00:15:36,803 --> 00:15:40,340 +What's even better is that +when the debug breakpoint is enabled, + +250 +00:15:40,374 --> 00:15:44,011 +Xcode conveniently shows +the exact line in the shader code + +251 +00:15:44,044 --> 00:15:47,447 +where shader validation +detects the problem. + +252 +00:15:47,481 --> 00:15:51,852 +In the case of the demo app, it is +the indices buffer that is not resident. + +253 +00:15:51,885 --> 00:15:55,422 +The fix is now straightforward. + +254 +00:15:55,455 --> 00:15:57,658 +Going back to the code, + +255 +00:15:57,691 --> 00:15:59,960 +the app now stores +the missing index buffer + +256 +00:15:59,993 --> 00:16:02,829 +into the resident resource set. + +257 +00:16:02,863 --> 00:16:05,532 +With these changes, +later at ray tracing time, + +258 +00:16:05,566 --> 00:16:10,838 +Metal knows to make the index buffers +available to the GPU, solving the issue. + +259 +00:16:10,871 --> 00:16:13,841 +This is an essential tool, +and a complete game changer, + +260 +00:16:13,874 --> 00:16:19,079 +that will potentially save you hours of +debugging time in your bindless journey. + +261 +00:16:19,112 --> 00:16:22,349 +So those are the enhancements +Metal 3 brings to help you organize + +262 +00:16:22,382 --> 00:16:24,685 +and refer to bindless resources. + +263 +00:16:24,718 --> 00:16:27,321 +Now I'm going to switch gears +and talk about how to maximize + +264 +00:16:27,354 --> 00:16:30,190 +your game's performance +when going bindless. + +265 +00:16:30,224 --> 00:16:32,793 +In this section, I will cover two topics: + +266 +00:16:32,826 --> 00:16:36,597 +unretained resources, +and untracked resources. + +267 +00:16:36,630 --> 00:16:40,133 +These tips will help you get +more performance out of both your CPU + +268 +00:16:40,167 --> 00:16:44,638 +and your GPU when you have +long-lived and aggregate resources. + +269 +00:16:44,671 --> 00:16:48,509 +Now, to talk about how to improve +CPU performance with long-lived resources, + +270 +00:16:48,542 --> 00:16:51,678 +I will first recap +the Metal resource lifecycle. + +271 +00:16:51,712 --> 00:16:56,884 +Objective-C and Swift handle +object lifecycles via reference counting. + +272 +00:16:56,917 --> 00:16:59,319 +Metal resources follow this model. + +273 +00:16:59,353 --> 00:17:02,222 +Resources start with a retainCount of 1 + +274 +00:17:02,256 --> 00:17:07,461 +and the runtime deallocates them +when all strong references disappear. + +275 +00:17:07,494 --> 00:17:10,998 +Because the CPU +and GPU operate in parallel, + +276 +00:17:11,031 --> 00:17:14,401 +it would be a problem if +the CPU deallocated a resource + +277 +00:17:14,434 --> 00:17:18,672 +by allowing its retainCount to reach 0 +while the GPU is still using it. + +278 +00:17:19,706 --> 00:17:23,377 +To prevent this, Metal command buffers +create strong references + +279 +00:17:23,410 --> 00:17:25,179 +to all resources they use, + +280 +00:17:25,212 --> 00:17:30,284 +ensuring their retainCount +is always at least 1. + +281 +00:17:30,317 --> 00:17:33,120 +Metal creates strong references for + +282 +00:17:33,153 --> 00:17:35,222 +resources you directly bind to a pipeline + +283 +00:17:35,255 --> 00:17:39,693 +with functions such as +setVertexBuffer or setFragmentTexture-- + +284 +00:17:39,726 --> 00:17:42,896 +and this also includes render +attachments-- + +285 +00:17:42,930 --> 00:17:48,168 +Metal heap objects you flag resident +via the useHeap API, + +286 +00:17:48,202 --> 00:17:50,637 +and indirect resources +that you make resident + +287 +00:17:50,671 --> 00:17:55,475 +via the useResource API, +even if they are part of a heap. + +288 +00:17:55,509 --> 00:17:58,078 +For more details +on Metal object lifecycles, + +289 +00:17:58,111 --> 00:18:03,317 +please check out the "Program Metal +in C++ with metal-cpp" talk this year. + +290 +00:18:03,350 --> 00:18:06,486 +Now, it's very useful that +Metal creates these references, + +291 +00:18:06,520 --> 00:18:08,722 +because as a programmer, +you never have to worry + +292 +00:18:08,755 --> 00:18:13,393 +that you might be deallocating an object +while the GPU is still using it. + +293 +00:18:13,427 --> 00:18:17,264 +This safety guarantee Metal gives you +is very fast to execute, + +294 +00:18:17,297 --> 00:18:20,534 +but it does comes with a small CPU cost. + +295 +00:18:20,567 --> 00:18:25,305 +Now, in the bindless model, +apps aggregate resources into heaps + +296 +00:18:25,339 --> 00:18:28,842 +and these tend to be long lived, +matching the application's domain. + +297 +00:18:28,876 --> 00:18:34,214 +For example, in a game, resources are +alive for the duration of a whole level. + +298 +00:18:34,248 --> 00:18:36,917 +When this is the case, it becomes +unnecessary for Metal + +299 +00:18:36,950 --> 00:18:40,587 +to provide additional guarantees +about resource lifecycles. + +300 +00:18:41,288 --> 00:18:44,324 +What you can do then +is recoup this CPU cost + +301 +00:18:44,358 --> 00:18:49,329 +by asking Metal command buffers +not to retain resources they reference. + +302 +00:18:50,564 --> 00:18:53,133 +To turn off Metal's +automatic resource retaining, + +303 +00:18:53,166 --> 00:18:57,271 +simply create a command buffer +with unretained references. + +304 +00:18:57,304 --> 00:19:00,040 +You do this directly +from the MTLCommandQueue, + +305 +00:19:00,073 --> 00:19:03,143 +just like you create +any regular command buffer. + +306 +00:19:03,177 --> 00:19:05,479 +You don't need to make +any other changes to your app, + +307 +00:19:05,512 --> 00:19:08,815 +as long as you are already guaranteeing +your resource lifecycles. + +308 +00:19:09,783 --> 00:19:12,519 +Keep in mind that +the granularity level for this setting + +309 +00:19:12,553 --> 00:19:14,454 +is the entire command buffer. + +310 +00:19:14,488 --> 00:19:18,825 +It will either retain all referenced +resources or none of them. + +311 +00:19:19,826 --> 00:19:21,361 +In a small microbenchmark, + +312 +00:19:21,395 --> 00:19:26,099 +we measured a CPU usage reduction of 2% in +the command buffer's lifecycle + +313 +00:19:26,133 --> 00:19:29,403 +just by switching to command buffers +with unretained references. + +314 +00:19:29,436 --> 00:19:32,172 +but this time was spent entirely + +315 +00:19:32,206 --> 00:19:36,043 +creating and destroying +unnecessary strong references. + +316 +00:19:37,044 --> 00:19:42,216 +In summary, unretained resources provides +an opportunity for some extra CPU savings + +317 +00:19:42,249 --> 00:19:46,220 +when you are already guaranteeing +resource lifecycles. + +318 +00:19:46,253 --> 00:19:49,456 +Similar to unretained resources, +untracked resources + +319 +00:19:49,489 --> 00:19:52,359 +provides an opportunity to disable +a safety feature + +320 +00:19:52,392 --> 00:19:55,095 +to obtain more performance. + +321 +00:19:55,128 --> 00:19:58,599 +Many visual techniques consist in +rendering to intermediate textures + +322 +00:19:58,632 --> 00:20:02,803 +and writing into buffers and then +consuming them in later passes. + +323 +00:20:02,836 --> 00:20:07,674 +Shadow mapping, skinning, and +post-processing are good examples of this. + +324 +00:20:07,708 --> 00:20:11,378 +Now, producing +and immediately consuming resources + +325 +00:20:11,411 --> 00:20:15,115 +introduces read-after-write hazards. + +326 +00:20:15,148 --> 00:20:18,318 +Additionally, when several passes +write to the same resource, + +327 +00:20:18,352 --> 00:20:19,520 +such as two render passes + +328 +00:20:19,553 --> 00:20:22,956 +drawing into the same attachment, +one after the other, + +329 +00:20:22,990 --> 00:20:26,226 +or two blit encoders +write into the same resource, + +330 +00:20:26,260 --> 00:20:28,662 +it produces write-after-write hazards + +331 +00:20:28,695 --> 00:20:33,133 +because of the way Metal schedules work +on the GPU. + +332 +00:20:33,166 --> 00:20:36,436 +When you use tracked resources, +Metal automatically uses + +333 +00:20:36,470 --> 00:20:41,375 +synchronization primitives to +avoid hazards on the GPU timeline. + +334 +00:20:41,408 --> 00:20:43,810 +For example, Metal makes the GPU wait + +335 +00:20:43,844 --> 00:20:46,547 +for a compute skinning pass +to finish writing into a buffer + +336 +00:20:46,580 --> 00:20:50,617 +before starting a scene rendering pass +that reads from the same buffer. + +337 +00:20:51,652 --> 00:20:52,819 +This is great, + +338 +00:20:52,853 --> 00:20:57,090 +and it's a big part of why Metal is +such an approachable graphics API, + +339 +00:20:57,124 --> 00:20:59,193 +but there are some performance +considerations for apps + +340 +00:20:59,226 --> 00:21:02,229 +that aggregate resources into heaps. + +341 +00:21:03,230 --> 00:21:05,265 +Consider this example. + +342 +00:21:05,299 --> 00:21:06,867 +Here, the GPU is busy, + +343 +00:21:06,900 --> 00:21:09,036 +drawing two frames +that do vertex skinning, + +344 +00:21:09,069 --> 00:21:13,640 +render the scene, and apply tone mapping, +one after the other. + +345 +00:21:13,674 --> 00:21:17,578 +As the app keeps the GPU busy, +Metal identifies opportunities + +346 +00:21:17,611 --> 00:21:22,850 +where render and compute work can overlap, +based on resource dependencies. + +347 +00:21:22,883 --> 00:21:26,019 +When there are no dependencies, +and the conditions are right, + +348 +00:21:26,053 --> 00:21:29,756 +Metal schedules work to overlap +and run in parallel. + +349 +00:21:29,790 --> 00:21:33,293 +This saturates the GPU and allows it +to get more work done + +350 +00:21:33,327 --> 00:21:36,296 +in the same amount of wall-clock time. + +351 +00:21:38,298 --> 00:21:41,568 +Now, when the app aggregates +resources together in a heap, + +352 +00:21:41,602 --> 00:21:45,606 +all of its subresources appear +as a single one to Metal. + +353 +00:21:45,639 --> 00:21:48,642 +This is what makes heaps +so efficient to work with. + +354 +00:21:48,675 --> 00:21:52,779 +But this means that Metal sees +read and write work on the same resource + +355 +00:21:52,813 --> 00:21:56,950 +and must conservatively schedule work +to avoid any race conditions, + +356 +00:21:56,984 --> 00:22:00,087 +even when no actual hazard exists. + +357 +00:22:02,823 --> 00:22:06,393 +This situation is called "false sharing" +and, as you might expect, + +358 +00:22:06,426 --> 00:22:10,464 +it increases the execution +wall-clock time of the GPU work. + +359 +00:22:10,497 --> 00:22:12,232 +So here's the performance tip. + +360 +00:22:12,266 --> 00:22:15,969 +If you know there are no dependencies +between the resources in the heap, + +361 +00:22:16,003 --> 00:22:17,938 +then you can avoid this behavior. + +362 +00:22:18,939 --> 00:22:23,310 +To avoid false sharing, you can opt +resources out of hazard tracking + +363 +00:22:23,343 --> 00:22:27,681 +and directly signal +fine-grained dependencies to Metal. + +364 +00:22:27,714 --> 00:22:29,383 +You opt out of resource tracking + +365 +00:22:29,416 --> 00:22:34,121 +by setting a resource descriptor's +hazardTracking property to Untracked. + +366 +00:22:34,154 --> 00:22:37,591 +Because this is so important, +it is the default behavior for heaps, + +367 +00:22:37,624 --> 00:22:40,494 +as it allows you to unlock more +opportunities for the GPU + +368 +00:22:40,527 --> 00:22:43,397 +to run your work in parallel +right out of the gate. + +369 +00:22:43,430 --> 00:22:45,499 +Once you start using untracked resources, + +370 +00:22:45,532 --> 00:22:48,969 +you express dependencies +using the following primitives. + +371 +00:22:49,002 --> 00:22:54,675 +Depending on the situation, +use Fences, Events, + +372 +00:22:54,708 --> 00:22:58,946 +Shared events, or Memory barriers. + +373 +00:22:58,979 --> 00:23:02,282 +Metal Fences synchronize access +to one or more resources + +374 +00:23:02,316 --> 00:23:04,585 +across different render +and compute passes, + +375 +00:23:04,618 --> 00:23:07,921 +within the context +of a single command queue. + +376 +00:23:07,955 --> 00:23:10,290 +This is a split barrier kind-of primitive, + +377 +00:23:10,324 --> 00:23:15,195 +so the consumer pass waits +until the producer signals the Fence. + +378 +00:23:18,966 --> 00:23:22,202 +The only requirement you need to +keep in mind when using Fences + +379 +00:23:22,236 --> 00:23:26,006 +is to commit or enqueue +your producer command buffers + +380 +00:23:26,039 --> 00:23:29,643 +before your consuming command buffers. + +381 +00:23:29,676 --> 00:23:31,378 +When you can't guarantee this order, + +382 +00:23:31,411 --> 00:23:34,615 +or need to synchronize across +multiple queues on the same device, + +383 +00:23:34,648 --> 00:23:37,150 +use MTL Events. + +384 +00:23:37,184 --> 00:23:41,622 +Using Events, the consumer command buffer +waits for the producer command buffer + +385 +00:23:41,655 --> 00:23:44,992 +to signal the Event with a given value. + +386 +00:23:45,025 --> 00:23:48,829 +After it signals the value, +it is safe to read the resource. + +387 +00:23:48,862 --> 00:23:54,568 +Use Events to tell a GPU to pause work +until a command signals an Event. + +388 +00:23:54,601 --> 00:23:58,372 +MTLSharedEvents behave very similarly +to regular Events, + +389 +00:23:58,405 --> 00:24:02,743 +but work at larger scope +that goes beyond a single GPU. + +390 +00:24:02,776 --> 00:24:04,945 +Use these to synchronize +access to resources + +391 +00:24:04,978 --> 00:24:08,815 +across different Metal devices +and even with the CPU. + +392 +00:24:08,849 --> 00:24:11,185 +For example, use Shared Events +to process the results + +393 +00:24:11,218 --> 00:24:14,188 +of a GPU calculation from the CPU. + +394 +00:24:14,221 --> 00:24:16,123 +Here is an example. + +395 +00:24:16,156 --> 00:24:19,526 +The GPU in this case +skins a mesh in a compute pass + +396 +00:24:19,560 --> 00:24:23,530 +and the CPU stores the pose to disk. + +397 +00:24:23,564 --> 00:24:25,899 +Because these two are independent devices, + +398 +00:24:25,933 --> 00:24:31,471 +use a Shared Event to have the CPU wait +until the GPU produces the resource. + +399 +00:24:31,505 --> 00:24:34,107 +In the beginning, +the CPU unconditionally starts to wait + +400 +00:24:34,141 --> 00:24:38,846 +for the GPU to signal the Shared Event. + +401 +00:24:38,879 --> 00:24:42,482 +When the GPU produces the resource +and places it into unified memory, + +402 +00:24:42,516 --> 00:24:44,518 +it signals the Shared Event. + +403 +00:24:44,551 --> 00:24:47,921 +At this point, the waiting thread +on the CPU wakes up + +404 +00:24:47,955 --> 00:24:50,424 +and safely consumes the resource. + +405 +00:24:52,426 --> 00:24:55,462 +The last primitive type +is Memory Barriers. + +406 +00:24:55,495 --> 00:24:58,765 +A Memory Barrier forces all subsequent +commands within a single render + +407 +00:24:58,799 --> 00:25:02,936 +or compute pass to wait +until all the previous commands finish. + +408 +00:25:02,970 --> 00:25:08,208 +The cost of a barrier is similar to the +cost of a Fence in almost all cases. + +409 +00:25:08,242 --> 00:25:10,944 +There is, however, one exception. + +410 +00:25:14,348 --> 00:25:19,186 +That exception is barriers after +the fragment stage in a render pass. + +411 +00:25:19,219 --> 00:25:21,622 +These barriers have a very high cost + +412 +00:25:21,655 --> 00:25:24,525 +that’s similar to splitting +the render pass. + +413 +00:25:24,558 --> 00:25:28,462 +Metal disables barriers after +the fragment stage on Apple GPUs + +414 +00:25:28,495 --> 00:25:32,199 +to help your apps +stay on the fastest driver path. + +415 +00:25:32,232 --> 00:25:35,469 +The Metal debug layer even +generates a validation error + +416 +00:25:35,502 --> 00:25:39,940 +if you add an after-fragment barrier +on Apple GPUs. + +417 +00:25:39,973 --> 00:25:43,110 +It is recommended to use a Fence +to synchronize resources + +418 +00:25:43,143 --> 00:25:45,345 +after the fragment stage. + +419 +00:25:46,413 --> 00:25:48,916 +Here's a short summary +of the synchronization primitives + +420 +00:25:48,949 --> 00:25:51,185 +and when to use them. + +421 +00:25:51,218 --> 00:25:53,987 +Prefer using Fences +for the lowest overhead + +422 +00:25:54,021 --> 00:25:55,789 +when committing or enqueueing work + +423 +00:25:55,822 --> 00:25:59,793 +to a single command queue +in producer, then consumer order. + +424 +00:25:59,826 --> 00:26:04,298 +Fences are great +for the majority of common cases. + +425 +00:26:04,331 --> 00:26:06,500 +When the submission order +can't be guaranteed, + +426 +00:26:06,533 --> 00:26:11,138 +or there are multiple command queues, +use Metal Events. + +427 +00:26:11,171 --> 00:26:14,107 +Shared Events allows synchronization +of multiple GPUs + +428 +00:26:14,141 --> 00:26:16,243 +between themselves and with the CPU. + +429 +00:26:16,276 --> 00:26:20,948 +Use them only in these specific +multi-device cases. + +430 +00:26:20,981 --> 00:26:25,586 +Use Memory Barriers for cases where +it's desired to synchronize within a pass. + +431 +00:26:25,619 --> 00:26:28,255 +Barriers are a fast primitive +in most cases, + +432 +00:26:28,288 --> 00:26:32,226 +such as concurrent compute passes, +and vertex stages between draw calls. + +433 +00:26:32,259 --> 00:26:36,230 +But friendly reminder, use a Fence +between passes instead of a barrier + +434 +00:26:36,263 --> 00:26:38,765 +for synchronizing +after the fragment stages, + +435 +00:26:38,799 --> 00:26:42,936 +because these barriers are very expensive +and Apple GPUs don't allow it. + +436 +00:26:44,071 --> 00:26:47,741 +Using untracked resources +and manual fine-grained tracking, + +437 +00:26:47,774 --> 00:26:51,712 +you can now have +all the advantages of data aggregation, + +438 +00:26:51,745 --> 00:26:55,782 +while maximizing GPU parallelism. + +439 +00:26:55,816 --> 00:26:59,186 +And those are the performance tips +to get the most out of the CPU + +440 +00:26:59,219 --> 00:27:02,923 +and GPU when going bindless. + +441 +00:27:02,956 --> 00:27:05,359 +I've talked a lot about how Metal 3 +unlocks simplified + +442 +00:27:05,392 --> 00:27:07,194 +and efficient bindless workflows. + +443 +00:27:07,227 --> 00:27:09,429 +But writing code is only half +the equation. + +444 +00:27:09,463 --> 00:27:12,266 +The other half is how the available tools +can help you verify + +445 +00:27:12,299 --> 00:27:15,002 +how the GPU sees and executes work. + +446 +00:27:15,035 --> 00:27:16,570 +I am now going to hand it over to Mayur + +447 +00:27:16,603 --> 00:27:19,806 +to talk about what's new +with Metal 3 tooling for bindless. + +448 +00:27:19,840 --> 00:27:22,576 +Mayur: Thank you Alè. + +449 +00:27:22,609 --> 00:27:26,046 +Today, I'm excited to show you +some of the great new features + +450 +00:27:26,079 --> 00:27:31,752 +in the Metal Debugger that will help you +debug and optimize your bindless Apps. + +451 +00:27:31,785 --> 00:27:34,788 +I just took a frame capture +of the HybridRendering app + +452 +00:27:34,821 --> 00:27:37,191 +that Alè just showed you. + +453 +00:27:37,224 --> 00:27:39,660 +When you capture a frame in +the Metal Debugger, + +454 +00:27:39,693 --> 00:27:41,795 +you'll arrive to the Summary page, + +455 +00:27:41,828 --> 00:27:44,698 +which provides you +with an overview of your frame + +456 +00:27:44,731 --> 00:27:50,037 +alongside helpful insights on how +to improve your app's performance. + +457 +00:27:50,070 --> 00:27:54,308 +But today, I’m excited to show you +the new dependency viewer. + +458 +00:27:54,341 --> 00:28:00,347 +To open it, just click on +Dependencies here on the left. + +459 +00:28:00,380 --> 00:28:02,683 +Here is the new dependency viewer + +460 +00:28:02,716 --> 00:28:08,088 +and it features a brand new design +that’s packed with powerful new features. + +461 +00:28:08,121 --> 00:28:12,593 +The dependency viewer shows you +a graph-based representation + +462 +00:28:12,626 --> 00:28:14,928 +of your workload. + +463 +00:28:14,962 --> 00:28:18,866 +Each node in the graph represents a pass, + +464 +00:28:18,899 --> 00:28:25,439 +which is encoded by a command encoder, +and its output resources. + +465 +00:28:25,472 --> 00:28:31,245 +The edges represent resource dependencies +between passes. + +466 +00:28:31,278 --> 00:28:34,381 +New this year, +you can analyze your workload + +467 +00:28:34,414 --> 00:28:37,751 +by focusing on two types of dependencies. + +468 +00:28:37,784 --> 00:28:40,387 +data flow and synchronization. + +469 +00:28:40,420 --> 00:28:43,690 +The solid lines represent data flow + +470 +00:28:43,724 --> 00:28:47,294 +and they show you +how data flows in your app. + +471 +00:28:47,327 --> 00:28:52,199 +The dotted lines represent synchronization +and they show you dependencies + +472 +00:28:52,232 --> 00:28:57,738 +that introduce GPU synchronization +between passes. + +473 +00:28:57,771 --> 00:29:03,343 +To learn more, you can click on +any encoder, resource, or edge, + +474 +00:29:03,377 --> 00:29:09,283 +and the debugger will show you a lot of +detailed information in the new sidebar. + +475 +00:29:09,316 --> 00:29:12,886 +For example, +this edge adds synchronization + +476 +00:29:12,920 --> 00:29:17,391 +and also has data flow +between these passes. + +477 +00:29:17,424 --> 00:29:21,295 +By default, the dependency viewer +shows both data flow + +478 +00:29:21,328 --> 00:29:23,964 +and synchronization dependencies, + +479 +00:29:23,997 --> 00:29:27,501 +but you can use this menu +down here at the bottom + +480 +00:29:27,534 --> 00:29:31,238 +to focus on +just one of the dependency types. + +481 +00:29:31,271 --> 00:29:36,977 +Here, I will focus on just +synchronization. + +482 +00:29:37,010 --> 00:29:41,281 +As Alè said earlier, +false sharing is a common problem + +483 +00:29:41,315 --> 00:29:45,853 +when reading and writing +different resources from a tracked heap. + +484 +00:29:45,886 --> 00:29:50,424 +The dependency viewer makes it easy +to catch these issues. + +485 +00:29:50,457 --> 00:29:54,027 +This demo I captured is from +an early development version + +486 +00:29:54,061 --> 00:29:57,097 +that has this issue. + +487 +00:29:57,130 --> 00:30:02,069 +If I click on this heap, +the dependency viewer shows me + +488 +00:30:02,102 --> 00:30:04,438 +that this heap is tracked + +489 +00:30:04,471 --> 00:30:09,910 +and therefore adds synchronization +between these two passes. + +490 +00:30:09,943 --> 00:30:16,016 +The dependency viewer also highlights +the resources allocated inside the heap, + +491 +00:30:16,049 --> 00:30:21,054 +such as this render target texture +that the render encoder stores, + +492 +00:30:21,088 --> 00:30:26,593 +and a buffer that the compute encoder +reads and writes to. + +493 +00:30:26,627 --> 00:30:32,633 +The problem is this synchronization +between these two passes is not needed + +494 +00:30:32,666 --> 00:30:39,139 +because the compute encoder is not using +any resources from previous encoders. + +495 +00:30:39,173 --> 00:30:44,545 +To remove this dependency, I can modify +the app to use an untracked heap + +496 +00:30:44,578 --> 00:30:48,615 +and insert Fences +where synchronization is needed. + +497 +00:30:48,649 --> 00:30:55,022 +With that change, +these two passes can now run in parallel. + +498 +00:30:55,055 --> 00:30:58,192 +Another great improvement in Xcode 14 + +499 +00:30:58,225 --> 00:31:02,729 +to help debug your bindless apps +is the new resource list. + +500 +00:31:02,763 --> 00:31:09,002 +I can navigate to a draw call +that I want to debug and open it. + +501 +00:31:09,036 --> 00:31:13,373 +When using bindless, +hundreds or even thousands of resources + +502 +00:31:13,407 --> 00:31:17,144 +are available to the GPU at any time. + +503 +00:31:17,177 --> 00:31:20,714 +This year, the Metal debugger gives you +the ability + +504 +00:31:20,747 --> 00:31:24,585 +to check which of these resources +a draw call accessed, + +505 +00:31:24,618 --> 00:31:29,189 +just by clicking on the +"Accessed" mode at the top. + +506 +00:31:29,223 --> 00:31:33,594 +Now the debugger shows me +only the handful of resources + +507 +00:31:33,627 --> 00:31:40,267 +that this draw call accesses +and the type of each access. + +508 +00:31:40,300 --> 00:31:46,173 +This is really useful for understanding +what resources your shader has accessed + +509 +00:31:46,206 --> 00:31:48,675 +from argument buffers. + +510 +00:31:48,709 --> 00:31:53,213 +Knowing what resources +your draw call uses is great, + +511 +00:31:53,247 --> 00:31:56,550 +but if it shows resources +that you weren’t expecting, + +512 +00:31:56,583 --> 00:32:01,321 +you can use the shader debugger +to figure out what’s going on. + +513 +00:32:01,355 --> 00:32:03,223 +To start the shader debugger, + +514 +00:32:03,257 --> 00:32:07,694 +just click on the debug button here +in the bottom bar, + +515 +00:32:07,728 --> 00:32:11,465 +select the pixel that you want to debug, + +516 +00:32:11,498 --> 00:32:15,102 +and hit the Debug button. + +517 +00:32:15,135 --> 00:32:19,072 +And now you are in the shader debugger. + +518 +00:32:19,106 --> 00:32:24,912 +The shader debugger shows +how your code was executed line by line, + +519 +00:32:24,945 --> 00:32:29,149 +including which resources were accessed. + +520 +00:32:29,183 --> 00:32:35,122 +For these lines, this shader reads +textures from an argument buffer. + +521 +00:32:35,155 --> 00:32:39,026 +I can expand the detailed views +on the right sidebar + +522 +00:32:39,059 --> 00:32:43,730 +to check which resources were read. + +523 +00:32:43,764 --> 00:32:47,835 +This can help identify issues +where your shader accesses + +524 +00:32:47,868 --> 00:32:51,605 +the wrong argument buffer element. + +525 +00:32:51,638 --> 00:32:56,009 +In this demo, I’ve shown you +how to use the new dependency viewer + +526 +00:32:56,043 --> 00:32:59,446 +to analyze and validate +resource dependencies, + +527 +00:32:59,479 --> 00:33:03,717 +how to use the new resource list +to understand what resources + +528 +00:33:03,750 --> 00:33:05,686 +a draw call accessed, + +529 +00:33:05,719 --> 00:33:09,690 +and how to use the shader debugger +to analyze, line by line, + +530 +00:33:09,723 --> 00:33:12,159 +how a shader was executed. + +531 +00:33:12,192 --> 00:33:15,262 +I can’t wait to see how you use +these new features + +532 +00:33:15,295 --> 00:33:17,931 +to create great Metal bindless apps. + +533 +00:33:17,965 --> 00:33:19,666 +Back to you Alè. + +534 +00:33:19,700 --> 00:33:23,003 +Alè: Thank you, Mayur. +That was an awesome demo. + +535 +00:33:23,036 --> 00:33:27,474 +To wrap up, Metal 3 brings a lot +to the table for going bindless. + +536 +00:33:27,508 --> 00:33:29,743 +With simplified argument buffer encoding, + +537 +00:33:29,776 --> 00:33:31,812 +acceleration structures from heaps, + +538 +00:33:31,845 --> 00:33:34,715 +improvements to the validation layer +and tools, + +539 +00:33:34,748 --> 00:33:36,650 +Metal 3 is an excellent API + +540 +00:33:36,683 --> 00:33:40,654 +to bring effective and performant bindless +to your games and apps. + +541 +00:33:40,687 --> 00:33:45,225 +With this year's enhancements, the hybrid +rendering app is looking better than ever. + +542 +00:33:45,259 --> 00:33:47,461 +We are releasing +this updated version of the app + +543 +00:33:47,494 --> 00:33:51,098 +with full source code +in the Metal sample code gallery. + +544 +00:33:51,131 --> 00:33:53,367 +You can download, study, and modify it, + +545 +00:33:53,400 --> 00:33:56,103 +and as an exercise, I challenge you to +take it even further + +546 +00:33:56,136 --> 00:33:59,306 +and add recursive reflections +to the mirror surfaces. + +547 +00:33:59,339 --> 00:34:01,074 +I can't wait to see what you do with it. + +548 +00:34:01,108 --> 00:34:04,845 +There has never been a better time +to go bindless with Metal 3. + +549 +00:34:04,878 --> 00:34:06,914 +Thank you for watching. + +550 +00:34:06,947 --> 00:34:09,950 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 10102 Target and optimize GPU binaries with Metal 3 en.srt b/eng/2022 Session 10102 Target and optimize GPU binaries with Metal 3 en.srt new file mode 100644 index 0000000..ef69acf --- /dev/null +++ b/eng/2022 Session 10102 Target and optimize GPU binaries with Metal 3 en.srt @@ -0,0 +1,1207 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:10,744 +Hello and welcome. + +3 +00:00:10,777 --> 00:00:13,747 +I'm Galo Avila, +engineering manager in GPU Software. + +4 +00:00:13,780 --> 00:00:15,782 +In this session, +Eylon and I are excited to share + +5 +00:00:15,816 --> 00:00:19,353 +how you can improve your app's GPU +binary generation with Metal 3. + +6 +00:00:19,386 --> 00:00:22,222 +First, I'll describe +how offline compilation can help you + +7 +00:00:22,256 --> 00:00:26,660 +reduce in app stutters, +first launch, and new level load times. + +8 +00:00:26,693 --> 00:00:30,364 +Then Eylon will explain how you can use +the optimize for size compiler option, + +9 +00:00:30,397 --> 00:00:34,067 +to tune code expanding transformations +and improve your compile times. + +10 +00:00:35,636 --> 00:00:39,873 +Offline compilation lets you move GPU +binary generation to project build time. + +11 +00:00:39,907 --> 00:00:43,443 +To fully understand the benefits adoption +can bring to your Metal application, + +12 +00:00:43,477 --> 00:00:47,948 +I'll start by reviewing the ways in which +you can already generate a GPU binary. + +13 +00:00:47,981 --> 00:00:52,920 +In your Metal app, GPU binary generation +happens both at build time and runtime. + +14 +00:00:52,953 --> 00:00:56,924 +For example, suppose you instantiate +a metal library from source. + +15 +00:00:56,957 --> 00:01:00,127 +This generates at app runtime +Apple's Intermediate Representation, + +16 +00:01:00,160 --> 00:01:02,129 +also known as AIR. + +17 +00:01:02,162 --> 00:01:04,298 +This can be a CPU intensive operation, + +18 +00:01:04,331 --> 00:01:05,699 +which you can move to app build time + +19 +00:01:05,732 --> 00:01:08,202 +by pre-compiling your source +to a Metal library, + +20 +00:01:08,235 --> 00:01:11,171 +and instantiating from a file instead. + +21 +00:01:11,205 --> 00:01:14,575 +Once your Metal library is in memory, +creating a Pipeline State Descriptor + +22 +00:01:14,608 --> 00:01:18,278 +containing state and functions +is a lightweight operation. + +23 +00:01:18,312 --> 00:01:20,280 +Until you create +your pipeline state object, + +24 +00:01:20,314 --> 00:01:22,850 +which can be another +CPU intensive operation, + +25 +00:01:22,883 --> 00:01:26,220 +where just-in-time +GPU binary generation takes place. + +26 +00:01:28,522 --> 00:01:32,759 +Since GPU binary generation at runtime +can be a CPU intensive operation, + +27 +00:01:32,793 --> 00:01:36,063 +Metal helps you speed up +pipeline state object creation. + +28 +00:01:36,096 --> 00:01:37,431 +When you instantiate a PSO, + +29 +00:01:37,464 --> 00:01:41,235 +Metal stores your GPU binaries +in its file system cache. + +30 +00:01:41,268 --> 00:01:43,337 +And every time a new PSO is created, + +31 +00:01:43,370 --> 00:01:46,373 +any newly generated functions are added. + +32 +00:01:46,406 --> 00:01:48,976 +So any previously generated binaries +that are referenced + +33 +00:01:49,009 --> 00:01:50,577 +will be loaded from the cache. + +34 +00:01:52,546 --> 00:01:56,316 +Metal also lets you explicitly control +when and where GPU binaries are cached + +35 +00:01:56,350 --> 00:01:58,519 +using Binary Archives. + +36 +00:01:58,552 --> 00:02:03,156 +Simply use a PSO descriptor +to cache a GPU binary in an archive, + +37 +00:02:03,190 --> 00:02:05,526 +as many times as you need. + +38 +00:02:05,559 --> 00:02:09,630 +Then your PSO creation becomes +a lightweight operation. + +39 +00:02:09,663 --> 00:02:11,965 +Binary archives enable +more flexible caching, + +40 +00:02:11,999 --> 00:02:14,668 +but they still have to be +generated at runtime. + +41 +00:02:14,701 --> 00:02:16,236 +In many cases, +what you really want + +42 +00:02:16,270 --> 00:02:18,438 +is to generate those archives +at build time, + +43 +00:02:18,472 --> 00:02:20,474 +and now you finally can. + +44 +00:02:20,507 --> 00:02:23,610 +With offline binary generation, +you specify a new artifact + +45 +00:02:23,644 --> 00:02:26,280 +at project build time +called Metal pipelines script, + +46 +00:02:26,313 --> 00:02:29,550 +along with Metal source +or a Metal library. + +47 +00:02:29,583 --> 00:02:32,152 +A pipelines script +is your compiler toolchain equivalent + +48 +00:02:32,186 --> 00:02:35,222 +to a collection of pipeline descriptors +in the API. + +49 +00:02:35,255 --> 00:02:38,659 +The output of your compilation process +is a binary archive. + +50 +00:02:38,692 --> 00:02:42,029 +No further GPU code generation +takes place at app runtime. + +51 +00:02:42,062 --> 00:02:46,600 +Merely load your binary archive built +offline to accelerate your PSO creation. + +52 +00:02:48,435 --> 00:02:52,339 +Offline compilation benefits your app +by reducing runtime CPU overhead, + +53 +00:02:52,372 --> 00:02:55,876 +which is at the core +of what makes Metal a low level API. + +54 +00:02:55,909 --> 00:03:00,681 +Further, adoption can improve your app's +experience in two noticeable ways. + +55 +00:03:00,714 --> 00:03:03,951 +First launch and new level load times +can become dramatically faster, + +56 +00:03:03,984 --> 00:03:07,454 +potentially resulting in greater +engagement and interaction. + +57 +00:03:07,487 --> 00:03:10,123 +Stutters or frame rate drops, +due to runtime compilation + +58 +00:03:10,157 --> 00:03:11,592 +can be removed at last, + +59 +00:03:11,625 --> 00:03:15,429 +without the memory +or CPU cost of pre-warming frames. + +60 +00:03:15,462 --> 00:03:18,465 +I'll explore these benefits +in more detail next. + +61 +00:03:19,499 --> 00:03:22,803 +Here you have your traditional +app runtime binary generation. + +62 +00:03:22,836 --> 00:03:25,706 +In this example, your app spends +roughly 2/3 of its time + +63 +00:03:25,739 --> 00:03:28,275 +compiling GPU binaries +behind a loading screen, + +64 +00:03:28,308 --> 00:03:31,011 +before you can begin interacting with it. + +65 +00:03:31,044 --> 00:03:35,616 +But with offline compilation your runtime +shader generation moves to app build time, + +66 +00:03:35,649 --> 00:03:38,018 +PSO creation happens +in a fraction of the time, + +67 +00:03:38,051 --> 00:03:40,187 +and you are able to interact +with your app sooner + +68 +00:03:40,220 --> 00:03:42,890 +instead of idling away at a load screen. + +69 +00:03:43,924 --> 00:03:47,694 +Offline compilation +also helps to reduce stutters. + +70 +00:03:47,728 --> 00:03:49,930 +With traditional +runtime binary generation, + +71 +00:03:49,963 --> 00:03:52,900 +you may have too many pipeline states +to create at load time, + +72 +00:03:52,933 --> 00:03:56,470 +so you might instead be creating +some just-in-time. + +73 +00:03:56,503 --> 00:03:59,139 +And when that happens, +you may experience frame drops + +74 +00:03:59,173 --> 00:04:02,476 +caused by compilation temporarily +interrupting your command encoding. + +75 +00:04:03,343 --> 00:04:05,979 +Offline compilation +removes those pesky bubbles, + +76 +00:04:06,013 --> 00:04:10,217 +because you can compile many more shaders +at app build time. + +77 +00:04:10,250 --> 00:04:12,352 +Next, I will share +a new developer workflow + +78 +00:04:12,386 --> 00:04:15,389 +to help you harness the benefits +of offline compilation. + +79 +00:04:16,590 --> 00:04:19,693 +In the following workflow you'll learn +how to use new toolchain features + +80 +00:04:19,726 --> 00:04:21,962 +to build GPU binaries offline. + +81 +00:04:21,995 --> 00:04:25,732 +I'll show you how to generate +your new pipelines script input artifact. + +82 +00:04:25,766 --> 00:04:29,703 +Then, how to invoke the toolchain +to generate a GPU binary. + +83 +00:04:29,736 --> 00:04:32,940 +A pipelines script artifact is +a JSON formatted description + +84 +00:04:32,973 --> 00:04:35,976 +of one or more API pipeline +state descriptors + +85 +00:04:36,009 --> 00:04:38,145 +and can be generated in many ways. + +86 +00:04:38,178 --> 00:04:41,181 +For example, +author them in your favorite JSON editor, + +87 +00:04:41,215 --> 00:04:46,520 +or Harvest them from binary archives +serialized during development and testing. + +88 +00:04:46,553 --> 00:04:50,324 +Here you have a snippet of Metal code that +generates a render pipeline descriptor + +89 +00:04:50,357 --> 00:04:54,528 +with state and functions +and its equivalent JSON representation. + +90 +00:04:54,561 --> 00:04:59,633 +First, your API metal library file +is specified as a libraries path property. + +91 +00:04:59,666 --> 00:05:04,471 +Then your API render descriptor function +names as render pipelines properties. + +92 +00:05:04,505 --> 00:05:07,241 +Lastly, other pipeline state, +like raster_sample_count + +93 +00:05:07,274 --> 00:05:11,111 +or pixel formats, +are also captured as script properties. + +94 +00:05:11,144 --> 00:05:15,082 +Look for further JSON schema details +in Metal's developer documentation. + +95 +00:05:16,917 --> 00:05:19,753 +You also may want to kickstart +JSON script generation, + +96 +00:05:19,786 --> 00:05:22,289 +and using the Metal runtime can help. + +97 +00:05:22,322 --> 00:05:24,525 +Simply generate +your binary archives at runtime, + +98 +00:05:24,558 --> 00:05:27,728 +and serialize them during your development +and testing process. + +99 +00:05:27,761 --> 00:05:31,098 +Now I'll show you how you can accomplish +this with the Metal API. + +100 +00:05:32,933 --> 00:05:34,968 +You begin the runtime harvesting process + +101 +00:05:35,002 --> 00:05:38,939 +by creating your pipeline descriptor +with state and functions, + +102 +00:05:38,972 --> 00:05:43,076 +adding it to an archive, +which generates GPU binary, + +103 +00:05:43,110 --> 00:05:45,779 +and serializing it +to the file system to import into + +104 +00:05:45,812 --> 00:05:47,981 +and load from your app's bundle. + +105 +00:05:48,015 --> 00:05:52,419 +The Metal 3 runtime stores your pipeline +descriptor alongside the GPU binary. + +106 +00:05:52,452 --> 00:05:55,622 +Now I'll show you how to extract them. + +107 +00:05:55,656 --> 00:05:58,292 +metal-source allows you +to extract your JSON pipelines script + +108 +00:05:58,325 --> 00:06:00,494 +from an existing archive. + +109 +00:06:00,527 --> 00:06:05,599 +This is handy for migrating your binary +generation from runtime to app build time. + +110 +00:06:05,632 --> 00:06:07,801 +Just invoke metal-source +with the flatbuffers + +111 +00:06:07,835 --> 00:06:10,137 +and output directory options. + +112 +00:06:10,170 --> 00:06:11,805 +The result is a pipelines script file, + +113 +00:06:11,839 --> 00:06:14,942 +which you can edit +to generate additional binaries. + +114 +00:06:14,975 --> 00:06:18,912 +Now, I'll show you +how to invoke the toolchain. + +115 +00:06:18,946 --> 00:06:22,983 +Generating a GPU binary from +an Xcode project build phase is easy. + +116 +00:06:23,016 --> 00:06:26,553 +Simply invoke metal as you would +from the terminal with your source, + +117 +00:06:26,587 --> 00:06:29,523 +pipelines script, and output file. + +118 +00:06:29,556 --> 00:06:32,326 +Your output metal library +now contains GPU binary, + +119 +00:06:32,359 --> 00:06:36,330 +and can be deployed across +any toolchain supported device. + +120 +00:06:36,363 --> 00:06:38,866 +And if instead of source, +you have a Metal library, + +121 +00:06:38,899 --> 00:06:42,569 +you can pass that to the toolchain too. + +122 +00:06:42,603 --> 00:06:45,072 +Generating a binary from +a pre-existing Metal library + +123 +00:06:45,105 --> 00:06:48,141 +is just as easy +with Metal translator tool. + +124 +00:06:48,175 --> 00:06:51,512 +Just call metal-tt as you would +in a terminal with your source, + +125 +00:06:51,545 --> 00:06:53,947 +pipelines script, and output file. + +126 +00:06:53,981 --> 00:06:56,550 +Your resulting Metal library +now contains GPU binary + +127 +00:06:56,583 --> 00:07:00,787 +for all toolchain supported devices. + +128 +00:07:00,821 --> 00:07:03,056 +Now that you know +how to create binaries offline, + +129 +00:07:03,090 --> 00:07:05,459 +I'll review how to load them. + +130 +00:07:05,492 --> 00:07:09,162 +Simply provide the binary URL +when creating an archive descriptor + +131 +00:07:09,196 --> 00:07:10,964 +and use it to instantiate an archive. + +132 +00:07:10,998 --> 00:07:12,299 +That's it! + +133 +00:07:12,332 --> 00:07:14,935 +For more information about +Metal's binary archive API, + +134 +00:07:14,968 --> 00:07:18,071 +please refer to our previous years' talks. + +135 +00:07:18,105 --> 00:07:21,275 +Finally, a note on how Metal handles +GPU binary compatibility + +136 +00:07:21,308 --> 00:07:25,145 +for offline generated artifacts. + +137 +00:07:25,179 --> 00:07:27,681 +To ensure your offline generated binaries +are forward compatible + +138 +00:07:27,714 --> 00:07:30,317 +with future OS versions and products. + +139 +00:07:30,350 --> 00:07:33,420 +Metal gracefully upgrades +your binary archives during OS updates + +140 +00:07:33,453 --> 00:07:34,688 +or at app install time. + +141 +00:07:34,721 --> 00:07:38,392 +It does so asynchronously, +and in the background. + +142 +00:07:38,425 --> 00:07:41,495 +I can't wait for you to harness +the benefits of offline compilation + +143 +00:07:41,528 --> 00:07:45,632 +to remove runtime stutters and reduce +first launch and new level load times. + +144 +00:07:45,666 --> 00:07:47,701 +Such improvements can be tangible +to others + +145 +00:07:47,734 --> 00:07:49,837 +and enhance their overall app experience. + +146 +00:07:49,870 --> 00:07:51,538 +Now, over to Eylon. + +147 +00:07:51,572 --> 00:07:53,507 +Eylon: Thanks, Galo. + +148 +00:07:53,540 --> 00:07:55,976 +Next, I'll introduce +the new compile option, + +149 +00:07:56,009 --> 00:07:58,278 +optimize for size. + +150 +00:07:58,312 --> 00:08:02,549 +The Metal compiler optimizes code +aggressively for runtime performance. + +151 +00:08:02,583 --> 00:08:05,919 +Some optimizations +expand the GPU program size, + +152 +00:08:05,953 --> 00:08:09,223 +which may introduce unexpected costs. + +153 +00:08:09,256 --> 00:08:12,292 +For example, +function inlining is an optimization + +154 +00:08:12,326 --> 00:08:14,828 +to avoid the overhead of a function call. + +155 +00:08:14,862 --> 00:08:19,099 +It works by inlining the body +of the called function into the call site. + +156 +00:08:19,132 --> 00:08:22,035 +This example kernel +doesn't look like a lot of code, + +157 +00:08:22,069 --> 00:08:23,670 +but after inlining, + +158 +00:08:23,704 --> 00:08:26,206 +it would contain a copy +of functions 'f' and 'g', + +159 +00:08:26,240 --> 00:08:29,176 +and potentially also of functions +called from 'f' and 'g', + +160 +00:08:29,209 --> 00:08:32,412 +such as helpers +and non-primitive library functions. + +161 +00:08:34,114 --> 00:08:36,817 +Another optimization is loop unrolling, + +162 +00:08:36,850 --> 00:08:39,052 +which inlines additional copies +of a loop body, + +163 +00:08:39,086 --> 00:08:41,388 +to expose parallelism across iterations + +164 +00:08:41,421 --> 00:08:44,591 +and to avoid branching overhead. + +165 +00:08:44,625 --> 00:08:48,562 +The compiler may unroll as few +as two iterations of the loop + +166 +00:08:48,595 --> 00:08:53,734 +or as many as all the iterations of a loop +that has fixed bounds. + +167 +00:08:53,767 --> 00:08:56,937 +When optimizations like these +create a very large program, + +168 +00:08:56,970 --> 00:09:00,073 +the compiler also has to spend +a lot more time compiling it, + +169 +00:09:00,107 --> 00:09:04,511 +and in some situations, +you may prefer to avoid those costs. + +170 +00:09:04,545 --> 00:09:10,684 +Xcode 14 introduces a new Metal +optimization mode: optimize for size. + +171 +00:09:10,717 --> 00:09:13,287 +This mode limits size-expanding +transformations, + +172 +00:09:13,320 --> 00:09:15,389 +such as inlining and loop unrolling, + +173 +00:09:15,422 --> 00:09:18,592 +when the compiler applies +performance optimizations. + +174 +00:09:18,625 --> 00:09:21,895 +The intended benefit +is to keep the GPU binary smaller, + +175 +00:09:21,929 --> 00:09:25,399 +and the compile time shorter, +in cases when default optimization + +176 +00:09:25,432 --> 00:09:27,534 +proves to be too expensive. + +177 +00:09:27,568 --> 00:09:29,203 +When optimizing for size, + +178 +00:09:29,236 --> 00:09:32,606 +it is possible for the result +to have lower runtime performance. + +179 +00:09:32,639 --> 00:09:35,275 +Whether that actually happens +depends on the program, + +180 +00:09:35,309 --> 00:09:39,713 +so you will need to try both +optimization modes and compare. + +181 +00:09:39,746 --> 00:09:43,650 +Optimize for size may not improve size +and compile time for all shaders. + +182 +00:09:43,684 --> 00:09:46,553 +It is most likely to have benefit +for large programs + +183 +00:09:46,587 --> 00:09:48,488 +with deep call paths and loops, + +184 +00:09:48,522 --> 00:09:50,958 +where inlining and unrolling are common. + +185 +00:09:50,991 --> 00:09:52,926 +The option is worth trying +whenever you encounter + +186 +00:09:52,960 --> 00:09:57,064 +an unexpectedly long compile time +from default optimization. + +187 +00:09:57,097 --> 00:10:02,236 +The option is available whether compiling +at project build time or at app runtime. + +188 +00:10:02,269 --> 00:10:06,206 +Here's a case where optimize for size +can make a difference. + +189 +00:10:06,240 --> 00:10:09,843 +Cycles is a an open source project +implementing a production renderer + +190 +00:10:09,877 --> 00:10:12,112 +for the Blender 3D design environment, + +191 +00:10:12,145 --> 00:10:15,148 +and was recently updated to support Metal. + +192 +00:10:15,182 --> 00:10:17,918 +Apple recently joined +the Blender Development Fund, + +193 +00:10:17,951 --> 00:10:21,688 +and one of the things we learned +was that Blender's path tracing algorithms + +194 +00:10:21,722 --> 00:10:25,425 +use large compute shaders, +with many helper functions and loops, + +195 +00:10:25,459 --> 00:10:28,328 +and its compile times +can add up to minutes. + +196 +00:10:28,362 --> 00:10:31,365 +It turns out those are exactly +the kind of shaders that can benefit + +197 +00:10:31,398 --> 00:10:34,334 +from Metal 3's +new optimize for size option. + +198 +00:10:36,136 --> 00:10:39,439 +When rendering these scenes +on an Apple Silicon GPU, + +199 +00:10:39,473 --> 00:10:42,776 +enabling optimize for size +improved Blender's Setup Time, + +200 +00:10:42,809 --> 00:10:48,215 +which includes compiling shader pipelines, +by up to 1.4x. + +201 +00:10:48,248 --> 00:10:52,953 +And it provided that speedup with +little or no degradation in Render Time. + +202 +00:10:52,986 --> 00:10:55,022 +Some renders slowed up to 4%, + +203 +00:10:55,055 --> 00:10:57,457 +and some did not slow at all. + +204 +00:10:57,491 --> 00:11:00,227 +So lower runtime performance is possible. + +205 +00:11:00,260 --> 00:11:04,531 +But in some cases, optimize for size +can also improve runtime performance. + +206 +00:11:04,565 --> 00:11:07,334 +Here's an example. + +207 +00:11:07,367 --> 00:11:10,204 +These are Render Time speedups +for the same scenes + +208 +00:11:10,237 --> 00:11:14,174 +from enabling optimize for size +on Intel GPUs. + +209 +00:11:14,208 --> 00:11:16,610 +The benefit was not just faster compiles, + +210 +00:11:16,643 --> 00:11:20,480 +but also some faster renders, +by up to 1.6x. + +211 +00:11:20,514 --> 00:11:21,481 +How? + +212 +00:11:21,515 --> 00:11:25,018 +Because a smaller GPU program +can avoid some of the runtime penalties + +213 +00:11:25,052 --> 00:11:26,620 +that come with large size: + +214 +00:11:26,653 --> 00:11:28,922 +it may enjoy +fewer instruction cache misses, + +215 +00:11:28,956 --> 00:11:30,757 +or need fewer registers, + +216 +00:11:30,791 --> 00:11:32,659 +which translates to fewer spills to memory + +217 +00:11:32,693 --> 00:11:35,295 +or even more threads in parallel. + +218 +00:11:35,329 --> 00:11:38,932 +Keep in mind, these results +are not typical of all shaders and scenes, + +219 +00:11:38,966 --> 00:11:40,868 +and a performance drop is possible. + +220 +00:11:40,901 --> 00:11:44,104 +For your project, you will need +to evaluate the actual impact + +221 +00:11:44,137 --> 00:11:47,107 +to both compile time +and runtime performance. + +222 +00:11:47,140 --> 00:11:50,677 +You can enable optimize for size +when compiling from Metal source, + +223 +00:11:50,711 --> 00:11:53,046 +in three different environments. + +224 +00:11:53,080 --> 00:11:55,349 +In the Xcode 14 user interface, + +225 +00:11:55,382 --> 00:11:58,352 +specify optimize for size +as a build setting. + +226 +00:11:58,385 --> 00:12:00,888 +Under "Metal Compiler - Build Options" + +227 +00:12:00,921 --> 00:12:03,290 +find the setting "Optimization Level". + +228 +00:12:03,323 --> 00:12:07,528 +Level "Default" optimizes for performance, +as Metal has done in the past. + +229 +00:12:07,561 --> 00:12:10,397 +Level "Size" enables optimize for size. + +230 +00:12:11,899 --> 00:12:14,268 +When compiling Metal source +by command line, + +231 +00:12:14,301 --> 00:12:18,238 +specify optimize for size +using option'-Os'. + +232 +00:12:18,272 --> 00:12:23,343 +The first example specifies the option +to a single compile-and-link command. + +233 +00:12:23,377 --> 00:12:25,946 +The second example +has two compile commands + +234 +00:12:25,979 --> 00:12:28,115 +and specifies the option +to just one of them + +235 +00:12:28,148 --> 00:12:30,551 +to enable it for only some shaders. + +236 +00:12:30,584 --> 00:12:32,920 +The option does not need to be passed +to the link command + +237 +00:12:32,953 --> 00:12:34,721 +or to any subsequent commands. + +238 +00:12:34,755 --> 00:12:38,125 +And you can use optimize for size +either with, or without, + +239 +00:12:38,158 --> 00:12:42,529 +generating a GPU binary using the commands +presented earlier in this talk. + +240 +00:12:44,398 --> 00:12:49,069 +Finally, when compiling Metal source +at app runtime with a Metal Framework API + +241 +00:12:49,102 --> 00:12:51,138 +such as 'newLibraryWithSource', + +242 +00:12:51,171 --> 00:12:54,942 +specify optimize for size +in a 'MTLCompileOptions' object + +243 +00:12:54,975 --> 00:12:57,811 +using property 'optimizationLevel'. + +244 +00:12:57,845 --> 00:13:02,316 +The optimization level +may be 'default' or 'size'. + +245 +00:13:02,349 --> 00:13:05,719 +I hope your project will benefit +from this new optimization mode + +246 +00:13:05,752 --> 00:13:07,988 +in the Metal compiler. + +247 +00:13:08,021 --> 00:13:10,657 +Galo: To wrap up, +I presented offline compilation, + +248 +00:13:10,691 --> 00:13:14,328 +a new workflow for generating GPU binaries +entirely at app build time, + +249 +00:13:14,361 --> 00:13:18,298 +to reduce in-app stutters, +first launch, and new level load times. + +250 +00:13:18,332 --> 00:13:20,934 +Eylon: Then I presented optimize for size, + +251 +00:13:20,968 --> 00:13:23,871 +a new Metal optimization mode +when compiling from source, + +252 +00:13:23,904 --> 00:13:27,541 +to reduce program size and compile time. + +253 +00:13:27,574 --> 00:13:29,710 +Galo: We hope these improvements +help your app or game + +254 +00:13:29,743 --> 00:13:31,578 +deliver an improved user experience. + +255 +00:13:31,612 --> 00:13:34,648 +Eylon: With shorter setup and load times, +fewer stutters, + +256 +00:13:34,681 --> 00:13:39,253 +and new workflows, +thanks to lower compile costs at runtime. + +257 +00:13:39,286 --> 00:13:41,221 +Thank you for watching. ♪ ♪ + diff --git a/eng/2022 Session 10103 Boost performance with MetalFX Upscaling en.srt b/eng/2022 Session 10103 Boost performance with MetalFX Upscaling en.srt new file mode 100644 index 0000000..ae616ce --- /dev/null +++ b/eng/2022 Session 10103 Boost performance with MetalFX Upscaling en.srt @@ -0,0 +1,2042 @@ +1 +00:00:00,334 --> 00:00:03,837 +♪ ♪ + +2 +00:00:10,043 --> 00:00:11,411 +Hello and welcome. + +3 +00:00:11,445 --> 00:00:14,948 +My name is Kelvin Chiu from +the GPU Software team here at Apple. + +4 +00:00:14,982 --> 00:00:17,985 +Today, I'll talk about how to +boost your Metal application performance + +5 +00:00:18,018 --> 00:00:20,087 +with MetalFX Upscaling. + +6 +00:00:20,721 --> 00:00:23,090 +MetalFX is a new API that provides + +7 +00:00:23,123 --> 00:00:26,627 +platform optimized graphics effects +for Metal applications. + +8 +00:00:27,361 --> 00:00:29,730 +It enables high performance upscaling, + +9 +00:00:29,763 --> 00:00:31,865 +which will boost +your application performance + +10 +00:00:31,899 --> 00:00:34,868 +while retaining its rendering quality. + +11 +00:00:34,902 --> 00:00:38,172 +Rendering a frame +at high resolution costs GPU time. + +12 +00:00:38,205 --> 00:00:41,041 +To reduce that time, +rendering at a lower resolution will + +13 +00:00:41,074 --> 00:00:42,910 +usually do the trick. + +14 +00:00:42,943 --> 00:00:45,879 +However, the tradeoff is +a lower rendering quality. + +15 +00:00:45,913 --> 00:00:47,748 +With MetalFX Upscaling, + +16 +00:00:47,781 --> 00:00:49,716 +your application can now render frames + +17 +00:00:49,750 --> 00:00:52,686 +at a lower resolution, +reducing rendering time, + +18 +00:00:52,719 --> 00:00:55,923 +without compromising rendering quality. + +19 +00:00:55,956 --> 00:00:58,292 +MetalFX Upscaling is optimized + +20 +00:00:58,325 --> 00:01:00,928 +to run best on Apple devices. + +21 +00:01:00,961 --> 00:01:04,364 +and it is also easy +to adopt in your game. + +22 +00:01:04,398 --> 00:01:07,000 +MetalFX provides +two upscaling effects, + +23 +00:01:07,034 --> 00:01:09,236 +which I will describe in detail. + +24 +00:01:09,269 --> 00:01:11,338 +Spatial upscaling is simple to use + +25 +00:01:11,371 --> 00:01:13,674 +and gives a great performance boost. + +26 +00:01:14,541 --> 00:01:17,611 +Temporal anti-aliasing +and upscaling integrates information + +27 +00:01:17,644 --> 00:01:20,914 +from multiple frames +to produce a higher quality output. + +28 +00:01:22,216 --> 00:01:24,418 +I will then talk about best practices + +29 +00:01:24,451 --> 00:01:27,020 +for using these effects. + +30 +00:01:27,054 --> 00:01:29,022 +Finally, I will end the session + +31 +00:01:29,056 --> 00:01:31,625 +with demos showing them in action. + +32 +00:01:32,893 --> 00:01:35,329 +Let's start with Spatial upscaling. + +33 +00:01:36,096 --> 00:01:37,865 +MetalFX Spatial upscaling + +34 +00:01:37,898 --> 00:01:40,167 +analyzes the input’s spatial information + +35 +00:01:40,200 --> 00:01:43,003 +to produce new, upscaled samples. + +36 +00:01:43,036 --> 00:01:45,506 +Integrating Spatial upscaling is simple. + +37 +00:01:45,539 --> 00:01:48,475 +It only requires anti-aliased +input color to produce + +38 +00:01:48,509 --> 00:01:51,445 +a spatially upscaled color output. + +39 +00:01:51,478 --> 00:01:54,248 +In a typical game rendering pipeline, + +40 +00:01:54,281 --> 00:01:57,484 +there are various rendering passes +including anti-aliased render + +41 +00:01:57,518 --> 00:02:00,721 +and various post processing effects. + +42 +00:02:00,754 --> 00:02:02,623 +Add MetalFX spatial upscaling + +43 +00:02:02,656 --> 00:02:04,725 +right after +the game's tone-mapping process + +44 +00:02:04,758 --> 00:02:05,792 +is completed. + +45 +00:02:05,826 --> 00:02:08,595 +It will perform best if the input +has been tone mapped and is + +46 +00:02:08,629 --> 00:02:10,497 +in a perceptual color space. + +47 +00:02:10,531 --> 00:02:13,634 +Let’s checkout MetalFX +spatial upscaling in action. + +48 +00:02:15,135 --> 00:02:18,505 +This chess scene is produced +with a high quality reference renderer + +49 +00:02:18,539 --> 00:02:20,140 +in 4K resolution. + +50 +00:02:20,174 --> 00:02:23,076 +It is path traced, +with complex graphics effects, + +51 +00:02:23,110 --> 00:02:26,413 +like ray-traced reflections and shadows. + +52 +00:02:26,446 --> 00:02:28,882 +Here is a side by side comparison, + +53 +00:02:28,916 --> 00:02:31,451 +with 540p input on the left... + +54 +00:02:32,553 --> 00:02:36,023 +…and 1080p output with MetalFX +spatial upscaling on the right. + +55 +00:02:38,926 --> 00:02:41,595 +If I zoom in on the queen, on the left, + +56 +00:02:41,628 --> 00:02:44,965 +the image lacks details +and is low resolution. + +57 +00:02:44,998 --> 00:02:47,568 +On the right, +the spatially upscaled output + +58 +00:02:47,601 --> 00:02:50,938 +has sharper reflections +and more refined edges. + +59 +00:02:52,439 --> 00:02:55,943 +Next, I'll walk you through how +to implement MetalFX spatial upscaling. + +60 +00:02:57,044 --> 00:03:00,080 +In Metal, you would +normally create a command encoder + +61 +00:03:00,113 --> 00:03:02,382 +to encode commands into a command buffer + +62 +00:03:02,416 --> 00:03:04,885 +and produce input for the effect. + +63 +00:03:04,918 --> 00:03:08,388 +Similarly, +you can create a MetalFX effect object + +64 +00:03:08,422 --> 00:03:11,758 +to encode commands into a command buffer +and perform the effect. + +65 +00:03:12,226 --> 00:03:15,362 +Finally, create a third command encoder +to encode commands + +66 +00:03:15,395 --> 00:03:17,965 +that make use of the MetalFX output. + +67 +00:03:17,998 --> 00:03:20,634 +You should only create +a new spatial scaler object + +68 +00:03:20,667 --> 00:03:22,302 +when your application first starts + +69 +00:03:22,336 --> 00:03:24,505 +or when a display switches resolutions + +70 +00:03:24,538 --> 00:03:26,507 +because they are expensive to create. + +71 +00:03:27,541 --> 00:03:29,676 +First, create and configure + +72 +00:03:29,710 --> 00:03:32,846 +a MTLFXSpatialScalerDescriptor. + +73 +00:03:32,880 --> 00:03:34,948 +Then, create a scaler object + +74 +00:03:34,982 --> 00:03:37,818 +by calling the makeSpatialScaler() method. + +75 +00:03:37,851 --> 00:03:39,720 +In the initialization code, + +76 +00:03:39,753 --> 00:03:41,822 +start with the descriptor. + +77 +00:03:41,855 --> 00:03:43,290 +Fill both the width and height + +78 +00:03:43,323 --> 00:03:45,559 +of the input and output texture. + +79 +00:03:45,592 --> 00:03:48,662 +Then, set the texture format +for the textures that will be set later + +80 +00:03:48,695 --> 00:03:50,597 +on the scaler object. + +81 +00:03:50,631 --> 00:03:53,200 +Set the color processing mode. + +82 +00:03:53,233 --> 00:03:55,435 +This tells the API which color space + +83 +00:03:55,469 --> 00:03:57,804 +the input and output is in. + +84 +00:03:57,838 --> 00:04:00,474 +You can set the value +to be in either perceptual, + +85 +00:04:00,507 --> 00:04:03,310 +linear, or HDR color space. + +86 +00:04:03,343 --> 00:04:06,647 +Once the descriptor is filled, +create the scaler object. + +87 +00:04:08,549 --> 00:04:11,985 +Once the scaler object is created, +you can modify the properties + +88 +00:04:12,019 --> 00:04:14,354 +of the object as frequently as you want + +89 +00:04:14,388 --> 00:04:17,491 +and call the encode() method +to start the upscaling process. + +90 +00:04:18,458 --> 00:04:21,628 +In your per frame draw code, +make sure the correct input + +91 +00:04:21,662 --> 00:04:24,331 +and output textures are being set +on the scaler object + +92 +00:04:24,364 --> 00:04:27,501 +before encoding the scaling effect +into the command buffer. + +93 +00:04:28,335 --> 00:04:31,839 +Spatial upscaling offers +a simple way to boost performance. + +94 +00:04:32,906 --> 00:04:35,209 +And when you want +even higher quality rendering, + +95 +00:04:35,242 --> 00:04:37,377 +that's where +MetalFX temporal anti-aliasing + +96 +00:04:37,411 --> 00:04:39,479 +and upscaling comes in. + +97 +00:04:39,513 --> 00:04:42,583 +Temporal AA and upscaling +is a technique that uses data + +98 +00:04:42,616 --> 00:04:45,819 +from previous frames to produce +high-quality upscaled output. + +99 +00:04:46,286 --> 00:04:49,089 +This means, the output of upscaling +from the previous frame + +100 +00:04:49,122 --> 00:04:52,559 +will be used as one of the inputs +for the current frame upscaling. + +101 +00:04:53,961 --> 00:04:57,364 +To better understand why Temporal AA +and upscaling requires data + +102 +00:04:57,397 --> 00:04:58,565 +from the previous frames, + +103 +00:04:58,599 --> 00:05:01,368 +I'll first review the concept +of supersampling. + +104 +00:05:02,536 --> 00:05:03,937 +In supersampling, + +105 +00:05:03,971 --> 00:05:06,707 +multiple samples are calculated per pixel, + +106 +00:05:06,740 --> 00:05:09,877 +which is then integrated +into a single pixel value. + +107 +00:05:09,910 --> 00:05:12,279 +The more samples +that we integrate per pixel, + +108 +00:05:12,312 --> 00:05:14,548 +the better the result will be. + +109 +00:05:14,581 --> 00:05:16,783 +However, it comes at a great cost + +110 +00:05:16,817 --> 00:05:18,652 +to calculate multiple samples per pixel + +111 +00:05:18,685 --> 00:05:21,121 +within a single frame. + +112 +00:05:21,154 --> 00:05:23,524 +Instead of sampling multiple locations + +113 +00:05:23,557 --> 00:05:25,292 +per pixel in a single frame, + +114 +00:05:25,325 --> 00:05:27,995 +you can perform temporal sampling. + +115 +00:05:28,028 --> 00:05:30,430 +Temporal sampling +is the concept of rendering + +116 +00:05:30,464 --> 00:05:31,865 +a different sample location + +117 +00:05:31,899 --> 00:05:34,368 +for all the pixels in a given frame. + +118 +00:05:34,401 --> 00:05:37,004 +This enables you to achieve +supersampling quality + +119 +00:05:37,037 --> 00:05:40,507 +over multiple frames +at a significantly lower cost. + +120 +00:05:41,308 --> 00:05:44,378 +By accumulating samples +from multiple frames and accounting + +121 +00:05:44,411 --> 00:05:46,113 +for sample positions, + +122 +00:05:46,146 --> 00:05:47,948 +temporal AA & upscaling can + +123 +00:05:47,981 --> 00:05:49,683 +integrate samples appropriately + +124 +00:05:49,716 --> 00:05:51,919 +in target resolution pixels, + +125 +00:05:52,052 --> 00:05:55,556 +resulting in a high quality +anti-aliased upscaled output. + +126 +00:05:56,323 --> 00:05:59,760 +However, since content +often changes between frames, + +127 +00:05:59,793 --> 00:06:02,963 +it will require more input data +to detect these changes. + +128 +00:06:03,931 --> 00:06:06,133 +Besides the previous frame output, + +129 +00:06:06,166 --> 00:06:08,569 +Temporal AA and upscaling also requires + +130 +00:06:08,602 --> 00:06:10,871 +a color input that is jittered, + +131 +00:06:10,904 --> 00:06:14,341 +as well as motion +and depth data from the scene. + +132 +00:06:14,942 --> 00:06:18,245 +I'll go through each of them +to explain why they are required. + +133 +00:06:19,613 --> 00:06:21,982 +Let's start with the jittered color input. + +134 +00:06:23,383 --> 00:06:26,820 +Here is a red triangle rendered +without jitter. + +135 +00:06:26,854 --> 00:06:30,123 +The bright white outline represents +the triangle being rendered. + +136 +00:06:30,757 --> 00:06:33,760 +Each one of the small squares +represent a pixel. + +137 +00:06:33,794 --> 00:06:37,130 +And the gray dot in the middle +is where the pixel is sampled. + +138 +00:06:38,999 --> 00:06:40,868 +This is the output of the same triangle + +139 +00:06:40,901 --> 00:06:43,270 +when rendered with a small jitter. + +140 +00:06:43,303 --> 00:06:46,573 +The gray dots show +the sampling location for a given pixel. + +141 +00:06:47,174 --> 00:06:50,377 +The jitter offset should be unique +for a set number of frames + +142 +00:06:50,410 --> 00:06:53,380 +in order to fully gather +the desired number of samples. + +143 +00:06:54,147 --> 00:06:57,451 +I will cover the topic +of jitter sequence in detail later. + +144 +00:06:58,685 --> 00:07:01,889 +Next is the motion information +from the scene. + +145 +00:07:01,922 --> 00:07:03,824 +Motion data from the scene indicates + +146 +00:07:03,857 --> 00:07:05,559 +how much and which direction + +147 +00:07:05,592 --> 00:07:09,096 +the objects had moved +from the previous frame. + +148 +00:07:09,129 --> 00:07:12,566 +Temporal AA and upscaling uses +the motion information to back track + +149 +00:07:12,599 --> 00:07:15,102 +and find corresponding locations +in the previous frame + +150 +00:07:15,135 --> 00:07:17,437 +in order to correctly gather samples. + +151 +00:07:18,739 --> 00:07:21,842 +Another input is the depth information +from the scene. + +152 +00:07:22,476 --> 00:07:23,744 +Depth data from the scene + +153 +00:07:23,777 --> 00:07:26,914 +indicates what's in the foreground +and what's in the background. + +154 +00:07:26,947 --> 00:07:30,217 +This is important when prioritizing +foreground edge anti-aliasing + +155 +00:07:30,651 --> 00:07:33,887 +and provides clues on what +other objects might be newly exposed + +156 +00:07:33,921 --> 00:07:37,257 +when gathering samples +from previous frames. + +157 +00:07:37,291 --> 00:07:40,627 +The last piece of input data is +the previous frame’s output. + +158 +00:07:42,729 --> 00:07:45,365 +The previous frame’s output contains all + +159 +00:07:45,399 --> 00:07:48,836 +of the samples that have been +integrated previously, + +160 +00:07:48,869 --> 00:07:52,139 +and it will be blended with +the current frame’s jittered color input + +161 +00:07:52,172 --> 00:07:55,409 +in order to increase the number +of samples per pixel. + +162 +00:07:56,176 --> 00:07:59,513 +By combining information +from both the current and previous frame, + +163 +00:07:59,546 --> 00:08:02,649 +the resulting image now has more details. + +164 +00:08:02,683 --> 00:08:05,552 +MetalFX keeps track +of the upscaled output, + +165 +00:08:05,586 --> 00:08:07,521 +so you only need to pass the color, + +166 +00:08:07,554 --> 00:08:10,490 +motion, and depth +from the current rendered frame. + +167 +00:08:11,191 --> 00:08:13,861 +Going back to a typical +game’s rendering pipeline, + +168 +00:08:13,894 --> 00:08:16,697 +MetalFX temporal AA +and upscaling should run + +169 +00:08:16,730 --> 00:08:18,832 +before any post processing effects, + +170 +00:08:18,866 --> 00:08:20,834 +since these effects will interfere + +171 +00:08:20,868 --> 00:08:23,103 +with the result of the upscaling. + +172 +00:08:24,371 --> 00:08:26,306 +Here's the chess rendering again, + +173 +00:08:26,340 --> 00:08:29,810 +this time using +MetalFX Temporal AA and upscaling. + +174 +00:08:29,843 --> 00:08:33,347 +This is a side by side comparison +of 1080p input on the left + +175 +00:08:34,248 --> 00:08:36,583 +and 4K upscaled output on the right. + +176 +00:08:41,488 --> 00:08:44,258 +Zooming in closer to the queen, + +177 +00:08:44,291 --> 00:08:47,361 +the input is low resolution and aliased, + +178 +00:08:47,394 --> 00:08:50,430 +while the temporally upscaled output +on the right is high resolution + +179 +00:08:50,464 --> 00:08:53,934 +with a smooth outline and has +more fine details in the reflections. + +180 +00:08:55,068 --> 00:08:58,372 +Just as with spatial scaler, +creating a new temporal scaler + +181 +00:08:58,405 --> 00:09:00,440 +is expensive and should only be done + +182 +00:09:00,474 --> 00:09:02,209 +when your application first starts + +183 +00:09:02,242 --> 00:09:05,312 +or when a display switches resolutions. + +184 +00:09:05,345 --> 00:09:07,247 +First, you'll need to allocate and fill in + +185 +00:09:07,281 --> 00:09:10,117 +a MTLFXTemporalScalerDescriptor. + +186 +00:09:10,851 --> 00:09:14,354 +Then call makeTemporalScaler() method +to create the scaler object. + +187 +00:09:15,656 --> 00:09:19,092 +In your initialization code, +start with the descriptor. + +188 +00:09:19,726 --> 00:09:23,230 +Fill in both the width and height +of the input and output textures. + +189 +00:09:23,897 --> 00:09:25,632 +Then set the jittered color, + +190 +00:09:25,666 --> 00:09:27,501 +depth, and motion texture formats + +191 +00:09:27,534 --> 00:09:30,971 +for the textures that will be bound later +on the scaler object as inputs. + +192 +00:09:32,339 --> 00:09:35,042 +Finally, set the format +for the output texture + +193 +00:09:35,075 --> 00:09:38,378 +where MetalFX will store +the upscaled output. + +194 +00:09:38,412 --> 00:09:41,515 +Once the descriptor is filled, +create the scaler object. + +195 +00:09:42,482 --> 00:09:45,686 +On the scaler object, +set the motion scale properties. + +196 +00:09:45,719 --> 00:09:49,223 +This helps you scale the app's motion data +to what the API expects. + +197 +00:09:50,057 --> 00:09:51,992 +MetalFX expects motion data + +198 +00:09:52,025 --> 00:09:54,661 +in the render resolution pixel space + +199 +00:09:54,695 --> 00:09:57,497 +with direction that goes +from the current frame’s position + +200 +00:09:57,531 --> 00:09:59,733 +to the previous frame’s position. + +201 +00:09:59,766 --> 00:10:01,034 +As an example, + +202 +00:10:01,068 --> 00:10:03,637 +I'll use a render resolution of 1080p. + +203 +00:10:03,670 --> 00:10:06,707 +Suppose you have an object +that moves from clip space coordinate + +204 +00:10:06,740 --> 00:10:09,243 +(-0.75, -0.75). + +205 +00:10:09,276 --> 00:10:12,346 +to clip space coordinate (0.25, 0.25). + +206 +00:10:12,913 --> 00:10:15,582 +The motion data is stored as (1, 1). + +207 +00:10:17,150 --> 00:10:19,119 +Set the motion vector scale property + +208 +00:10:19,152 --> 00:10:21,021 +to (-960, 540) + +209 +00:10:21,054 --> 00:10:24,358 +so that MetalFX can interpret +your game's motion data correctly. + +210 +00:10:26,660 --> 00:10:29,530 +You can modify the properties +of the scaler object as frequently + +211 +00:10:29,563 --> 00:10:30,464 +as you want. + +212 +00:10:30,497 --> 00:10:33,934 +And call the encode() method +to start the upscaling process. + +213 +00:10:35,402 --> 00:10:37,304 +For your per frame draw code, + +214 +00:10:37,337 --> 00:10:39,606 +first set the resetHistory property. + +215 +00:10:39,640 --> 00:10:42,476 +Set this to true when your application +loads the first frame + +216 +00:10:42,509 --> 00:10:44,745 +or when there is a scene cut. + +217 +00:10:44,778 --> 00:10:48,148 +Then set the textures +that are inputs for the effect, + +218 +00:10:48,182 --> 00:10:50,984 +followed by the output texture. + +219 +00:10:51,018 --> 00:10:53,253 +Next, set the reversedDepth property + +220 +00:10:53,287 --> 00:10:56,323 +to indicate whether the depth values are +in reversed-Z mapping. + +221 +00:10:57,558 --> 00:10:58,825 +The last property to set + +222 +00:10:58,859 --> 00:11:02,062 +before encoding the scaling effect +is the current jitter offset. + +223 +00:11:03,263 --> 00:11:05,465 +Getting jitter offset correctly +is essential + +224 +00:11:05,499 --> 00:11:06,967 +to the quality of the output. + +225 +00:11:07,000 --> 00:11:10,103 +Let us take a quick look +on how to set jitter offset. + +226 +00:11:11,371 --> 00:11:14,775 +As an example, on the left +is a triangle rendered with jitter. + +227 +00:11:16,376 --> 00:11:19,379 +On the right +is a zoomed in view of a pixel. + +228 +00:11:19,413 --> 00:11:22,716 +The sample is located at (0.625, 0.78). + +229 +00:11:23,150 --> 00:11:26,153 +The pixel center is represented +by the orange dot. + +230 +00:11:26,186 --> 00:11:28,889 +It is located at (0.5, 0.5). + +231 +00:11:30,357 --> 00:11:31,425 +In this example, + +232 +00:11:31,458 --> 00:11:34,895 +the jitter offset is (-0.125, -0.28). + +233 +00:11:35,796 --> 00:11:37,431 +Note that jitter offsets are always + +234 +00:11:37,464 --> 00:11:40,634 +in the range of -0.5 to 0.5. + +235 +00:11:40,667 --> 00:11:43,670 +To verify that you are providing +the correct jitter offset, + +236 +00:11:43,704 --> 00:11:46,340 +render a scene +without camera and object motion + +237 +00:11:46,373 --> 00:11:49,343 +using a sequence +of different jitter offsets. + +238 +00:11:49,376 --> 00:11:51,445 +On the left is an example + +239 +00:11:51,478 --> 00:11:53,881 +when incorrect jitter offset is specified. + +240 +00:11:53,914 --> 00:11:57,050 +Static objects could shift, +and fine lines are fuzzy. + +241 +00:11:57,718 --> 00:11:59,453 +On the right is the output + +242 +00:11:59,486 --> 00:12:01,688 +when correct jitter offset is specified. + +243 +00:12:01,722 --> 00:12:03,056 +Objects stay in place, + +244 +00:12:03,090 --> 00:12:05,626 +and fine lines are resolved progressively. + +245 +00:12:05,659 --> 00:12:08,328 +The MetalFX +“temporal AA and upscaling” effect + +246 +00:12:08,362 --> 00:12:09,930 +boosts your application performance + +247 +00:12:09,963 --> 00:12:13,033 +and gives an upscaling quality +that’s comparable to the quality + +248 +00:12:13,066 --> 00:12:16,069 +of the native target resolution rendering. + +249 +00:12:16,103 --> 00:12:18,672 +In order to get optimal quality +and performance + +250 +00:12:18,705 --> 00:12:20,607 +when using both upscaling effects, + +251 +00:12:20,641 --> 00:12:24,011 +l’ll now cover +implementation best practices. + +252 +00:12:25,245 --> 00:12:28,081 +Starting with spatial upscaling. + +253 +00:12:28,115 --> 00:12:30,584 +For the best spatial upscaling quality, + +254 +00:12:30,617 --> 00:12:33,754 +the color input should be +anti-aliased and noise free. + +255 +00:12:33,787 --> 00:12:35,289 +This is because noise effects + +256 +00:12:35,322 --> 00:12:38,325 +and aliased images +prevent good edge determination, + +257 +00:12:38,358 --> 00:12:41,094 +which will worsen +the spatial upscaling quality. + +258 +00:12:41,929 --> 00:12:43,597 +For the best performance, + +259 +00:12:43,630 --> 00:12:46,066 +use the perceptual color processing mode. + +260 +00:12:46,099 --> 00:12:48,702 +This means, +your input color should be tone mapped, + +261 +00:12:48,735 --> 00:12:52,172 +with values from 0-1, +in the sRGB color space. + +262 +00:12:53,073 --> 00:12:56,043 +And finally, +set the appropriate negative mip bias + +263 +00:12:56,076 --> 00:12:58,712 +for higher texture detail. + +264 +00:12:58,745 --> 00:13:00,848 +The recommended mip bias calculation + +265 +00:13:00,881 --> 00:13:03,450 +for spatial upscaling is to apply log2 + +266 +00:13:03,483 --> 00:13:05,018 +of the render resolution width, + +267 +00:13:05,052 --> 00:13:07,187 +divided by the target resolution width. + +268 +00:13:08,388 --> 00:13:11,525 +For example, +scaling each render resolution dimension + +269 +00:13:11,558 --> 00:13:15,062 +by 2x will result in -1 mip bias, + +270 +00:13:15,095 --> 00:13:17,865 +while scaling each dimension by 1.5x + +271 +00:13:17,898 --> 00:13:20,801 +will result in -0.58 mip bias. + +272 +00:13:21,602 --> 00:13:24,838 +Note that lower mip levels +might result in flickering for textures + +273 +00:13:24,872 --> 00:13:26,573 +with high frequency patterns. + +274 +00:13:26,607 --> 00:13:28,876 +You should adjust the mip bias +for certain textures + +275 +00:13:28,909 --> 00:13:31,378 +if you spot such artifacts. + +276 +00:13:31,411 --> 00:13:33,514 +I will talk next about TemporalAA + +277 +00:13:33,547 --> 00:13:35,449 +and upscaling best practices. + +278 +00:13:36,183 --> 00:13:39,486 +To get the best quality +from Temporal AA and upscaling, + +279 +00:13:39,520 --> 00:13:42,990 +it's important to choose +a good jitter sequence. + +280 +00:13:43,023 --> 00:13:44,324 +Look for a jitter sequence + +281 +00:13:44,358 --> 00:13:46,560 +that will provide +a good distribution of samples + +282 +00:13:46,593 --> 00:13:47,628 +across all the pixels + +283 +00:13:47,661 --> 00:13:50,230 +in an upscaled target resolution. + +284 +00:13:50,264 --> 00:13:53,333 +Usually, eight jittered samples +per output pixel + +285 +00:13:53,367 --> 00:13:56,703 +produces a high-quality +anti-aliased upscaled output. + +286 +00:13:57,671 --> 00:14:00,908 +In the case of 2x upscaling, +the recommendation is to use + +287 +00:14:00,941 --> 00:14:03,710 +a Halton (2,3) sequence with 32 jitters + +288 +00:14:03,744 --> 00:14:06,513 +to produce your jittered color input. + +289 +00:14:06,547 --> 00:14:09,483 +Here’s a plot +of the first 32 sample locations + +290 +00:14:09,516 --> 00:14:11,318 +from Halton (2,3) sequence, + +291 +00:14:11,351 --> 00:14:14,521 +producing approximately +eight samples per output pixel. + +292 +00:14:15,322 --> 00:14:18,525 +It's also important to set +the appropriate negative mip bias + +293 +00:14:18,559 --> 00:14:20,661 +for higher texture detail. + +294 +00:14:20,694 --> 00:14:22,529 +The recommended mip bias calculation + +295 +00:14:22,563 --> 00:14:24,498 +for temporal AA and upscaling + +296 +00:14:24,531 --> 00:14:27,367 +is to apply log2 +of render resolution width, + +297 +00:14:27,401 --> 00:14:30,771 +divided by target resolution width, +subtracted by 1. + +298 +00:14:31,538 --> 00:14:34,808 +For example, +scaling each render resolution dimension + +299 +00:14:34,842 --> 00:14:38,011 +by 2x will result in a -2 mip bias, + +300 +00:14:38,045 --> 00:14:40,480 +while scaling each dimension + +301 +00:14:40,514 --> 00:14:43,483 +by 1.5x will result in a -1.58 mip bias. + +302 +00:14:44,318 --> 00:14:47,487 +Next, I will show you examples +of how mip bias affects your output + +303 +00:14:47,521 --> 00:14:49,656 +in different situations. + +304 +00:14:50,524 --> 00:14:52,659 +Here are MetalFX temporal AA + +305 +00:14:52,693 --> 00:14:54,461 +and upscaling outputs of the same scene + +306 +00:14:54,494 --> 00:14:57,998 +using mip bias of 0, -1, and -2. + +307 +00:15:00,167 --> 00:15:01,869 +Mip bias of -2 produces + +308 +00:15:01,902 --> 00:15:03,904 +the sharpest and clearest output, + +309 +00:15:03,937 --> 00:15:05,639 +while mip bias of 0 produces + +310 +00:15:05,672 --> 00:15:08,175 +the softest and most blurry output. + +311 +00:15:09,610 --> 00:15:11,712 +Here are three renderings +of a circuit board + +312 +00:15:11,745 --> 00:15:14,381 +that use the temporal upscaling effect. + +313 +00:15:14,414 --> 00:15:16,984 +From top to bottom, +the mip bias values applied + +314 +00:15:17,017 --> 00:15:19,319 +when sampling textures are 0, + +315 +00:15:19,353 --> 00:15:21,922 +-1, and -2. + +316 +00:15:21,955 --> 00:15:25,359 +Because the circuit board’s textures +have high-frequency patterns, + +317 +00:15:25,392 --> 00:15:27,661 +such as its tiny trace wires, + +318 +00:15:27,694 --> 00:15:30,264 +mip bias of -2 generates flickering + +319 +00:15:30,297 --> 00:15:31,865 +and moire effects. + +320 +00:15:31,899 --> 00:15:34,134 +However, mip bias of -1 + +321 +00:15:34,168 --> 00:15:35,769 +greatly reduces these effects, + +322 +00:15:35,802 --> 00:15:38,705 +and mip bias of 0 +completely eliminates them. + +323 +00:15:39,673 --> 00:15:43,143 +Lower mip levels +generally result in more details. + +324 +00:15:43,177 --> 00:15:45,913 +Use our mip bias suggestion +as a starting point, + +325 +00:15:45,946 --> 00:15:47,948 +but be mindful when choosing a mip bias + +326 +00:15:47,981 --> 00:15:50,150 +for textures with high-frequency patterns. + +327 +00:15:50,184 --> 00:15:53,120 +Follow these practices +to ensure an anti-aliased, + +328 +00:15:53,153 --> 00:15:54,621 +high-quality upscaled output + +329 +00:15:54,655 --> 00:15:57,858 +from MetalFX Temporal AA and upscaling. + +330 +00:16:00,561 --> 00:16:02,863 +Finally, I will cover +performance best practice + +331 +00:16:02,896 --> 00:16:05,399 +when using MetalFX Upscaling. + +332 +00:16:05,432 --> 00:16:08,468 +To get the best performance +with MetalFX Upscaling, + +333 +00:16:08,502 --> 00:16:11,338 +you should be careful +to avoid binding the same resources + +334 +00:16:11,371 --> 00:16:14,174 +for reading and writing +in two non-dependent render + +335 +00:16:14,208 --> 00:16:16,043 +or compute passes. + +336 +00:16:16,076 --> 00:16:18,879 +Doing so creates false dependencies. + +337 +00:16:18,912 --> 00:16:21,014 +In Metal, it's always a good idea + +338 +00:16:21,048 --> 00:16:22,816 +to avoid false dependencies. + +339 +00:16:22,850 --> 00:16:25,786 +But this is especially important +for MetalFX Upscaling, + +340 +00:16:25,819 --> 00:16:27,688 +as I will describe next. + +341 +00:16:27,721 --> 00:16:30,424 +In this example, there are two frames. + +342 +00:16:30,457 --> 00:16:33,794 +The shadow and the post processing passes +are completely unrelated + +343 +00:16:33,827 --> 00:16:36,997 +and have no resource dependencies. + +344 +00:16:37,030 --> 00:16:39,666 +Metal will overlap the next frame’s +shadow pass + +345 +00:16:39,700 --> 00:16:42,369 +with the current frame’s +post processing pass. + +346 +00:16:43,504 --> 00:16:45,906 +However, +if the post processing pass writes + +347 +00:16:45,939 --> 00:16:48,475 +to a Metal buffer while the shadow pass + +348 +00:16:48,509 --> 00:16:50,410 +also reads from the same buffer, + +349 +00:16:50,444 --> 00:16:53,347 +Metal will prevent the GPU +from running these two passes + +350 +00:16:53,380 --> 00:16:56,617 +in parallel in order +to avoid the potential hazard of reading + +351 +00:16:56,650 --> 00:16:59,786 +and writing to the same resource +at the same time. + +352 +00:16:59,820 --> 00:17:01,555 +False dependencies between frames + +353 +00:17:01,588 --> 00:17:03,223 +can negatively affect performance + +354 +00:17:03,257 --> 00:17:05,492 +of MetalFX Upscaling. + +355 +00:17:05,526 --> 00:17:07,160 +Notice that if there is no + +356 +00:17:07,194 --> 00:17:09,496 +false dependency between frames, + +357 +00:17:09,530 --> 00:17:11,965 +the next frame’s shadow pass +could have overlapped + +358 +00:17:11,999 --> 00:17:14,935 +with the previous frame's MetalFX Upscaling. + +359 +00:17:14,968 --> 00:17:17,204 +However, because of the false dependency + +360 +00:17:17,237 --> 00:17:18,272 +between frames, + +361 +00:17:18,305 --> 00:17:19,540 +the performance loss now + +362 +00:17:19,573 --> 00:17:20,674 +also includes the time + +363 +00:17:20,707 --> 00:17:22,109 +it takes for MetalFX Upscaling + +364 +00:17:22,142 --> 00:17:23,944 +to finish its process. + +365 +00:17:23,977 --> 00:17:25,479 +Ideally, you should ensure that + +366 +00:17:25,512 --> 00:17:26,847 +there are no false dependencies + +367 +00:17:26,880 --> 00:17:28,715 +between frames to allow overlapping + +368 +00:17:28,749 --> 00:17:30,851 +of workloads between different frames, + +369 +00:17:30,884 --> 00:17:32,786 +ensuring the most optimal performance + +370 +00:17:32,819 --> 00:17:35,822 +when using MetalFX Upscaling. + +371 +00:17:35,856 --> 00:17:36,957 +In this example, + +372 +00:17:36,990 --> 00:17:38,725 +you can instead create a separate buffer + +373 +00:17:38,759 --> 00:17:40,894 +for the post processing and shadow passes + +374 +00:17:40,928 --> 00:17:44,264 +to prevent the false dependency, + +375 +00:17:44,298 --> 00:17:46,233 +resulting in parallel execution + +376 +00:17:46,266 --> 00:17:48,435 +of independent passes. + +377 +00:17:49,436 --> 00:17:52,306 +Avoiding false dependencies +is something you always want + +378 +00:17:52,339 --> 00:17:55,142 +to keep in mind +when adopting MetalFX Upscaling. + +379 +00:17:55,175 --> 00:17:57,978 +When deciding which +of these two effects to choose, + +380 +00:17:58,011 --> 00:18:01,215 +there are some considerations +you should also keep in mind. + +381 +00:18:01,949 --> 00:18:05,152 +With ever-increasing shading costs +and pixel counts, + +382 +00:18:05,185 --> 00:18:07,855 +temporal AA and upscaling +is here to stay. + +383 +00:18:07,888 --> 00:18:11,058 +Amortizing pixels temporally +increases visual fidelity + +384 +00:18:11,091 --> 00:18:12,993 +and boosts performance. + +385 +00:18:13,026 --> 00:18:15,996 +If you don’t already have +a great temporal AA solution + +386 +00:18:16,029 --> 00:18:17,664 +and can render jittered color, + +387 +00:18:17,698 --> 00:18:19,933 +motion, and depth buffers, + +388 +00:18:19,967 --> 00:18:22,336 +MetalFX temporal AA and upscaling provides + +389 +00:18:22,369 --> 00:18:24,438 +a compelling platform-optimized solution + +390 +00:18:24,471 --> 00:18:26,206 +that you should consider. + +391 +00:18:26,240 --> 00:18:28,275 +If you don’t have the necessary inputs + +392 +00:18:28,308 --> 00:18:30,644 +or already have a well tuned AA solution, + +393 +00:18:30,677 --> 00:18:34,114 +consider using MetalFX spatial upscaling. + +394 +00:18:34,147 --> 00:18:36,917 +With that, hopefully you now +have a better understanding + +395 +00:18:36,950 --> 00:18:39,219 +of which upscaling effect to choose. + +396 +00:18:39,253 --> 00:18:42,256 +I will next show examples +of both of these effects running live + +397 +00:18:42,289 --> 00:18:44,658 +in Metal applications. + +398 +00:18:44,691 --> 00:18:47,361 +Here’s a side by side comparison +of the “Bistro” scene + +399 +00:18:47,394 --> 00:18:50,130 +from our “Modern Rendering with Metal” +sample code, + +400 +00:18:50,163 --> 00:18:52,666 +which features +real-time rendering algorithms, + +401 +00:18:52,699 --> 00:18:56,203 +like ambient occlusion and volumetric fog. + +402 +00:18:56,236 --> 00:18:58,839 +Native rendering at 1080p on the left + +403 +00:18:58,872 --> 00:19:00,140 +versus 4K output + +404 +00:19:00,174 --> 00:19:02,876 +with MetalFX Spatial upscaling +on the right. + +405 +00:19:02,910 --> 00:19:06,246 +Note that this sample has +its own temporal anti-aliasing solution, + +406 +00:19:06,280 --> 00:19:09,550 +which we use as input +for MetalFX spatial upscaling. + +407 +00:19:10,584 --> 00:19:13,520 +Zooming in more closely at the scooter... + +408 +00:19:15,422 --> 00:19:18,692 +On the left, the image is a bit blurry, +while on the right, + +409 +00:19:18,725 --> 00:19:20,894 +the spatially upscaled output results + +410 +00:19:20,928 --> 00:19:23,697 +in a sharper image with cleaner edges. + +411 +00:19:23,730 --> 00:19:26,967 +The straight line +on the handle bar is nicely antialiased. + +412 +00:19:28,268 --> 00:19:31,305 +The curve on the body +is also much smoother. + +413 +00:19:32,239 --> 00:19:35,509 +Let's do a performance comparison. + +414 +00:19:35,542 --> 00:19:38,512 +On the left is a native rendering at 4K. + +415 +00:19:38,545 --> 00:19:40,547 +On the right is the 4K output + +416 +00:19:40,581 --> 00:19:42,850 +from MetalFX Spatial upscaling. + +417 +00:19:44,184 --> 00:19:46,954 +As the camera moves, +the native rendering on the left + +418 +00:19:46,987 --> 00:19:48,956 +is running at a choppy frame rate, + +419 +00:19:48,989 --> 00:19:50,891 +while the spatially upscaled output + +420 +00:19:50,924 --> 00:19:53,060 +on the right is much smoother. + +421 +00:19:55,429 --> 00:19:57,531 +Next is a side-by-side comparison + +422 +00:19:57,564 --> 00:20:00,400 +of a ray-traced scene +with many reflections and shadows. + +423 +00:20:01,235 --> 00:20:04,438 +On the left is +a native rendering at 1080p. + +424 +00:20:04,471 --> 00:20:06,507 +On the right is the 4K output + +425 +00:20:06,540 --> 00:20:08,976 +with MetalFX Temporal AA and upscaling. + +426 +00:20:10,310 --> 00:20:13,347 +Zooming in more closely +at the chandelier... + +427 +00:20:14,448 --> 00:20:17,651 +The native output +on the left has an aliased look, + +428 +00:20:17,684 --> 00:20:19,720 +while the temporally upscaled output +on the right + +429 +00:20:19,753 --> 00:20:22,890 +has sharp edges with more fine details. + +430 +00:20:22,923 --> 00:20:26,393 +The shadow is nice and crisp +rather than fuzzy looking. + +431 +00:20:26,426 --> 00:20:29,396 +And fine details on the chandelier +can now be recognized. + +432 +00:20:32,266 --> 00:20:34,201 +Performance gains are also apparent + +433 +00:20:34,234 --> 00:20:37,037 +with MetalFX Temporal AA and upscaling. + +434 +00:20:37,070 --> 00:20:40,007 +On the left is native rendering at 4K. + +435 +00:20:40,040 --> 00:20:41,909 +On the right is the 4K output + +436 +00:20:41,942 --> 00:20:44,978 +with MetalFX Temporal AA and upscaling. + +437 +00:20:45,012 --> 00:20:47,748 +As the camera moves, +the native rendering on the left + +438 +00:20:47,781 --> 00:20:49,983 +is running at a very low frame rate, + +439 +00:20:50,017 --> 00:20:51,585 +while the temporally upscaled output + +440 +00:20:51,618 --> 00:20:53,654 +on the right is much smoother. + +441 +00:21:06,967 --> 00:21:08,836 +Leading game developers are excited + +442 +00:21:08,869 --> 00:21:11,605 +by the capabilities of MetalFX Upscaling + +443 +00:21:11,638 --> 00:21:13,807 +and will be bringing "Grid: Legends," + +444 +00:21:13,841 --> 00:21:15,576 +"Resident Evil: Village," + +445 +00:21:15,609 --> 00:21:18,512 +and "No Man’s Sky" to Mac later this year. + +446 +00:21:18,545 --> 00:21:21,682 +Next, I’ll show you some early work +using the framework. + +447 +00:21:21,715 --> 00:21:24,718 +[rumbling and impacts] + +448 +00:21:25,986 --> 00:21:28,689 +In this scene, +we can see the incredible visuals + +449 +00:21:28,722 --> 00:21:30,824 +and fluid gameplay of "No Man’s Sky" + +450 +00:21:30,858 --> 00:21:33,794 +using MetalFX Temporal AA and Upscaling. + +451 +00:21:33,827 --> 00:21:34,795 +[ship beeps] + +452 +00:21:34,828 --> 00:21:37,331 +[engine rumbles] + +453 +00:21:41,301 --> 00:21:42,269 +To recap, + +454 +00:21:42,302 --> 00:21:45,639 +MetalFX is a new API +with a focus on upscaling. + +455 +00:21:45,672 --> 00:21:47,841 +Spatial upscaling is easy to adopt + +456 +00:21:47,875 --> 00:21:50,043 +and delivers +substantial performance gains, + +457 +00:21:50,077 --> 00:21:52,246 +and you can use Temporal AA and upscaling + +458 +00:21:52,279 --> 00:21:54,715 +to deliver higher quality rendering. + +459 +00:21:54,748 --> 00:21:57,818 +Following the best practices +I talked about earlier will ensure + +460 +00:21:57,851 --> 00:22:01,121 +you get the most out of MetalFX Upscaling. + +461 +00:22:01,154 --> 00:22:02,656 +Thank you for watching. + +462 +00:22:02,689 --> 00:22:06,193 +♪ ♪ + diff --git a/eng/2022 Session 10104 Load resources faster with Metal 3 en.srt b/eng/2022 Session 10104 Load resources faster with Metal 3 en.srt new file mode 100644 index 0000000..0cea7be --- /dev/null +++ b/eng/2022 Session 10104 Load resources faster with Metal 3 en.srt @@ -0,0 +1,2132 @@ +1 +00:00:00,267 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:10,077 +♪ + +3 +00:00:10,077 --> 00:00:11,912 +Hi, my name is Jaideep Joshi, + +4 +00:00:11,912 --> 00:00:14,781 +and I'm a GPU software engineer +at Apple. + +5 +00:00:14,781 --> 00:00:16,516 +In this session, +I will introduce + +6 +00:00:16,516 --> 00:00:19,253 +a new feature in Metal 3 +that will simplify + +7 +00:00:19,253 --> 00:00:23,223 +and optimize resource loading +for your games and apps. + +8 +00:00:23,223 --> 00:00:24,458 +I'll start by showing you + +9 +00:00:24,458 --> 00:00:26,426 +how the fast resource loading +feature + +10 +00:00:26,426 --> 00:00:30,230 +can fit into your app's +asset-loading pipeline. + +11 +00:00:30,230 --> 00:00:33,700 +It has several key features that +harness new storage technologies + +12 +00:00:33,700 --> 00:00:36,236 +on Apple products. + +13 +00:00:36,236 --> 00:00:39,039 +Fast resource loading +has some advanced features + +14 +00:00:39,039 --> 00:00:43,343 +that solve interesting scenarios +your applications may run into. + +15 +00:00:43,343 --> 00:00:45,646 +There are a few best practice +recommendations + +16 +00:00:45,646 --> 00:00:47,414 +you should know about +that will help you + +17 +00:00:47,414 --> 00:00:52,319 +effectively use +these features in your apps. + +18 +00:00:52,319 --> 00:00:55,022 +As you add fast resource loading +to your apps, + +19 +00:00:55,022 --> 00:00:58,392 +tools like Metal System Trace +and the GPU debugger + +20 +00:00:58,392 --> 00:01:02,896 +can help profile and fix +issues you may run into. + +21 +00:01:02,896 --> 00:01:05,232 +At the end, I'll walk through +an example + +22 +00:01:05,232 --> 00:01:09,636 +that shows fast resource loading +in action. + +23 +00:01:09,636 --> 00:01:13,173 +So here's what you can do with +Metal 3's fast resource loading. + +24 +00:01:15,175 --> 00:01:17,277 +With Metal 3's +fast resource loading, + +25 +00:01:17,277 --> 00:01:20,547 +your games and apps +can load assets with low latency + +26 +00:01:20,547 --> 00:01:23,050 +and high throughput +by taking advantage + +27 +00:01:23,050 --> 00:01:25,652 +of the Apple silicon +unified memory architecture + +28 +00:01:25,652 --> 00:01:30,357 +and fast SSD storage +included with Apple platforms. + +29 +00:01:30,357 --> 00:01:31,658 +You will learn the best ways + +30 +00:01:31,658 --> 00:01:34,094 +to stream data +and reduce load times + +31 +00:01:34,094 --> 00:01:38,598 +to ensure that your game's +assets are ready on time. + +32 +00:01:38,598 --> 00:01:41,001 +A key aspect +of reducing load times + +33 +00:01:41,001 --> 00:01:42,903 +is to load only what you need + +34 +00:01:42,903 --> 00:01:45,973 +at the smallest +possible granularity. + +35 +00:01:45,973 --> 00:01:49,142 +The high throughput +and low latency in Metal 3 + +36 +00:01:49,142 --> 00:01:52,012 +lets your apps stream +higher-quality assets, + +37 +00:01:52,012 --> 00:01:56,283 +including textures, audio, +and geometry data. + +38 +00:01:56,283 --> 00:01:59,219 +Now I'll walk you through +an example of asset loading + +39 +00:01:59,219 --> 00:02:01,621 +in a game. + +40 +00:02:01,621 --> 00:02:03,623 +Games typically show +a loading screen + +41 +00:02:03,623 --> 00:02:07,361 +when they first start up, or +at the beginning of a new level, + +42 +00:02:07,361 --> 00:02:10,931 +so they can load +the game's assets into memory. + +43 +00:02:10,931 --> 00:02:12,866 +As the player moves +through the level, + +44 +00:02:12,866 --> 00:02:16,436 +the game loads +more assets for the scene. + +45 +00:02:16,436 --> 00:02:19,940 +The downside is the player +has to wait a long time + +46 +00:02:19,940 --> 00:02:21,842 +while the game makes +multiple requests + +47 +00:02:21,842 --> 00:02:25,846 +to the storage system +to load assets up front. + +48 +00:02:25,846 --> 00:02:29,850 +Plus, those assets can have +a large memory footprint. + +49 +00:02:29,850 --> 00:02:34,221 +There are a few ways +to improve this experience. + +50 +00:02:34,221 --> 00:02:35,822 +Games can improve +this experience + +51 +00:02:35,822 --> 00:02:37,691 +by dynamically streaming objects + +52 +00:02:37,691 --> 00:02:40,427 +as the player +gets closer to them. + +53 +00:02:40,427 --> 00:02:44,131 +This way, the game only loads +what it needs at first + +54 +00:02:44,131 --> 00:02:46,366 +and gradually streams +other resources + +55 +00:02:46,366 --> 00:02:49,369 +as the player moves +through the level. + +56 +00:02:49,369 --> 00:02:53,340 +For example, the game +initially loads this chalkboard + +57 +00:02:53,340 --> 00:02:56,710 +at a lower resolution, but as +the player walks towards it, + +58 +00:02:56,710 --> 00:03:00,313 +the game loads +a higher-resolution version. + +59 +00:03:00,313 --> 00:03:03,050 +This approach reduces +the time the player waits + +60 +00:03:03,050 --> 00:03:05,218 +at the loading screen. + +61 +00:03:05,218 --> 00:03:08,922 +However, the player might +still see lower-resolution items + +62 +00:03:08,922 --> 00:03:11,758 +in the scene even +when they are up close, + +63 +00:03:11,758 --> 00:03:13,760 +because it takes too long +to load + +64 +00:03:13,760 --> 00:03:16,797 +the higher-resolution versions. + +65 +00:03:16,797 --> 00:03:19,900 +One way to deal with this +is to stream smaller portions + +66 +00:03:19,900 --> 00:03:23,270 +of each asset. + +67 +00:03:23,270 --> 00:03:26,206 +For example, your game could +load only the visible regions + +68 +00:03:26,206 --> 00:03:29,876 +of the scene with sparse +textures that stream tiles + +69 +00:03:29,876 --> 00:03:32,279 +instead of whole mip levels. + +70 +00:03:32,279 --> 00:03:34,781 +This vastly reduces +the amount of data + +71 +00:03:34,781 --> 00:03:37,317 +your app needs to stream. + +72 +00:03:37,317 --> 00:03:40,053 +With that approach, +the load requests get smaller, + +73 +00:03:40,053 --> 00:03:41,955 +and there are more of them. + +74 +00:03:41,955 --> 00:03:44,891 +But that's OK, +because modern storage hardware + +75 +00:03:44,891 --> 00:03:48,528 +can run multiple +load requests at once. + +76 +00:03:48,528 --> 00:03:50,730 +This means that you can +increase the resolution + +77 +00:03:50,730 --> 00:03:55,202 +and scale of your scene without +compromising the gameplay. + +78 +00:03:55,202 --> 00:03:58,405 +Along with issuing a large +number of small-load requests, + +79 +00:03:58,405 --> 00:04:02,075 +you also have the ability to +prioritize your load requests, + +80 +00:04:02,075 --> 00:04:06,379 +to ensure that high-priority +requests finish in time. + +81 +00:04:06,379 --> 00:04:07,948 +Now that I have covered the ways + +82 +00:04:07,948 --> 00:04:11,952 +to boost visual fidelity of +games while reducing load times, + +83 +00:04:11,952 --> 00:04:15,021 +I'll show you how Metal 3's +fast resource loading + +84 +00:04:15,021 --> 00:04:16,523 +helps you do this. + +85 +00:04:16,523 --> 00:04:19,659 +Fast resource loading +is an asynchronous API + +86 +00:04:19,659 --> 00:04:22,295 +that loads resources +from storage. + +87 +00:04:22,295 --> 00:04:26,166 +Unlike existing load APIs, the +thread which issues the loads + +88 +00:04:26,166 --> 00:04:29,503 +does not need to wait +for the loads to finish. + +89 +00:04:29,503 --> 00:04:32,239 +The load operations +execute concurrently + +90 +00:04:32,239 --> 00:04:36,309 +to better utilize the throughput +of faster storage. + +91 +00:04:36,309 --> 00:04:38,278 +You can batch load operations + +92 +00:04:38,278 --> 00:04:42,048 +to further minimize the overhead +of resource loading. + +93 +00:04:42,048 --> 00:04:43,984 +And finally, with Metal 3, + +94 +00:04:43,984 --> 00:04:48,622 +you can prioritize load +operations for lower latency. + +95 +00:04:48,622 --> 00:04:50,524 +Now I'll show you +the key features + +96 +00:04:50,524 --> 00:04:53,160 +that will help you build +your asset-loading pipeline, + +97 +00:04:53,160 --> 00:04:56,429 +starting with the steps +to load resources. + +98 +00:04:56,429 --> 00:04:59,099 +There are three steps +to load resources: + +99 +00:04:59,099 --> 00:05:00,500 +open a file, + +100 +00:05:00,500 --> 00:05:02,936 +issue the necessary +load commands, + +101 +00:05:02,936 --> 00:05:07,040 +and then synchronize these load +commands with rendering work. + +102 +00:05:07,040 --> 00:05:10,911 +Here's how you do that, +starting with opening a file. + +103 +00:05:10,911 --> 00:05:14,414 +You open an existing file +by creating a file handle + +104 +00:05:14,414 --> 00:05:16,816 +with a Metal device instance. + +105 +00:05:16,816 --> 00:05:20,420 +For example, this code uses +the Metal device instance + +106 +00:05:20,420 --> 00:05:21,821 +to create a file handle + +107 +00:05:21,821 --> 00:05:28,562 +by calling its new makeIOHandle +method with a file path URL. + +108 +00:05:28,562 --> 00:05:30,297 +Once you have a file handle, + +109 +00:05:30,297 --> 00:05:33,934 +you can use it +to issue load commands. + +110 +00:05:33,934 --> 00:05:36,469 +Here's a typical scenario +in an application, + +111 +00:05:36,469 --> 00:05:40,273 +where it performs load +operations and encodes GPU work. + +112 +00:05:40,273 --> 00:05:43,577 +With existing load APIs, the app +has to wait for the loading work + +113 +00:05:43,577 --> 00:05:46,680 +to finish before it can encode +the rendering work. + +114 +00:05:46,680 --> 00:05:48,381 +Metal 3 lets your app + +115 +00:05:48,381 --> 00:05:51,851 +asynchronously +execute load commands. + +116 +00:05:51,851 --> 00:05:55,255 +Start by creating +a Metal IO command queue. + +117 +00:05:55,255 --> 00:05:58,792 +Then use that queue +to create IO command buffers + +118 +00:05:58,792 --> 00:06:02,529 +and encode load commands +to those buffers. + +119 +00:06:02,529 --> 00:06:05,799 +However, as command buffers +execute asynchronously + +120 +00:06:05,799 --> 00:06:09,002 +on the command queue, +your app does not need to wait + +121 +00:06:09,002 --> 00:06:12,205 +for the load operations +to finish. + +122 +00:06:12,205 --> 00:06:14,174 +In fact, not only do +all commands + +123 +00:06:14,174 --> 00:06:17,010 +within an IO command buffer +execute concurrently, + +124 +00:06:17,010 --> 00:06:20,180 +IO command buffers themselves +execute concurrently + +125 +00:06:20,180 --> 00:06:22,616 +and complete out of order. + +126 +00:06:22,616 --> 00:06:26,086 +This concurrent execution model +better utilizes + +127 +00:06:26,086 --> 00:06:30,323 +faster-storage hardware +by maximizing throughput. + +128 +00:06:30,323 --> 00:06:35,195 +You can encode three types of +IO commands to a command buffer: + +129 +00:06:35,195 --> 00:06:37,831 +loadTexture, which loads +to a Metal texture + +130 +00:06:37,831 --> 00:06:39,432 +for texture streaming; + +131 +00:06:39,432 --> 00:06:41,835 +loadBuffer, which loads +to a Metal buffer + +132 +00:06:41,835 --> 00:06:44,371 +for streaming scene +or geometry data; + +133 +00:06:44,371 --> 00:06:49,476 +and loadBytes, which loads +to CPU-accessible memory. + +134 +00:06:49,476 --> 00:06:53,513 +You create IO command buffers +from an IO command queue. + +135 +00:06:53,513 --> 00:06:56,516 +To create a queue, +first make and configure + +136 +00:06:56,516 --> 00:06:59,419 +an IO command +queue descriptor. + +137 +00:06:59,419 --> 00:07:02,389 +By default, the queues +are concurrent, + +138 +00:07:02,389 --> 00:07:04,791 +but you can also set them +to run command buffers + +139 +00:07:04,791 --> 00:07:08,161 +sequentially and completely +in order. + +140 +00:07:08,161 --> 00:07:10,163 +Then pass the queue descriptor + +141 +00:07:10,163 --> 00:07:16,369 +to the Metal device instance's +makeIOCommandQueue method. + +142 +00:07:16,369 --> 00:07:18,171 +Create an IO command buffer + +143 +00:07:18,171 --> 00:07:22,842 +by calling the command queue's +makeCommandBuffer method. + +144 +00:07:22,842 --> 00:07:26,446 +Then use that command buffer +to encode load commands + +145 +00:07:26,446 --> 00:07:29,115 +that load textures and buffers. + +146 +00:07:29,115 --> 00:07:32,085 +Metal's validation layer +will catch encoding errors + +147 +00:07:32,085 --> 00:07:33,553 +at runtime. + +148 +00:07:33,553 --> 00:07:36,823 +The load commands are what use +the fileHandle instance + +149 +00:07:36,823 --> 00:07:39,392 +created earlier. + +150 +00:07:39,392 --> 00:07:42,362 +When you are done adding load +commands to a command buffer, + +151 +00:07:42,362 --> 00:07:44,664 +submit it to the queue +for execution + +152 +00:07:44,664 --> 00:07:49,202 +by calling the command buffer's +commit method. + +153 +00:07:49,202 --> 00:07:52,138 +Now that I've covered how +to create IO command queues, + +154 +00:07:52,138 --> 00:07:54,341 +command buffers, +issue load commands, + +155 +00:07:54,341 --> 00:07:57,143 +and submit them to the queue, +I want to show you + +156 +00:07:57,143 --> 00:08:01,915 +how you can synchronize loading +work with the other GPU work. + +157 +00:08:01,915 --> 00:08:04,517 +An app typically kicks off +its rendering work + +158 +00:08:04,517 --> 00:08:08,488 +after it finishes loading +resources for that rendering. + +159 +00:08:08,488 --> 00:08:11,358 +But an app that uses +fast resource loading + +160 +00:08:11,358 --> 00:08:14,294 +needs a way to synchronize +the IO command queue + +161 +00:08:14,294 --> 00:08:17,197 +with the render command queue. + +162 +00:08:17,197 --> 00:08:21,301 +You can synchronize these queues +with a Metal shared event. + +163 +00:08:21,301 --> 00:08:24,571 +Metal hared events let you +synchronize the command buffers + +164 +00:08:24,571 --> 00:08:27,140 +from your IO queue +with the command buffers + +165 +00:08:27,140 --> 00:08:30,110 +from your rendering queue. + +166 +00:08:30,110 --> 00:08:33,046 +You can tell a command buffer +to wait for a shared event + +167 +00:08:33,046 --> 00:08:35,782 +by encoding a waitEvent command. + +168 +00:08:35,782 --> 00:08:38,284 +Similarly, you can tell +that command buffer + +169 +00:08:38,284 --> 00:08:42,455 +to signal a shared event by +encoding a signalEvent command. + +170 +00:08:42,455 --> 00:08:44,791 +Metal ensures +that all IO commands + +171 +00:08:44,791 --> 00:08:46,926 +within the command buffer +are complete + +172 +00:08:46,926 --> 00:08:50,130 +before it signals +the shared event. + +173 +00:08:50,130 --> 00:08:52,098 +To synchronize +between command buffers, + +174 +00:08:52,098 --> 00:08:55,001 +you first need +a Metal shared event. + +175 +00:08:55,001 --> 00:08:57,837 +You can tell a command buffer +to wait for a shared event + +176 +00:08:57,837 --> 00:09:01,174 +by calling +its waitForEvent method. + +177 +00:09:01,174 --> 00:09:03,309 +Similarly, you can tell +a command buffer + +178 +00:09:03,309 --> 00:09:04,611 +to signal a shared event + +179 +00:09:04,611 --> 00:09:07,680 +by calling +its signalEvent method. + +180 +00:09:07,680 --> 00:09:08,948 +You can add similar logic + +181 +00:09:08,948 --> 00:09:11,017 +to a corresponding +GPU command buffer + +182 +00:09:11,017 --> 00:09:13,219 +so that it waits for +the IO command buffer + +183 +00:09:13,219 --> 00:09:15,488 +to signal the same shared event. + +184 +00:09:15,488 --> 00:09:18,291 +To recap, here are +the key features and APIs + +185 +00:09:18,291 --> 00:09:21,761 +that load resources +in your Metal apps. + +186 +00:09:21,761 --> 00:09:25,365 +Open a file by creating +a Metal file handle. + +187 +00:09:25,365 --> 00:09:28,668 +Issue load commands by creating +an IO command queue + +188 +00:09:28,668 --> 00:09:30,870 +and an IO command buffer. + +189 +00:09:30,870 --> 00:09:33,606 +Then, encode load commands +to the command buffer + +190 +00:09:33,606 --> 00:09:36,109 +for execution on the queue. + +191 +00:09:36,109 --> 00:09:38,912 +And finally, use wait +and signalEvent commands + +192 +00:09:38,912 --> 00:09:41,648 +with Metal shared events +to synchronize loading + +193 +00:09:41,648 --> 00:09:43,983 +and rendering. + +194 +00:09:43,983 --> 00:09:46,219 +Now, I'll go over +a few advanced features + +195 +00:09:46,219 --> 00:09:48,688 +that you might find helpful. + +196 +00:09:48,688 --> 00:09:50,089 +Here's a typical scenario + +197 +00:09:50,089 --> 00:09:53,393 +where a game can't fit +its entire map in memory, + +198 +00:09:53,393 --> 00:09:56,296 +which is why it subdivides +the map into regions. + +199 +00:09:56,296 --> 00:09:58,431 +As the player progresses +through the map, + +200 +00:09:58,431 --> 00:10:02,535 +the game starts preloading +regions of the map. + +201 +00:10:02,535 --> 00:10:05,171 +Based on the player's direction, +the game determines + +202 +00:10:05,171 --> 00:10:07,974 +that the best regions to preload +are the northwest, + +203 +00:10:07,974 --> 00:10:10,276 +west, and southwest regions. + +204 +00:10:10,276 --> 00:10:13,112 +However, once the player +moves to the western region + +205 +00:10:13,112 --> 00:10:14,814 +and starts heading south, + +206 +00:10:14,814 --> 00:10:18,751 +preloading the northwestern +region is no longer beneficial. + +207 +00:10:18,751 --> 00:10:21,120 +To reduce the latency +of future loads, + +208 +00:10:21,120 --> 00:10:25,859 +Metal 3 allows you to attempt +to cancel load operation. + +209 +00:10:25,859 --> 00:10:29,696 +Let's look at how to do that +in practice. + +210 +00:10:29,696 --> 00:10:31,764 +When the player is +in the center region, + +211 +00:10:31,764 --> 00:10:36,035 +encode and commit IO command +buffers for three regions. + +212 +00:10:36,035 --> 00:10:38,104 +Then when the player +is in the western region + +213 +00:10:38,104 --> 00:10:41,207 +and heading south, +use the tryCancel method + +214 +00:10:41,207 --> 00:10:44,811 +to cancel the loads +for the northwestern region. + +215 +00:10:44,811 --> 00:10:48,214 +Cancelling is at +the command buffer granularity, + +216 +00:10:48,214 --> 00:10:51,851 +so you can cancel the +command buffer mid-execution. + +217 +00:10:51,851 --> 00:10:53,987 +If at some later point, +you want to know + +218 +00:10:53,987 --> 00:10:56,389 +whether the region +was completely loaded, + +219 +00:10:56,389 --> 00:11:00,927 +you can check the status +of the command buffer. + +220 +00:11:00,927 --> 00:11:05,665 +Metal 3 also lets you +prioritize your IO work. + +221 +00:11:05,665 --> 00:11:08,501 +Consider a game scenario +where the player teleports + +222 +00:11:08,501 --> 00:11:12,138 +to a new part of the scene and +your game starts streaming in + +223 +00:11:12,138 --> 00:11:14,641 +large amounts +of graphics assets. + +224 +00:11:14,641 --> 00:11:16,009 +At the same time, + +225 +00:11:16,009 --> 00:11:19,546 +the game needs to play +the teleportation sound effect. + +226 +00:11:19,546 --> 00:11:23,182 +Fast resource loading allows you +to load all your app's assets, + +227 +00:11:23,182 --> 00:11:25,518 +including audio data. + +228 +00:11:25,518 --> 00:11:28,221 +To load audio, you can use +the loadBytes command + +229 +00:11:28,221 --> 00:11:32,492 +discussed earlier to load to +application-allocated memory. + +230 +00:11:32,492 --> 00:11:36,129 +In this example, texture +and audio IO command buffers + +231 +00:11:36,129 --> 00:11:40,667 +are concurrently executing +on a single IO command queue. + +232 +00:11:40,667 --> 00:11:43,102 +This simplified diagram +shows the requests + +233 +00:11:43,102 --> 00:11:45,004 +at the storage layer. + +234 +00:11:45,004 --> 00:11:46,973 +The storage system +is able to execute + +235 +00:11:46,973 --> 00:11:50,577 +both audio and texture +load requests in parallel. + +236 +00:11:50,577 --> 00:11:53,079 +To avoid delayed audio, +it is critical + +237 +00:11:53,079 --> 00:11:55,515 +that the streaming system +be able to prioritize + +238 +00:11:55,515 --> 00:11:58,685 +audio requests +over texture requests. + +239 +00:11:58,685 --> 00:12:01,421 +To prioritize audio requests, +you can create + +240 +00:12:01,421 --> 00:12:06,326 +a separate IO command queue, +and set its priority to high. + +241 +00:12:06,326 --> 00:12:10,430 +The storage system will ensure +that high-priority IO requests + +242 +00:12:10,430 --> 00:12:15,268 +have a lower latency and are +prioritized over other requests. + +243 +00:12:15,268 --> 00:12:17,870 +After creating a separate +high-priority queue + +244 +00:12:17,870 --> 00:12:20,373 +for audio assets, +the execution time + +245 +00:12:20,373 --> 00:12:22,976 +of the audio load requests +has gotten smaller, + +246 +00:12:22,976 --> 00:12:25,211 +while that of the parallel +texture load requests + +247 +00:12:25,211 --> 00:12:28,147 +has gotten larger. + +248 +00:12:28,147 --> 00:12:31,084 +Here's how you create +a high-priority queue. + +249 +00:12:31,084 --> 00:12:34,554 +Simply set the command queue +descriptor's priority property + +250 +00:12:34,554 --> 00:12:36,089 +to high. + +251 +00:12:36,089 --> 00:12:39,392 +You can also set the priority +to normal or low, + +252 +00:12:39,392 --> 00:12:41,427 +then create a new +IO command queue + +253 +00:12:41,427 --> 00:12:44,330 +from the descriptor as usual. + +254 +00:12:44,330 --> 00:12:47,266 +Just remember that you cannot +change a queue's priority level + +255 +00:12:47,266 --> 00:12:49,669 +after you create it. + +256 +00:12:49,669 --> 00:12:52,572 +As you add fast resource loading +to your apps, + +257 +00:12:52,572 --> 00:12:56,442 +here's some best practices +to keep in mind. + +258 +00:12:56,442 --> 00:12:59,212 +First, consider +compressing your assets. + +259 +00:12:59,212 --> 00:13:01,881 +You can reduce +your app's disk footprint + +260 +00:13:01,881 --> 00:13:05,018 +by using built-in +or custom compression. + +261 +00:13:05,018 --> 00:13:07,587 +Compression lets you trade +runtime performance + +262 +00:13:07,587 --> 00:13:10,456 +for a smaller disk footprint. + +263 +00:13:10,456 --> 00:13:12,959 +Additionally, you can improve +storage throughput + +264 +00:13:12,959 --> 00:13:17,397 +by tuning the sparse page size +when using sparse textures. + +265 +00:13:17,397 --> 00:13:20,099 +I'll go through each of these +in more detail, + +266 +00:13:20,099 --> 00:13:22,301 +starting with compression. + +267 +00:13:22,301 --> 00:13:24,804 +You can use Metals 3's APIs + +268 +00:13:24,804 --> 00:13:28,474 +to compress +your asset files offline. + +269 +00:13:28,474 --> 00:13:31,644 +First, create a compression +context and configure it + +270 +00:13:31,644 --> 00:13:35,214 +with a chunk size +and compression method. + +271 +00:13:35,214 --> 00:13:38,284 +Then pass parts of +your asset files to the context + +272 +00:13:38,284 --> 00:13:42,555 +to produce a single compressed +version of all your files. + +273 +00:13:42,555 --> 00:13:46,059 +The compression context works +by chunking all the data + +274 +00:13:46,059 --> 00:13:48,795 +and compresses it with the codec +of your choosing + +275 +00:13:48,795 --> 00:13:51,731 +and stores it to a pack file. + +276 +00:13:51,731 --> 00:13:53,466 +In this example, + +277 +00:13:53,466 --> 00:13:57,336 +the context compresses +the data in 64K chunks, + +278 +00:13:57,336 --> 00:13:59,672 +but you can choose +a suitable chunk size + +279 +00:13:59,672 --> 00:14:03,843 +based on the size and type +of data you want to compress. + +280 +00:14:03,843 --> 00:14:08,114 +Here's how you use +the compression APIs in Metal 3. + +281 +00:14:08,114 --> 00:14:10,383 +First, create +a compression context + +282 +00:14:10,383 --> 00:14:13,519 +by providing a path for creating +the compressed file, + +283 +00:14:13,519 --> 00:14:16,856 +a compression method, +and a chunk size. + +284 +00:14:16,856 --> 00:14:20,860 +Next, get file data +and append it to the context. + +285 +00:14:20,860 --> 00:14:24,764 +Here, the file data is +in an NSData object. + +286 +00:14:24,764 --> 00:14:27,133 +You can append data +from different files + +287 +00:14:27,133 --> 00:14:31,137 +by making multiple calls +to append data. + +288 +00:14:31,137 --> 00:14:32,839 +When you're done adding data, + +289 +00:14:32,839 --> 00:14:34,907 +finalize and save +the compressed file + +290 +00:14:34,907 --> 00:14:40,480 +by calling the flush and destroy +compression context function. + +291 +00:14:40,480 --> 00:14:43,349 +You can open and access +a compressed file + +292 +00:14:43,349 --> 00:14:45,685 +by creating a file handle. + +293 +00:14:45,685 --> 00:14:49,655 +This file handle is used +when issuing load commands. + +294 +00:14:49,655 --> 00:14:53,593 +For compressed files, Metal 3 +performs inline decompression, + +295 +00:14:53,593 --> 00:14:56,095 +by translating the offsets +to a list of chunks + +296 +00:14:56,095 --> 00:15:00,633 +it needs to decompress, and +loads them to your resources. + +297 +00:15:00,633 --> 00:15:05,638 +You create a file handle +with a Metal device instance. + +298 +00:15:05,638 --> 00:15:08,741 +For example, this code uses +the Metal device instance + +299 +00:15:08,741 --> 00:15:10,243 +to create a file handle + +300 +00:15:10,243 --> 00:15:12,578 +by providing +the compressed file path + +301 +00:15:12,578 --> 00:15:16,349 +to the makeIOHandle method +I covered earlier. + +302 +00:15:16,349 --> 00:15:18,885 +For compressed files, +an additional parameter + +303 +00:15:18,885 --> 00:15:21,220 +is the compression method. + +304 +00:15:21,220 --> 00:15:22,855 +This is the same +compression method + +305 +00:15:22,855 --> 00:15:26,959 +you used at the time of creating +the compressed file. + +306 +00:15:26,959 --> 00:15:28,027 +Now, I'll go through + +307 +00:15:28,027 --> 00:15:30,463 +the different compression +methods supported + +308 +00:15:30,463 --> 00:15:32,665 +and the characteristics +of each of them, + +309 +00:15:32,665 --> 00:15:36,135 +so you can better understand +how to choose between them. + +310 +00:15:36,135 --> 00:15:39,438 +Use LZ4 when decompression +speed is critical + +311 +00:15:39,438 --> 00:15:42,775 +and your app can afford +a large disk footprint. + +312 +00:15:42,775 --> 00:15:45,645 +If a balance between codec +speed and compression ratio + +313 +00:15:45,645 --> 00:15:51,250 +is important to you, +use ZLib, LZBitmap, or LZFSE. + +314 +00:15:51,250 --> 00:15:53,085 +Amongst the balanced codecs, + +315 +00:15:53,085 --> 00:15:56,756 +ZLib works better +with non-Apple devices. + +316 +00:15:56,756 --> 00:16:00,760 +LZBitmap is fast +at encoding and decoding, + +317 +00:16:00,760 --> 00:16:04,263 +and LZFSE has +a high compression ratio. + +318 +00:16:04,263 --> 00:16:06,666 +If you need the best +compression ratio, + +319 +00:16:06,666 --> 00:16:09,101 +consider using the LZMA codec, + +320 +00:16:09,101 --> 00:16:14,407 +if your app can afford the extra +time it takes to decode assets. + +321 +00:16:14,407 --> 00:16:18,411 +It is also possible to use +your own compression scheme. + +322 +00:16:18,411 --> 00:16:20,947 +You may have cases +where your data benefits + +323 +00:16:20,947 --> 00:16:23,282 +from a custom compression codec. + +324 +00:16:23,282 --> 00:16:26,252 +In that case, you can replace +the compression context + +325 +00:16:26,252 --> 00:16:29,055 +with your own compressor +and translate offsets + +326 +00:16:29,055 --> 00:16:33,392 +and perform decompression +at runtime yourself. + +327 +00:16:33,392 --> 00:16:35,461 +Now that you have seen +how to use compression + +328 +00:16:35,461 --> 00:16:37,296 +to reduce disk footprint, + +329 +00:16:37,296 --> 00:16:40,633 +let's look at tuning +sparse page size. + +330 +00:16:40,633 --> 00:16:43,336 +Earlier versions +of Metal support loading tiles + +331 +00:16:43,336 --> 00:16:47,373 +to sparse textures +at a 16K granularity. + +332 +00:16:47,373 --> 00:16:51,777 +With Metal 3, you can specify +two new sparse tile sizes: + +333 +00:16:51,777 --> 00:16:54,747 +64 and 256K. + +334 +00:16:54,747 --> 00:16:57,450 +These new sizes let you +to stream textures + +335 +00:16:57,450 --> 00:17:00,219 +at a larger granularity +to better utilize + +336 +00:17:00,219 --> 00:17:02,989 +and saturate +the storage hardware. + +337 +00:17:02,989 --> 00:17:04,690 +Note that there is a tradeoff + +338 +00:17:04,690 --> 00:17:06,959 +between streaming +larger tiles sizes + +339 +00:17:06,959 --> 00:17:09,328 +and the amount of data +you stream, + +340 +00:17:09,328 --> 00:17:12,665 +so you'll have to experiment +to see which sizes work best + +341 +00:17:12,665 --> 00:17:16,102 +with your app +and its sparse textures. + +342 +00:17:16,102 --> 00:17:18,271 +Next, let's look at +how you can use + +343 +00:17:18,271 --> 00:17:21,574 +the set of Metal Developer Tools +to profile and debug + +344 +00:17:21,574 --> 00:17:24,110 +fast resource loading +in your app. + +345 +00:17:24,110 --> 00:17:28,948 +Xcode 14 includes full support +for fast resource loading. + +346 +00:17:28,948 --> 00:17:32,018 +From runtime profiling +with Metal System Trace + +347 +00:17:32,018 --> 00:17:35,321 +to API inspection and +advanced dependency analysis + +348 +00:17:35,321 --> 00:17:37,256 +with Metal debugger. + +349 +00:17:39,392 --> 00:17:42,128 +Let's start with +runtime profiling. + +350 +00:17:42,128 --> 00:17:45,932 +In Xcode 14, Instruments can +profile fast resource loading + +351 +00:17:45,932 --> 00:17:48,434 +with the Metal System Trace +template. + +352 +00:17:48,434 --> 00:17:51,537 +Instruments is a powerful +analysis and profiling tool + +353 +00:17:51,537 --> 00:17:53,506 +that will help you achieve +the best performance + +354 +00:17:53,506 --> 00:17:56,042 +in your Metal app. + +355 +00:17:56,042 --> 00:17:58,678 +The Metal System Trace template +allows you to check + +356 +00:17:58,678 --> 00:18:02,181 +when load operations +are encoded and executed. + +357 +00:18:02,181 --> 00:18:04,483 +You will be able to understand +how they correlate + +358 +00:18:04,483 --> 00:18:06,786 +with the activity that +your app is performing + +359 +00:18:06,786 --> 00:18:10,256 +on both the CPU and the GPU. + +360 +00:18:10,256 --> 00:18:13,659 +To learn how to profile your +Metal app with Instruments, + +361 +00:18:13,659 --> 00:18:16,095 +please check out +these previous sessions, + +362 +00:18:16,095 --> 00:18:19,365 +"Optimize Metal apps and games +with GPU counters" + +363 +00:18:19,365 --> 00:18:23,235 +and "Optimize high-end games +for Apple GPUs." + +364 +00:18:23,235 --> 00:18:26,372 +Now, let's switch gears +to debugging. + +365 +00:18:26,372 --> 00:18:28,908 +With the Metal debugger +in Xcode 14, + +366 +00:18:28,908 --> 00:18:30,776 +you can now analyze +your game's use + +367 +00:18:30,776 --> 00:18:33,980 +of the new +fast resource loading API. + +368 +00:18:33,980 --> 00:18:36,716 +Once you take a frame capture, +you will be able to inspect + +369 +00:18:36,716 --> 00:18:40,553 +all fast resource loading +API calls. + +370 +00:18:40,553 --> 00:18:43,122 +From the IO command buffers +created + +371 +00:18:43,122 --> 00:18:46,559 +to the load operations +that were issued. + +372 +00:18:46,559 --> 00:18:48,227 +You can now visually inspect + +373 +00:18:48,227 --> 00:18:50,296 +fast resource loading +dependencies + +374 +00:18:50,296 --> 00:18:53,366 +with the new +Dependency viewer. + +375 +00:18:53,366 --> 00:18:56,202 +The Dependency viewer +gives a detailed overview + +376 +00:18:56,202 --> 00:18:59,305 +of resource dependencies +between IO command buffers + +377 +00:18:59,305 --> 00:19:01,540 +and Metal passes. + +378 +00:19:01,540 --> 00:19:04,143 +From here, you can use +all the features + +379 +00:19:04,143 --> 00:19:05,911 +in the new +Dependency viewer, + +380 +00:19:05,911 --> 00:19:09,482 +such as the new synchronization +edges and graph filtering, + +381 +00:19:09,482 --> 00:19:13,586 +to deep dive and optimize your +resource loading dependencies. + +382 +00:19:13,586 --> 00:19:17,356 +To learn more about the new +Dependency viewer in Xcode 14, + +383 +00:19:17,356 --> 00:19:18,758 +please check out this year's + +384 +00:19:18,758 --> 00:19:22,361 +"Go bindless with Metal 3" +session. + +385 +00:19:22,361 --> 00:19:26,332 +Now, let's look at +fast resource loading in action. + +386 +00:19:26,332 --> 00:19:27,967 +This is a test scene that uses + +387 +00:19:27,967 --> 00:19:31,337 +the new fast resource loading +APIs to stream texture data + +388 +00:19:31,337 --> 00:19:35,307 +by using sparse textures +with a tile size of 16 kilobytes. + +389 +00:19:35,307 --> 00:19:38,778 +This video is from a MacBook Pro +with an M1 Pro chip. + +390 +00:19:38,778 --> 00:19:40,246 +The streaming system queries + +391 +00:19:40,246 --> 00:19:44,517 +the GPU's sparse texture access +counters to identify two things: + +392 +00:19:44,517 --> 00:19:46,685 +the tiles it has sampled +but not loaded + +393 +00:19:46,685 --> 00:19:49,422 +and the loaded tiles +the app isn't using. + +394 +00:19:49,422 --> 00:19:52,658 +The app uses this information +to encode a list of loads + +395 +00:19:52,658 --> 00:19:54,126 +for the tiles it needs + +396 +00:19:54,126 --> 00:19:57,396 +and a list of evictions +for tiles it doesn't need. + +397 +00:19:57,396 --> 00:20:00,232 +That way, the working set +contains only the tiles + +398 +00:20:00,232 --> 00:20:03,202 +the app is mostly likely to use. + +399 +00:20:03,202 --> 00:20:06,338 +If the player decides to travel +to another part of the scene, + +400 +00:20:06,338 --> 00:20:08,908 +the app needs to stream in +a completely new set + +401 +00:20:08,908 --> 00:20:11,010 +of high-resolution textures. + +402 +00:20:11,010 --> 00:20:13,345 +If the streaming system +is fast enough, + +403 +00:20:13,345 --> 00:20:17,116 +the player will not notice +this streaming occurring. + +404 +00:20:17,116 --> 00:20:18,384 +If I pause the scene, + +405 +00:20:18,384 --> 00:20:21,320 +you can observe the image +differences more clearly. + +406 +00:20:21,320 --> 00:20:23,622 +The left side +is loading sparse tiles + +407 +00:20:23,622 --> 00:20:26,525 +on a single thread +using the pread API. + +408 +00:20:26,525 --> 00:20:28,627 +The right side +is loading sparse tiles + +409 +00:20:28,627 --> 00:20:31,730 +using the fast resource loading +APIs. + +410 +00:20:31,730 --> 00:20:33,666 +As the player enters the scene, + +411 +00:20:33,666 --> 00:20:36,735 +most of the textures +haven't fully loaded. + +412 +00:20:36,735 --> 00:20:39,905 +Once the loads complete, the +final high-resolution version + +413 +00:20:39,905 --> 00:20:42,908 +of the textures is visible. + +414 +00:20:42,908 --> 00:20:46,112 +If I go back to the beginning +of this scene and slow it down, + +415 +00:20:46,112 --> 00:20:48,481 +it is easier to notice +the improvements + +416 +00:20:48,481 --> 00:20:51,417 +that fast resource loading +provides. + +417 +00:20:51,417 --> 00:20:54,053 +To highlight the differences, +this rendering + +418 +00:20:54,053 --> 00:20:59,425 +marks tiles the app has not +yet loaded with a red tint. + +419 +00:20:59,425 --> 00:21:01,527 +At first, the scene shows + +420 +00:21:01,527 --> 00:21:05,631 +that the app hasn't loaded +most of the tiles. + +421 +00:21:05,631 --> 00:21:08,601 +However, as the player +enters the scene, + +422 +00:21:08,601 --> 00:21:11,036 +fast resource loading +improves the loading + +423 +00:21:11,036 --> 00:21:14,006 +of high-resolution tiles +and minimizes the delay + +424 +00:21:14,006 --> 00:21:18,711 +compared to the single-threaded +pread version. + +425 +00:21:18,711 --> 00:21:21,780 +Metal 3's fast resource loading +helps you build a powerful + +426 +00:21:21,780 --> 00:21:24,683 +and efficient asset-streaming +system that lets your app + +427 +00:21:24,683 --> 00:21:28,387 +take advantage of the latest +storage technologies. + +428 +00:21:28,387 --> 00:21:32,791 +Use it to reduce load times by +streaming assets just in time, + +429 +00:21:32,791 --> 00:21:35,761 +including higher-quality images. + +430 +00:21:35,761 --> 00:21:40,232 +Use Metal's shared events +to asynchronously load assets + +431 +00:21:40,232 --> 00:21:43,102 +while the GPU renders a scene. + +432 +00:21:43,102 --> 00:21:46,005 +For assets that your app +needs in a hurry, + +433 +00:21:46,005 --> 00:21:48,807 +minimize latency +by creating a command queue + +434 +00:21:48,807 --> 00:21:51,010 +with a higher priority. + +435 +00:21:51,010 --> 00:21:54,013 +And remember, +keep the storage system busy + +436 +00:21:54,013 --> 00:21:56,282 +by sending load commands early. + +437 +00:21:56,282 --> 00:22:00,953 +You can always cancel +the ones you don't need. + +438 +00:22:00,953 --> 00:22:04,256 +Fast resource loading in Metal 3 +introduces new ways + +439 +00:22:04,256 --> 00:22:06,859 +to harness the power +of modern storage hardware + +440 +00:22:06,859 --> 00:22:09,828 +for high-throughput +asset loading. + +441 +00:22:09,828 --> 00:22:12,264 +I can't wait to see +how you use these features + +442 +00:22:12,264 --> 00:22:15,968 +to improve your app's visual +quality and responsiveness. + +443 +00:22:15,968 --> 00:22:17,136 +Thanks for watching. + +444 +00:22:17,136 --> 00:22:20,873 +♪ + diff --git a/eng/2022 Session 10105 Maximize your Metal ray tracing performance en.srt b/eng/2022 Session 10105 Maximize your Metal ray tracing performance en.srt new file mode 100644 index 0000000..287e45c --- /dev/null +++ b/eng/2022 Session 10105 Maximize your Metal ray tracing performance en.srt @@ -0,0 +1,2647 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,545 +Hi, my name is Yi. + +3 +00:00:11,578 --> 00:00:13,814 +Hi, my name is Dominik. + +4 +00:00:13,847 --> 00:00:16,283 +And we are GPU software engineers. + +5 +00:00:16,316 --> 00:00:20,287 +Today, Dominik and I are going to talk +about the performance enhancements + +6 +00:00:20,320 --> 00:00:23,690 +and features we've added +to the Metal Ray Tracing API this year + +7 +00:00:23,724 --> 00:00:27,060 +to help you maximize the performance +of your ray tracing applications. + +8 +00:00:27,094 --> 00:00:30,731 +Ray tracing applications simulate +individual rays of light + +9 +00:00:30,764 --> 00:00:33,000 +bouncing around a scene. + +10 +00:00:33,033 --> 00:00:36,570 +This is used in games +and offline rendering to produce + +11 +00:00:36,603 --> 00:00:41,975 +photorealistic reflections, +shadows, global illumination, and more. + +12 +00:00:42,009 --> 00:00:44,545 +This requires simulating a lot of rays, + +13 +00:00:44,578 --> 00:00:47,814 +so performance is critical +for these applications. + +14 +00:00:47,848 --> 00:00:51,585 +Fortunately, Metal has +built-in support for ray tracing + +15 +00:00:51,618 --> 00:00:54,388 +optimized for all Apple devices. + +16 +00:00:54,421 --> 00:00:57,357 +Let's briefly review +how ray tracing works in Metal. + +17 +00:00:57,391 --> 00:01:01,662 +The Metal ray tracing API +is available from within shader functions + +18 +00:01:01,695 --> 00:01:05,265 +such as compute or fragment functions. + +19 +00:01:05,299 --> 00:01:09,503 +We start by generating some rays +which are emitted into the scene. + +20 +00:01:09,536 --> 00:01:13,473 +Next, we create an intersector object +and use it to check + +21 +00:01:13,507 --> 00:01:18,011 +for intersections between our rays +and the geometry in the scene. + +22 +00:01:18,045 --> 00:01:21,114 +A bit later, +I will describe some of the new features + +23 +00:01:21,148 --> 00:01:24,418 +we've added this year +to speed up the intersection search. + +24 +00:01:24,451 --> 00:01:26,920 +This process depends +on a special data structure + +25 +00:01:26,954 --> 00:01:29,056 +called an acceleration structure, + +26 +00:01:29,089 --> 00:01:32,292 +which also represents +the geometry in the scene. + +27 +00:01:32,326 --> 00:01:36,129 +I will also talk about several new +features and performance improvements + +28 +00:01:36,163 --> 00:01:38,732 +focused on acceleration structures today. + +29 +00:01:38,765 --> 00:01:41,835 +The intersector returns +an intersection result object + +30 +00:01:41,869 --> 00:01:45,038 +describing the primitive each ray hit. + +31 +00:01:45,072 --> 00:01:50,711 +The intersection result is used to produce +a color to write into the output image. + +32 +00:01:50,744 --> 00:01:53,514 +It can also be used +to produce additional rays + +33 +00:01:53,547 --> 00:01:55,749 +which go through the process again. + +34 +00:01:55,782 --> 00:01:58,485 +We can repeat this process +as many times as we'd like + +35 +00:01:58,519 --> 00:02:01,054 +to simulate light +bouncing around the scene. + +36 +00:02:01,088 --> 00:02:05,392 +If you want to learn more about +the basics of the Metal ray tracing API, + +37 +00:02:05,425 --> 00:02:09,796 +I recommend you review +our previous WWDC sessions. + +38 +00:02:09,830 --> 00:02:14,334 +We first introduced the Metal +ray tracing API at WWDC20, + +39 +00:02:14,368 --> 00:02:19,139 +and last year, we introduced new features +including support for motion blur. + +40 +00:02:19,173 --> 00:02:22,576 +Today, I'm going to talk +about three things. + +41 +00:02:22,609 --> 00:02:26,680 +First, I will tell you about new features +which enable you to improve + +42 +00:02:26,713 --> 00:02:29,082 +ray tracing performance +in your applications. + +43 +00:02:30,817 --> 00:02:33,954 +Next, I will talk about +improvements and features + +44 +00:02:33,987 --> 00:02:36,323 +we've added +to the acceleration structure API. + +45 +00:02:38,258 --> 00:02:40,994 +Finally, Dominik will tell you +about improvements + +46 +00:02:41,028 --> 00:02:43,897 +to our GPU tools for ray tracing. + +47 +00:02:43,931 --> 00:02:46,266 +This year, we've added three new features + +48 +00:02:46,300 --> 00:02:48,802 +aimed at either improving +ray tracing performance + +49 +00:02:48,836 --> 00:02:51,004 +or simplifying your code. + +50 +00:02:51,038 --> 00:02:54,708 +They are per-primitive data, +the ability to retrieve buffers + +51 +00:02:54,741 --> 00:02:56,844 +from intersection function tables, + +52 +00:02:56,877 --> 00:02:59,980 +and support for ray tracing +from indirect command buffers. + +53 +00:03:01,949 --> 00:03:04,484 +Let's start with per-primitive data. + +54 +00:03:04,518 --> 00:03:08,956 +Applications usually have data associated +with the primitive in their scene + +55 +00:03:08,989 --> 00:03:12,559 +such as vertex colors, normals, +and texture coordinates. + +56 +00:03:13,894 --> 00:03:16,296 +This year, +we've added the ability to store + +57 +00:03:16,330 --> 00:03:21,235 +small amounts of data for each primitive +directly in the acceleration structure. + +58 +00:03:21,268 --> 00:03:25,305 +This data can be accessed with fewer +memory indirections and cache misses, + +59 +00:03:25,339 --> 00:03:27,374 +improving performance. + +60 +00:03:27,407 --> 00:03:31,645 +This also reduces the need to store +complicated auxiliary data structure + +61 +00:03:31,678 --> 00:03:35,883 +which are typically required to look up +the data associated with your primitives. + +62 +00:03:37,184 --> 00:03:38,785 +Let's look at an example. + +63 +00:03:39,853 --> 00:03:42,823 +Alpha testing is a technique +used to add complexity + +64 +00:03:42,856 --> 00:03:47,127 +to transparent geometry +without increasing the triangle count. + +65 +00:03:47,160 --> 00:03:51,064 +In this technique, the alpha channel +of a texture mapped onto a triangle + +66 +00:03:51,098 --> 00:03:55,435 +is used to determine if the ray should +hit the triangle or continue further. + +67 +00:03:56,670 --> 00:03:59,573 +To achieve this, +you need to configure the intersector + +68 +00:03:59,606 --> 00:04:03,477 +to call your custom intersection function +when a triangle is hit by the ray. + +69 +00:04:04,645 --> 00:04:08,982 +The ultimate goal is to sample from +the texture associated with the triangle + +70 +00:04:09,016 --> 00:04:13,187 +and test if the alpha value allows the ray +to continue through the primitive. + +71 +00:04:13,220 --> 00:04:16,757 +To get there, +you need two pieces of information: + +72 +00:04:16,790 --> 00:04:20,093 +the texture object and the UV coordinates. + +73 +00:04:20,127 --> 00:04:22,829 +In a typical implementation +of alpha testing, + +74 +00:04:22,863 --> 00:04:25,832 +you would need to access +a number of intermediate buffers + +75 +00:04:25,866 --> 00:04:29,002 +in Metal device memory +in order to get this information. + +76 +00:04:30,370 --> 00:04:34,341 +First, you would store the texture +associated with the primitive + +77 +00:04:34,374 --> 00:04:36,410 +in some kind of material structure. + +78 +00:04:37,878 --> 00:04:41,081 +Several materials would be +packed into a buffer. + +79 +00:04:41,114 --> 00:04:45,052 +It would be impractical to store +material structure for every primitive, + +80 +00:04:45,085 --> 00:04:48,856 +as they may be pretty big +and there may be a lot of primitives. + +81 +00:04:48,889 --> 00:04:52,125 +Instead, you would want to store +just the material IDs + +82 +00:04:52,159 --> 00:04:56,630 +for each primitive in a buffer +and use them to look up the materials. + +83 +00:04:56,663 --> 00:05:00,200 +Next, to calculate UVs, +you need to load the UVs + +84 +00:05:00,234 --> 00:05:04,104 +for each vertex from another buffer +and interpolate them. + +85 +00:05:04,137 --> 00:05:07,875 +Finally, let's say you are +using instanced geometry. + +86 +00:05:07,908 --> 00:05:12,279 +You may want each instance +to have its own materials and UV mappings. + +87 +00:05:12,312 --> 00:05:14,848 +To support that, you would store pointers + +88 +00:05:14,882 --> 00:05:19,119 +to UV and Material ID buffers +in an Instance Data buffer, + +89 +00:05:19,152 --> 00:05:22,556 +adding yet another level +of indirection to your function. + +90 +00:05:22,589 --> 00:05:26,827 +This approach requires you to maintain +a fairly complex buffer setup + +91 +00:05:26,860 --> 00:05:31,798 +and involves many layers of indirection +to get to the data that you need. + +92 +00:05:31,832 --> 00:05:33,901 +This may also lead to cache misses + +93 +00:05:33,934 --> 00:05:37,371 +that would negatively affect +the performance. + +94 +00:05:37,404 --> 00:05:40,908 +Let's look at the code needed +to implement this diagram. + +95 +00:05:40,941 --> 00:05:44,278 +Then I will show you +how you can simplify it step-by-step + +96 +00:05:44,311 --> 00:05:46,180 +using per-primitive data. + +97 +00:05:46,213 --> 00:05:50,817 +This is the original implementation of +the alpha testing intersection function. + +98 +00:05:50,851 --> 00:05:54,922 +This function is invoked when +the ray hits an alpha tested triangle. + +99 +00:05:54,955 --> 00:05:58,859 +The function starts by loading +the instance data from the memory. + +100 +00:05:58,892 --> 00:06:02,896 +This is the buffer that contains pointers +to UV and material buffers + +101 +00:06:02,930 --> 00:06:04,464 +used by the instance. + +102 +00:06:04,498 --> 00:06:07,267 +Next, the function loads +the UV coordinates + +103 +00:06:07,301 --> 00:06:10,237 +from the UV buffer and interpolates them. + +104 +00:06:10,270 --> 00:06:12,105 +This is another memory load. + +105 +00:06:12,139 --> 00:06:15,976 +Then, the function loads +the material index from another buffer. + +106 +00:06:16,009 --> 00:06:18,445 +And finally, +the function loads the material + +107 +00:06:18,478 --> 00:06:20,681 +and samples the corresponding texture. + +108 +00:06:20,714 --> 00:06:24,051 +At this point, the function has +the alpha value it needs + +109 +00:06:24,084 --> 00:06:26,086 +and can compare it to the threshold. + +110 +00:06:26,119 --> 00:06:29,656 +Now, I will show you +how you can simplify this code + +111 +00:06:29,690 --> 00:06:33,327 +and improve its performance +using per-primitive data. + +112 +00:06:33,360 --> 00:06:35,829 +Instead of using +a complicated buffer setup + +113 +00:06:35,863 --> 00:06:38,198 +with multiple layers of indirection, + +114 +00:06:38,232 --> 00:06:41,702 +you can simply store only the data +the intersection function will need + +115 +00:06:41,735 --> 00:06:45,405 +for each primitive +directly in the acceleration structure. + +116 +00:06:45,439 --> 00:06:47,941 +In this example, you can create a struct + +117 +00:06:47,975 --> 00:06:51,645 +containing the texture and UV coordinates +for each primitive. + +118 +00:06:51,678 --> 00:06:54,815 +You provide this data when building +the acceleration structure + +119 +00:06:54,848 --> 00:06:57,384 +and the intersection function +simply receives a pointer + +120 +00:06:57,417 --> 00:07:00,254 +to that data +when a ray hits the primitive. + +121 +00:07:00,287 --> 00:07:03,190 +You can store anything you like +in the per-primitive data, + +122 +00:07:03,223 --> 00:07:07,327 +but keeping the size small will +help achieve the best performance. + +123 +00:07:07,361 --> 00:07:10,597 +I will start with the inputs +to the intersection function. + +124 +00:07:10,631 --> 00:07:13,901 +Having access to all of them gives you +a lot of flexibility + +125 +00:07:13,934 --> 00:07:15,869 +when it comes to the implementation, + +126 +00:07:15,903 --> 00:07:19,406 +but it can also increase +the register usage on the GPU. + +127 +00:07:19,439 --> 00:07:22,676 +With per-primitive data, +instead of all the buffers, + +128 +00:07:22,709 --> 00:07:26,146 +you only need to access +the primitive data pointer. + +129 +00:07:26,180 --> 00:07:30,317 +This is the data you store +directly in the acceleration structure. + +130 +00:07:30,350 --> 00:07:34,354 +In this case, each primitive has +its own texture object and UVs + +131 +00:07:34,388 --> 00:07:36,757 +for all of its vertices. + +132 +00:07:36,790 --> 00:07:41,461 +Next up are the loads from the global +material buffer and instance data buffer. + +133 +00:07:41,495 --> 00:07:43,063 +You won't need either of them. + +134 +00:07:43,096 --> 00:07:47,467 +Instead, you can do one load +from the per-primitive data pointer. + +135 +00:07:47,501 --> 00:07:51,004 +This is the only device memory access +needed in this function. + +136 +00:07:51,038 --> 00:07:52,606 +Next up are the UVs. + +137 +00:07:52,639 --> 00:07:56,343 +Instead of dereferencing a pointer +retrieved from the instance data, + +138 +00:07:56,376 --> 00:08:01,014 +you can simply access the data embedded +in the per-primitive data structure. + +139 +00:08:01,048 --> 00:08:04,785 +The change in the code is subtle, +but is important for performance, + +140 +00:08:04,818 --> 00:08:07,721 +as no additional +memory loads are involved. + +141 +00:08:07,754 --> 00:08:10,424 +Finally, there are +the material properties. + +142 +00:08:10,457 --> 00:08:13,794 +Since the only part of the material +needed is the texture, + +143 +00:08:13,827 --> 00:08:15,729 +you can encode the primitive's texture + +144 +00:08:15,762 --> 00:08:18,098 +directly in the per-primitive +data structure. + +145 +00:08:18,131 --> 00:08:20,767 +This means you don't need to access +the material + +146 +00:08:20,801 --> 00:08:22,936 +and material index buffers anymore. + +147 +00:08:22,970 --> 00:08:24,972 +You can simply use the texture directly + +148 +00:08:25,005 --> 00:08:28,442 +without paying the cost +of additional memory dereferences. + +149 +00:08:28,475 --> 00:08:31,178 +This is how much simpler +your intersection code can be + +150 +00:08:31,211 --> 00:08:33,313 +when using per-primitive data. + +151 +00:08:33,347 --> 00:08:35,916 +All the costly memory accesses +are replaced + +152 +00:08:35,949 --> 00:08:39,486 +with just one load +from the primitive data pointer. + +153 +00:08:39,520 --> 00:08:43,524 +On top of that, the code is +much simpler and easier to follow. + +154 +00:08:44,224 --> 00:08:47,160 +Next, I will show you +how to store the primitive data + +155 +00:08:47,194 --> 00:08:49,363 +in the acceleration structure. + +156 +00:08:49,396 --> 00:08:53,233 +You will need to do this before you can +access it from an intersection function. + +157 +00:08:53,267 --> 00:08:55,369 +You will need to set a few fields + +158 +00:08:55,402 --> 00:08:57,804 +in the acceleration structure +geometry descriptor. + +159 +00:08:57,838 --> 00:09:01,008 +First, set the Metal buffer +where all the data is stored. + +160 +00:09:01,041 --> 00:09:05,345 +Next, specify the size of the data +that will be stored for each primitive. + +161 +00:09:05,379 --> 00:09:07,814 +If your data is not tightly packed +in the buffer + +162 +00:09:07,848 --> 00:09:10,284 +or doesn't start +at the beginning of the buffer, + +163 +00:09:10,317 --> 00:09:13,287 +you can also specify +the stride and the offset. + +164 +00:09:13,320 --> 00:09:17,591 +Otherwise, these values default to 0 +so you don't need to set them. + +165 +00:09:17,624 --> 00:09:20,527 +You have already seen +how you can use per-primitive data + +166 +00:09:20,561 --> 00:09:21,828 +in an intersection function. + +167 +00:09:21,862 --> 00:09:24,398 +It's simply passed +into the function as a pointer. + +168 +00:09:24,431 --> 00:09:28,402 +But that's not all–you have access +to this data wherever you need it. + +169 +00:09:28,435 --> 00:09:32,306 +That includes the final intersection +result returned by the intersector. + +170 +00:09:32,339 --> 00:09:36,844 +And if you are using intersection query, +the primitive data is also available + +171 +00:09:36,877 --> 00:09:39,746 +for both candidate +and committed intersections. + +172 +00:09:39,780 --> 00:09:43,116 +This means you can use per-primitive data +for shading + +173 +00:09:43,150 --> 00:09:45,319 +in addition to intersection testing. + +174 +00:09:45,352 --> 00:09:48,021 +Per-primitive data can +improve the performance + +175 +00:09:48,055 --> 00:09:50,657 +of both your intersection code +and your shading code + +176 +00:09:50,691 --> 00:09:54,828 +by reducing the number +of memory accesses and indirections. + +177 +00:09:54,862 --> 00:09:58,265 +In fact, we found +in one of our own test applications + +178 +00:09:58,298 --> 00:10:03,704 +that using per-primitive data resulted +in a 10% to 16% performance improvement. + +179 +00:10:03,737 --> 00:10:05,472 +We can't wait for you to try it out + +180 +00:10:05,506 --> 00:10:08,008 +and see what kinds of improvements +you can get + +181 +00:10:08,041 --> 00:10:11,712 +in performance and code quality. + +182 +00:10:11,745 --> 00:10:14,748 +This year, we've also added +another convenience feature + +183 +00:10:14,781 --> 00:10:18,952 +to the Metal shading language to help you +simplify your ray tracing kernels. + +184 +00:10:18,986 --> 00:10:22,322 +Applications often pass +the same set of bindings + +185 +00:10:22,356 --> 00:10:26,593 +to both their intersection functions +and their main ray tracing kernel. + +186 +00:10:26,627 --> 00:10:29,530 +For example, +our ray tracing sample code uses + +187 +00:10:29,563 --> 00:10:32,065 +an intersection function +to render spheres. + +188 +00:10:32,099 --> 00:10:35,068 +This intersection function +accesses a resource buffer + +189 +00:10:35,102 --> 00:10:37,471 +containing information about each sphere. + +190 +00:10:37,504 --> 00:10:40,174 +In order to pass this buffer +into the intersection function, + +191 +00:10:40,207 --> 00:10:43,477 +the app binds the buffer +to the intersection function table. + +192 +00:10:43,510 --> 00:10:48,315 +However, the main ray tracing kernel +also needs access to the resource buffer, + +193 +00:10:48,348 --> 00:10:50,851 +so the app binds the buffer there as well. + +194 +00:10:50,884 --> 00:10:53,554 +This year, +the Metal shading language allows you + +195 +00:10:53,587 --> 00:10:57,191 +to access the buffers +bound to intersection function tables. + +196 +00:10:57,224 --> 00:10:59,726 +With this new feature, +you can save the effort + +197 +00:10:59,760 --> 00:11:03,797 +of binding the buffer for the kernel, +and instead access it directly + +198 +00:11:03,830 --> 00:11:05,699 +from the intersection function table. + +199 +00:11:05,732 --> 00:11:08,468 +You can do this by calling +the get_buffer method + +200 +00:11:08,502 --> 00:11:11,839 +on the intersection function table, +providing its pointer type. + +201 +00:11:11,872 --> 00:11:15,642 +You can also access visible +function tables by their function type. + +202 +00:11:15,676 --> 00:11:20,180 +Indirect command buffers allow you to +encode GPU work independently on the GPU + +203 +00:11:20,214 --> 00:11:24,618 +and represent a fundamental element +of GPU driven pipelines. + +204 +00:11:24,651 --> 00:11:26,653 +To learn more about +indirect command buffers + +205 +00:11:26,687 --> 00:11:29,756 +and GPU-driven rendering, +we recommend you review + +206 +00:11:29,790 --> 00:11:33,894 +the "Modern rendering with Metal" +session from WWDC 2019. + +207 +00:11:33,927 --> 00:11:37,464 +Enabling ray tracing support +in an indirect command buffer is easy. + +208 +00:11:37,497 --> 00:11:40,601 +All you have to do is to set +the supportRayTracing flag + +209 +00:11:40,634 --> 00:11:42,069 +on the descriptor. + +210 +00:11:42,102 --> 00:11:46,139 +Indirect command buffers +dispatch graphics and compute functions, + +211 +00:11:46,173 --> 00:11:50,344 +so you can simply use ray tracing +from those functions as usual. + +212 +00:11:50,377 --> 00:11:53,881 +That's a rundown of all +the new features we've added this year + +213 +00:11:53,914 --> 00:11:58,218 +to help you get improved ray tracing +performance in your applications. + +214 +00:11:58,252 --> 00:12:01,755 +Next, let's talk +about acceleration structures. + +215 +00:12:01,788 --> 00:12:04,424 +We've implemented several +performance improvements + +216 +00:12:04,458 --> 00:12:08,228 +and added features focused +on building acceleration structures. + +217 +00:12:08,262 --> 00:12:10,631 +Let's recap what they are used for. + +218 +00:12:10,664 --> 00:12:13,033 +Acceleration structures +are data structures + +219 +00:12:13,066 --> 00:12:15,802 +which accelerate the ray tracing process. + +220 +00:12:15,836 --> 00:12:18,805 +They do this +by recursively partitioning space + +221 +00:12:18,839 --> 00:12:23,343 +so we can quickly find which triangles +are likely to intersect a ray. + +222 +00:12:23,377 --> 00:12:25,412 +To support building complex scenes, + +223 +00:12:25,445 --> 00:12:28,549 +Metal supports two types +of acceleration structures: + +224 +00:12:28,582 --> 00:12:31,518 +primitive and instance +acceleration structures. + +225 +00:12:31,552 --> 00:12:34,221 +Individual pieces of geometry +are represented + +226 +00:12:34,254 --> 00:12:36,690 +using primitive acceleration structures. + +227 +00:12:36,723 --> 00:12:40,194 +They can be something simple +like a plane or a cube, + +228 +00:12:40,227 --> 00:12:44,398 +or something more complex +like a sphere or a triangle mesh. + +229 +00:12:44,431 --> 00:12:48,635 +You can create more complex scenes +using an instance acceleration structure. + +230 +00:12:48,669 --> 00:12:51,338 +Instance acceleration structures +create copies + +231 +00:12:51,371 --> 00:12:53,140 +of primitive acceleration structures. + +232 +00:12:53,173 --> 00:12:58,078 +First, define transformation matrices +for each object in your scene. + +233 +00:12:58,111 --> 00:13:01,014 +Then, use the array +of transformation matrices + +234 +00:13:01,048 --> 00:13:02,916 +and primitive acceleration structures + +235 +00:13:02,950 --> 00:13:05,619 +to build +an instance acceleration structure. + +236 +00:13:05,652 --> 00:13:09,823 +That's how you can build a static scene +using acceleration structures. + +237 +00:13:09,857 --> 00:13:12,659 +Next, let's see how dynamic applications + +238 +00:13:12,693 --> 00:13:15,128 +like a game would use +acceleration structures. + +239 +00:13:16,196 --> 00:13:18,131 +Let's start at the beginning: + +240 +00:13:18,165 --> 00:13:19,933 +there are several tasks you need to do + +241 +00:13:19,967 --> 00:13:22,970 +when first launching a game +or loading a new level. + +242 +00:13:23,003 --> 00:13:27,674 +This includes the normal tasks +like loading models and textures. + +243 +00:13:27,708 --> 00:13:31,612 +With ray tracing, you also need to build +primitive acceleration structures + +244 +00:13:31,645 --> 00:13:34,515 +for all of the models which will be used. + +245 +00:13:34,548 --> 00:13:37,951 +We recommend that you build as many +of your primitive acceleration structures + +246 +00:13:37,985 --> 00:13:42,189 +as possible at load time +to save time in your main rendering loop. + +247 +00:13:42,222 --> 00:13:44,391 +You can use +an instance acceleration structure + +248 +00:13:44,424 --> 00:13:48,529 +to add or remove these objects +from the scene as needed. + +249 +00:13:48,562 --> 00:13:51,765 +Once your app is done loading, +it enters the main loop. + +250 +00:13:51,798 --> 00:13:54,868 +Every frame, +it renders the scene using a combination + +251 +00:13:54,902 --> 00:13:58,438 +of rasterization, ray tracing, +and post-processing. + +252 +00:13:58,472 --> 00:14:01,041 +However, since games are very dynamic, + +253 +00:14:01,074 --> 00:14:05,045 +you will probably need to update +some of the acceleration structures. + +254 +00:14:05,078 --> 00:14:08,348 +This typically includes refitting +a handful of deforming + +255 +00:14:08,382 --> 00:14:11,518 +or animated models +such as skinned characters. + +256 +00:14:11,552 --> 00:14:14,688 +Refitting an existing +acceleration structure is much faster + +257 +00:14:14,721 --> 00:14:18,859 +than a full rebuild, so we recommend +using it for cases like this. + +258 +00:14:18,892 --> 00:14:22,563 +You should also do a full rebuild +of the instance acceleration structure. + +259 +00:14:22,596 --> 00:14:26,500 +This is necessary since objects may have +been added or removed + +260 +00:14:26,533 --> 00:14:30,504 +from the scene since the last frame, +or they may have moved significantly. + +261 +00:14:30,537 --> 00:14:33,974 +Doing a full rebuild is fine in this case +since there's only one + +262 +00:14:34,007 --> 00:14:35,576 +instance acceleration structure + +263 +00:14:35,609 --> 00:14:39,246 +and it usually only contains at most +a few thousand objects. + +264 +00:14:39,279 --> 00:14:43,483 +This year, we've improved performance +for all of these cases. + +265 +00:14:43,517 --> 00:14:46,253 +First, acceleration structure builds + +266 +00:14:46,286 --> 00:14:49,957 +are now up to 2.3 times faster +on Apple Silicon. + +267 +00:14:49,990 --> 00:14:53,427 +Second, refitting is also +up to 38% faster. + +268 +00:14:54,428 --> 00:14:58,832 +This means that both load times +and per-frame overhead are reduced. + +269 +00:14:58,866 --> 00:15:00,534 +But it gets even better. + +270 +00:15:00,567 --> 00:15:03,370 +Some applications build hundreds +or even thousands + +271 +00:15:03,403 --> 00:15:05,606 +of small primitive +acceleration structures. + +272 +00:15:05,639 --> 00:15:08,475 +These small builds +don't do enough work individually + +273 +00:15:08,509 --> 00:15:13,547 +to fill up the GPU, resulting in +long periods of low GPU utilization. + +274 +00:15:13,580 --> 00:15:16,450 +Therefore, multiple builds are +now automatically performed + +275 +00:15:16,483 --> 00:15:19,520 +in parallel whenever possible +on Apple Silicon. + +276 +00:15:19,553 --> 00:15:22,322 +This results +in up to 2.8 times faster builds + +277 +00:15:22,356 --> 00:15:24,224 +when they run in parallel. + +278 +00:15:24,258 --> 00:15:26,426 +This further reduces load times. + +279 +00:15:26,460 --> 00:15:28,395 +And this doesn't just apply to builds: + +280 +00:15:28,428 --> 00:15:31,365 +it applies to all +of the acceleration structure operations + +281 +00:15:31,398 --> 00:15:34,134 +including compacting and refitting, + +282 +00:15:34,168 --> 00:15:37,504 +so your per-frame overhead +is reduced as well. + +283 +00:15:37,538 --> 00:15:39,840 +There are a few guidelines +you will need to follow + +284 +00:15:39,873 --> 00:15:43,043 +to ensure that you can benefit +from this optimization. + +285 +00:15:43,076 --> 00:15:46,780 +Here is an example that builds +an array of acceleration structures. + +286 +00:15:46,813 --> 00:15:49,850 +To build them in parallel, +you will need to ensure that you use + +287 +00:15:49,883 --> 00:15:53,387 +the same acceleration structure +command encoder for many builds. + +288 +00:15:53,420 --> 00:15:58,492 +Additionally, builds which use the same +scratch buffer can't run in parallel. + +289 +00:15:58,525 --> 00:16:02,396 +Therefore, you will want to ensure +that you are looping through a small pool + +290 +00:16:02,429 --> 00:16:06,033 +of scratch buffers rather than using +the same scratch buffer for each build. + +291 +00:16:07,234 --> 00:16:09,369 +Those are all the performance improvements + +292 +00:16:09,403 --> 00:16:12,606 +we've made to building +acceleration structures. + +293 +00:16:12,639 --> 00:16:14,675 +We've also added three new features + +294 +00:16:14,708 --> 00:16:18,212 +to make building acceleration structures +easier and more efficient. + +295 +00:16:19,546 --> 00:16:24,284 +They are support for additional +vertex formats, transformation matrices, + +296 +00:16:24,318 --> 00:16:26,954 +and acceleration structure allocation +from heaps. + +297 +00:16:29,089 --> 00:16:32,326 +Let's start with vertex formats. + +298 +00:16:32,359 --> 00:16:35,596 +A common performance optimization +is to use quantized + +299 +00:16:35,629 --> 00:16:39,166 +or reduced precision formats +for vertex data, + +300 +00:16:39,199 --> 00:16:41,468 +resulting in lower memory usage. + +301 +00:16:41,502 --> 00:16:44,137 +This year, you can build +acceleration structures + +302 +00:16:44,171 --> 00:16:46,607 +from a wide range of vertex formats. + +303 +00:16:46,640 --> 00:16:50,577 +This includes half precision +floating point formats, + +304 +00:16:50,611 --> 00:16:53,981 +two component vertex formats +for planar geometry, + +305 +00:16:54,014 --> 00:16:57,584 +and all the usual +normalized integer formats. + +306 +00:16:57,618 --> 00:17:00,687 +Previously, +acceleration structures have required + +307 +00:17:00,721 --> 00:17:04,191 +three component, +full-precision floating point vertex data. + +308 +00:17:04,224 --> 00:17:07,427 +In this example, +the application has vertex data + +309 +00:17:07,461 --> 00:17:10,097 +in a half precision vertex format. + +310 +00:17:10,130 --> 00:17:14,067 +This data needs to be unpacked +and copied into a temporary buffer + +311 +00:17:14,101 --> 00:17:16,537 +just to build the acceleration structure. + +312 +00:17:16,570 --> 00:17:18,605 +With the new vertex formats feature, + +313 +00:17:18,639 --> 00:17:21,808 +acceleration structure builds +can now consume vertex data + +314 +00:17:21,842 --> 00:17:24,111 +in any of the supported formats, + +315 +00:17:24,144 --> 00:17:27,281 +eliminating the need to create +a temporary copy. + +316 +00:17:27,314 --> 00:17:29,850 +Setting the vertex format +couldn't be simpler. + +317 +00:17:29,883 --> 00:17:34,121 +All you need to do is set the property +on your geometry descriptor. + +318 +00:17:34,154 --> 00:17:37,357 +Next, let's talk about +transformation matrices. + +319 +00:17:37,391 --> 00:17:40,227 +This feature complements +the new vertex formats, + +320 +00:17:40,260 --> 00:17:42,629 +so that you can pre-transform +your vertex data + +321 +00:17:42,663 --> 00:17:45,199 +before building +the acceleration structure. + +322 +00:17:45,232 --> 00:17:47,701 +For example, you might want to use them + +323 +00:17:47,734 --> 00:17:51,505 +to unpack complex meshes +stored in a normalized format. + +324 +00:17:51,538 --> 00:17:54,808 +Let's consider +the Red Panda model in this scene. + +325 +00:17:54,842 --> 00:17:58,712 +To normalize the geometry to use +one of our compressed formats, + +326 +00:17:58,745 --> 00:18:01,715 +you take the mesh, calculate its bounds, + +327 +00:18:01,748 --> 00:18:04,952 +and then scale them +to a zero to one range. + +328 +00:18:04,985 --> 00:18:09,189 +You can then use one of the normalized +integer vertex formats to store the mesh, + +329 +00:18:09,223 --> 00:18:13,760 +reducing the amount of space +it takes up on disk and in memory. + +330 +00:18:13,794 --> 00:18:17,598 +At runtime, +you provide a matrix that will scale + +331 +00:18:17,631 --> 00:18:21,134 +and offset each vertex +to the final position. + +332 +00:18:21,168 --> 00:18:25,038 +Applying that matrix +retrieves the original model. + +333 +00:18:25,072 --> 00:18:28,509 +Now let's walk through +how to set up acceleration structure + +334 +00:18:28,542 --> 00:18:30,611 +passing a transformation matrix. + +335 +00:18:30,644 --> 00:18:33,714 +You start by creating +the transform buffer. + +336 +00:18:33,747 --> 00:18:38,519 +One way of doing this is to create +an MTLPackedFloat4x3 object + +337 +00:18:38,552 --> 00:18:41,889 +containing the scale +and offset transformation matrix. + +338 +00:18:41,922 --> 00:18:46,193 +Then, create a Metal Buffer +big enough to hold the matrix. + +339 +00:18:46,226 --> 00:18:49,796 +And finally, +copy the matrix to the Buffer. + +340 +00:18:49,830 --> 00:18:52,666 +Next, set up the acceleration structure. + +341 +00:18:52,699 --> 00:18:55,536 +First, create +a triangle geometry descriptor. + +342 +00:18:55,569 --> 00:18:58,705 +Then, specify +the transformation Matrix Buffer. + +343 +00:18:58,739 --> 00:19:01,575 +And finally the Buffer Offset. + +344 +00:19:01,608 --> 00:19:05,179 +That's all you need to do to set up +the transformation matrix. + +345 +00:19:05,212 --> 00:19:09,550 +These matrices can also be used +to combine simple acceleration structures + +346 +00:19:09,583 --> 00:19:11,985 +to improve ray tracing performance. + +347 +00:19:12,019 --> 00:19:14,188 +Let's see an example scene. + +348 +00:19:14,221 --> 00:19:19,092 +Here, the boxes and the spheres +are all relatively simple meshes. + +349 +00:19:19,126 --> 00:19:22,729 +This presents an opportunity +to optimize the acceleration structure + +350 +00:19:22,763 --> 00:19:25,566 +for this group at the front of the scene. + +351 +00:19:25,599 --> 00:19:28,535 +Focusing on +the instance acceleration structure, + +352 +00:19:28,569 --> 00:19:32,272 +there is an overhead for each instance +that your rays hit. + +353 +00:19:32,306 --> 00:19:34,441 +There is a cost for transforming the ray + +354 +00:19:34,474 --> 00:19:38,412 +and then switching from the instance +to the primitive acceleration structure. + +355 +00:19:38,445 --> 00:19:42,316 +This occurs more often +with overlapping instances. + +356 +00:19:42,349 --> 00:19:44,151 +To reduce the instance count, + +357 +00:19:44,184 --> 00:19:46,954 +you can generate +a single primitive acceleration structure + +358 +00:19:46,987 --> 00:19:50,591 +that contains +both the boxes and the sphere. + +359 +00:19:50,624 --> 00:19:53,427 +To do this, +you can create a geometry descriptor + +360 +00:19:53,460 --> 00:19:57,397 +for each object, +each with its own transformation matrix. + +361 +00:19:57,431 --> 00:20:00,834 +The resulting primitive acceleration +structure is a single instance + +362 +00:20:00,868 --> 00:20:05,606 +in the instance acceleration structure +and contains the boxes and sphere. + +363 +00:20:05,639 --> 00:20:09,309 +This should result in a better +performing acceleration structure. + +364 +00:20:09,343 --> 00:20:11,378 +Let's see how to set this up in code. + +365 +00:20:12,779 --> 00:20:16,416 +You start with the descriptor +that defines the sphere geometry. + +366 +00:20:16,450 --> 00:20:20,721 +Next, set the vertex buffer, +index buffer, and other properties + +367 +00:20:20,754 --> 00:20:24,024 +as usual +for a primitive acceleration structure. + +368 +00:20:24,057 --> 00:20:26,927 +The difference is, you also specify +the transform buffer + +369 +00:20:26,960 --> 00:20:30,797 +that contains the transformation matrix +used for the copy of the sphere. + +370 +00:20:32,466 --> 00:20:35,502 +For the boxes, +you have multiple geometry descriptors + +371 +00:20:35,536 --> 00:20:38,372 +sharing a vertex and index buffer. + +372 +00:20:38,405 --> 00:20:42,609 +You just need to specify different +transform buffers for each copy. + +373 +00:20:42,643 --> 00:20:47,114 +Finally, when creating the descriptor +for the primitive acceleration structure, + +374 +00:20:47,147 --> 00:20:49,750 +add all the geometry descriptors. + +375 +00:20:49,783 --> 00:20:52,419 +This will result +in a primitive acceleration structure + +376 +00:20:52,452 --> 00:20:56,123 +that you can instance into the scene +with an identity transform. + +377 +00:20:56,156 --> 00:20:59,393 +This primitive acceleration structure +will take less time to build + +378 +00:20:59,426 --> 00:21:03,330 +than separate acceleration structures +and will be faster to intersect. + +379 +00:21:04,998 --> 00:21:08,502 +Finally, heap allocation +of acceleration structures has been + +380 +00:21:08,535 --> 00:21:11,605 +one of our most requested features. + +381 +00:21:11,638 --> 00:21:14,041 +With this feature, +you now have more control + +382 +00:21:14,074 --> 00:21:16,577 +over acceleration structure allocation. + +383 +00:21:16,610 --> 00:21:20,047 +It also allows you to reuse +heap memory between allocations, + +384 +00:21:20,080 --> 00:21:23,250 +avoiding expensive buffer allocations. + +385 +00:21:23,283 --> 00:21:27,054 +Heaps can also help improve performance +by reducing calls + +386 +00:21:27,087 --> 00:21:31,525 +to the useResource: method when using +instance acceleration structures. + +387 +00:21:31,558 --> 00:21:33,794 +Going back to the example scene, + +388 +00:21:33,827 --> 00:21:36,430 +the instance acceleration structure +references + +389 +00:21:36,463 --> 00:21:39,132 +primitive acceleration structures +indirectly. + +390 +00:21:39,166 --> 00:21:42,870 +This means that each time you want to use +an instance acceleration structure + +391 +00:21:42,903 --> 00:21:46,507 +with a command encoder, +you need to call useResource: method + +392 +00:21:46,540 --> 00:21:49,243 +for each primitive acceleration structure. + +393 +00:21:49,276 --> 00:21:52,346 +For large scenes, +this could require thousands of calls + +394 +00:21:52,379 --> 00:21:56,116 +to useResource: each time you use +the instance acceleration structure. + +395 +00:21:56,149 --> 00:21:58,852 +Knowing that you have +so many useResource: calls, + +396 +00:21:58,886 --> 00:22:02,656 +you could call useResources: +to reduce the number of API calls, + +397 +00:22:02,689 --> 00:22:06,260 +but you still need to maintain +an array of your acceleration structures + +398 +00:22:06,293 --> 00:22:09,062 +and Metal still needs to loop +through the array. + +399 +00:22:09,096 --> 00:22:11,064 +Instead, you can allocate + +400 +00:22:11,098 --> 00:22:14,668 +all of these primitive acceleration +structures from the same heap. + +401 +00:22:14,701 --> 00:22:17,704 +When you want to use +the instance acceleration structure, + +402 +00:22:17,738 --> 00:22:20,874 +you can simply make a single call +to the useHeap: method + +403 +00:22:20,908 --> 00:22:24,077 +to reference all +of the primitive acceleration structures. + +404 +00:22:24,111 --> 00:22:27,147 +We saw a small performance improvement +in one application + +405 +00:22:27,181 --> 00:22:29,816 +simply by replacing the calls +to useResource: + +406 +00:22:29,850 --> 00:22:32,186 +with a single call to useHeap:. + +407 +00:22:32,219 --> 00:22:35,455 +Let's see how you can allocate +an acceleration structure from a heap. + +408 +00:22:35,489 --> 00:22:39,793 +You can directly allocate an acceleration +structure by calling a method on the heap + +409 +00:22:39,826 --> 00:22:43,197 +that takes the acceleration structure +descriptor as the input. + +410 +00:22:43,230 --> 00:22:46,066 +If you are not allocating +using the descriptor, + +411 +00:22:46,099 --> 00:22:49,703 +the Metal device determines +the size and alignment requirement + +412 +00:22:49,736 --> 00:22:52,606 +for allocating +the acceleration structure from a heap. + +413 +00:22:52,639 --> 00:22:55,309 +You can get this information +from the Metal device + +414 +00:22:55,342 --> 00:22:59,012 +by providing the descriptor +or acceleration structure size. + +415 +00:22:59,046 --> 00:23:01,148 +Once the final size is determined, + +416 +00:23:01,181 --> 00:23:04,351 +you can allocate the acceleration +structure from the heap. + +417 +00:23:04,384 --> 00:23:07,621 +There are a few things to remember +when using heaps. + +418 +00:23:07,654 --> 00:23:12,025 +First, remember to call useHeap: +to make all of the acceleration structures + +419 +00:23:12,059 --> 00:23:15,596 +in the heap resident +for the duration of the ray tracing pass. + +420 +00:23:15,629 --> 00:23:20,734 +Second, by default, Metal doesn't track +resources you allocate from a heap. + +421 +00:23:20,767 --> 00:23:23,570 +You can either opt-in +to resource hazard tracking, + +422 +00:23:23,604 --> 00:23:27,407 +or you can manually manage +your own synchronization. + +423 +00:23:27,441 --> 00:23:31,512 +You can use MTLFences to synchronize +across command encoders + +424 +00:23:31,545 --> 00:23:35,215 +and MTLEvents to synchronize +across command buffers. + +425 +00:23:35,249 --> 00:23:38,352 +Those are the new features +and performance improvements + +426 +00:23:38,385 --> 00:23:40,921 +in the Metal ray tracing API this year. + +427 +00:23:40,954 --> 00:23:43,991 +Next, Dominik will talk about improvements + +428 +00:23:44,024 --> 00:23:47,294 +to Xcode's Metal tools +that will boost your productivity + +429 +00:23:47,327 --> 00:23:49,196 +when developing ray tracing applications. + +430 +00:23:49,229 --> 00:23:50,964 +Dominik: Thanks, Yi. + +431 +00:23:50,998 --> 00:23:54,401 +There are a lot of enhancements +to the Metal tools in Xcode 14, + +432 +00:23:54,434 --> 00:23:57,104 +but here, I would like to highlight +just a few + +433 +00:23:57,137 --> 00:24:00,674 +that are especially useful +when developing ray tracing applications + +434 +00:24:01,642 --> 00:24:04,678 +Starting with the Metal debugger, +I'll talk about improvements + +435 +00:24:04,711 --> 00:24:09,583 +to the Acceleration Structure Viewer, +Shader Profiler, and Shader Debugger. + +436 +00:24:10,551 --> 00:24:14,388 +Then I'll round it up +with the runtime Shader Validation. + +437 +00:24:16,523 --> 00:24:21,161 +First, let's take a look +at the Acceleration Structure Viewer. + +438 +00:24:21,195 --> 00:24:25,999 +The Acceleration Structure Viewer in +the Metal Debugger enables you to inspect, + +439 +00:24:26,033 --> 00:24:29,169 +in great detail, +all of the geometries and instances + +440 +00:24:29,203 --> 00:24:32,573 +of all the meshes that make up +your acceleration structure. + +441 +00:24:34,141 --> 00:24:37,744 +Xcode 14 now supports +debugging acceleration structures + +442 +00:24:37,778 --> 00:24:41,982 +with primitive or instanced motion +and a new highlight mode + +443 +00:24:42,015 --> 00:24:46,954 +for visualizing primitives +with an inspector for per-primitive data. + +444 +00:24:46,987 --> 00:24:48,722 +Let's see them in action. + +445 +00:24:49,523 --> 00:24:52,593 +If you are using +acceleration structures with motion, + +446 +00:24:52,626 --> 00:24:55,095 +you now have a scrubber in the bottom bar + +447 +00:24:55,128 --> 00:24:58,899 +for viewing your acceleration structure +at different points in time. + +448 +00:24:58,932 --> 00:25:02,135 +On the right of the scrubber +is a "play" button. + +449 +00:25:02,169 --> 00:25:05,339 +You can use it to play the animation +back and forth in a loop. + +450 +00:25:05,372 --> 00:25:08,976 +Now let me show you +how to inspect individual primitives + +451 +00:25:09,009 --> 00:25:11,144 +in your acceleration structure. + +452 +00:25:11,178 --> 00:25:15,516 +This is especially useful if you are +using the new per-primitive data API. + +453 +00:25:15,549 --> 00:25:19,853 +And so there's a new highlight mode +just for this. + +454 +00:25:19,887 --> 00:25:23,790 +Primitive highlight mode gives you access +to all primitive data... + +455 +00:25:25,292 --> 00:25:29,296 +And allows you to select specific +primitives for detailed inspection. + +456 +00:25:30,631 --> 00:25:34,468 +In the left sidebar, +you can find arrows next to the data rows. + +457 +00:25:35,736 --> 00:25:38,438 +Clicking on an arrow will reveal a popover + +458 +00:25:38,472 --> 00:25:41,308 +that displays the corresponding data +for the primitive. + +459 +00:25:41,341 --> 00:25:44,178 +These additions +to the acceleration structure viewer + +460 +00:25:44,211 --> 00:25:47,581 +ensure you have full access, +down to each primitive, + +461 +00:25:47,614 --> 00:25:51,952 +to all of the components +that make up your acceleration structure. + +462 +00:25:51,985 --> 00:25:55,522 +Next, let's talk about improvements +to the Shader Profiler. + +463 +00:25:55,556 --> 00:26:00,160 +The Shader Profiler gives you insights +into the performance of your shader, + +464 +00:26:00,194 --> 00:26:03,297 +providing per-pipeline +execution timing costs, + +465 +00:26:03,330 --> 00:26:07,968 +and on Apple GPUs, it provides +more granularity at the source level, + +466 +00:26:08,001 --> 00:26:13,240 +showing the execution costs per-line +distributed across instruction categories. + +467 +00:26:13,273 --> 00:26:17,477 +In Xcode 14, +profiling GPU captures has been updated + +468 +00:26:17,511 --> 00:26:22,516 +to support intersection functions, +visible functions, and dynamic libraries. + +469 +00:26:23,884 --> 00:26:28,388 +Here we have a ray tracing kernel +using an intersection function. + +470 +00:26:28,422 --> 00:26:31,091 +You can now view +the per-line profiling results + +471 +00:26:31,124 --> 00:26:33,861 +inside of the intersection function. + +472 +00:26:33,894 --> 00:26:36,496 +This includes a breakdown +of the instruction categories + +473 +00:26:36,530 --> 00:26:38,365 +that contribute to the cost. + +474 +00:26:41,969 --> 00:26:45,005 +Profiling visible functions +works the same way. + +475 +00:26:46,306 --> 00:26:49,409 +And similarly, +detailed profiling information + +476 +00:26:49,443 --> 00:26:53,180 +is now available for shader code +from linked dynamic libraries. + +477 +00:26:53,213 --> 00:26:56,283 +With these additions, +you now have the full breakdown + +478 +00:26:56,316 --> 00:26:59,953 +of the performance of your pipeline, +down to each line of code. + +479 +00:27:02,122 --> 00:27:04,024 +Moving on to the Shader Debugger. + +480 +00:27:04,057 --> 00:27:05,158 +The Shader Debugger provides + +481 +00:27:05,192 --> 00:27:07,528 +a unique +and incredibly productive workflow + +482 +00:27:07,561 --> 00:27:10,397 +for debugging the correctness +of your shader code. + +483 +00:27:10,430 --> 00:27:13,634 +Just like with the Shader Profiler, +we've also extended support + +484 +00:27:13,667 --> 00:27:17,271 +to enable debugging of Linked functions +and Dynamic libraries. + +485 +00:27:17,304 --> 00:27:19,806 +Here we have a ray tracing kernel +that calls out + +486 +00:27:19,840 --> 00:27:24,011 +to a linked visible function passed in +through a visible function table. + +487 +00:27:27,247 --> 00:27:30,250 +You are now able to follow +the execution of a kernel + +488 +00:27:30,284 --> 00:27:32,920 +all the way +into your visible function code + +489 +00:27:32,953 --> 00:27:36,123 +to verify that the code behaves +as you expect it to. + +490 +00:27:37,257 --> 00:27:41,195 +Again, the same applies +to debugging dynamic libraries. + +491 +00:27:41,228 --> 00:27:45,265 +You are also able to jump into +and out of any executed dynamic libraries + +492 +00:27:45,299 --> 00:27:47,134 +that are linked to your pipeline. + +493 +00:27:47,167 --> 00:27:48,802 +With these additions, you now have + +494 +00:27:48,836 --> 00:27:51,104 +a complete picture +of your shader execution + +495 +00:27:51,138 --> 00:27:54,141 +across linked functions +and libraries in your pipeline. + +496 +00:27:55,943 --> 00:27:59,379 +Now, before you capture +and jump into the Shader Debugger, + +497 +00:27:59,413 --> 00:28:03,050 +it is often a good idea to enable +Shader Validation at runtime. + +498 +00:28:05,686 --> 00:28:10,324 +Shader validation is a great way +to diagnose runtime errors on the GPU, + +499 +00:28:10,357 --> 00:28:14,027 +catching issues +such as out-of-bound memory accesses, + +500 +00:28:14,061 --> 00:28:16,463 +null texture reads, and more. + +501 +00:28:16,496 --> 00:28:20,534 +To enable Shader Validation in Xcode, +all you need to do is to go + +502 +00:28:20,567 --> 00:28:24,037 +to the "Edit Scheme" dialog, +select the "Run" action, + +503 +00:28:24,071 --> 00:28:28,775 +and under the "diagnostics" tab +tick the "Shader Validation" checkbox. + +504 +00:28:28,809 --> 00:28:31,044 +And you are all set to go. + +505 +00:28:31,078 --> 00:28:34,748 +In Metal 3, we have added +Stack Overflow detection + +506 +00:28:34,781 --> 00:28:36,350 +which will help you quickly find issues + +507 +00:28:36,383 --> 00:28:40,220 +that would otherwise result +in undefined behavior. + +508 +00:28:40,254 --> 00:28:43,524 +I'll quickly elaborate +on the function stack in Metal shaders + +509 +00:28:43,557 --> 00:28:45,726 +and the problem of Stack Overflow. + +510 +00:28:45,759 --> 00:28:48,795 +The call stack is a region +in device memory + +511 +00:28:48,829 --> 00:28:53,267 +where Metal stores the values of local +data used in your shader functions. + +512 +00:28:53,300 --> 00:28:56,103 +If the called function is +not known at compile time, + +513 +00:28:56,136 --> 00:28:58,405 +Metal needs your help in estimating + +514 +00:28:58,438 --> 00:29:01,642 +the amount of memory required +for the stack. + +515 +00:29:01,675 --> 00:29:05,345 +An example of a call to a function +that is unknown at compile time + +516 +00:29:05,379 --> 00:29:08,448 +may be a ray tracing +intersection function. + +517 +00:29:08,482 --> 00:29:11,051 +If you are using +custom intersection functions, + +518 +00:29:11,084 --> 00:29:16,123 +maximum call stack depth should be +set to 1, to allocate space for it. + +519 +00:29:16,156 --> 00:29:19,826 +This is the default value, so there is +nothing more that you need to do. + +520 +00:29:19,860 --> 00:29:24,231 +However, if you are using Function Tables +to call into a Visible function, + +521 +00:29:24,264 --> 00:29:28,302 +this is another example of a function call +unknown at compile time. + +522 +00:29:28,335 --> 00:29:31,038 +If you perform such a call +from an intersection function, + +523 +00:29:31,071 --> 00:29:34,708 +like in this example, +your call stack will be two levels deep. + +524 +00:29:36,376 --> 00:29:39,813 +Another example are calls +to dynamic libraries + +525 +00:29:39,847 --> 00:29:43,250 +and calling a local function +using a function pointer. + +526 +00:29:43,283 --> 00:29:47,020 +In this example, our call stack has +four levels with nested calls + +527 +00:29:47,054 --> 00:29:48,789 +to different types of functions + +528 +00:29:48,822 --> 00:29:51,625 +that cannot be resolved +when the shader is compiled. + +529 +00:29:51,658 --> 00:29:55,095 +To properly configure Metal +to allocate the right amount of memory, + +530 +00:29:55,128 --> 00:29:59,633 +you need to specify a maximum +call stack depth of 4 yourself. + +531 +00:29:59,666 --> 00:30:02,469 +The important thing to remember +is that when the value + +532 +00:30:02,503 --> 00:30:05,772 +of the max Call Stack Depth is set +too low for your program, + +533 +00:30:05,806 --> 00:30:09,843 +Stack Overflow can happen, +resulting in undefined behavior. + +534 +00:30:09,877 --> 00:30:13,313 +But if you are running +with Shader Validation enabled, + +535 +00:30:13,347 --> 00:30:15,582 +such situations will be caught early, + +536 +00:30:15,616 --> 00:30:20,254 +and you will see information in Xcode +about where the Stack Overflow occurred. + +537 +00:30:20,287 --> 00:30:22,856 +You can then go and fix your shader code, + +538 +00:30:22,890 --> 00:30:26,560 +or adjust your maximum call stack depth +in the pipeline descriptor. + +539 +00:30:26,593 --> 00:30:30,297 +All of these new improvements +to the Metal tools in Xcode 14 + +540 +00:30:30,330 --> 00:30:33,133 +ensure you have an even more +complete picture and insight + +541 +00:30:33,166 --> 00:30:37,304 +into the performance and correctness +of your ray tracing applications. + +542 +00:30:37,337 --> 00:30:40,340 +For more on how to get the most +out of the Metal tools for debugging + +543 +00:30:40,374 --> 00:30:43,243 +and profiling, +check out these other sessions + +544 +00:30:45,412 --> 00:30:48,782 +This session has been all about +maximizing Metal ray tracing performance + +545 +00:30:48,815 --> 00:30:50,284 +for your applications. + +546 +00:30:50,317 --> 00:30:52,953 +We talked about +how you can squeeze out more performance + +547 +00:30:52,986 --> 00:30:57,558 +and simplify your code using new features +such as per-primitive data. + +548 +00:30:57,591 --> 00:31:00,627 +We also described +optimization techniques and features + +549 +00:31:00,661 --> 00:31:04,097 +that make building accelerations +structures faster and more convenient + +550 +00:31:04,131 --> 00:31:05,832 +than ever before. + +551 +00:31:05,866 --> 00:31:09,236 +Finally, we covered all the new +enhancements to the Metal tools + +552 +00:31:09,269 --> 00:31:11,405 +in Xcode 14 that will provide you + +553 +00:31:11,438 --> 00:31:14,107 +with deeper insight +during your development. + +554 +00:31:14,141 --> 00:31:15,809 +Thanks for watching. + diff --git a/eng/2022 Session 10106 Profile and optimize your game's memory en.srt b/eng/2022 Session 10106 Profile and optimize your game's memory en.srt new file mode 100644 index 0000000..048b4d5 --- /dev/null +++ b/eng/2022 Session 10106 Profile and optimize your game's memory en.srt @@ -0,0 +1,2962 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,810 --> 00:00:13,046 +Welcome to Profile and +Optimize Your Game's Memory. + +3 +00:00:13,080 --> 00:00:16,149 +I'm Jack Xu (许) from GPU Software team +at Apple. + +4 +00:00:16,183 --> 00:00:18,952 +We're joined by my colleague Seth Lù (陆). + +5 +00:00:18,986 --> 00:00:21,688 +In the last few years, +our teams have been working + +6 +00:00:21,722 --> 00:00:23,490 +with game developers like you + +7 +00:00:23,524 --> 00:00:26,560 +to understand +and improve game memory together. + +8 +00:00:26,593 --> 00:00:28,996 +Today, we want to share our learnings, + +9 +00:00:29,029 --> 00:00:32,933 +so you can also have a head start +when you debug your game's memory + +10 +00:00:32,966 --> 00:00:36,803 +and create awesome games +with the best player experience. + +11 +00:00:37,905 --> 00:00:42,943 +We're going to break down memory usage in +your game, from both CPU and GPU objects. + +12 +00:00:43,877 --> 00:00:46,680 +Plus, analyze allocations in your game, + +13 +00:00:46,713 --> 00:00:48,549 +actual use on physical memory, + +14 +00:00:48,582 --> 00:00:51,285 +and references between objects. + +15 +00:00:51,318 --> 00:00:53,554 +Because there are many facets of memory, + +16 +00:00:53,587 --> 00:00:58,292 +our developer tools also reveal +memory mysteries from different angles. + +17 +00:00:58,325 --> 00:01:02,296 +We'll take a guided tour, +and experience how we use all of them, + +18 +00:01:02,329 --> 00:01:06,800 +including Xcode, Instruments, +and command line tools in Terminal. + +19 +00:01:06,834 --> 00:01:12,039 +In today's guided tour, we will begin +with a prelude to understand game memory. + +20 +00:01:12,072 --> 00:01:15,275 +And start to profile memory +and memory growth, + +21 +00:01:15,309 --> 00:01:18,679 +where Seth will tell us about Instruments. + +22 +00:01:18,712 --> 00:01:23,383 +After taking the temporal approach with +Instruments, we will further our journey, + +23 +00:01:23,417 --> 00:01:28,488 +and analyze the memory graph of your game +using tools in Xcode and Terminal. + +24 +00:01:28,522 --> 00:01:31,658 +These workflows focus on +the current state of memory use, + +25 +00:01:31,692 --> 00:01:35,329 +and the breakdown +of the total game memory. + +26 +00:01:35,362 --> 00:01:38,765 +Finally, Seth will share +how you can use Metal Debugger + +27 +00:01:38,799 --> 00:01:40,734 +to optimize Metal resources, + +28 +00:01:40,767 --> 00:01:45,739 +which is a somewhat standalone, +yet core area in game memory. + +29 +00:01:45,772 --> 00:01:50,177 +Now, let's start rolling +with understanding game memory. + +30 +00:01:51,044 --> 00:01:53,213 +When you launch your game from Xcode, + +31 +00:01:53,247 --> 00:01:56,316 +such as the Modern Rendering +with Metal sample code, + +32 +00:01:56,350 --> 00:02:00,420 +you can open this Memory Report +in Xcode's debug navigator. + +33 +00:02:00,454 --> 00:02:04,825 +It is your first view on the game's +current and recent memory usage, + +34 +00:02:04,858 --> 00:02:07,127 +and its impact level on the system. + +35 +00:02:08,862 --> 00:02:12,866 +The number on the gauge shows you +the current memory use of the game. + +36 +00:02:12,900 --> 00:02:15,002 +An important first step +in memory debugging + +37 +00:02:15,035 --> 00:02:17,504 +is to understand what this number means. + +38 +00:02:19,173 --> 00:02:20,707 +To put it in one line, + +39 +00:02:20,741 --> 00:02:25,979 +actual memory use in your game +is not the same as allocations. + +40 +00:02:26,013 --> 00:02:28,682 +Actual memory use is on physical memory. + +41 +00:02:28,715 --> 00:02:31,952 +While allocations +is the memory requested by the game, + +42 +00:02:31,985 --> 00:02:34,221 +on the virtual memory address space. + +43 +00:02:34,254 --> 00:02:38,125 +And different kinds of allocations +are naturally calculated separately. + +44 +00:02:41,061 --> 00:02:42,896 +When your game allocates memory, + +45 +00:02:42,930 --> 00:02:47,134 +those new allocations do not immediately +nor directly take up space + +46 +00:02:47,167 --> 00:02:48,602 +on physical memory. + +47 +00:02:48,635 --> 00:02:53,006 +On the contrary, they will reserve some +space on virtual memory address space, + +48 +00:02:53,040 --> 00:02:56,577 +which the system provides +for each process. + +49 +00:02:56,610 --> 00:02:59,646 +And when the program actually +uses this allocation later, + +50 +00:02:59,680 --> 00:03:02,850 +will the system prepare space +on physical memory. + +51 +00:03:04,518 --> 00:03:08,055 +Allocations of the same kind +are grouped into categories, + +52 +00:03:08,088 --> 00:03:11,291 +and sparsely occupy +the virtual address space. + +53 +00:03:11,325 --> 00:03:13,760 +These categories may include: + +54 +00:03:13,794 --> 00:03:16,363 +the program's executable binary; + +55 +00:03:16,396 --> 00:03:18,799 +all the libraries and frameworks; + +56 +00:03:18,832 --> 00:03:22,002 +the stack, providing storage +for local and temporary variables + +57 +00:03:22,035 --> 00:03:24,872 +as well as some function arguments; + +58 +00:03:24,905 --> 00:03:27,641 +dynamic memory regions also known as heap; + +59 +00:03:27,674 --> 00:03:29,543 +including class instance storage + +60 +00:03:29,576 --> 00:03:32,679 +and memory the program manually allocates; + +61 +00:03:32,713 --> 00:03:37,017 +regions mapped from read-only resources +such as game asset files; + +62 +00:03:37,050 --> 00:03:39,620 +and of course, Metal objects in your game, + +63 +00:03:39,653 --> 00:03:44,691 +such as buffers, textures, +and pipeline state objects. + +64 +00:03:44,725 --> 00:03:48,729 +And these categories are made of regions. + +65 +00:03:48,762 --> 00:03:53,767 +Under the hood, memory operations +work at the granularity of memory pages, + +66 +00:03:53,800 --> 00:03:57,905 +which are 16 kibibytes each +on modern Apple devices. + +67 +00:03:57,938 --> 00:04:01,708 +This means each region takes up +one or more pages, + +68 +00:04:01,742 --> 00:04:04,344 +and is at least 16 kibibytes large. + +69 +00:04:05,646 --> 00:04:10,017 +As the game continues, +the state of its memory keeps evolving; + +70 +00:04:10,050 --> 00:04:11,952 +new objects get allocated, + +71 +00:04:11,985 --> 00:04:14,021 +old items get destroyed, + +72 +00:04:14,054 --> 00:04:16,757 +the regions keep changing. + +73 +00:04:16,790 --> 00:04:20,227 +But only used pages on the regions +are on physical memory, + +74 +00:04:20,260 --> 00:04:23,263 +which the system diligently charges +to your game, + +75 +00:04:23,297 --> 00:04:25,332 +as to any other apps. + +76 +00:04:27,234 --> 00:04:30,070 +Memory pages in your game +can be one of three kinds: + +77 +00:04:30,103 --> 00:04:32,973 +dirty, compressed, and clean. + +78 +00:04:33,006 --> 00:04:35,776 +Let's check out what they are. + +79 +00:04:35,809 --> 00:04:39,980 +Dirty memory pages includes memory +that your game has written to. + +80 +00:04:40,013 --> 00:04:42,850 +This includes memory allocations in heap, + +81 +00:04:42,883 --> 00:04:47,888 +and frameworks, once your game modifies +those variables or symbols. + +82 +00:04:47,921 --> 00:04:49,890 +On devices with Apple silicon, + +83 +00:04:49,923 --> 00:04:53,660 +accessed Metal resources +also fall into this category, + +84 +00:04:53,694 --> 00:04:59,132 +this is because CPU and GPU share +the same pool of fast unified memory. + +85 +00:05:00,501 --> 00:05:04,071 +However, if some dirty pages +are not used for a long time, + +86 +00:05:04,104 --> 00:05:07,207 +the system might reduce +their presence on physical memory + +87 +00:05:07,241 --> 00:05:10,944 +by compressing these pages +or storing them on flash or disk, + +88 +00:05:10,978 --> 00:05:12,913 +which we call swapping. + +89 +00:05:12,946 --> 00:05:17,050 +This will allow the device +to run more apps and services. + +90 +00:05:17,084 --> 00:05:20,387 +Later, when your game asks +for these pages again, + +91 +00:05:20,420 --> 00:05:24,024 +the system will decompress +or page in them from disk. + +92 +00:05:24,057 --> 00:05:29,663 +Note, your game will still get charged +for their uncompressed size. + +93 +00:05:29,696 --> 00:05:31,598 +For clean memory pages, + +94 +00:05:31,632 --> 00:05:34,635 +they include read-only files +mapped from disk, + +95 +00:05:34,668 --> 00:05:37,804 +such as texture or audio assets, + +96 +00:05:37,838 --> 00:05:40,874 +and frameworks loaded into the process. + +97 +00:05:40,908 --> 00:05:45,279 +The system can empty or reload them +from disk at any time, + +98 +00:05:45,312 --> 00:05:48,582 +so they don't count towards +your game's memory footprint. + +99 +00:05:48,615 --> 00:05:51,852 +However, they may be resident on memory, + +100 +00:05:51,885 --> 00:05:57,057 +and excessive use will slow down +the system and your game. + +101 +00:05:57,090 --> 00:06:01,361 +It's usually the most interesting +to look at the first two parts, + +102 +00:06:01,395 --> 00:06:04,064 +which combined, we call memory footprint. + +103 +00:06:04,097 --> 00:06:07,868 +And the system uses this +to enforce memory limit. + +104 +00:06:09,970 --> 00:06:13,507 +In some terminologies, +people say "dirty memory" + +105 +00:06:13,540 --> 00:06:18,345 +when they mean memory footprint, +as dirty is the opposite of clean. + +106 +00:06:18,378 --> 00:06:21,281 +But don't worry, when things become fuzzy, + +107 +00:06:21,315 --> 00:06:24,184 +we will call out which one we mean. + +108 +00:06:24,218 --> 00:06:29,089 +So now you know how memory works, +and how system charges it to the game. + +109 +00:06:29,990 --> 00:06:32,292 +Besides this Xcode memory gauge, + +110 +00:06:32,326 --> 00:06:36,129 +you can find memory footprint +in many places on the system, + +111 +00:06:36,163 --> 00:06:39,433 +including the Activity Monitor app on Mac. + +112 +00:06:39,466 --> 00:06:43,770 +And some Apple platforms use it +for app memory limits. + +113 +00:06:43,804 --> 00:06:47,908 +Your game can also use this metric +to guide its memory use. + +114 +00:06:47,941 --> 00:06:53,413 +There are useful APIs to query +current footprint and available memory. + +115 +00:06:53,447 --> 00:06:54,915 +Here's a quick look. + +116 +00:06:54,948 --> 00:07:00,153 +To get available system memory +for your iOS, iPadOS or tvOS game, + +117 +00:07:00,187 --> 00:07:05,459 +call os_proc_available_memory, +which is in os/proc.h header file. + +118 +00:07:07,261 --> 00:07:10,264 +And for memory footprint +on any Apple platform, + +119 +00:07:10,297 --> 00:07:13,100 +you can get it via proc_pid_rusage, + +120 +00:07:13,133 --> 00:07:15,802 +with process ID from "get pid", + +121 +00:07:15,836 --> 00:07:19,573 +"rusage_info_current", +which is version 6 at the moment, + +122 +00:07:19,606 --> 00:07:22,543 +and the data store. + +123 +00:07:22,576 --> 00:07:26,847 +And retrieve its physical footprint or +lifetime max physical footprint property. + +124 +00:07:28,148 --> 00:07:33,687 +To recap, in this first section, +we reviewed some concepts about memory. + +125 +00:07:33,720 --> 00:07:37,624 +Allocations in your game happen +on virtual memory address space, + +126 +00:07:37,658 --> 00:07:42,496 +and they will take up physical +memory space as 16 kibibyte pages, + +127 +00:07:42,529 --> 00:07:45,299 +once they're accessed by your game. + +128 +00:07:45,332 --> 00:07:50,771 +Memory footprint is the primary +and universal metric on Apple platforms + +129 +00:07:50,804 --> 00:07:53,574 +to determine your game's +actual memory use. + +130 +00:07:53,607 --> 00:07:58,779 +Memory footprint contains dirty, +compressed, and swapped pages. + +131 +00:07:58,812 --> 00:08:03,183 +It includes both CPU and GPU objects +on Apple silicon. + +132 +00:08:03,217 --> 00:08:06,920 +And it's used for +memory limit enforcement. + +133 +00:08:06,954 --> 00:08:12,893 +Your game can call system APIs to +get its footprint, and available memory. + +134 +00:08:12,926 --> 00:08:16,063 +Now that you know how memory works +behind the scenes, + +135 +00:08:16,096 --> 00:08:18,332 +let's find out how it looks in your game. + +136 +00:08:18,365 --> 00:08:21,869 +Let me hand over to Seth, +to tell you more. + +137 +00:08:21,902 --> 00:08:23,170 +Seth Lù: Thanks, Jack. + +138 +00:08:23,203 --> 00:08:26,573 +Now, let's get started with capturing +the memory growth for a game. + +139 +00:08:26,607 --> 00:08:30,611 +And I'll continue to use +the Modern Renderer sample project. + +140 +00:08:30,644 --> 00:08:32,446 +When you run a game from Xcode, + +141 +00:08:32,479 --> 00:08:36,183 +the Memory Gauge shows you +the memory footprint over time. + +142 +00:08:36,216 --> 00:08:39,286 +However, you can get a much more detailed +look at the memory use + +143 +00:08:39,319 --> 00:08:42,289 +by profiling the game in Instruments. + +144 +00:08:42,322 --> 00:08:46,260 +Because oftentimes a game could +allocate a lot of memory at launch time, + +145 +00:08:46,293 --> 00:08:48,495 +you may want to begin profiling +from a new game launch + +146 +00:08:48,529 --> 00:08:51,732 +instead of attaching to an existing run. + +147 +00:08:51,765 --> 00:08:54,968 +From Xcode, +to quickly begin profiling your game, + +148 +00:08:55,002 --> 00:08:59,206 +press and hold the run button, +then choose "Profile". + +149 +00:08:59,239 --> 00:09:02,109 +This will automatically bring you +to Instruments. + +150 +00:09:02,142 --> 00:09:05,012 +The Instruments app includes +a collection of profiling tools + +151 +00:09:05,045 --> 00:09:07,447 +that record different aspects +of the system + +152 +00:09:07,481 --> 00:09:10,517 +and visualizes the recorded data +on a timeline. + +153 +00:09:10,551 --> 00:09:12,853 +New this year is the Game Memory template + +154 +00:09:12,886 --> 00:09:15,856 +that can help you better understand +the memory growth in your Metal game. + +155 +00:09:17,291 --> 00:09:21,428 +This template comes with the Allocations +and Metal Resource Events instruments + +156 +00:09:21,461 --> 00:09:24,531 +to record memory allocations with history, + +157 +00:09:24,565 --> 00:09:27,801 +VM Tracker to record memory footprint, + +158 +00:09:27,835 --> 00:09:31,605 +Virtual Memory Trace +to record virtual memory activity, + +159 +00:09:31,638 --> 00:09:35,442 +and Metal Application and GPU +to record Metal-related events. + +160 +00:09:37,477 --> 00:09:40,814 +And in this demo, I'll highlight +the first three instruments: + +161 +00:09:40,848 --> 00:09:44,685 +Allocations, Metal Resource Events, +and VM Tracker. + +162 +00:09:44,718 --> 00:09:47,955 +But first, +let's record a trace for the game. + +163 +00:09:47,988 --> 00:09:51,391 +You can press the record button here +to start recording. + +164 +00:09:51,425 --> 00:09:53,193 +And later, to stop recording, + +165 +00:09:53,227 --> 00:09:57,130 +you can press the same button +or simply quit the game. + +166 +00:09:57,164 --> 00:09:59,399 +While Instruments is recording +the Modern Renderer, + +167 +00:09:59,433 --> 00:10:03,036 +let me show you an alternative way +to record a trace. + +168 +00:10:03,070 --> 00:10:06,974 +The xctrace command allows you +to programmatically perform recordings, + +169 +00:10:07,007 --> 00:10:09,376 +which may be useful +in automation workflows. + +170 +00:10:10,944 --> 00:10:13,514 +Additionally, +you can specify the device name + +171 +00:10:13,547 --> 00:10:17,751 +to choose an iPhone, +iPad, or Apple TV as the target. + +172 +00:10:18,819 --> 00:10:21,021 +Now that I have captured +an Instruments trace, + +173 +00:10:21,054 --> 00:10:23,624 +let's first take a look at Allocations. + +174 +00:10:23,657 --> 00:10:27,261 +The Allocations instrument gives you +a detailed view of memory allocations, + +175 +00:10:27,294 --> 00:10:30,330 +their sizes, and object reference counts. + +176 +00:10:30,364 --> 00:10:34,067 +However, it doesn't include +private Metal resources. + +177 +00:10:34,101 --> 00:10:38,338 +The Statistics view displays +all heap allocations and anonymous VM. + +178 +00:10:40,274 --> 00:10:45,779 +The All Heap Allocations include malloc'ed +buffers which may contain objects, + +179 +00:10:45,812 --> 00:10:50,617 +and All Anonymous VM includes +interesting VM regions that may be dirty. + +180 +00:10:50,651 --> 00:10:54,388 +And we'll see in a moment that some +Metal resources belong to this category. + +181 +00:10:56,123 --> 00:10:59,660 +Now, let's take a look +inside All Heap Allocations. + +182 +00:10:59,693 --> 00:11:03,964 +Usually, the larger allocations +are more interesting for optimization. + +183 +00:11:03,997 --> 00:11:07,634 +To find the single largest allocation, you +can click on the Size table column + +184 +00:11:07,668 --> 00:11:09,837 +to sort the allocations by size. + +185 +00:11:11,238 --> 00:11:13,774 +For an allocation, +you can click on this arrow + +186 +00:11:13,807 --> 00:11:17,878 +to see the reference count changes +for Swift and Objective-C objects. + +187 +00:11:20,147 --> 00:11:22,583 +And with this large allocation selected +in the list, + +188 +00:11:22,616 --> 00:11:26,887 +there is the stack trace of +the allocation history in the inspector. + +189 +00:11:26,920 --> 00:11:31,592 +Clicking on the button allows +hiding the system libraries or frameworks. + +190 +00:11:31,625 --> 00:11:34,228 +And here, according to the stack trace, + +191 +00:11:34,261 --> 00:11:37,531 +the allocation happened +when Modern Renderer loaded the assets. + +192 +00:11:38,866 --> 00:11:43,136 +Double clicking on the frame +can also bring you to the source code. + +193 +00:11:43,170 --> 00:11:47,374 +Now, let's go back and take a look inside +the "All Anonymous VM" category. + +194 +00:11:48,809 --> 00:11:52,646 +In Metal games, you may find +a lot of allocations in the IOAccelerator + +195 +00:11:52,679 --> 00:11:55,582 +and IOSurface categories. + +196 +00:11:55,616 --> 00:11:59,453 +Allocations in IOAccelerator +correspond to Metal resources. + +197 +00:12:00,854 --> 00:12:04,892 +From the stack trace, you can see this +allocation happened while loading assets. + +198 +00:12:06,493 --> 00:12:10,497 +Allocations in IOSurface +correspond to drawables. + +199 +00:12:10,531 --> 00:12:14,401 +And here, the stack trace shows +the MetalKit view requested the drawable. + +200 +00:12:16,036 --> 00:12:20,307 +The Allocations instrument, by default, +visualizes the allocation size. + +201 +00:12:20,340 --> 00:12:23,544 +However, it also comes with +alternative looks. + +202 +00:12:23,577 --> 00:12:26,013 +You can on the arrow button +in the Allocations track + +203 +00:12:26,046 --> 00:12:29,616 +to customize the display mode +to visualize Allocation Density. + +204 +00:12:29,650 --> 00:12:33,520 +This will update the graph to show you the +amount of allocations performed over time + +205 +00:12:33,554 --> 00:12:36,657 +and reveal spikes for memory allocations. + +206 +00:12:36,690 --> 00:12:39,726 +These spikes may be sources +of memory growth. + +207 +00:12:39,760 --> 00:12:43,263 +So the data shown in Allocations +is quite low-level. + +208 +00:12:43,297 --> 00:12:46,300 +To get a better understanding +of the allocated Metal resources, + +209 +00:12:46,333 --> 00:12:50,003 +let's move on to Metal Resource Events. + +210 +00:12:50,037 --> 00:12:54,775 +The Metal Resource Events instrument +is designed around Metal resources. + +211 +00:12:54,808 --> 00:12:56,310 +In the Resource Events view, + +212 +00:12:56,343 --> 00:13:00,681 +you can find a history of Metal resource +allocations and deallocations. + +213 +00:13:00,714 --> 00:13:04,218 +Here, you could also identify +the Metal resources by their labels + +214 +00:13:04,251 --> 00:13:08,222 +which you can specify programmatically +through the Metal API. + +215 +00:13:08,255 --> 00:13:10,290 +And similar to the Allocations instrument, + +216 +00:13:10,324 --> 00:13:13,894 +you can find a stack trace for +the allocation history in the inspector. + +217 +00:13:15,929 --> 00:13:19,399 +This instrument also adds the Allocation +and the Deallocations track + +218 +00:13:19,433 --> 00:13:21,034 +under the Metal device. + +219 +00:13:21,068 --> 00:13:23,804 +They help visualize the density of events. + +220 +00:13:23,837 --> 00:13:26,740 +So far, +Allocations and Metal Resource Events + +221 +00:13:26,773 --> 00:13:29,676 +can help understand memory allocations. + +222 +00:13:29,710 --> 00:13:33,814 +However, allocations don't always +translate to memory footprint. + +223 +00:13:33,847 --> 00:13:37,351 +So let's move on to VM Tracker +to investigate the actual memory use. + +224 +00:13:38,352 --> 00:13:41,154 +The VM Tracker instrument +shows the non-compressed dirty + +225 +00:13:41,188 --> 00:13:44,057 +and compressed or swapped memory. + +226 +00:13:44,091 --> 00:13:48,061 +The Dirty Size represents +the non-compressed dirty memory. + +227 +00:13:48,095 --> 00:13:52,165 +And the Swapped Size represents +the compressed or swapped memory. + +228 +00:13:52,199 --> 00:13:54,868 +In this recording, there's no compressed +or swapped memory use + +229 +00:13:54,902 --> 00:13:57,538 +from the Modern Renderer. + +230 +00:13:57,571 --> 00:14:00,874 +The detailed Summary view +shows the VM regions. + +231 +00:14:00,908 --> 00:14:04,244 +And in the "mapped file" region, +you may find some memory-mapped resources + +232 +00:14:04,278 --> 00:14:06,280 +like your game assets. + +233 +00:14:06,313 --> 00:14:10,384 +Here, Modern Renderer maps +the bistro asset file into memory. + +234 +00:14:10,417 --> 00:14:13,320 +So that's a brief overview +of the Allocations, + +235 +00:14:13,353 --> 00:14:17,090 +Metal Resource Events +and VM Tracker in Instruments. + +236 +00:14:17,124 --> 00:14:20,394 +To quickly recap +how you can profile memory growth: + +237 +00:14:20,427 --> 00:14:22,930 +First, choose the Game Memory template, + +238 +00:14:22,963 --> 00:14:25,766 +and then, record and analyze the trace. + +239 +00:14:25,799 --> 00:14:28,168 +Sometimes, you may repeat this process +a few times + +240 +00:14:28,202 --> 00:14:31,839 +when reproducing or verifying +memory growth patterns. + +241 +00:14:31,872 --> 00:14:34,474 +We hope the new Game Memory template +can help you better understand + +242 +00:14:34,508 --> 00:14:37,945 +the memory allocation +or footprint growth in your game. + +243 +00:14:37,978 --> 00:14:42,115 +And please check out these other videos +to learn more about using Instruments. + +244 +00:14:42,149 --> 00:14:43,917 +Now, back to Jack. + +245 +00:14:45,619 --> 00:14:48,055 +The game Memory template +looks really cool, + +246 +00:14:48,088 --> 00:14:51,491 +and it's going to be so useful +to help with understanding changes + +247 +00:14:51,525 --> 00:14:53,861 +of memory use over time. + +248 +00:14:53,894 --> 00:14:57,631 +In addition, you might also want +to capture the memory state of the game + +249 +00:14:57,664 --> 00:15:01,268 +at a given time, so you can dig deeper +into that memory state + +250 +00:15:01,301 --> 00:15:04,571 +and examine it through different lenses. + +251 +00:15:04,605 --> 00:15:08,275 +And for that, we have memory graphs +and a suite of tools. + +252 +00:15:09,710 --> 00:15:13,347 +Memory graph is a file +to efficiently store a complete snapshot + +253 +00:15:13,380 --> 00:15:15,082 +of your game's memory state, + +254 +00:15:15,115 --> 00:15:17,150 +including object creation history, + +255 +00:15:17,184 --> 00:15:20,153 +references, +and any compression or swapping. + +256 +00:15:21,788 --> 00:15:23,957 +You can take a snapshot anytime you want, + +257 +00:15:23,991 --> 00:15:27,160 +such as when an issue occurs, +or a pair of those, + +258 +00:15:27,194 --> 00:15:31,865 +before and after an issue happened +for comparison. + +259 +00:15:31,899 --> 00:15:35,035 +To spice things up, +let's use a cookbook analogy + +260 +00:15:35,068 --> 00:15:38,739 +on how to analyze memory +with memory graphs. + +261 +00:15:38,772 --> 00:15:41,909 +It includes an ingredient +and a preparation part. + +262 +00:15:43,710 --> 00:15:47,080 +For the Ingredients, well, +you would need your game; + +263 +00:15:47,114 --> 00:15:49,550 +something called Malloc Stack Logging; + +264 +00:15:49,583 --> 00:15:52,586 +and, a captured memory graph. + +265 +00:15:52,619 --> 00:15:55,055 +It's quick to configure +Malloc Stack Logging + +266 +00:15:55,088 --> 00:15:56,723 +and to capture a memory graph. + +267 +00:15:58,592 --> 00:16:03,030 +Malloc Stack Logging records allocation +information in the game process. + +268 +00:16:03,063 --> 00:16:05,299 +You can find it in Scheme settings. + +269 +00:16:05,332 --> 00:16:08,669 +Choose the Run action, +go to Diagnostics, + +270 +00:16:08,702 --> 00:16:11,271 +and tick the Malloc Stack Logging +checkbox. + +271 +00:16:13,040 --> 00:16:15,642 +In case you wonder what the two +options are; + +272 +00:16:15,676 --> 00:16:19,313 +All Allocation and Free History +keeps track of all objects + +273 +00:16:19,346 --> 00:16:22,049 +even after they are deallocated. + +274 +00:16:22,082 --> 00:16:24,351 +The logging data may take up more memory, + +275 +00:16:24,384 --> 00:16:28,622 +but it's useful for debugging issues +such as fragmentation. + +276 +00:16:28,655 --> 00:16:33,160 +On the other hand, Live Allocation Only +discards deallocated objects + +277 +00:16:33,193 --> 00:16:36,263 +from its history, so it's lighter. + +278 +00:16:36,296 --> 00:16:39,199 +In this case, +I'm only investigating references, + +279 +00:16:39,233 --> 00:16:43,437 +which are on live objects, +so I can pick this option. + +280 +00:16:43,470 --> 00:16:45,572 +In fact, most of the time, + +281 +00:16:45,606 --> 00:16:49,209 +Live Allocation Only will be +your recommended option. + +282 +00:16:50,677 --> 00:16:56,049 +Alternatively, you can set environment +variable if not launching from Xcode. + +283 +00:16:56,083 --> 00:17:00,187 +Check out the malloc manual page +for some additional recording modes. + +284 +00:17:00,220 --> 00:17:03,190 +After that, also prepare a memory graph. + +285 +00:17:03,223 --> 00:17:05,626 +Just click on +the debug memory graph button + +286 +00:17:05,659 --> 00:17:07,895 +in the debug area. + +287 +00:17:07,928 --> 00:17:09,863 +Xcode will take a memory snapshot, + +288 +00:17:09,897 --> 00:17:13,667 +process it, and enter the memory debugger. + +289 +00:17:13,700 --> 00:17:18,505 +Xcode Memory Debugger provides intuitive +perspectives into the game's memory use. + +290 +00:17:18,539 --> 00:17:22,142 +Let's take a minute to explore the view. + +291 +00:17:22,176 --> 00:17:25,445 +On the left side, +Debug Navigator gives you + +292 +00:17:25,479 --> 00:17:28,482 +a hierarchical list of object instances. + +293 +00:17:30,484 --> 00:17:34,421 +On the right, File Inspector provides +useful information + +294 +00:17:34,454 --> 00:17:38,625 +such as memory footprint, +uptime, and capture date. + +295 +00:17:40,928 --> 00:17:44,164 +In the middle area +shines the memory graph view + +296 +00:17:44,198 --> 00:17:47,134 +where you have the selected object +from the left, + +297 +00:17:47,167 --> 00:17:50,771 +and how references connect to this object. + +298 +00:17:50,804 --> 00:17:53,740 +I'll come back to this graph in a bit. + +299 +00:17:56,109 --> 00:17:59,847 +And the File menu gives you the option +to save this memory graph + +300 +00:17:59,880 --> 00:18:03,517 +for future analysis, +or to easily share with your team. + +301 +00:18:05,118 --> 00:18:07,888 +For a Mac game, +you can also capture a memory graph + +302 +00:18:07,921 --> 00:18:12,659 +with the leaks command line program, +using process ID or name. + +303 +00:18:12,693 --> 00:18:16,163 +This means you could do it remotely +in a secure shell, + +304 +00:18:16,196 --> 00:18:18,031 +so the cursor stays in the game, + +305 +00:18:18,065 --> 00:18:23,103 +in case your game is running fullscreen +and needs to stay in focus. + +306 +00:18:23,136 --> 00:18:26,907 +So that's what you'll need +to start memory graph analysis. + +307 +00:18:28,275 --> 00:18:32,880 +Now it's time to examine this memory graph +using Xcode Memory Debugger, + +308 +00:18:32,913 --> 00:18:36,083 +plus some versatile command line tools +in Terminal, + +309 +00:18:36,116 --> 00:18:41,121 +to find out allocations, +footprint, and even more. + +310 +00:18:41,154 --> 00:18:45,759 +A good first step +is to break down memory use by categories. + +311 +00:18:45,792 --> 00:18:48,629 +The footprint program does just that. + +312 +00:18:50,430 --> 00:18:56,203 +Footprint uses information in the memory +graph to recreate this high level summary. + +313 +00:18:56,236 --> 00:19:01,241 +Typically, you will want to first focus +on the larger categories. + +314 +00:19:01,275 --> 00:19:05,812 +For game memory graphs such as this one +from the Modern Rendering sample code, + +315 +00:19:05,846 --> 00:19:09,783 +IOAccelerator is usually the largest one. + +316 +00:19:09,816 --> 00:19:14,988 +As Seth said, it includes Metal resources. + +317 +00:19:15,022 --> 00:19:20,494 +Here, heap allocations go to several +MALLOC_(prefixed) categories, + +318 +00:19:20,527 --> 00:19:25,899 +since the system groups heap allocations +to size pools to improve performance. + +319 +00:19:25,933 --> 00:19:30,170 +These objects may come from many places, +such as third-party plugins, + +320 +00:19:30,204 --> 00:19:34,875 +or libraries, where your game does +sound effects or physics simulation. + +321 +00:19:36,343 --> 00:19:39,479 +Here's a memory graph +from an awesome Apple Arcade game, + +322 +00:19:39,513 --> 00:19:43,283 +Manifold Garden, +created by William "Cheer" Studio. + +323 +00:19:43,317 --> 00:19:46,753 +I'm glad they allow me to show you +the game's memory usage. + +324 +00:19:46,787 --> 00:19:51,558 +If your game uses a game engine, +like Manifold Garden using Unity, + +325 +00:19:51,592 --> 00:19:54,528 +or a custom allocator +on top of memory map, + +326 +00:19:54,561 --> 00:19:58,899 +that memory would be shown as +untagged VM_ALLOCATE like this. + +327 +00:19:58,932 --> 00:20:01,702 +Here's a pro tip: on Apple platforms, + +328 +00:20:01,735 --> 00:20:04,872 +your game can use +up to 16 app-specific tags, + +329 +00:20:04,905 --> 00:20:08,442 +so you can have more clarity +when drilling down memory usage. + +330 +00:20:08,475 --> 00:20:11,144 +It's as easy as a one line change. + +331 +00:20:12,312 --> 00:20:16,283 +First, make the tag +from one of 16 options. + +332 +00:20:16,316 --> 00:20:21,522 +Then replace the minus one with +this new tag as the "file descriptor", + +333 +00:20:21,555 --> 00:20:24,091 +when calling "em map". + +334 +00:20:24,124 --> 00:20:28,562 +Check out "em map's" manual page to learn +how tags and categories are defined. + +335 +00:20:30,597 --> 00:20:32,833 +If you use "mach VM allocate", + +336 +00:20:32,866 --> 00:20:37,171 +include the same flag in +the flag argument when allocating. + +337 +00:20:39,573 --> 00:20:41,642 +In the world of footprint program, + +338 +00:20:41,675 --> 00:20:45,245 +the dirty size also includes swap +and compressed, + +339 +00:20:45,279 --> 00:20:48,982 +so think of it as total charged +for each category. + +340 +00:20:50,250 --> 00:20:53,754 +That's a brief idea of the composition +of current memory use, + +341 +00:20:53,787 --> 00:20:56,857 +and how it makes up the footprint. + +342 +00:20:56,890 --> 00:21:01,361 +Some of this memory is less used +and become compressed or swapped. + +343 +00:21:01,395 --> 00:21:04,364 +They might be sources of memory savings. + +344 +00:21:04,398 --> 00:21:07,167 +The next step is to find out +how much compressed + +345 +00:21:07,201 --> 00:21:10,871 +or swapped memory the game uses, +and optimize. + +346 +00:21:12,773 --> 00:21:16,610 +For this, you can run the memory graph +with vmmap. + +347 +00:21:16,643 --> 00:21:19,580 +It gives you dirty and swapped sizes, + +348 +00:21:19,613 --> 00:21:22,082 +instead of two combined. + +349 +00:21:22,115 --> 00:21:24,985 +This dirty column includes +currently not swapped + +350 +00:21:25,018 --> 00:21:27,487 +or compressed regular dirty memory, + +351 +00:21:27,521 --> 00:21:30,524 +while the swapped column includes +the original size + +352 +00:21:30,557 --> 00:21:33,694 +of compressed or swapped memory. + +353 +00:21:33,727 --> 00:21:37,965 +The system adds these two columns together +to determine footprint. + +354 +00:21:37,998 --> 00:21:42,636 +But since content in the swapped size +column isn't used as often, + +355 +00:21:42,669 --> 00:21:45,772 +it is a good indicator +for what to look for + +356 +00:21:45,806 --> 00:21:48,442 +to optimize your game's memory. + +357 +00:21:48,475 --> 00:21:51,345 +Oh, by the way, +here is the allocation size, + +358 +00:21:51,378 --> 00:21:54,114 +with the virtual size column. + +359 +00:21:54,147 --> 00:21:58,285 +And the resident size includes clean pages +such as executables + +360 +00:21:58,318 --> 00:22:00,521 +and memory mapped files. + +361 +00:22:02,422 --> 00:22:07,294 +Conveniently, vmmap shows +heap allocations with a separate table. + +362 +00:22:07,327 --> 00:22:09,363 +At the bottom of its output, + +363 +00:22:09,396 --> 00:22:13,300 +vmmap groups heap memory by zones. + +364 +00:22:13,333 --> 00:22:18,172 +These zones reflect their usage +or lifecycle in your game. + +365 +00:22:18,205 --> 00:22:20,674 +Because I turned on MallocStackLogging, + +366 +00:22:20,707 --> 00:22:24,811 +allocations on the heap +are in the tool's zone. + +367 +00:22:24,845 --> 00:22:27,714 +Otherwise, +they would be in two default zones: + +368 +00:22:27,748 --> 00:22:33,887 +MallocHelperZone and DefaultMallocZone, +based on the allocation size. + +369 +00:22:33,921 --> 00:22:40,594 +And usually you could skip smaller system +utility zones such as QuartzCore zone. + +370 +00:22:42,196 --> 00:22:47,334 +Also, if you suspect fragmentation, +indicated by high fragmentation size + +371 +00:22:47,367 --> 00:22:52,439 +or percentage, +like dozens or hundreds of megabytes, + +372 +00:22:52,472 --> 00:22:57,411 +the WWDC 2021 session covers more +about fragmentation issues. + +373 +00:22:58,645 --> 00:23:02,282 +And running vmmap +without dash dash summary, + +374 +00:23:02,316 --> 00:23:04,718 +or to use vmmap in standard mode, + +375 +00:23:04,751 --> 00:23:09,223 +shows each vm region within +those categories line by line. + +376 +00:23:09,256 --> 00:23:14,595 +Just like how the virtual address space +looks like, as we discussed earlier. + +377 +00:23:14,628 --> 00:23:21,068 +So with vmmap, you can distill less used +dirty memory from actively used ones. + +378 +00:23:21,101 --> 00:23:23,871 +And typically, +there are also a good amount + +379 +00:23:23,904 --> 00:23:27,207 +of dynamic allocations of various sizes, + +380 +00:23:27,241 --> 00:23:30,010 +or malloc'd heap memory usage in the game. + +381 +00:23:30,043 --> 00:23:32,045 +They need a special look. + +382 +00:23:32,880 --> 00:23:36,550 +Heap tool groups malloc'd resources +by their classes, + +383 +00:23:36,583 --> 00:23:39,253 +and sorts them by instance count. + +384 +00:23:39,286 --> 00:23:43,524 +These classes are determined in +C++ with a VTable, + +385 +00:23:43,557 --> 00:23:45,993 +Objective-C, or Swift. + +386 +00:23:47,628 --> 00:23:49,997 +We are using the —quiet argument + +387 +00:23:50,030 --> 00:23:53,734 +to skip the header about some metadata. + +388 +00:23:53,767 --> 00:23:59,206 +New this year, heap is more intelligent +at identifying object types. + +389 +00:23:59,239 --> 00:24:02,676 +It uses information recorded +by Malloc Stack Logging + +390 +00:24:02,709 --> 00:24:05,979 +to present the caller +or responsible library, + +391 +00:24:06,013 --> 00:24:09,683 +so a huge non-object +is a thing of the past. + +392 +00:24:10,584 --> 00:24:14,888 +And here is the memory graph +from Manifold Garden again. + +393 +00:24:14,922 --> 00:24:18,392 +In this example, +it is revealed for the first time + +394 +00:24:18,425 --> 00:24:23,463 +how much heap usage is taken up +by plugins like FMOD Studio, + +395 +00:24:23,497 --> 00:24:27,801 +and game components +such as GameAssembly.dylib. + +396 +00:24:27,835 --> 00:24:32,306 +So now you can be more informed +on how the memory is spread out. + +397 +00:24:32,339 --> 00:24:38,078 +And it also hints which direction to go +for getting more info on these objects. + +398 +00:24:38,111 --> 00:24:42,216 +In this example, +the developer can open FMOD Studio + +399 +00:24:42,249 --> 00:24:45,919 +to fine tune the soundtrack +and sound effects in the game, + +400 +00:24:45,953 --> 00:24:50,691 +or go to Unity to look for +game code optimizations, and so on. + +401 +00:24:52,593 --> 00:24:55,295 +Sometimes, +sorting by class total size + +402 +00:24:55,329 --> 00:24:58,765 +rather than class instance count +is more helpful. + +403 +00:24:58,799 --> 00:25:01,535 +In the memory graph +of the Modern Rendering sample project, + +404 +00:25:01,568 --> 00:25:07,641 +the top contributor is a class +using over 258 million bytes. + +405 +00:25:07,674 --> 00:25:11,512 +To continue looking for larger objects +in the Modern Rendering sample, + +406 +00:25:11,545 --> 00:25:17,584 +use heap to sort objects by class +total size with —sortBySize, + +407 +00:25:17,618 --> 00:25:21,688 +and list all objects +with —showSizes, + +408 +00:25:21,722 --> 00:25:24,725 +rather than a summary of each class. + +409 +00:25:24,758 --> 00:25:28,629 +And, there is one object of +NSConcreteMutableData + +410 +00:25:28,662 --> 00:25:34,201 +in Bytes Storage with a size +of 255 million bytes: + +411 +00:25:34,234 --> 00:25:37,971 +that looks like one worth looking at. + +412 +00:25:38,005 --> 00:25:40,908 +Next, I want to find out what it is. + +413 +00:25:40,941 --> 00:25:44,278 +And I want to know +its address for a start. + +414 +00:25:44,311 --> 00:25:49,816 +I add —address and enter +the pattern NSConcreteMutableData + +415 +00:25:49,850 --> 00:25:53,153 +followed by wildcard– +dot star, + +416 +00:25:53,187 --> 00:25:55,355 +and a size filter in the bracket + +417 +00:25:55,389 --> 00:26:00,093 +to only list objects +10 megabytes large and upwards. + +418 +00:26:00,127 --> 00:26:02,796 +And here is the address of the object. + +419 +00:26:02,829 --> 00:26:07,334 +I'll use it in the following steps +for more in-depth analysis. + +420 +00:26:07,367 --> 00:26:13,407 +So that's heap tool, with improved +object identification for instances. + +421 +00:26:13,440 --> 00:26:16,276 +So far, you've seen three tools +to understand + +422 +00:26:16,310 --> 00:26:18,979 +what objects are using memory in the game, + +423 +00:26:19,012 --> 00:26:21,548 +and they all provide different views. + +424 +00:26:21,582 --> 00:26:24,351 +What I showed was just one workflow. + +425 +00:26:24,384 --> 00:26:26,620 +Depending on +the particular memory patterns, + +426 +00:26:26,653 --> 00:26:28,889 +or technologies used in your game, + +427 +00:26:28,922 --> 00:26:31,692 +you can use them +in any way that suits your needs. + +428 +00:26:33,894 --> 00:26:36,163 +With discoveries for objects + +429 +00:26:36,196 --> 00:26:39,066 +that we are not quite sure +of their existence, + +430 +00:26:39,099 --> 00:26:41,835 +the next step is to get its origin, + +431 +00:26:41,869 --> 00:26:44,104 +which is its allocation call stack. + +432 +00:26:45,172 --> 00:26:48,575 +In the case of the 200 million bytes +object in Modern Rendering, + +433 +00:26:48,609 --> 00:26:50,944 +I use the —callTree mode + +434 +00:26:50,978 --> 00:26:54,915 +and pass in its address to malloc_history. + +435 +00:26:54,948 --> 00:26:57,451 +Together with additional invert argument, + +436 +00:26:57,484 --> 00:27:01,655 +I can focus on functions +closest to the allocation. + +437 +00:27:01,688 --> 00:27:03,156 +And voila. + +438 +00:27:03,190 --> 00:27:06,793 +Here is the back trace of the allocation. + +439 +00:27:06,827 --> 00:27:12,065 +Similarly, Xcode Memory Debugger shows +the allocation history of an object too + +440 +00:27:12,099 --> 00:27:13,600 +in the inspector. + +441 +00:27:13,634 --> 00:27:17,371 +Just select an object, +click on the Memory Inspector, + +442 +00:27:17,404 --> 00:27:20,774 +and there it is. + +443 +00:27:20,807 --> 00:27:24,344 +As another example, +pass in VM_ALLOCATE + +444 +00:27:24,378 --> 00:27:27,281 +as the class pattern +instead of an address, + +445 +00:27:27,314 --> 00:27:31,084 +to check for anonymous VM usage +in your game or plugin, + +446 +00:27:31,118 --> 00:27:34,888 +such as debugging a custom allocator. + +447 +00:27:34,922 --> 00:27:37,958 +Whether using Xcode or malloc_history, + +448 +00:27:37,991 --> 00:27:40,294 +you can know the allocation back trace, + +449 +00:27:40,327 --> 00:27:43,030 +and decide if you want to dig deeper, + +450 +00:27:43,063 --> 00:27:46,800 +including setting a breakpoint +at the line, for a start. + +451 +00:27:48,802 --> 00:27:55,475 +And last but not least, it's also helpful +to investigate object references. + +452 +00:27:55,509 --> 00:27:58,846 +Memory graph always records +object references, + +453 +00:27:58,879 --> 00:28:04,017 +even when MallocStackLogging +is not enabled for various reasons. + +454 +00:28:04,051 --> 00:28:08,689 +We've used leaks before to capture +a memory graph outside of Xcode. + +455 +00:28:08,722 --> 00:28:10,190 +Leaks does more. + +456 +00:28:10,224 --> 00:28:12,993 +It checks for all references +in the memory graph, + +457 +00:28:13,026 --> 00:28:17,431 +and that is why it knows about leaks +and retain cycles. + +458 +00:28:17,464 --> 00:28:20,634 +Leaks gets this tree of references +to the object + +459 +00:28:20,667 --> 00:28:24,905 +by using the trace tree argument +and the object address from heap. + +460 +00:28:24,938 --> 00:28:28,909 +However, because this is a rather +large tree in this example, + +461 +00:28:28,942 --> 00:28:32,679 +there is a somewhat better way +to view it than in Terminal. + +462 +00:28:34,281 --> 00:28:37,751 +With Xcode 14, +we redesigned the memory graph view + +463 +00:28:37,784 --> 00:28:42,289 +to show both ingoing and outgoing edges +of the selected object. + +464 +00:28:43,357 --> 00:28:46,059 +It even has a new +neighbor selection popover, + +465 +00:28:46,093 --> 00:28:49,396 +to choose the edges +you want Xcode to draw. + +466 +00:28:49,429 --> 00:28:52,132 +This is going to greatly improve +productivity + +467 +00:28:52,165 --> 00:28:56,603 +when trying to understand object +references in complex game states. + +468 +00:28:58,105 --> 00:29:00,541 +After exploring around for a little bit, + +469 +00:29:00,574 --> 00:29:05,345 +I'm pretty sure the texture manager +is what is accessing this object. + +470 +00:29:05,379 --> 00:29:07,814 +For your game, +consider using leaks tool + +471 +00:29:07,848 --> 00:29:12,386 +and the memory graph view to find +important object reference relationships, + +472 +00:29:12,419 --> 00:29:16,190 +to learn how these objects +are accessed in the game. + +473 +00:29:16,223 --> 00:29:18,992 +So that how to view and find out +important references + +474 +00:29:19,026 --> 00:29:22,729 +of an object using leaks or Xcode. + +475 +00:29:22,763 --> 00:29:24,565 +Please check out leaks' manual page, + +476 +00:29:24,598 --> 00:29:28,268 +and Xcode help +for more usage of these tools. + +477 +00:29:29,603 --> 00:29:32,039 +In this memory graph analysis cookbook, + +478 +00:29:32,072 --> 00:29:35,375 +each step uses some specific tools. + +479 +00:29:35,409 --> 00:29:40,047 +All of them work together to complete +the analysis on a memory graph. + +480 +00:29:41,548 --> 00:29:45,552 +To summarize, the first thing +is to enable MallocStackLogging + +481 +00:29:45,586 --> 00:29:50,991 +when you expect to capture and analyze +memory with a memory graph. + +482 +00:29:51,024 --> 00:29:54,595 +Then capture a memory graph +with Xcode for your game, + +483 +00:29:54,628 --> 00:29:58,765 +or alternatively use leaks tool +for your Mac game. + +484 +00:29:58,799 --> 00:30:02,870 +Next, find large and troublesome objects. + +485 +00:30:02,903 --> 00:30:07,474 +Footprint, vmmap, and heap tools +provide breakdown of memory, + +486 +00:30:07,508 --> 00:30:11,111 +both on a high level and in details. + +487 +00:30:11,144 --> 00:30:15,282 +With malloc_history, you can find out +where objects are allocated + +488 +00:30:15,315 --> 00:30:19,820 +and leaks can analyze object usage +or references. + +489 +00:30:19,853 --> 00:30:22,756 +These previous sessions +include in-depth walkthroughs + +490 +00:30:22,789 --> 00:30:26,260 +as well as demos +of more usage of these tools. + +491 +00:30:26,293 --> 00:30:30,063 +Until now, we have deferred probing +into Metal resources. + +492 +00:30:30,097 --> 00:30:32,533 +Well, now is the time. + +493 +00:30:32,566 --> 00:30:35,769 +To tell you more, here's Seth. + +494 +00:30:35,802 --> 00:30:37,104 +Hi again! + +495 +00:30:37,137 --> 00:30:40,807 +In games, Metal resources +can use a big chunk of memory. + +496 +00:30:40,841 --> 00:30:43,310 +But there are ways to optimize +their memory use. + +497 +00:30:44,411 --> 00:30:46,513 +Here I've summarized a list +of memory savings + +498 +00:30:46,547 --> 00:30:50,551 +that you can use when optimizing +the Metal resources in your game. + +499 +00:30:50,584 --> 00:30:54,254 +We'll take a look at how Metal Debugger +can help you with auditing the resources + +500 +00:30:54,288 --> 00:30:58,025 +and learn some advanced techniques +for further reducing your game memory. + +501 +00:30:59,026 --> 00:31:03,230 +Metal Debugger is the one stop shop +for debugging your Metal games. + +502 +00:31:03,263 --> 00:31:05,365 +After taking a GPU frame capture, + +503 +00:31:05,399 --> 00:31:07,334 +you can find a summary page. + +504 +00:31:07,367 --> 00:31:10,604 +This provides you with some general stats +about the captured workload. + +505 +00:31:12,172 --> 00:31:13,740 +In the lower half of the page, + +506 +00:31:13,774 --> 00:31:17,477 +there is a list of insights +divided in four categories. + +507 +00:31:17,511 --> 00:31:22,115 +Insights in the "Memory" category +suggest memory savings for your game. + +508 +00:31:22,149 --> 00:31:25,752 +There aren't many memory insights +particular for this trace; + +509 +00:31:25,786 --> 00:31:29,356 +we can save just a few megabytes +of memory after addressing these insights. + +510 +00:31:31,225 --> 00:31:35,095 +However, there may be more memory savings +specific to your game. + +511 +00:31:35,128 --> 00:31:38,265 +To get a more complete picture of +the memory used by Metal resources, + +512 +00:31:38,298 --> 00:31:41,435 +you can use the Memory Viewer +from clicking on the Show Memory button. + +513 +00:31:42,903 --> 00:31:47,441 +The Memory Viewer offers you a full list +of resources captured from the game. + +514 +00:31:47,474 --> 00:31:50,844 +The upper half shows +different categories for filtering. + +515 +00:31:50,878 --> 00:31:54,681 +You can quickly use this to +look up resources, say, textures. + +516 +00:31:54,715 --> 00:31:58,785 +And in the lower half, +the table displays just the textures. + +517 +00:31:58,819 --> 00:32:01,755 +Let's take out the filter for now. + +518 +00:32:01,788 --> 00:32:06,059 +The resource table has a collection +of columns to help you optimize your game. + +519 +00:32:06,093 --> 00:32:09,329 +And I'd like to highlight a few columns +that may help you quickly identify + +520 +00:32:09,363 --> 00:32:10,964 +some interesting resources. + +521 +00:32:13,100 --> 00:32:17,337 +The Insights column is similar to +what we just saw on the summary page. + +522 +00:32:17,371 --> 00:32:19,706 +You may quickly view all the resources +with insights + +523 +00:32:19,740 --> 00:32:23,343 +when sorting the table by this column. + +524 +00:32:23,377 --> 00:32:26,313 +And clicking on an insight icon +will reveal a popover + +525 +00:32:26,346 --> 00:32:30,717 +explaining the finding +and providing some possible actions. + +526 +00:32:30,751 --> 00:32:34,288 +Right next to this column +is Allocated Size. + +527 +00:32:34,321 --> 00:32:37,991 +You can sort by this column +to see the largest resources. + +528 +00:32:38,025 --> 00:32:41,361 +It may be useful to audit if some +resources actually make good use + +529 +00:32:41,395 --> 00:32:43,397 +of their memory size. + +530 +00:32:43,430 --> 00:32:47,601 +For example, some textures may be resized +to smaller resolutions + +531 +00:32:47,634 --> 00:32:51,305 +and some models loaded in buffers +may use a lower poly count, + +532 +00:32:51,338 --> 00:32:55,242 +given that doing so won't affect +the visual quality of the game. + +533 +00:32:55,275 --> 00:32:57,377 +There are some alternative ways +to save texture memory + +534 +00:32:57,411 --> 00:32:59,680 +that I'll mention in a minute. + +535 +00:32:59,713 --> 00:33:03,050 +Another interesting column here +is Time Since Last Bound. + +536 +00:33:03,083 --> 00:33:08,222 +You can sort the resources by this column +to find which haven't been used recently. + +537 +00:33:08,255 --> 00:33:09,857 +If a resource is never used, + +538 +00:33:09,890 --> 00:33:14,428 +it may be a good idea to double check +if it's worth loading the asset. + +539 +00:33:14,461 --> 00:33:16,763 +For a resource +that hasn't been bound for a while, + +540 +00:33:16,797 --> 00:33:20,734 +you may consider releasing it +if it won't be used again in the future. + +541 +00:33:20,767 --> 00:33:24,972 +Alternatively, you can set +its purgeable state to volatile. + +542 +00:33:25,005 --> 00:33:28,475 +A Metal resource may be in +one of the three purgeable states: + +543 +00:33:28,509 --> 00:33:32,346 +non-volatile, volatile, and empty. + +544 +00:33:32,379 --> 00:33:35,949 +By default, resources are non-volatile. + +545 +00:33:35,983 --> 00:33:37,918 +By setting the purgeable state +to volatile, + +546 +00:33:37,951 --> 00:33:39,887 +Metal may evict the resource from memory + +547 +00:33:39,920 --> 00:33:42,656 +in case of high memory pressure +in the system. + +548 +00:33:42,689 --> 00:33:45,325 +Once the resource is empty, +the system will no longer charge it + +549 +00:33:45,359 --> 00:33:47,995 +towards the game's footprint. + +550 +00:33:48,028 --> 00:33:49,663 +When your game needs the resource again, + +551 +00:33:49,696 --> 00:33:53,367 +check if the content's still there, +and reload if needed. + +552 +00:33:53,400 --> 00:33:56,870 +Consider only using volatile +for infrequently used resources, + +553 +00:33:56,904 --> 00:33:59,339 +so the purgeable state +doesn't work against you. + +554 +00:34:01,375 --> 00:34:05,012 +So those are some of the general notes +for all resources. + +555 +00:34:05,045 --> 00:34:08,015 +And now, +let's take a closer look at textures. + +556 +00:34:08,949 --> 00:34:12,219 +Not all columns are shown by default +in Memory Viewer. + +557 +00:34:12,252 --> 00:34:15,289 +Right clicking on the table header +will allow showing and hiding columns + +558 +00:34:15,322 --> 00:34:18,158 +like texture's Pixel Format. + +559 +00:34:18,192 --> 00:34:23,163 +You may get different amount of savings by +optimizing the pixel format for a texture. + +560 +00:34:23,197 --> 00:34:27,000 +Many textures in a game could use +a 16-bit half precision pixel format + +561 +00:34:27,034 --> 00:34:29,837 +to reduce memory use and bandwidth. + +562 +00:34:29,870 --> 00:34:32,940 +In cases when you need a texture +with a single alpha component, + +563 +00:34:32,973 --> 00:34:35,642 +you may avoid multiple color channels. + +564 +00:34:35,676 --> 00:34:39,146 +And lastly, some read-only textures +may benefit from block compression + +565 +00:34:39,179 --> 00:34:41,615 +for lower memory use. + +566 +00:34:41,648 --> 00:34:43,717 +For block compressed pixel formats, + +567 +00:34:43,750 --> 00:34:46,687 +there are options such as ASTC and BC. + +568 +00:34:46,720 --> 00:34:49,122 +Additionally, since A15 Bionic, + +569 +00:34:49,156 --> 00:34:51,892 +you can use lossy compression for textures +and render targets + +570 +00:34:51,925 --> 00:34:55,562 +to save memory while preserving +quality wherever possible. + +571 +00:34:55,596 --> 00:34:58,432 +Please check out these previous +videos for more details. + +572 +00:35:00,634 --> 00:35:03,203 +And those are some of the memory savings +you can quickly discover + +573 +00:35:03,237 --> 00:35:05,172 +from using Memory Viewer. + +574 +00:35:05,205 --> 00:35:07,341 +But there are a few additional techniques +you may take + +575 +00:35:07,374 --> 00:35:09,977 +to further optimize your game. + +576 +00:35:10,010 --> 00:35:12,546 +If a texture is only used by single pass, + +577 +00:35:12,579 --> 00:35:14,648 +you can set its storage mode to memoryless + +578 +00:35:14,681 --> 00:35:16,783 +to save memory and bandwidth. + +579 +00:35:16,817 --> 00:35:19,620 +Memoryless textures work well +for temporary render targets, + +580 +00:35:19,653 --> 00:35:23,156 +like depth, stencil, +or multi-sampled textures. + +581 +00:35:23,190 --> 00:35:26,293 +Otherwise, if the texture +is only used by the GPU, + +582 +00:35:26,326 --> 00:35:28,061 +you can set its storage mode to private, + +583 +00:35:28,095 --> 00:35:31,131 +or else shared or managed. + +584 +00:35:31,164 --> 00:35:34,601 +As a reminder, managed mode is not needed +on Apple silicon Macs, + +585 +00:35:34,635 --> 00:35:37,371 +just like on iPhone and iPad. + +586 +00:35:37,404 --> 00:35:39,473 +Here's an example case. + +587 +00:35:39,506 --> 00:35:42,876 +The game has +a Depth32Float_Stencil8 texture. + +588 +00:35:42,910 --> 00:35:46,079 +The depth texture is used across passes, + +589 +00:35:46,113 --> 00:35:48,215 +but the stencil texture's content +is discarded + +590 +00:35:48,248 --> 00:35:51,318 +and won't be used later in the frame. + +591 +00:35:51,351 --> 00:35:54,154 +So instead, +the game could use two textures + +592 +00:35:54,188 --> 00:35:57,925 +and make the stencil texture memoryless, +to save memory and bandwidth. + +593 +00:35:59,359 --> 00:36:02,930 +Lastly, I'd like to mention another +technique that might be interesting to you + +594 +00:36:02,963 --> 00:36:05,432 +for making the most out of the memory +in your game. + +595 +00:36:05,465 --> 00:36:07,901 +You may use aliased resources +from a heap + +596 +00:36:07,935 --> 00:36:09,937 +if your game doesn't use them +at the same time. + +597 +00:36:09,970 --> 00:36:12,973 +They can share the memory backed +by the same allocation. + +598 +00:36:13,006 --> 00:36:17,411 +But be extra careful when synchronizing +the accesses to those resources. + +599 +00:36:17,444 --> 00:36:19,780 +You can check out +the "Go bindless with Metal 3" talk + +600 +00:36:19,813 --> 00:36:23,317 +to learn more about using resources +allocated from a heap. + +601 +00:36:23,350 --> 00:36:26,820 +So that wraps up our checklist +of memory savings. + +602 +00:36:26,854 --> 00:36:30,190 +And I hope this checklist will help you +audit the Metal resources in your game. + +603 +00:36:31,725 --> 00:36:33,861 +To learn more about using +Metal Debugger for optimizing + +604 +00:36:33,894 --> 00:36:38,031 +your game memory, +please check out these other WWDC talks. + +605 +00:36:38,065 --> 00:36:39,499 +And back to you, Jack. + +606 +00:36:41,235 --> 00:36:42,503 +Thank you, Seth. + +607 +00:36:42,536 --> 00:36:46,340 +Today, we took a guided tour +and explored many interesting things + +608 +00:36:46,373 --> 00:36:50,177 +you can do to understand and improve +your game's memory usage. + +609 +00:36:50,210 --> 00:36:53,981 +First, memory footprint +is the primary metric + +610 +00:36:54,014 --> 00:36:56,650 +in understanding your game's memory usage, + +611 +00:36:56,683 --> 00:37:01,722 +and it includes dirty plus compressed +and swapped memory. + +612 +00:37:01,755 --> 00:37:05,859 +Then, we experienced powerful +memory debugging tools. + +613 +00:37:05,893 --> 00:37:09,496 +Seth showed us how Instruments +empower memory profiling + +614 +00:37:09,530 --> 00:37:11,865 +with useful telemetry tracks. + +615 +00:37:11,899 --> 00:37:16,537 +The new Game Memory template +is exactly tailored for this job. + +616 +00:37:16,570 --> 00:37:22,442 +After that, I presented memory graph +to store a snapshot of game memory state. + +617 +00:37:22,476 --> 00:37:25,712 +There are flexible and powerful +command line programs + +618 +00:37:25,746 --> 00:37:28,315 +to analyze memory graphs for objects, + +619 +00:37:28,348 --> 00:37:31,652 +references, and allocation history. + +620 +00:37:31,685 --> 00:37:35,822 +Improvements in the heap tool +and redesigned Xcode Memory Debugger + +621 +00:37:35,856 --> 00:37:38,825 +will supercharge game memory analysis. + +622 +00:37:38,859 --> 00:37:42,329 +Lastly, Seth shared +a memory savings checklist + +623 +00:37:42,362 --> 00:37:44,231 +for Metal resources, + +624 +00:37:44,264 --> 00:37:47,334 +and how Metal Debugger +can help answer questions + +625 +00:37:47,367 --> 00:37:51,138 +about Metal resource usage in your game. + +626 +00:37:51,171 --> 00:37:54,575 +You could also learn more +from other WWDC sessions, + +627 +00:37:54,608 --> 00:37:57,444 +documentation, and manual pages. + +628 +00:37:58,445 --> 00:38:02,349 +We are constantly advancing +our best and most flexible tools for you. + +629 +00:38:02,382 --> 00:38:04,318 +So why not try them out? + +630 +00:38:04,351 --> 00:38:06,720 +They may just be what you are looking for. + +631 +00:38:07,955 --> 00:38:11,525 +And don't hesitate to share with us +any feedback you may have, + +632 +00:38:11,558 --> 00:38:15,162 +via any channels +such as the Feedback Assistant. + +633 +00:38:15,195 --> 00:38:17,064 +Have fun in your memory journey, + +634 +00:38:17,097 --> 00:38:18,465 +and thanks for watching. ♪ ♪ + diff --git a/eng/2022 Session 10107 Get it right (to left) en.srt b/eng/2022 Session 10107 Get it right (to left) en.srt new file mode 100644 index 0000000..5474e69 --- /dev/null +++ b/eng/2022 Session 10107 Get it right (to left) en.srt @@ -0,0 +1,3023 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,776 --> 00:00:13,013 +Rich Gillam: Hi, I'm Rich, +and I'm here to help you get it right + +3 +00:00:13,046 --> 00:00:14,648 +to left. + +4 +00:00:14,681 --> 00:00:18,418 +So you’ve already localized +your application for a bunch of languages, + +5 +00:00:18,452 --> 00:00:20,521 +including the most common +European languages, + +6 +00:00:20,554 --> 00:00:22,422 +the most common Asian languages. + +7 +00:00:22,456 --> 00:00:25,225 +And now you want to localize it +for Arabic and Hebrew. + +8 +00:00:25,259 --> 00:00:26,793 +This is a good choice; + +9 +00:00:26,827 --> 00:00:30,797 +Arabic is one of the ten most used +languages on our platform, + +10 +00:00:30,831 --> 00:00:33,367 +but it brings +with it some challenges you don’t face + +11 +00:00:33,400 --> 00:00:35,569 +when developing for other languages. + +12 +00:00:35,602 --> 00:00:38,939 +That’s what this talk is about, +how to develop your application + +13 +00:00:38,972 --> 00:00:44,278 +so that it can be localized into languages +such as Arabic and Hebrew. + +14 +00:00:44,311 --> 00:00:46,980 +Arabic and Hebrew are +the most commonly used + +15 +00:00:47,014 --> 00:00:49,583 +of the so-called +“right-to-left languages.” + +16 +00:00:49,616 --> 00:00:51,285 +Why are they called this? + +17 +00:00:51,318 --> 00:00:55,589 +English, French, Chinese, Thai, +and many other languages are written + +18 +00:00:55,622 --> 00:01:00,060 +so that their characters run +from left to right like this. + +19 +00:01:00,093 --> 00:01:04,765 +In Hebrew, the characters run +from right to left like this. + +20 +00:01:04,798 --> 00:01:07,067 +They do the same thing in Arabic, + +21 +00:01:07,100 --> 00:01:10,737 +and in Arabic, +the characters are also cursively joined. + +22 +00:01:10,771 --> 00:01:14,808 +The four letters in “salaam” look +like this when they're written separately. + +23 +00:01:15,976 --> 00:01:18,846 +It’s not just Arabic and Hebrew, +by the way. + +24 +00:01:18,879 --> 00:01:21,348 +Apple actually has font +and keyboard support + +25 +00:01:21,381 --> 00:01:24,785 +for 15 right-to-left languages. + +26 +00:01:24,818 --> 00:01:26,887 +Here’s a paragraph of Hebrew text. + +27 +00:01:26,920 --> 00:01:28,488 +This one is from the Hebrew version + +28 +00:01:28,522 --> 00:01:32,125 +of the “Formulas & Functions Help” +page for Numbers. + +29 +00:01:32,159 --> 00:01:35,762 +Notice that the text is aligned +on the right and ragged on the left, + +30 +00:01:35,796 --> 00:01:38,432 +and that many lines, +including the last one, + +31 +00:01:38,465 --> 00:01:41,168 +have punctuation on the left-hand side. + +32 +00:01:41,201 --> 00:01:43,403 +There’s also a number in this paragraph. + +33 +00:01:43,437 --> 00:01:45,839 +The number still goes from left to right. + +34 +00:01:45,873 --> 00:01:50,844 +This one is telling us +that Numbers supports over 250 functions. + +35 +00:01:50,878 --> 00:01:53,881 +If we widen our view +to include another paragraph, + +36 +00:01:53,914 --> 00:01:56,583 +we see that this one +has some English words, + +37 +00:01:56,617 --> 00:01:59,386 +the names of iWork +and its constituent applications, + +38 +00:01:59,419 --> 00:02:01,688 +Pages, Numbers, and Keynote. + +39 +00:02:01,722 --> 00:02:03,790 +These are also written from left to right, + +40 +00:02:03,824 --> 00:02:05,659 +even in a Hebrew paragraph. + +41 +00:02:05,692 --> 00:02:09,329 +So for many paragraphs, +the text is bidirectional. + +42 +00:02:09,363 --> 00:02:12,165 +This is an inherent property +of Arabic and Hebrew, + +43 +00:02:12,199 --> 00:02:16,603 +and it’s why they’re also often referred +to as the “bidi” languages. + +44 +00:02:17,804 --> 00:02:19,740 +If we widen our view even further, + +45 +00:02:19,773 --> 00:02:22,709 +we see the entire page is +laid out from right to left. + +46 +00:02:22,743 --> 00:02:25,479 +We have a table here, +and the text is to the right + +47 +00:02:25,512 --> 00:02:28,048 +of the images instead of to their left. + +48 +00:02:29,183 --> 00:02:32,719 +And if we widen it even further +to see Safari’s window frame, + +49 +00:02:32,753 --> 00:02:35,389 +we see that it doesn’t stop +with the content. + +50 +00:02:35,422 --> 00:02:39,593 +Lines of Arabic and Hebrew text begin +on the right and progress to the left, + +51 +00:02:39,626 --> 00:02:43,363 +so it’s natural to expect +other UI elements to do the same. + +52 +00:02:43,397 --> 00:02:47,501 +Just as readers expect things to start +on the left and progress to the right, + +53 +00:02:47,534 --> 00:02:50,637 +Arabic and Hebrew readers +expect the opposite. + +54 +00:02:50,671 --> 00:02:54,074 +So here, +Safari’s toolbar runs from right to left + +55 +00:02:54,107 --> 00:02:56,844 +with the traffic light buttons +in the upper right corner + +56 +00:02:56,877 --> 00:02:59,379 +and the buttons progressing to the left. + +57 +00:03:00,247 --> 00:03:03,483 +And if we widen our focus +to include the entire screen, + +58 +00:03:03,517 --> 00:03:05,686 +we see that it extends everywhere. + +59 +00:03:05,719 --> 00:03:08,355 +This is Numbers’ help screen, +and we see that all + +60 +00:03:08,388 --> 00:03:10,824 +of the elements +in Numbers have also flipped. + +61 +00:03:10,858 --> 00:03:12,526 +The sidebar is on the left. + +62 +00:03:12,559 --> 00:03:16,763 +The tab bar runs from right to left. +Even the document itself is flipped. + +63 +00:03:16,797 --> 00:03:21,802 +And the Mac menu bar and dock run +from right to left as well. + +64 +00:03:21,835 --> 00:03:24,471 +Getting all of this right +can be complicated, + +65 +00:03:24,505 --> 00:03:27,708 +but the great news is that we do most +of the heavy lifting for you. + +66 +00:03:27,741 --> 00:03:30,344 +Most support +for right to left comes for free. + +67 +00:03:30,377 --> 00:03:32,646 +But there are things to keep in mind. + +68 +00:03:32,679 --> 00:03:36,083 +So that’s what we’re gonna talk about, +what the system does for you, + +69 +00:03:36,116 --> 00:03:37,985 +when you need to opt in or out, + +70 +00:03:38,018 --> 00:03:41,188 +and what to think about when implementing +your own right to left support. + +71 +00:03:41,221 --> 00:03:47,227 +We’ll talk about text, images, +control orientation, and UI layout. + +72 +00:03:47,261 --> 00:03:50,697 +We'll also cover +displaying numbers in Arabic. + +73 +00:03:50,731 --> 00:03:54,134 +And finally, +we’ll talk a little about how to test + +74 +00:03:54,168 --> 00:03:57,604 +that your app is +handling right to left correctly. + +75 +00:03:57,638 --> 00:03:59,239 +So let’s dive in. + +76 +00:03:59,273 --> 00:04:01,975 +First, we’ll talk a little bit more +about how text works + +77 +00:04:02,009 --> 00:04:04,878 +and introduce some terminology. + +78 +00:04:04,912 --> 00:04:07,781 +Let’s start with the concept +of writing direction. + +79 +00:04:07,814 --> 00:04:13,120 +As we’ve already seen, +English is written from left to right, + +80 +00:04:13,153 --> 00:04:16,723 +and Hebrew is written from right to left. + +81 +00:04:16,757 --> 00:04:19,393 +But what happens if we mix them? + +82 +00:04:21,161 --> 00:04:24,264 +If you’ve got +a multilingual sentence like these, + +83 +00:04:24,298 --> 00:04:27,801 +the individual components +still keep their writing direction, + +84 +00:04:27,835 --> 00:04:30,704 +but this means that each +of these sentences now consists + +85 +00:04:30,737 --> 00:04:32,439 +of three components: + +86 +00:04:32,472 --> 00:04:35,409 +Two separate snippets +of text in the native language + +87 +00:04:35,442 --> 00:04:38,545 +sandwiching one snippet +in a different language. + +88 +00:04:38,579 --> 00:04:41,281 +When we talk about the writing direction +of a paragraph, + +89 +00:04:41,315 --> 00:04:44,852 +we’re talking about the order +of these individual snippets. + +90 +00:04:44,885 --> 00:04:48,155 +When we say the English sentence +has left to right writing direction, + +91 +00:04:48,188 --> 00:04:51,592 +it's because these three boxes +run from left to right, + +92 +00:04:51,625 --> 00:04:55,162 +and when we say the Hebrew sentence +has right to left writing direction, + +93 +00:04:55,195 --> 00:04:58,899 +it’s because those three boxes +run from right to left. + +94 +00:05:00,200 --> 00:05:03,504 +A different but related concept +is text alignment. + +95 +00:05:03,537 --> 00:05:05,639 +If you read a left to right language, + +96 +00:05:05,672 --> 00:05:09,743 +your eye goes to the left-hand side +of the page and progresses to the right. + +97 +00:05:09,776 --> 00:05:13,247 +If you read a right to left language, +it’s the opposite. + +98 +00:05:13,280 --> 00:05:18,051 +So text gets aligned on the right-hand +side in right to left languages. + +99 +00:05:18,085 --> 00:05:20,521 +The good news is that most of the time, + +100 +00:05:20,554 --> 00:05:23,023 +you don’t have to worry +about either of these things. + +101 +00:05:23,056 --> 00:05:25,893 +Not only does CoreText take care +of arranging all + +102 +00:05:25,926 --> 00:05:29,530 +of the characters properly +on a line or paragraph of text, + +103 +00:05:29,563 --> 00:05:33,534 +including when text of different +directions is mixed on one line, + +104 +00:05:33,567 --> 00:05:37,137 +but all of our UI frameworks +automatically set the writing direction + +105 +00:05:37,171 --> 00:05:38,906 +and alignment as well. + +106 +00:05:38,939 --> 00:05:42,809 +All UI widgets default to something +called “natural writing direction" + +107 +00:05:42,843 --> 00:05:44,578 +and “natural alignment." + +108 +00:05:44,611 --> 00:05:47,314 +The writing direction +of a text widget defaults to match + +109 +00:05:47,347 --> 00:05:50,551 +the normal writing direction +for the user’s UI language. + +110 +00:05:50,584 --> 00:05:53,520 +That is, +if your UI is in Hebrew or Arabic, + +111 +00:05:53,554 --> 00:05:57,691 +the writing direction of your text widgets +defaults to right to left. + +112 +00:05:57,724 --> 00:06:00,794 +Natural alignment +follows the writing direction. + +113 +00:06:00,827 --> 00:06:04,498 +That is, if the writing direction +of a text widget is right to left, + +114 +00:06:04,531 --> 00:06:07,134 +it’ll also be right-aligned. + +115 +00:06:07,167 --> 00:06:11,205 +Most of the time, this is what you want, +but you can override the defaults. + +116 +00:06:11,238 --> 00:06:15,576 +We’ll talk more about this +in the section on control orientation. + +117 +00:06:15,609 --> 00:06:19,713 +Now seems like a good time to start +a little chart of terms you’ll run across. + +118 +00:06:19,746 --> 00:06:22,683 +What we’re saying here is +that natural alignment corresponds + +119 +00:06:22,716 --> 00:06:25,285 +to left alignment +in left to right languages + +120 +00:06:25,319 --> 00:06:28,322 +and to right alignment +in right to left languages. + +121 +00:06:28,355 --> 00:06:30,858 +We’ll add to this chart as we go. + +122 +00:06:30,891 --> 00:06:33,961 +But of course, it’s not just text +you have to worry about. + +123 +00:06:33,994 --> 00:06:36,463 +Having text that’s read +in the opposite direction has + +124 +00:06:36,496 --> 00:06:40,100 +a profound effect on elements +of your application other than text. + +125 +00:06:40,133 --> 00:06:44,638 +Let’s talk about how it can affect icons +and other pictorial elements. + +126 +00:06:45,506 --> 00:06:49,276 +This is the Pages toolbar +in English and in Arabic. + +127 +00:06:49,309 --> 00:06:52,679 +Let’s take a closer look at some +of the icons in the toolbar. + +128 +00:06:52,713 --> 00:06:55,616 +Many, such as the “Add page” +and “Media” buttons, + +129 +00:06:55,649 --> 00:06:57,818 +look the same in both languages. + +130 +00:06:57,851 --> 00:07:02,189 +This is either because they’re symmetrical +or because their directionality isn't tied + +131 +00:07:02,222 --> 00:07:05,425 +to the language, +and they look fine in both languages. + +132 +00:07:05,459 --> 00:07:08,629 +Other buttons, +such as the “View” and “Document” buttons, + +133 +00:07:08,662 --> 00:07:11,231 +flip to their mirror images in Arabic. + +134 +00:07:11,265 --> 00:07:14,168 +The “View” button +brings up the Pages sidebar, + +135 +00:07:14,201 --> 00:07:17,104 +which appears on the opposite side +of the window in Arabic, + +136 +00:07:17,137 --> 00:07:19,973 +so the icon has to change to reflect that. + +137 +00:07:20,007 --> 00:07:24,044 +The “Document” button changes to show +that if the user is writing in Arabic, + +138 +00:07:24,077 --> 00:07:27,080 +the pages turn in the opposite direction. + +139 +00:07:27,114 --> 00:07:30,951 +And you can have situations +where the icon changes completely. + +140 +00:07:30,984 --> 00:07:34,821 +The letter on the “Insert text box” button +changes to a different letter + +141 +00:07:34,855 --> 00:07:36,990 +to reflect the user’s language. + +142 +00:07:38,091 --> 00:07:42,129 +Once again, the great news is +that a lot of this gets handled for you. + +143 +00:07:42,162 --> 00:07:45,732 +You have to think about image orientation +a little more than you generally have + +144 +00:07:45,766 --> 00:07:50,337 +to think about text writing direction, +but getting things right is fairly simple. + +145 +00:07:50,370 --> 00:07:54,241 +This is the “View” menu in Mac Pages +in English and Arabic. + +146 +00:07:54,274 --> 00:07:57,477 +Notice that the “Show Ruler” icon +reverses itself. + +147 +00:07:57,511 --> 00:07:59,179 +In right to left languages, + +148 +00:07:59,213 --> 00:08:02,482 +the vertical ruler is +on the right-hand side. + +149 +00:08:02,516 --> 00:08:04,952 +For a custom image like the ruler icon, + +150 +00:08:04,985 --> 00:08:08,655 +right to left behavior is controlled +in the Xcode imageset editor. + +151 +00:08:08,689 --> 00:08:11,558 +If your image is the same +in left to right and right to left, + +152 +00:08:11,592 --> 00:08:13,560 +you don’t have +to do anything special. + +153 +00:08:13,594 --> 00:08:17,464 +The system can also +algorithmically mirror your image for you + +154 +00:08:17,497 --> 00:08:20,501 +when your app is running in a language +with the opposite writing direction + +155 +00:08:20,534 --> 00:08:22,336 +from your development language. + +156 +00:08:22,369 --> 00:08:25,539 +You can ask for this feature +in Xcode’s imageset editor. + +157 +00:08:25,572 --> 00:08:29,009 +In the sidebar, +you’ll find a control marked “Direction.” + +158 +00:08:29,042 --> 00:08:32,246 +If you click on this, +you get a menu with four choices. + +159 +00:08:32,279 --> 00:08:36,717 +If your image is the same regardless +of the UI language, you pick “Fixed.” + +160 +00:08:36,750 --> 00:08:38,785 +If you want algorithmic mirroring, + +161 +00:08:38,819 --> 00:08:41,655 +you opt in by picking one +of the “Mirrors” options. + +162 +00:08:41,688 --> 00:08:45,559 +Which one depends on the writing direction +of your development language. + +163 +00:08:45,592 --> 00:08:48,495 +If you have an image +you can’t mirror algorithmically, + +164 +00:08:48,529 --> 00:08:51,064 +either it has multiple elements +that behave differently + +165 +00:08:51,098 --> 00:08:52,799 +when the UI changes direction, + +166 +00:08:52,833 --> 00:08:56,436 +or you’ve got shading +you don’t want to move, you pick “Both," + +167 +00:08:56,470 --> 00:08:59,606 +and three new wells get added +to the image set editor, + +168 +00:08:59,640 --> 00:09:02,009 +allowing you to create separate images + +169 +00:09:02,042 --> 00:09:05,479 +to use for left to right +and right to left contexts. + +170 +00:09:06,346 --> 00:09:10,450 +But if you use images from SF Symbols, +as the Pages toolbar did, + +171 +00:09:10,484 --> 00:09:12,085 +things are even easier. + +172 +00:09:12,119 --> 00:09:14,688 +Almost all +of the work has been done for you. + +173 +00:09:14,721 --> 00:09:16,290 +The symbols that need to change + +174 +00:09:16,323 --> 00:09:19,326 +for right to left languages +do so automatically. + +175 +00:09:19,359 --> 00:09:22,462 +Consider this bulleted list icon +for example. + +176 +00:09:22,496 --> 00:09:25,299 +For many images, +SF Symbols’ sidebar has + +177 +00:09:25,332 --> 00:09:28,569 +a "localization" section +that shows localized versions + +178 +00:09:28,602 --> 00:09:30,204 +of the image you chose. + +179 +00:09:30,237 --> 00:09:32,639 +In this case, +the bulleted-list icon has + +180 +00:09:32,673 --> 00:09:35,042 +different left to right +and right to left versions. + +181 +00:09:35,075 --> 00:09:37,077 +It mirrors automatically. + +182 +00:09:38,078 --> 00:09:41,081 +The localization feature can +go beyond just mirroring + +183 +00:09:41,114 --> 00:09:42,616 +for right to left languages. + +184 +00:09:42,649 --> 00:09:46,253 +Here’s the localization tab +for the “insert text box” icon. + +185 +00:09:46,286 --> 00:09:50,157 +It has localized versions +not just for the Latin and Arabic scripts, + +186 +00:09:50,190 --> 00:09:52,659 +but for a variety of others as well. + +187 +00:09:53,861 --> 00:09:58,065 +One case that’s specifically important +for Arabic support is the question mark, + +188 +00:09:58,098 --> 00:10:00,501 +which is often used as a “help” icon. + +189 +00:10:00,534 --> 00:10:02,936 +In Arabic, +the question mark is the reverse + +190 +00:10:02,970 --> 00:10:04,538 +of the Latin question mark. + +191 +00:10:04,571 --> 00:10:07,708 +Using icons from SF Symbols for help icons + +192 +00:10:07,741 --> 00:10:10,944 +gets you this version +without any extra work. + +193 +00:10:11,845 --> 00:10:14,648 +One class of images you have +to think about carefully + +194 +00:10:14,681 --> 00:10:17,651 +is arrows +and other directional indicators. + +195 +00:10:17,684 --> 00:10:20,621 +Here we have four arrow-in-a-circle icons. + +196 +00:10:20,654 --> 00:10:22,422 +You’ll notice they’re in two pairs. + +197 +00:10:22,456 --> 00:10:25,826 +You have two pointing to the left +and two pointing to the right. + +198 +00:10:25,859 --> 00:10:28,495 +If we look at just the two +that point to the left, + +199 +00:10:28,529 --> 00:10:31,965 +you’ll see that one is called +“arrow.backward.circle.” + +200 +00:10:31,999 --> 00:10:35,402 +This one flips +to point to the right in right to left. + +201 +00:10:35,435 --> 00:10:38,672 +The other one +is called “arrow.left..circle” + +202 +00:10:38,705 --> 00:10:41,008 +and does NOT flip for right to left. + +203 +00:10:41,041 --> 00:10:44,511 +SF Symbols follows +this naming convention throughout + +204 +00:10:44,545 --> 00:10:48,282 +with icons that you may or may not want +to have flip for right to left. + +205 +00:10:48,315 --> 00:10:52,586 +The “forward” and “backward” ones flip, +and the “left” and “right” ones don’t. + +206 +00:10:52,619 --> 00:10:54,755 +If you’re using an arrow or other shape + +207 +00:10:54,788 --> 00:10:57,024 +to convey the idea +of “forward” or "backward," + +208 +00:10:57,057 --> 00:10:59,193 +you want to use the flipping versions, + +209 +00:10:59,226 --> 00:11:02,262 +and if you’re using the arrow +to convey an absolute direction, + +210 +00:11:02,296 --> 00:11:04,932 +you want to use the non-flipping versions. + +211 +00:11:06,333 --> 00:11:08,936 +And let’s add a row +to our “Terminology” chart. + +212 +00:11:08,969 --> 00:11:12,606 +When choosing images in SF Symbols, +remember that “left” and “right” + +213 +00:11:12,639 --> 00:11:15,843 +always point those directions +and “forward” and "backward" point + +214 +00:11:15,876 --> 00:11:19,513 +in different directions +depending on the UI language. + +215 +00:11:19,546 --> 00:11:23,016 +Now let’s talk about how controls +and other UI widgets + +216 +00:11:23,050 --> 00:11:25,419 +are handled in right to left. + +217 +00:11:25,452 --> 00:11:28,822 +This is the Mac Keynote sidebar +in English and Arabic + +218 +00:11:28,856 --> 00:11:31,725 +showing the format inspector for a shape. + +219 +00:11:31,758 --> 00:11:35,195 +Notice that everything has +flipped its appearance for right to left. + +220 +00:11:35,229 --> 00:11:38,665 +We have lots of pop-up menu buttons +where the menu indicator moves + +221 +00:11:38,699 --> 00:11:41,301 +to the left-hand side for right to left. + +222 +00:11:41,335 --> 00:11:43,437 +We have a couple of checkboxes, +where in Arabic, + +223 +00:11:43,470 --> 00:11:45,706 +the checkbox is to the right of the label. + +224 +00:11:45,739 --> 00:11:48,375 +The opacity slider has changed for Arabic + +225 +00:11:48,408 --> 00:11:52,179 +so that the minimum is on the right +and the maximum on the left. + +226 +00:11:52,212 --> 00:11:56,216 +And so on throughout all +the other controls in this inspector. + +227 +00:11:56,250 --> 00:11:59,186 +The great news +is that you get this behavior for free. + +228 +00:11:59,219 --> 00:12:02,923 +All of the standard UI controls +in all our UI frameworks + +229 +00:12:02,956 --> 00:12:06,393 +automatically reverse their appearance +for right to left languages. + +230 +00:12:06,426 --> 00:12:09,696 +There are, however, +situations where you may not want this + +231 +00:12:09,730 --> 00:12:12,900 +or where you need to have some control +over how it happens. + +232 +00:12:12,933 --> 00:12:15,969 +Let’s take a look at a few +of the interesting cases. + +233 +00:12:16,703 --> 00:12:21,008 +Let’s talk about buttons with both +a textual label and an icon on them. + +234 +00:12:21,041 --> 00:12:24,511 +This is the Keynote animation +inspector showing the controls + +235 +00:12:24,545 --> 00:12:26,480 +for the “Move In” animation. + +236 +00:12:26,513 --> 00:12:30,384 +This inspector has two buttons +with both a label and an icon. + +237 +00:12:30,417 --> 00:12:32,953 +Notice that the arrow +on the Preview button flips + +238 +00:12:32,986 --> 00:12:34,655 +with the change in UI direction, + +239 +00:12:34,688 --> 00:12:38,158 +but the arrow +on the animation direction menu doesn’t. + +240 +00:12:38,192 --> 00:12:40,794 +Both flip sides with the UI direction, + +241 +00:12:40,827 --> 00:12:44,398 +but if the direction control was a group +of buttons instead of a menu, + +242 +00:12:44,431 --> 00:12:47,634 +you can image maybe +not wanting it to change sides. + +243 +00:12:48,802 --> 00:12:52,039 +To show how to control this, +I've isolated those two examples + +244 +00:12:52,072 --> 00:12:54,608 +out into a small toy application. + +245 +00:12:55,709 --> 00:12:58,979 +Here’s the code +to build that UI in SwiftUI. + +246 +00:12:59,012 --> 00:13:01,081 +There are a few interesting things +to note here. + +247 +00:13:01,114 --> 00:13:02,749 +We’ll look at them one at a time. + +248 +00:13:02,783 --> 00:13:06,220 +Let’s start with the image names. + +249 +00:13:06,253 --> 00:13:08,922 +As we saw before, +for images from SF Symbols, + +250 +00:13:08,956 --> 00:13:12,459 +you choose either an icon +that reverses or one that doesn’t. + +251 +00:13:12,492 --> 00:13:15,495 +Here we’ve used +“arrowtriangle.forward.fill” + +252 +00:13:15,529 --> 00:13:16,763 +for the “Preview” button. + +253 +00:13:16,797 --> 00:13:20,534 +The “forward” in the name tells you +that it flips for right to left. + +254 +00:13:20,567 --> 00:13:24,404 +We’ve used “arrow.left” and “arrow.right” +for the direction buttons. + +255 +00:13:24,438 --> 00:13:26,240 +The “left” and “right” +in the name tell you + +256 +00:13:26,273 --> 00:13:28,275 +that they don’t flip for right to left. + +257 +00:13:29,443 --> 00:13:33,113 +If you’re working in AppKit or UIKit, +this works the same way. + +258 +00:13:33,146 --> 00:13:37,784 +Here’s my app in Xcode’s storyboard editor +with the “Preview” button selected. + +259 +00:13:37,818 --> 00:13:40,487 +You control the button’s icon +with the “Image” control + +260 +00:13:40,521 --> 00:13:42,689 +in the Attributes inspector. + +261 +00:13:43,524 --> 00:13:46,860 +And in code, you set this +with the button’s “image” property, + +262 +00:13:46,894 --> 00:13:51,632 +and it works basically the same way +in both AppKit and UIKit. + +263 +00:13:52,566 --> 00:13:56,236 +Coming back to our SwiftUI example, +the next question is how + +264 +00:13:56,270 --> 00:13:59,473 +you control which side +of the label the icon goes on. + +265 +00:13:59,506 --> 00:14:02,276 +You do this by setting a label style. + +266 +00:14:02,309 --> 00:14:07,047 +The built-in TitleAndIconLabelStyle +puts the icon before the label + +267 +00:14:07,080 --> 00:14:09,249 +in the user’s reading direction. + +268 +00:14:09,283 --> 00:14:11,752 +We can use this for the “Left” button. + +269 +00:14:11,785 --> 00:14:15,222 +For the other two buttons, +we want the icon to go after the label + +270 +00:14:15,255 --> 00:14:16,990 +in the user’s reading direction. + +271 +00:14:17,024 --> 00:14:22,596 +To do this, you need a custom label style, +but that’s pretty easy to do. + +272 +00:14:22,629 --> 00:14:26,466 +Your label style’s makeBody() +method just has to make an HStack + +273 +00:14:26,500 --> 00:14:28,669 +and add the title and icon into it. + +274 +00:14:28,702 --> 00:14:33,207 +As with any HStack, the order you add them +determines the order they display, + +275 +00:14:33,240 --> 00:14:37,477 +and the order automatically reverses +when appropriate for the UI direction. + +276 +00:14:37,511 --> 00:14:42,916 +This technique works on any view +that can take a Label, not just buttons. + +277 +00:14:42,950 --> 00:14:47,154 +Of course, you don’t want the icon +to change sides on the “Right” button. + +278 +00:14:47,187 --> 00:14:51,225 +You want it to always be on the right +regardless of the UI direction. + +279 +00:14:51,258 --> 00:14:55,028 +That brings us to the last +interesting thing in this code snippet. + +280 +00:14:55,062 --> 00:14:57,998 +Views in SwiftUI +pick up their directionality + +281 +00:14:58,031 --> 00:15:01,768 +from the SwiftUI environment, +which you can modify. + +282 +00:15:01,802 --> 00:15:05,472 +You do this by adding +an “environment” modifier to a view + +283 +00:15:05,506 --> 00:15:09,743 +and giving it the key and new value +for the property you want to change. + +284 +00:15:09,776 --> 00:15:13,180 +Here we’re overriding the environment’s +layoutDirection property + +285 +00:15:13,213 --> 00:15:15,182 +to always be left to right + +286 +00:15:15,215 --> 00:15:19,086 +regardless of what value +we might be inheriting from our parent. + +287 +00:15:19,119 --> 00:15:22,656 +Changing the environment +in this way works on all SwiftUI views + +288 +00:15:22,689 --> 00:15:25,792 +that respond to the user’s UI direction. + +289 +00:15:25,826 --> 00:15:28,629 +Notice we applied the modifier +to the HStack + +290 +00:15:28,662 --> 00:15:31,031 +that contains the “Left” +and “Right” buttons. + +291 +00:15:31,064 --> 00:15:34,401 +Any changes you make +to a view’s environment are inherited + +292 +00:15:34,434 --> 00:15:37,871 +by its child views, +so putting it here not only + +293 +00:15:37,905 --> 00:15:41,208 +keeps the HStack +from reversing the order of the buttons, + +294 +00:15:41,241 --> 00:15:45,979 +but it keeps both buttons +from reversing the layout of their labels. + +295 +00:15:46,013 --> 00:15:49,016 +And of course, +we didn’t apply our environment modifier + +296 +00:15:49,049 --> 00:15:51,718 +modifier to anything in the parent chain +for the “Preview” button, + +297 +00:15:51,752 --> 00:15:56,056 +so it still reverses when appropriate, +just as we want it to. + +298 +00:15:57,090 --> 00:16:00,127 +So to recap, +the “Left” button has its icon + +299 +00:16:00,160 --> 00:16:04,698 +on the left because we used +the built-in TitleAndIconLabelStyle, + +300 +00:16:04,731 --> 00:16:08,235 +and the “Preview” and “Right” buttons +have their icons on the right + +301 +00:16:08,268 --> 00:16:13,640 +because we used a custom label style +we called IconOnRightLabelStyle. + +302 +00:16:13,674 --> 00:16:16,276 +The “Left” and “Right” buttons +don’t change their order + +303 +00:16:16,310 --> 00:16:18,612 +or the internal arrangement +of their labels + +304 +00:16:18,645 --> 00:16:20,714 +because we added +an environment modifier + +305 +00:16:20,747 --> 00:16:22,683 +to the HStack that contains them, + +306 +00:16:22,716 --> 00:16:25,419 +setting the layout direction +to left to right. + +307 +00:16:25,452 --> 00:16:29,256 +The “Preview” button reverses +the internal arrangement of its label + +308 +00:16:29,289 --> 00:16:31,959 +because it doesn’t have that modifier. + +309 +00:16:32,926 --> 00:16:35,729 +This works differently +in AppKit and UIKit. + +310 +00:16:35,762 --> 00:16:39,032 +In both of those frameworks, +the position of the icon relative + +311 +00:16:39,066 --> 00:16:42,135 +to the label is controlled +with the “Position” control + +312 +00:16:42,169 --> 00:16:44,538 +in Xcode’s Attributes inspector. + +313 +00:16:44,571 --> 00:16:46,240 +If you click on this control, + +314 +00:16:46,273 --> 00:16:49,176 +you’ll see that the menu has, +among other options, + +315 +00:16:49,209 --> 00:16:53,080 +two pairs of options that align the label +and icon horizontally. + +316 +00:16:53,113 --> 00:16:56,250 +You have “Leading” and “Left,” +and you have “Trailing” and “Right.” + +317 +00:16:56,283 --> 00:17:00,254 +“Leading” and “Trailing” change +their meanings based on the UI direction, + +318 +00:17:00,287 --> 00:17:03,023 +and “Left” and “Right” don’t. + +319 +00:17:03,056 --> 00:17:07,227 +In AppKit, you control this +with the button’s imagePosition property. + +320 +00:17:07,261 --> 00:17:11,798 +In UIKit, it’s the imagePlacement property +on the button’s configuration, + +321 +00:17:11,832 --> 00:17:16,403 +which might mean you need +to set your button’s configuration first. + +322 +00:17:16,436 --> 00:17:19,072 +The icon +on the “Preview” button changes sides + +323 +00:17:19,106 --> 00:17:21,341 +because we set its position to “Trailing," + +324 +00:17:21,375 --> 00:17:24,478 +and the icon on the “Right” button +doesn't change sides + +325 +00:17:24,511 --> 00:17:27,881 +because we set its position to “Right.” + +326 +00:17:27,915 --> 00:17:31,718 +This also lets us fill in the last row +in our “terminology” chart. + +327 +00:17:31,752 --> 00:17:34,121 +The terms “leading” and “trailing” +will come up a lot + +328 +00:17:34,154 --> 00:17:35,822 +when discussing UI layout. + +329 +00:17:35,856 --> 00:17:37,224 +Like “forward” and “backward,” + +330 +00:17:37,257 --> 00:17:40,527 +you’ll often see them used +in contrast to “left” and “right.” + +331 +00:17:40,561 --> 00:17:44,464 +The “leading” edge of something is +the edge closest to beginning of the line + +332 +00:17:44,498 --> 00:17:48,402 +or to the side of the screen or window +where the reader would begin reading, + +333 +00:17:48,435 --> 00:17:51,605 +left for left to right +and right for right to left. + +334 +00:17:51,638 --> 00:17:53,907 +The trailing edge is the opposite side, + +335 +00:17:53,941 --> 00:17:55,609 +closest to the end of the line, + +336 +00:17:55,642 --> 00:17:59,046 +right for left to right +and left for right to left. + +337 +00:17:59,079 --> 00:18:02,649 +Most of the time, you want to use these +instead of “left” and “right,” + +338 +00:18:02,683 --> 00:18:07,120 +saving “left” and “right” only for things +that are tied to an absolute direction. + +339 +00:18:08,455 --> 00:18:10,591 +Let’s look at another interesting case. + +340 +00:18:10,624 --> 00:18:14,461 +This is part of the text format inspector +in Keynote on the iPhone, + +341 +00:18:14,494 --> 00:18:16,230 +in English and in Arabic. + +342 +00:18:16,263 --> 00:18:20,167 +This particular screenshot +has four segmented controls. + +343 +00:18:20,200 --> 00:18:23,303 +The top two, +the page selector for the inspector + +344 +00:18:23,337 --> 00:18:26,640 +and the standard +“bold/italic/underline” style buttons, + +345 +00:18:26,673 --> 00:18:30,310 +reverse the order of their segments +depending on the UI language. + +346 +00:18:30,344 --> 00:18:34,381 +If you don’t read Arabic, you’ll have +to trust me on the page selector. + +347 +00:18:34,414 --> 00:18:36,517 +As with the other +controls we’ve looked at, + +348 +00:18:36,550 --> 00:18:39,987 +this is the default; +you get this behavior for free. + +349 +00:18:40,020 --> 00:18:43,123 +The other two segmented controls, +the alignment controls, + +350 +00:18:43,156 --> 00:18:45,626 +don’t reverse the order of their segments. + +351 +00:18:45,659 --> 00:18:48,762 +This is because they move things +in absolute directions. + +352 +00:18:48,795 --> 00:18:50,964 +Left alignment is left alignment + +353 +00:18:50,998 --> 00:18:54,535 +regardless of whether +that’s the beginning or the end of a line. + +354 +00:18:54,568 --> 00:18:58,305 +Let’s look at how we keep +these controls from reversing. + +355 +00:18:58,338 --> 00:19:01,008 +We already know how to do this in SwiftUI. + +356 +00:19:01,041 --> 00:19:02,910 +You just apply an “environment” modifier + +357 +00:19:02,943 --> 00:19:06,947 +that changes the environment’s +layoutDirection property to left to right. + +358 +00:19:06,980 --> 00:19:09,950 +Here, we’re using this technique +to keep the alignment control + +359 +00:19:09,983 --> 00:19:11,451 +from reversing itself, + +360 +00:19:11,485 --> 00:19:16,323 +but letting the style control +reverse itself as it normally does. + +361 +00:19:16,356 --> 00:19:18,792 +In UIKit, this works differently. + +362 +00:19:18,825 --> 00:19:22,029 +Here’s a toy application +in Xcode designed to simulate + +363 +00:19:22,062 --> 00:19:24,131 +that segmented control behavior. + +364 +00:19:24,164 --> 00:19:26,200 +I have two segmented controls, + +365 +00:19:26,233 --> 00:19:29,203 +one that mimics +the bold/italic/underline behavior + +366 +00:19:29,236 --> 00:19:32,172 +and a second one +that mimics the alignment control. + +367 +00:19:32,206 --> 00:19:34,341 +The alignment control is selected. + +368 +00:19:34,374 --> 00:19:38,979 +In the attribute inspector, +you’ll find a menu labeled “Semantic.” + +369 +00:19:39,012 --> 00:19:42,382 +If you click on that menu, +you get five choices. + +370 +00:19:42,416 --> 00:19:46,353 +This menu controls something called +the semantic content attribute. + +371 +00:19:46,386 --> 00:19:49,623 +You use this to say what kind +of control this is, + +372 +00:19:49,656 --> 00:19:53,493 +and the system uses that to determine +if it reverses its appearance + +373 +00:19:53,527 --> 00:19:55,529 +based on the UI direction. + +374 +00:19:55,562 --> 00:19:57,698 +The default is “Unspecified," + +375 +00:19:57,731 --> 00:20:00,767 +which causes the control +to reverse its appearance. + +376 +00:20:00,801 --> 00:20:04,037 +"Playback" says the control is +a media playback control + +377 +00:20:04,071 --> 00:20:06,406 +or part of a group of playback controls. + +378 +00:20:06,440 --> 00:20:11,345 +"Spatial" says the control is a spatial +control or a part of a group of them. + +379 +00:20:11,378 --> 00:20:16,350 +Spatial controls move things around +in space in absolute directions. + +380 +00:20:16,383 --> 00:20:18,752 +And finally, you can force the control + +381 +00:20:18,785 --> 00:20:23,290 +to always lay itself out left to right +or right to left. + +382 +00:20:23,323 --> 00:20:27,895 +So the bold/italic/underline control +reverses its segments for right to left + +383 +00:20:27,928 --> 00:20:31,765 +because its semantic content attribute +is set to “Unspecified,” + +384 +00:20:31,798 --> 00:20:35,102 +and the alignment control +doesn’t reverse its segments + +385 +00:20:35,135 --> 00:20:39,306 +because its semantic content attribute +is set to “Spatial.” + +386 +00:20:39,339 --> 00:20:41,808 +The great thing about this +is that it doesn’t just work + +387 +00:20:41,842 --> 00:20:43,644 +for UISegmentedControl. + +388 +00:20:43,677 --> 00:20:46,680 +All UIViews have +a semantic content attribute, + +389 +00:20:46,713 --> 00:20:50,117 +and it controls all +of that control’s right to left behavior. + +390 +00:20:50,150 --> 00:20:53,453 +For any standard UIKit view +that has subcomponents, + +391 +00:20:53,487 --> 00:20:56,557 +the semantic content attribute +will determine whether the positioning + +392 +00:20:56,590 --> 00:21:01,061 +of that view’s subcomponents reverses +based on the UI language. + +393 +00:21:02,196 --> 00:21:05,065 +In AppKit, +you do this kind of thing differently. + +394 +00:21:05,098 --> 00:21:08,202 +For all NSControls, +the Xcode attributes inspector + +395 +00:21:08,235 --> 00:21:11,805 +contains two menus +marked “Layout” and “Mirror.” + +396 +00:21:11,839 --> 00:21:13,674 +The “Layout” menu corresponds + +397 +00:21:13,707 --> 00:21:17,044 +to the control’s +userInterfaceLayoutDirection property, + +398 +00:21:17,077 --> 00:21:21,181 +which says whether the control should +use left to right or right to left layout. + +399 +00:21:21,215 --> 00:21:24,551 +You don’t normally change this +when working in Interface Builder. + +400 +00:21:24,585 --> 00:21:27,087 +Instead, you use the “Mirror” menu. + +401 +00:21:27,120 --> 00:21:31,158 +Setting it to “Always” causes +the userInterfaceLayoutDirection, + +402 +00:21:31,191 --> 00:21:35,295 +and thus the control’s layout, +to be flipped when the nib is loaded + +403 +00:21:35,329 --> 00:21:37,898 +when the user’s UI language +is right to left, + +404 +00:21:37,931 --> 00:21:40,300 +and setting it to “Never” +defeats this behavior, + +405 +00:21:40,334 --> 00:21:42,336 +keeping the layout the same. + +406 +00:21:42,369 --> 00:21:45,372 +You keep the layout +of the alignment control the same + +407 +00:21:45,405 --> 00:21:47,975 +by setting this value to “Never”. + +408 +00:21:48,008 --> 00:21:49,910 +If you’re not working +in Interface Builder, + +409 +00:21:49,943 --> 00:21:52,112 +you accomplish the same thing in code + +410 +00:21:52,145 --> 00:21:54,882 +by setting the control’s +userInterfaceLayoutDirection + +411 +00:21:54,915 --> 00:21:57,484 +back to left to right directly. + +412 +00:21:57,518 --> 00:22:01,688 +userInterfaceLayoutDirection, +by the way, is a property on NSView, + +413 +00:22:01,722 --> 00:22:05,492 +but appears in Interface Builder +only on instances of NSControl, + +414 +00:22:05,526 --> 00:22:08,729 +so if you want to reverse something +that’s not an NSControl, + +415 +00:22:08,762 --> 00:22:11,198 +you need code like we’re showing here. + +416 +00:22:12,299 --> 00:22:15,335 +Before we move on, +I want to talk about text a little more. + +417 +00:22:15,369 --> 00:22:20,207 +This is the “Set document password” dialog +in iWork for the Mac. + +418 +00:22:20,240 --> 00:22:23,310 +In the Arabic version, +you’ll see everything has reversed. + +419 +00:22:23,343 --> 00:22:25,712 +But notice what’s happened to the labels. + +420 +00:22:25,746 --> 00:22:28,482 +In English, they were right-aligned +so that they’d be close + +421 +00:22:28,515 --> 00:22:29,950 +to the edit-text fields. + +422 +00:22:29,983 --> 00:22:32,152 +In Arabic, they’re left-aligned. + +423 +00:22:32,186 --> 00:22:35,322 +In other words, you have +the opposite of natural alignment, + +424 +00:22:35,355 --> 00:22:38,592 +trailing-edge alignment, if you will. + +425 +00:22:38,625 --> 00:22:42,362 +Getting this layout in SwiftUI +on the Mac is trivially easy. + +426 +00:22:42,396 --> 00:22:45,432 +Just use a Form +to gather the text fields together. + +427 +00:22:45,465 --> 00:22:48,669 +But this can get interesting if, +as in our example, + +428 +00:22:48,702 --> 00:22:51,738 +one of the labels is multiple lines. + +429 +00:22:51,772 --> 00:22:56,076 +If we expand that last label out +to be two lines, we get this. + +430 +00:22:56,109 --> 00:22:59,079 +The two one-line labels +are correctly right-aligned, + +431 +00:22:59,112 --> 00:23:01,148 +but the two-line label isn’t. + +432 +00:23:01,181 --> 00:23:04,785 +The thing is, +the bottom label really is right-aligned. + +433 +00:23:04,818 --> 00:23:07,454 +It’s just that its bounding box +is right-aligned, + +434 +00:23:07,487 --> 00:23:11,258 +not the individual lines of text +within that bounding box. + +435 +00:23:11,291 --> 00:23:15,696 +You fix this by adding +a multilineTextAlignment modifier + +436 +00:23:15,729 --> 00:23:17,397 +to the last label. + +437 +00:23:17,431 --> 00:23:21,134 +Text alignment in SwiftUI only comes +into play on text objects + +438 +00:23:21,168 --> 00:23:23,303 +that are more than one line long. + +439 +00:23:23,337 --> 00:23:25,138 +For single-line text objects, + +440 +00:23:25,172 --> 00:23:28,375 +their bounding box +tightly encloses the text itself, + +441 +00:23:28,408 --> 00:23:32,312 +and you align it +by aligning the entire text object. + +442 +00:23:32,346 --> 00:23:37,017 +Also notice that whether you’re aligning +the text’s bounding box or multiple lines + +443 +00:23:37,050 --> 00:23:39,086 +of text inside the bounding box, + +444 +00:23:39,119 --> 00:23:41,822 +you have a choice of leading +and trailing alignment, + +445 +00:23:41,855 --> 00:23:45,359 +which change meaning based +on the user’s UI direction. + +446 +00:23:45,392 --> 00:23:48,629 +To keep the alignment the same +regardless of UI direction, + +447 +00:23:48,662 --> 00:23:50,497 +you use an environment modifier + +448 +00:23:50,531 --> 00:23:54,234 +to change the environment’s +layout direction as we saw earlier. + +449 +00:23:55,202 --> 00:23:58,639 +In UIKit, +text is naturally aligned by default, + +450 +00:23:58,672 --> 00:24:02,276 +but you can change it to one +of the absolute directions when necessary. + +451 +00:24:02,309 --> 00:24:05,379 +In Interface Builder, +the control looks like this + +452 +00:24:05,412 --> 00:24:08,148 +and corresponds +to the textAlignment property + +453 +00:24:08,182 --> 00:24:10,517 +on UILabel and UITextView. + +454 +00:24:10,551 --> 00:24:13,287 +The button on the far right +with the dotted line + +455 +00:24:13,320 --> 00:24:16,256 +gives you natural, +or leading-edge, alignment. + +456 +00:24:16,290 --> 00:24:17,658 +The alignment of the label + +457 +00:24:17,691 --> 00:24:20,360 +will follow +the label’s semantic content attribute. + +458 +00:24:20,394 --> 00:24:24,298 +The other buttons give you fixed left, +right, or center alignment + +459 +00:24:24,331 --> 00:24:28,869 +regardless of the UI direction +or the label’s semantic content attribute. + +460 +00:24:28,902 --> 00:24:31,738 +There’s no built-in setting +for trailing-edge alignment. + +461 +00:24:31,772 --> 00:24:34,274 +You have to do that in code. + +462 +00:24:34,308 --> 00:24:36,443 +In AppKit, it’s a little different. + +463 +00:24:36,476 --> 00:24:38,345 +You still have the alignment control, + +464 +00:24:38,378 --> 00:24:41,081 +and it works +basically the same as in UIKit, + +465 +00:24:41,114 --> 00:24:44,952 +but the way it interacts with +userInterfaceLayoutDirection is different. + +466 +00:24:44,985 --> 00:24:47,254 +If you have “Mirror” set +to “Automatically,” + +467 +00:24:47,287 --> 00:24:49,756 +and the system sets +userInterfaceLayoutDirection + +468 +00:24:49,790 --> 00:24:51,124 +to right to left, + +469 +00:24:51,158 --> 00:24:53,994 +the meanings +of all the alignment settings reverse. + +470 +00:24:54,027 --> 00:24:56,196 +So if “Mirror” is set to “Automatically,” + +471 +00:24:56,230 --> 00:24:59,132 +left alignment is +really leading-edge alignment, + +472 +00:24:59,166 --> 00:25:03,403 +and right alignment is +really trailing-edge alignment. + +473 +00:25:03,437 --> 00:25:06,406 +So we’ve talked about how all +of the standard UI widgets + +474 +00:25:06,440 --> 00:25:10,143 +automatically reverse their layouts +to match the user’s writing direction, + +475 +00:25:10,177 --> 00:25:13,180 +but it’s fairly easy +to prevent that when necessary. + +476 +00:25:13,213 --> 00:25:18,185 +This extends to arranging +individual UI widgets on the screen. + +477 +00:25:18,218 --> 00:25:21,288 +If you’re using one +of the standard views or view controllers + +478 +00:25:21,321 --> 00:25:23,657 +that handles the positioning +of their subviews, + +479 +00:25:23,690 --> 00:25:26,159 +all of them +automatically reverse their layouts + +480 +00:25:26,193 --> 00:25:29,296 +when necessary +without you having to do anything. + +481 +00:25:29,329 --> 00:25:32,366 +Table views and collection views +handle scrolling correctly + +482 +00:25:32,399 --> 00:25:34,501 +in right to left languages too. + +483 +00:25:34,535 --> 00:25:38,105 +UINavigationController +automatically changes the directions + +484 +00:25:38,138 --> 00:25:42,042 +of its segue animations +to reflect the user’s writing direction, + +485 +00:25:42,075 --> 00:25:45,078 +and changes the “back” button +to match it as well. + +486 +00:25:45,112 --> 00:25:49,383 +And UIPageViewController automatically +reverses the paging direction + +487 +00:25:49,416 --> 00:25:53,187 +and the meanings of the swipe gestures +automatically as well. + +488 +00:25:53,220 --> 00:25:55,856 +You generally won’t want +to override these things, + +489 +00:25:55,889 --> 00:25:59,393 +except for when you’re using a stack view +to position child views, + +490 +00:25:59,426 --> 00:26:02,496 +but all of the views honor +their semantic content attribute + +491 +00:26:02,529 --> 00:26:06,166 +and use it to tell them how +to lay out their subviews. + +492 +00:26:06,200 --> 00:26:09,570 +The same goes for the standard +AppKit views, and again, + +493 +00:26:09,603 --> 00:26:13,106 +table and collection views +handle right to left scrolling as well. + +494 +00:26:13,140 --> 00:26:16,844 +The views all honor their +userInterfaceLayoutDirection property + +495 +00:26:16,877 --> 00:26:19,112 +in determining how +to lay out their subviews, + +496 +00:26:19,146 --> 00:26:21,415 +although Interface Builder +doesn’t let you set it. + +497 +00:26:21,448 --> 00:26:24,051 +You have to do that in code. + +498 +00:26:24,084 --> 00:26:26,954 +The standard SwiftUI views also reflect + +499 +00:26:26,987 --> 00:26:29,890 +the environment’s +layoutDirection property. + +500 +00:26:32,659 --> 00:26:36,063 +If you’re using Auto Layout +instead of stack and grid views + +501 +00:26:36,096 --> 00:26:37,164 +to lay out your view, + +502 +00:26:37,197 --> 00:26:39,933 +Auto Layout also +automatically reverses things + +503 +00:26:39,967 --> 00:26:41,668 +to account for the UI direction. + +504 +00:26:41,702 --> 00:26:43,971 +If you have horizontal constraints, + +505 +00:26:44,004 --> 00:26:46,306 +you’ll see that they +automatically connect things + +506 +00:26:46,340 --> 00:26:49,676 +to the leading and trailing edges, +and as we’ve seen, + +507 +00:26:49,710 --> 00:26:53,614 +"leading" and “trailing” have different +meanings depending on the UI direction. + +508 +00:26:53,647 --> 00:26:57,317 +You can set Auto Layout constraints +to absolute left and right directions, + +509 +00:26:57,351 --> 00:26:58,952 +though, if you need to. + +510 +00:26:58,986 --> 00:27:02,823 +You do this by clicking on the direction +for one side of the constraint + +511 +00:27:02,856 --> 00:27:05,325 +and turning off +“Respect language direction” + +512 +00:27:05,359 --> 00:27:06,960 +in the menu that pops up. + +513 +00:27:06,994 --> 00:27:09,930 +That’ll change the direction +of both ends of the constraint + +514 +00:27:09,963 --> 00:27:13,867 +from “leading” and “trailing” +to “left” and “right.” + +515 +00:27:13,901 --> 00:27:17,638 +There are a lot of different ways +to set up Auto Layout constraints in code. + +516 +00:27:17,671 --> 00:27:19,039 +Here is one of them. + +517 +00:27:19,072 --> 00:27:21,775 +Pretty much however you do it, +the thing to remember is + +518 +00:27:21,808 --> 00:27:24,912 +is to use "leading" and “trailing” +instead of “left” and “right,” + +519 +00:27:24,945 --> 00:27:28,749 +except in the comparatively +rare situations where you really want + +520 +00:27:28,782 --> 00:27:34,087 +the same layout orientation regardless +of the UI language’s writing direction. + +521 +00:27:34,121 --> 00:27:37,691 +Okay, that was a lot to take in, +so let's take a breath. + +522 +00:27:37,724 --> 00:27:40,494 +The main takeaway is +that we do most of the work + +523 +00:27:40,527 --> 00:27:42,829 +of handling +right to left languages for you + +524 +00:27:42,863 --> 00:27:45,899 +and that when you need to override it, +there are ways to do that. + +525 +00:27:45,933 --> 00:27:48,602 +Let's take one more look +at our terminology slide. + +526 +00:27:48,635 --> 00:27:52,339 +Remember that "left" and "right" +are always left and right + +527 +00:27:52,372 --> 00:27:54,775 +and that the other terms +reverse their meanings + +528 +00:27:54,808 --> 00:27:57,411 +depending on the overall UI direction. + +529 +00:27:58,745 --> 00:28:02,282 +Before we wrap up, let’s take a look +at one more important issue, + +530 +00:28:02,316 --> 00:28:04,251 +and that’s how to display numbers. + +531 +00:28:04,284 --> 00:28:07,888 +It’s not strictly a right to left issue, +but for many developers, + +532 +00:28:07,921 --> 00:28:10,657 +Arabic is the first language +they’ll localize for + +533 +00:28:10,691 --> 00:28:15,429 +that uses different digit characters +than the ones used in English. + +534 +00:28:15,462 --> 00:28:17,631 +Here’s what those digits look like. + +535 +00:28:17,664 --> 00:28:20,300 +There are lots of different +naming conventions for the digits, + +536 +00:28:20,334 --> 00:28:24,271 +but I'll call the ones used with most +European languages “Latin” digits + +537 +00:28:24,304 --> 00:28:27,608 +and the ones used +with Arabic “Arabic-Indic” digits. + +538 +00:28:27,641 --> 00:28:30,210 +There are other languages +that have their own digits. + +539 +00:28:30,244 --> 00:28:33,013 +These are the Devanagari digits +used with Hindi; + +540 +00:28:33,046 --> 00:28:36,850 +Hindi is the other common language +that uses different digits. + +541 +00:28:36,884 --> 00:28:40,087 +One important thing to keep +in mind is that neither Arabic + +542 +00:28:40,120 --> 00:28:43,490 +nor Hindi always uses their native digits. + +543 +00:28:43,524 --> 00:28:45,726 +For Arabic, it depends on the country, + +544 +00:28:45,759 --> 00:28:50,097 +with some, such as Saudi Arabia, +using native digits and others, + +545 +00:28:50,130 --> 00:28:53,634 +such as the United Arab Emirates, +using Latin digits. + +546 +00:28:53,667 --> 00:28:57,538 +Individual users can also +choose their preferred digits. + +547 +00:28:57,571 --> 00:29:00,774 +For Hindi, +we use Latin digits by default, + +548 +00:29:00,807 --> 00:29:04,444 +but users can elect +to use native digits instead. + +549 +00:29:05,546 --> 00:29:09,716 +You already know that constructing +UI strings like this is a bad idea. + +550 +00:29:09,750 --> 00:29:12,419 +The string is hard-coded +and can’t be translated, + +551 +00:29:12,452 --> 00:29:15,856 +the message doesn’t +change to handle plurals, and so forth. + +552 +00:29:15,889 --> 00:29:18,525 +But another reason +this isn’t good is that the value + +553 +00:29:18,559 --> 00:29:23,163 +of “peopleInChat” will always +get rendered with Latin digits. + +554 +00:29:23,197 --> 00:29:26,733 +You probably also already know +that the solution to that is + +555 +00:29:26,767 --> 00:29:29,436 +to use the “localized” init method +on String, + +556 +00:29:29,469 --> 00:29:32,773 +which will look up the actual string +in your application’s bundle + +557 +00:29:32,806 --> 00:29:36,009 +and handle plurals properly +if you have a stringsdict file. + +558 +00:29:36,043 --> 00:29:39,546 +The great news is +that it also handles numbers correctly. + +559 +00:29:39,580 --> 00:29:43,283 +The value of the “peopleInChat” +interpolation here will be rendered + +560 +00:29:43,317 --> 00:29:47,354 +with the correct localized digits +for the user’s locale and preferences. + +561 +00:29:47,387 --> 00:29:50,591 +This also works right +with text views in SwiftUI. + +562 +00:29:50,624 --> 00:29:54,328 +The text views initializer will also +render any string interpolations + +563 +00:29:54,361 --> 00:29:57,431 +with properly localized digits. + +564 +00:29:57,464 --> 00:30:01,768 +Always use String(localized:) +when constructing user-visible strings. + +565 +00:30:01,802 --> 00:30:05,372 +Many of the other APIs on String +that can format numbers, + +566 +00:30:05,405 --> 00:30:08,408 +including +stringWithFormat: +and the String init function + +567 +00:30:08,442 --> 00:30:12,613 +that takes a number, +always use Latin digits. + +568 +00:30:12,646 --> 00:30:16,650 +One wrinkle to be aware of is +static strings that contain numerals, + +569 +00:30:16,683 --> 00:30:17,951 +such as this one. + +570 +00:30:17,985 --> 00:30:19,253 +What’s the big deal? + +571 +00:30:19,286 --> 00:30:22,990 +You send it off to the translators, +they translate it, and you get this. + +572 +00:30:23,023 --> 00:30:25,092 +This is correct in a lot of places, + +573 +00:30:25,125 --> 00:30:27,528 +but in Saudi Arabia +and some other countries, + +574 +00:30:27,561 --> 00:30:29,062 +you want to see this + +575 +00:30:29,096 --> 00:30:33,233 +. The text is the same except +for the character used for the 3. + +576 +00:30:33,267 --> 00:30:35,536 +You could, of course, +have separate localizations + +577 +00:30:35,569 --> 00:30:38,772 +for the Arabic-speaking locales +that use Arabic-Indic digits + +578 +00:30:38,805 --> 00:30:40,707 +and the ones that use Latin digits, + +579 +00:30:40,741 --> 00:30:43,477 +but nobody does that, +and it’d be wasteful. + +580 +00:30:43,510 --> 00:30:45,646 +Worse, in both Arabic and Hindi, + +581 +00:30:45,679 --> 00:30:48,415 +the user can choose the digits +they want to use, + +582 +00:30:48,448 --> 00:30:50,684 +so you’d be having +to choose a localization + +583 +00:30:50,717 --> 00:30:55,088 +based on the user’s preferences, +not just on their locale. + +584 +00:30:55,122 --> 00:30:59,793 +The solution is to still just +have one Arabic or Hindi localization, + +585 +00:30:59,826 --> 00:31:02,262 +but to substitute +the number in at run time, + +586 +00:31:02,296 --> 00:31:04,965 +even though you know the value +at compile time. + +587 +00:31:04,998 --> 00:31:09,002 +In Swift, you can just use +a string interpolation to do this. + +588 +00:31:10,170 --> 00:31:12,873 +If you’ve got other elements +that travel with a number, + +589 +00:31:12,906 --> 00:31:16,510 +their placement relative +to the number can also be challenging. + +590 +00:31:16,543 --> 00:31:19,413 +It isn’t even the same +for all right to left languages. + +591 +00:31:19,446 --> 00:31:21,982 +Notice that the minus sign +and the percent sign are + +592 +00:31:22,015 --> 00:31:25,152 +on different sides +of the number in Arabic and Hebrew. + +593 +00:31:25,185 --> 00:31:28,322 +In fact, it doesn’t have to be +a right to left language at all. + +594 +00:31:28,355 --> 00:31:31,358 +Notice that in Turkish, +which is a left to right language, + +595 +00:31:31,391 --> 00:31:34,094 +the percent sign also goes on the left. + +596 +00:31:34,127 --> 00:31:37,831 +And, of course, keep in mind +that if you’re using native Arabic digits, + +597 +00:31:37,865 --> 00:31:41,401 +they use +a completely different percent symbol. + +598 +00:31:41,435 --> 00:31:44,938 +In other words, you really don’t want +to do this kind of thing, + +599 +00:31:44,972 --> 00:31:47,875 +where you’re appending the percent sign, +or currency sign, + +600 +00:31:47,908 --> 00:31:51,178 +unit abbreviation, or whatever yourself. + +601 +00:31:51,211 --> 00:31:54,748 +Instead, use a number formatter +to add the percent sign, + +602 +00:31:54,781 --> 00:31:56,850 +or currency symbol, or whatever. + +603 +00:31:56,884 --> 00:31:59,887 +In Swift, this is easy +to do with the formatted() method + +604 +00:31:59,920 --> 00:32:01,622 +on all the numeric types. + +605 +00:32:01,655 --> 00:32:04,791 +If it’s part of a larger string, +as in this example, + +606 +00:32:04,825 --> 00:32:08,095 +String(localized:) will also make sure +that the formatted number-- + +607 +00:32:08,128 --> 00:32:11,031 +or anything else substituted +into the string at runtime, + +608 +00:32:11,064 --> 00:32:12,833 +including other strings, + +609 +00:32:12,866 --> 00:32:15,836 +is surrounded with markup +that’ll keep the writing directions + +610 +00:32:15,869 --> 00:32:18,539 +of the formatted number +and the surrounding message + +611 +00:32:18,572 --> 00:32:21,141 +from messing each other up. + +612 +00:32:21,175 --> 00:32:25,179 +Finally, I want to leave you with one tip +for testing your app to make sure + +613 +00:32:25,212 --> 00:32:27,781 +that you’re doing the right thing +for right to left. + +614 +00:32:28,549 --> 00:32:31,518 +You don’t have to have +Arabic or Hebrew localizations + +615 +00:32:31,552 --> 00:32:34,688 +in your executable +to test your app in right to left. + +616 +00:32:34,721 --> 00:32:36,957 +You can actually test +right to left behavior + +617 +00:32:36,990 --> 00:32:38,926 +in your development language. + +618 +00:32:38,959 --> 00:32:42,329 +To do this, +bring up the scheme editor in Xcode. + +619 +00:32:42,362 --> 00:32:46,633 +Now go to the Options tab +and look for the “App Language” menu. + +620 +00:32:46,667 --> 00:32:50,370 +At the bottom of this menu are a bunch +of “pseudolanguage” options. + +621 +00:32:50,404 --> 00:32:54,274 +These are fake languages +that transform your UI in various ways + +622 +00:32:54,308 --> 00:32:56,910 +to allow you to check +for localization problems + +623 +00:32:56,944 --> 00:32:59,112 +without actual localizations. + +624 +00:32:59,146 --> 00:33:02,382 +Pick the “Right-to-Left Pseudolanguage” +option and click Run, + +625 +00:33:02,416 --> 00:33:06,286 +and your app will still be in English +or whatever your development language is, + +626 +00:33:06,320 --> 00:33:10,190 +but the UI will +all have been flipped for right to left. + +627 +00:33:10,224 --> 00:33:11,758 +And that’s all I have. + +628 +00:33:11,792 --> 00:33:14,728 +Localizing for right-to-left languages +involves attention + +629 +00:33:14,761 --> 00:33:18,031 +to some issues relating +to the change in writing direction, + +630 +00:33:18,065 --> 00:33:21,201 +but the system does most +of the heavy lifting for you. + +631 +00:33:21,235 --> 00:33:24,471 +There are cases, +usually around absolute directions, + +632 +00:33:24,505 --> 00:33:26,640 +where you may want +to opt of this behavior, + +633 +00:33:26,673 --> 00:33:28,408 +and that’s always possible. + +634 +00:33:28,442 --> 00:33:33,680 +And remember that not all languages +use Latin digits to render numbers. + +635 +00:33:33,714 --> 00:33:35,449 +Keep these things in mind, + +636 +00:33:35,482 --> 00:33:37,584 +and it shouldn’t be hard +to get things right + +637 +00:33:37,618 --> 00:33:38,685 +to left. + +638 +00:33:38,719 --> 00:33:42,990 +[upbeat music] + diff --git a/eng/2022 Session 10108 Streamline local authorization flows en.srt b/eng/2022 Session 10108 Streamline local authorization flows en.srt new file mode 100644 index 0000000..a518a5c --- /dev/null +++ b/eng/2022 Session 10108 Streamline local authorization flows en.srt @@ -0,0 +1,1365 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,843 --> 00:00:12,946 +Felix Acero: Hi, my name is Felix Acero, +and I am a Software Engineer + +3 +00:00:12,980 --> 00:00:15,916 +with the Security Engineering +and Architecture team. + +4 +00:00:15,949 --> 00:00:17,684 +In this video, I am going to show you + +5 +00:00:17,718 --> 00:00:19,786 +how you can use +the LocalAuthentication framework + +6 +00:00:19,820 --> 00:00:23,423 +to improve the authentication +and authorization flows of your app. + +7 +00:00:23,457 --> 00:00:25,659 +We will start by taking a look +at the generic concepts + +8 +00:00:25,692 --> 00:00:28,262 +of authentication and authorization + +9 +00:00:28,295 --> 00:00:31,164 +and how they apply to your application. + +10 +00:00:31,198 --> 00:00:34,902 +Then we will review +how the existing LocalAuthentication API, + +11 +00:00:34,935 --> 00:00:37,004 +and in particular the LAContext, + +12 +00:00:37,037 --> 00:00:41,108 +can help you implement a wide range +of authorization schemes. + +13 +00:00:41,141 --> 00:00:43,110 +And finally we will see how the new APIs + +14 +00:00:43,143 --> 00:00:45,312 +we are adding to LocalAuthentication +this year + +15 +00:00:45,345 --> 00:00:48,348 +can help you further streamline +your authorization code. + +16 +00:00:50,017 --> 00:00:53,954 +So let's start by talking +about authentication and authorization. + +17 +00:00:53,987 --> 00:00:56,857 +Authentication and authorization +are distinct + +18 +00:00:56,890 --> 00:00:59,326 +yet closely related security concepts. + +19 +00:00:59,359 --> 00:01:04,264 +On the one hand, authentication is the act +of verifying the identity of the user. + +20 +00:01:04,298 --> 00:01:08,535 +On the other hand, authorization is +the act of verifying whether a given user + +21 +00:01:08,569 --> 00:01:13,040 +is allowed to perform a specific operation +on a concrete resource. + +22 +00:01:13,073 --> 00:01:16,410 +Put together, we can see that, +since we first need to verify + +23 +00:01:16,443 --> 00:01:19,413 +that the user is who they claim to be +before we can evaluate + +24 +00:01:19,446 --> 00:01:22,149 +what resources and operations +are available to them, + +25 +00:01:22,182 --> 00:01:26,587 +we can say that authentication +in fact enables authorization. + +26 +00:01:26,620 --> 00:01:28,322 +To help illustrate these concepts, + +27 +00:01:28,355 --> 00:01:31,124 +let's look at a concrete example +involving a common security resource + +28 +00:01:31,158 --> 00:01:34,361 +managed by your applications, +such as Secure Enclave keys. + +29 +00:01:35,562 --> 00:01:38,498 +Secure Enclave keys are special types +of asymmetric keys + +30 +00:01:38,532 --> 00:01:41,268 +that are bound to a specific device +and which are protected + +31 +00:01:41,301 --> 00:01:46,173 +by a hardware-based key manager +that is isolated from the main processor. + +32 +00:01:46,206 --> 00:01:49,042 +What makes these keys special +is that when you store a private key + +33 +00:01:49,076 --> 00:01:51,578 +in the Secure Enclave, +you never actually handle the key + +34 +00:01:51,612 --> 00:01:56,350 +but instead instruct the Secure Enclave +to perform operations with it. + +35 +00:01:56,383 --> 00:01:59,486 +Secure Enclave keys can be associated +with access control lists + +36 +00:01:59,520 --> 00:02:02,322 +or ACLs for short. + +37 +00:02:02,356 --> 00:02:05,859 +An access control list specifies +the requirements that need to be satisfied + +38 +00:02:05,893 --> 00:02:10,130 +in order to perform specific operations +such as signing or decrypting a blob. + +39 +00:02:11,532 --> 00:02:14,535 +They can specify +when a given item is available, + +40 +00:02:14,568 --> 00:02:18,539 +for example after device unlock, +as well as the authentication requirements + +41 +00:02:18,572 --> 00:02:21,842 +needed to allow the execution +of certain operations. + +42 +00:02:21,875 --> 00:02:24,444 +For this example, +let's say that your app wants to protect + +43 +00:02:24,478 --> 00:02:28,282 +the sign and decrypt operations +of its key with biometric authentication, + +44 +00:02:28,315 --> 00:02:31,185 +while also ensuring +that the key is only available + +45 +00:02:31,218 --> 00:02:33,620 +after the device has been unlocked. + +46 +00:02:34,454 --> 00:02:37,224 +Now let's see how an authorization flow +would look like + +47 +00:02:37,257 --> 00:02:39,560 +for a sign operation involving this key. + +48 +00:02:40,661 --> 00:02:44,731 +First, your application issues a request +to sign a blob using the private key. + +49 +00:02:45,899 --> 00:02:49,503 +Then, after verifying +that your application can access the key, + +50 +00:02:49,536 --> 00:02:52,773 +the system proceeds to identify +the authorization requirements + +51 +00:02:52,806 --> 00:02:54,408 +for the sign operation. + +52 +00:02:54,441 --> 00:02:58,312 +In this case, the sign operation requires +a successful biometric authentication + +53 +00:02:58,345 --> 00:03:01,815 +from any of the currently enrolled users. + +54 +00:03:01,849 --> 00:03:03,584 +The system will then walk the user + +55 +00:03:03,617 --> 00:03:07,855 +through the biometric authentication +process via the standard UI. + +56 +00:03:07,888 --> 00:03:09,690 +Upon a successful authentication, + +57 +00:03:09,723 --> 00:03:10,858 +the system verifies + +58 +00:03:10,891 --> 00:03:15,195 +that all the remaining authorization +requirements have been satisfied + +59 +00:03:15,229 --> 00:03:18,265 +before finally performing +the sign operation + +60 +00:03:18,298 --> 00:03:20,834 +and returning a signed blob to your app. + +61 +00:03:21,969 --> 00:03:24,171 +Let's break down the main components +involved in this flow + +62 +00:03:24,204 --> 00:03:26,840 +to see how they fit +into our initial definitions. + +63 +00:03:26,874 --> 00:03:30,811 +First, we have a resource: +the Secure Enclave key. + +64 +00:03:30,844 --> 00:03:35,382 +Second, we have an operation +that can be performed with the key. + +65 +00:03:35,415 --> 00:03:38,685 +And third, we have a set of requirements +that, among other things, + +66 +00:03:38,719 --> 00:03:41,388 +specify who is allowed to perform +the operation + +67 +00:03:41,421 --> 00:03:43,690 +as well as the means of authentication +that should be used + +68 +00:03:43,724 --> 00:03:46,426 +to verify their identity. + +69 +00:03:46,460 --> 00:03:50,764 +Plugging the parameters of this example +into our definitions, we can see that + +70 +00:03:50,797 --> 00:03:54,168 +for authentication, the question +of whether this is the right user + +71 +00:03:54,201 --> 00:03:57,571 +is answered by means +of a biometric authentication; + +72 +00:03:57,604 --> 00:04:01,241 +while for authorization, the question +of whether the user is allowed + +73 +00:04:01,275 --> 00:04:04,611 +to perform a signature operation +using the private key + +74 +00:04:04,645 --> 00:04:09,750 +is answered by verifying the requirements +specified in the access control list. + +75 +00:04:09,783 --> 00:04:12,653 +Now that we have seen how this works +at a high level, + +76 +00:04:12,686 --> 00:04:15,722 +we can take a look at the way +a flow like this can be implemented + +77 +00:04:15,756 --> 00:04:18,725 +using the current API +of LocalAuthentication. + +78 +00:04:18,759 --> 00:04:22,429 +Let's start by quickly reviewing +the features offered by the LAContext, + +79 +00:04:22,462 --> 00:04:25,699 +which is one of the core components +of the framework. + +80 +00:04:25,732 --> 00:04:29,303 +An LAContext can be used to evaluate +the user's identity. + +81 +00:04:29,336 --> 00:04:33,907 +It handles user interaction when biometric +or passcode authentication is required. + +82 +00:04:33,941 --> 00:04:36,143 +And it also interfaces +with the Secure Enclave + +83 +00:04:36,176 --> 00:04:39,346 +to enable the secure management +of biometric data. + +84 +00:04:39,379 --> 00:04:41,648 +From this perspective, +the LAContext can be used + +85 +00:04:41,682 --> 00:04:44,785 +to support your authentication use cases. + +86 +00:04:44,818 --> 00:04:48,355 +The LAContext can also be used +in association with other frameworks + +87 +00:04:48,388 --> 00:04:50,924 +to support authorization flows. + +88 +00:04:50,958 --> 00:04:54,094 +For instance, you could use it +to evaluate access control lists + +89 +00:04:54,127 --> 00:04:56,630 +like the one we saw +in our previous example. + +90 +00:04:56,663 --> 00:04:58,732 +Let's take a closer look. + +91 +00:04:58,765 --> 00:05:02,035 +The first thing we need to do +is to get access the ACL + +92 +00:05:02,069 --> 00:05:05,038 +associated with our private key. + +93 +00:05:05,072 --> 00:05:08,542 +We can do this with the help +of the SecItemCopyMatching API + +94 +00:05:08,575 --> 00:05:10,644 +offered by the Security framework, + +95 +00:05:10,677 --> 00:05:14,748 +making sure that we provide the +return-attributes key inside of our query. + +96 +00:05:15,782 --> 00:05:18,619 +Once we obtain access +to the access control list, + +97 +00:05:18,652 --> 00:05:21,455 +we can evaluate it directly +using the LAContext + +98 +00:05:21,488 --> 00:05:24,958 +and the evaluateAccessControl API. + +99 +00:05:24,992 --> 00:05:28,128 +The biggest advantage that this approach +gives you is that it lets you decide + +100 +00:05:28,161 --> 00:05:30,664 +the right moment and the right place +in your application + +101 +00:05:30,697 --> 00:05:33,700 +to prompt the user for this authorization. + +102 +00:05:33,734 --> 00:05:36,370 +In this case, +since the access control list requires + +103 +00:05:36,403 --> 00:05:39,306 +biometric authentication +for the signature operation, + +104 +00:05:39,339 --> 00:05:43,143 +the LAContext will present +the familiar Face ID or Touch ID UI. + +105 +00:05:44,845 --> 00:05:48,582 +Once the ACL has been authorized +inside our LAContext, + +106 +00:05:48,615 --> 00:05:50,918 +we will be able to use it +as part of our query + +107 +00:05:50,951 --> 00:05:53,754 +for obtaining a reference to our key. + +108 +00:05:53,787 --> 00:05:57,024 +We do this by appending +the LAContext to our SecItem query + +109 +00:05:57,057 --> 00:05:59,293 +under the use-authentication-context key. + +110 +00:06:01,261 --> 00:06:04,131 +By binding the LAContext +to our private key reference, + +111 +00:06:04,164 --> 00:06:06,600 +we ensure that executing +the signature operation + +112 +00:06:06,633 --> 00:06:08,902 +will not trigger another authentication, + +113 +00:06:08,936 --> 00:06:13,473 +while allowing the operation to continue +without unnecessary prompts. + +114 +00:06:13,507 --> 00:06:16,443 +These binding also means +that no additional user interactions + +115 +00:06:16,476 --> 00:06:20,480 +will be required for future signatures +until the LAContext is invalidated. + +116 +00:06:22,649 --> 00:06:25,252 +The LAContext offers +a great deal of flexibility + +117 +00:06:25,285 --> 00:06:27,621 +and it lets you control +each of the steps and parameters + +118 +00:06:27,654 --> 00:06:30,157 +involved in your authorization flows. + +119 +00:06:30,190 --> 00:06:33,827 +It can be used in combination with other +frameworks such as the Security framework, + +120 +00:06:33,861 --> 00:06:37,831 +which in turn unlocks +a wide range of use cases. + +121 +00:06:37,865 --> 00:06:42,169 +This versatility, however, comes +at the cost of higher code complexity, + +122 +00:06:42,202 --> 00:06:47,040 +requiring you to carefully orchestrate +the APIs offered by several frameworks. + +123 +00:06:47,074 --> 00:06:48,242 +Depending on your use case, + +124 +00:06:48,275 --> 00:06:50,410 +the LAContext might be +the right tool for you, + +125 +00:06:50,444 --> 00:06:53,046 +especially if the main value proposition +of your app + +126 +00:06:53,080 --> 00:06:56,250 +requires low-level access +to keys, secrets, + +127 +00:06:56,283 --> 00:06:58,652 +contexts, and access control lists. + +128 +00:06:58,685 --> 00:07:03,824 +However, if all you need for your app is +a way of authorizing access to content + +129 +00:07:03,857 --> 00:07:07,461 +or a sensitive resource, then you may want +to trade off some of this flexibility + +130 +00:07:07,494 --> 00:07:09,363 +for a simpler API. + +131 +00:07:09,396 --> 00:07:13,634 +This brings us +to our last topic, streamline your app. + +132 +00:07:13,667 --> 00:07:18,839 +New to iOS 16 and macOS 13, +LocalAuthentication is introducing + +133 +00:07:18,872 --> 00:07:22,643 +a higher level, authorization focused API. + +134 +00:07:22,676 --> 00:07:26,747 +The new API builds on top of existing +concepts in LocalAuthentication + +135 +00:07:26,780 --> 00:07:31,118 +such as the LAContext and is geared +towards simplifying the implementation + +136 +00:07:31,151 --> 00:07:35,289 +of common authorization flows, +allowing you to focus all your energy + +137 +00:07:35,322 --> 00:07:38,725 +in the core value proposition +of your apps. + +138 +00:07:38,759 --> 00:07:42,896 +The most important abstraction introduced +by the new API is the LARight. + +139 +00:07:44,431 --> 00:07:46,333 +The simplest use case +you can give a LARight + +140 +00:07:46,366 --> 00:07:51,405 +is to help you authorize operations +on application defined resources. + +141 +00:07:51,438 --> 00:07:54,408 +For instance, you could use a right +to help you gate access + +142 +00:07:54,441 --> 00:07:57,010 +to the user profile section +of your application + +143 +00:07:57,044 --> 00:08:01,114 +by first requiring a successful +biometric authentication from your user. + +144 +00:08:02,950 --> 00:08:06,854 +By default, rights are protected by a set +of authentication requirements + +145 +00:08:06,887 --> 00:08:10,257 +that allow your users to authenticate +using Touch ID, + +146 +00:08:10,290 --> 00:08:13,427 +Face ID, Apple Watch, +or their device passcode + +147 +00:08:13,460 --> 00:08:15,295 +depending on the device they are using. + +148 +00:08:16,997 --> 00:08:19,099 +You can also choose +to associate your rights + +149 +00:08:19,132 --> 00:08:20,868 +with more granular requirements, + +150 +00:08:20,901 --> 00:08:24,571 +which allow you to further constrain +the means of authentication. + +151 +00:08:24,605 --> 00:08:27,608 +Let's have a look +at how we can use LARights in code. + +152 +00:08:29,109 --> 00:08:32,546 +The first thing that we need to do +is to instantiate our right. + +153 +00:08:32,579 --> 00:08:35,782 +We do this by specifying its requirements. + +154 +00:08:35,816 --> 00:08:39,253 +In this case, our login right will +require users to authenticate + +155 +00:08:39,286 --> 00:08:43,457 +using biometry +or providing the device passcode. + +156 +00:08:43,490 --> 00:08:48,328 +We then proceed to verify that the current +user can obtain the login right. + +157 +00:08:48,362 --> 00:08:50,430 +We use this information to determine + +158 +00:08:50,464 --> 00:08:52,866 +whether we can continue +with the login operation + +159 +00:08:52,900 --> 00:08:57,971 +or if instead we need to redirect +the user to the public section of our app. + +160 +00:08:58,005 --> 00:09:01,408 +Finally, we can proceed +with the actual authorize operation + +161 +00:09:01,441 --> 00:09:04,745 +providing a localized reason +that will be visible to the user + +162 +00:09:04,778 --> 00:09:06,380 +in the authorization UI. + +163 +00:09:08,081 --> 00:09:09,850 +When authorizing a right in this way, + +164 +00:09:09,883 --> 00:09:12,986 +a brand-new, +system-driven UI is presented. + +165 +00:09:13,020 --> 00:09:15,722 +The UI is rendered +inside your application window + +166 +00:09:15,756 --> 00:09:17,991 +and provides users +with relevant information + +167 +00:09:18,025 --> 00:09:21,461 +to help them understand +the origin and purpose of the operation. + +168 +00:09:21,495 --> 00:09:25,165 +We believe that the new look will +allow you to craft authorization flows + +169 +00:09:25,199 --> 00:09:27,768 +that integrate more seamlessly +with your application + +170 +00:09:27,801 --> 00:09:31,471 +and that provide more context +and information for your users. + +171 +00:09:32,573 --> 00:09:35,242 +Now that we have seen how to create +and authorize a right, + +172 +00:09:35,275 --> 00:09:38,445 +let's take a closer look at its lifecycle. + +173 +00:09:38,478 --> 00:09:42,249 +Rights start their lifecycle +in an unknown state. + +174 +00:09:42,282 --> 00:09:45,018 +As soon as your application issues +the authorize request, + +175 +00:09:45,052 --> 00:09:48,255 +the state of the right changes +to authorizing. + +176 +00:09:48,288 --> 00:09:51,658 +It is at this point that the user will be +presented with the authorization UI + +177 +00:09:51,692 --> 00:09:53,393 +that we saw in the previous slide. + +178 +00:09:54,595 --> 00:09:57,364 +Depending on the success or failure +of the operation, + +179 +00:09:57,397 --> 00:10:01,468 +the right may transition +to authorized or notAuthorized state. + +180 +00:10:01,502 --> 00:10:05,305 +This is the most important +state transition for your application. + +181 +00:10:05,339 --> 00:10:07,841 +Finally, the right can also move +from the authorized + +182 +00:10:07,875 --> 00:10:09,910 +to the notAuthorized state. + +183 +00:10:09,943 --> 00:10:12,279 +This occurs +when your application explicitly issues + +184 +00:10:12,312 --> 00:10:16,583 +a deauthorize request on the right, +or when the right instance is deallocated. + +185 +00:10:17,751 --> 00:10:20,187 +Be sure to keep +a strong reference to your right + +186 +00:10:20,220 --> 00:10:22,723 +in order to preserve its authorized state. + +187 +00:10:23,924 --> 00:10:27,194 +After a right has been deauthorized, +your application can continue + +188 +00:10:27,227 --> 00:10:31,098 +to issue authorization requests +to restart the cycle. + +189 +00:10:31,131 --> 00:10:34,968 +All the previous state transitions +can be queried and observed. + +190 +00:10:35,002 --> 00:10:37,104 +If you have access +to the LARight instance, + +191 +00:10:37,137 --> 00:10:40,274 +you can query its state property directly. + +192 +00:10:40,307 --> 00:10:44,611 +You can also observe all state transitions +using KVO or Combine. + +193 +00:10:44,645 --> 00:10:47,514 +Additionally, if your application +handles several rights, + +194 +00:10:47,548 --> 00:10:49,983 +you can observe the state of all them +from a single place + +195 +00:10:50,017 --> 00:10:52,119 +by listening to the didBecomeAuthorized + +196 +00:10:52,152 --> 00:10:54,755 +and the didBecomeUnauthorized +notifications, + +197 +00:10:54,788 --> 00:10:57,357 +which are published +to the default NotificationCenter + +198 +00:10:57,391 --> 00:11:00,294 +after a change +in the authorization state is detected. + +199 +00:11:01,728 --> 00:11:04,598 +Before we move on, +let's jump back to our example + +200 +00:11:04,631 --> 00:11:08,535 +and add a logout operation +to deauthorize our login right. + +201 +00:11:08,569 --> 00:11:11,705 +By doing this, we guarantee +that a new authorization will be required + +202 +00:11:11,738 --> 00:11:13,974 +the next time the user wants to log in. + +203 +00:11:15,909 --> 00:11:19,346 +So far, we have seen +how to use right instances + +204 +00:11:19,379 --> 00:11:23,517 +to authorize operations +on application-defined resources. + +205 +00:11:23,550 --> 00:11:26,687 +We have also seen how the lifecycle +and state of these rights + +206 +00:11:26,720 --> 00:11:28,789 +is ultimately tied to the runtime, + +207 +00:11:28,822 --> 00:11:31,725 +which means that on every session +of your application, + +208 +00:11:31,758 --> 00:11:35,963 +you need to instantiate +and configure these rights correctly. + +209 +00:11:35,996 --> 00:11:38,265 +So let's take a look +at how rights can be persisted + +210 +00:11:38,298 --> 00:11:41,034 +and what sort of possibilities +this enables for your app. + +211 +00:11:42,703 --> 00:11:46,073 +LARights can be persisted +with the help of the right store. + +212 +00:11:47,608 --> 00:11:51,245 +When persisted, rights are backed +by a unique Secure Enclave key + +213 +00:11:51,278 --> 00:11:54,014 +that is protected +with an access control list or ACL + +214 +00:11:54,047 --> 00:11:57,584 +that matches the authorization +requirements of the right. + +215 +00:11:57,618 --> 00:12:00,687 +This approach helps us ensure +that the authorization requirements + +216 +00:12:00,721 --> 00:12:03,757 +will remain immutable +after the right has been persisted. + +217 +00:12:05,125 --> 00:12:07,961 +You can also access the private key +that backs your right + +218 +00:12:07,995 --> 00:12:11,331 +and use it to perform +protected cryptographic operations + +219 +00:12:11,365 --> 00:12:15,602 +such as decryption, signature, +and key exchanges. + +220 +00:12:17,337 --> 00:12:19,773 +The corresponding public key +is also accessible + +221 +00:12:19,806 --> 00:12:21,642 +and can be used to perform operations + +222 +00:12:21,675 --> 00:12:25,779 +such as encryption +and signature verification. + +223 +00:12:25,812 --> 00:12:30,350 +Because this is a public key, you can +also export the bytes associated with it. + +224 +00:12:31,885 --> 00:12:33,987 +Private key operations are only allowed + +225 +00:12:34,021 --> 00:12:36,790 +after the right has been +successfully authorized. + +226 +00:12:36,823 --> 00:12:40,194 +In contrast, +public key operations are always allowed. + +227 +00:12:42,129 --> 00:12:44,965 +When persisting your right, +you also have a chance of storing + +228 +00:12:44,998 --> 00:12:48,368 +a single, immutable secret along with it. + +229 +00:12:48,402 --> 00:12:51,605 +The secret is also associated +with an access control list + +230 +00:12:51,638 --> 00:12:54,408 +that matches the authorization +requirements of your right + +231 +00:12:54,441 --> 00:12:57,978 +and it only becomes accessible +after the right has been authorized. + +232 +00:12:59,146 --> 00:13:01,982 +To summarize, +LAPersistedRights are created + +233 +00:13:02,015 --> 00:13:03,917 +with the help of the right store. + +234 +00:13:03,951 --> 00:13:05,619 +They are configured only once + +235 +00:13:05,652 --> 00:13:08,856 +and their authorization requirements +are immutable. + +236 +00:13:08,889 --> 00:13:10,791 +Because they are stored, they can be used + +237 +00:13:10,824 --> 00:13:13,260 +across different sessions +of your application. + +238 +00:13:13,293 --> 00:13:15,963 +Internally, +they are bound to a specific device + +239 +00:13:15,996 --> 00:13:18,665 +and are backed by a unique +Secure Enclave key + +240 +00:13:18,699 --> 00:13:21,668 +which can be used to perform +different cryptographic operations, + +241 +00:13:21,702 --> 00:13:25,305 +depending on the authorization status +of the right. + +242 +00:13:25,339 --> 00:13:28,775 +Finally, they can be used to protect +a single, immutable secret + +243 +00:13:28,809 --> 00:13:32,446 +that only becomes available +after the right has been authorized. + +244 +00:13:33,313 --> 00:13:36,817 +Now that we understand some of +the features offered by persisted rights, + +245 +00:13:36,850 --> 00:13:38,519 +let's see how they can help us implement + +246 +00:13:38,552 --> 00:13:41,255 +the scenario we discussed +at the beginning of the presentation, + +247 +00:13:41,288 --> 00:13:43,891 +where we wanted to authorize +a signature operation. + +248 +00:13:44,691 --> 00:13:49,730 +We start by instantiating a regular right +specifying its authorization requirements. + +249 +00:13:49,763 --> 00:13:52,900 +In this case, we want to ensure +that the right would only be granted + +250 +00:13:52,933 --> 00:13:56,003 +to users that have biometric enrollments +in the device + +251 +00:13:56,036 --> 00:13:58,939 +at the moment of the creation +of our right. + +252 +00:13:58,972 --> 00:14:01,742 +Therefore, we use +the biometryCurrentSet requirement. + +253 +00:14:03,177 --> 00:14:06,013 +We can then persist the right +with the help of the right store, + +254 +00:14:06,046 --> 00:14:08,615 +providing a unique identifier. + +255 +00:14:08,649 --> 00:14:12,119 +This identifier will be useful +the next time we need to fetch the right + +256 +00:14:12,152 --> 00:14:14,254 +in future sessions of our application. + +257 +00:14:15,622 --> 00:14:19,126 +Once the right is persisted, +we get immediate access to its public key + +258 +00:14:19,159 --> 00:14:22,095 +and can start performing +unprotected operations with it, + +259 +00:14:22,129 --> 00:14:25,199 +without the need +for an explicit authorization. + +260 +00:14:25,232 --> 00:14:28,535 +In this example, we are simply +exporting its public bytes. + +261 +00:14:30,437 --> 00:14:33,540 +Later on, when it's time to perform +a signature operation, + +262 +00:14:33,574 --> 00:14:35,642 +we can retrieve our right from the store + +263 +00:14:35,676 --> 00:14:39,813 +using the unique identifier we provided +during creation. + +264 +00:14:39,847 --> 00:14:42,149 +We can then proceed to authorize +the current user + +265 +00:14:42,182 --> 00:14:45,085 +through the authorize operation +on our right. + +266 +00:14:45,118 --> 00:14:48,388 +At this point, the system will walk the +user through the authentication process + +267 +00:14:48,422 --> 00:14:52,226 +and will verify that all the authorization +requirements are satisfied. + +268 +00:14:53,227 --> 00:14:55,996 +After the right has been authorized, +we can use its private key + +269 +00:14:56,029 --> 00:14:59,299 +to perform +protected cryptographic operations. + +270 +00:14:59,333 --> 00:15:02,236 +In this case, we are using the private key +to sign a challenge + +271 +00:15:02,269 --> 00:15:05,172 +issued by the backend server +of our application. + +272 +00:15:06,106 --> 00:15:09,376 +To wrap up, we talked about +the relationship that exists + +273 +00:15:09,409 --> 00:15:13,013 +between the generic concepts +of authentication and authorization, + +274 +00:15:13,046 --> 00:15:17,885 +and particularly how authentication +enables authorization. + +275 +00:15:17,918 --> 00:15:20,988 +We went over some of the features +offered by the LAContext + +276 +00:15:21,021 --> 00:15:23,657 +and how it can be combined +with frameworks such as Security + +277 +00:15:23,690 --> 00:15:27,561 +to unlock very powerful +and extensible authorization flows. + +278 +00:15:27,594 --> 00:15:30,697 +And finally, we looked into how +the newly added LARight + +279 +00:15:30,731 --> 00:15:32,332 +could help you streamline the code + +280 +00:15:32,366 --> 00:15:35,302 +to implement +certain authorization use cases. + +281 +00:15:35,335 --> 00:15:39,306 +We invite you take a look +at existing usages of LocalAuthentication + +282 +00:15:39,339 --> 00:15:42,843 +in your app and consider whether +some of the features we discussed today + +283 +00:15:42,876 --> 00:15:44,578 +can help you simplify your code + +284 +00:15:44,611 --> 00:15:48,215 +while still protecting +the privacy and security of your users. + +285 +00:15:48,248 --> 00:15:49,249 +Thanks. + diff --git "a/eng/2022 Session 10109 What\342\200\231s new in notarization for Mac apps en.srt" "b/eng/2022 Session 10109 What\342\200\231s new in notarization for Mac apps en.srt" new file mode 100644 index 0000000..55d5db6 --- /dev/null +++ "b/eng/2022 Session 10109 What\342\200\231s new in notarization for Mac apps en.srt" @@ -0,0 +1,864 @@ +1 +00:00:00,000 --> 00:00:03,170 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,170 --> 00:00:10,043 +♪ + +3 +00:00:10,043 --> 00:00:12,312 +Hello, my name is Johnathan. + +4 +00:00:12,312 --> 00:00:15,215 +macOS developers submit +software to the notary service + +5 +00:00:15,215 --> 00:00:16,850 +in order to help protect +their customers + +6 +00:00:16,850 --> 00:00:18,852 +from malicious software. + +7 +00:00:18,852 --> 00:00:21,622 +Last year we introduced +a faster and simpler way + +8 +00:00:21,622 --> 00:00:25,025 +to submit apps for notarization +with the notarytool CLI + +9 +00:00:25,025 --> 00:00:27,160 +or command-line interface. + +10 +00:00:27,160 --> 00:00:30,264 +This year we are excited to +continue championing performance + +11 +00:00:30,264 --> 00:00:32,833 +and flexibility with +some major improvements + +12 +00:00:32,833 --> 00:00:36,403 +for your interactions +with the notary service. + +13 +00:00:36,403 --> 00:00:39,907 +In this session, we'll be +talking about three main topics. + +14 +00:00:39,907 --> 00:00:41,909 +First, we'll go through +important deadlines + +15 +00:00:41,909 --> 00:00:45,078 +for the migration from +using altool for notarization + +16 +00:00:45,078 --> 00:00:47,814 +to using notarytool. + +17 +00:00:47,814 --> 00:00:51,118 +Next, we'll discuss improvements +to the Xcode integration + +18 +00:00:51,118 --> 00:00:53,520 +with the upcoming Xcode 14, + +19 +00:00:53,520 --> 00:00:57,457 +bringing the submission speed +of notarytool to Xcode. + +20 +00:00:57,457 --> 00:00:59,927 +And finally, we'll talk about +a flexible new way + +21 +00:00:59,927 --> 00:01:01,995 +to interact with +the notary service, + +22 +00:01:01,995 --> 00:01:05,933 +a REST API, letting you expand +the places you can upload, + +23 +00:01:05,933 --> 00:01:09,703 +check the status of, +and review submissions. + +24 +00:01:11,705 --> 00:01:14,775 +Last year we introduced +notarytool, + +25 +00:01:14,775 --> 00:01:18,211 +a replacement for altool +for notarization. + +26 +00:01:18,211 --> 00:01:20,847 +Later in this talk, +I'll be discussing Xcode + +27 +00:01:20,847 --> 00:01:25,218 +moving to our updated backend +with Xcode 14. + +28 +00:01:25,218 --> 00:01:27,921 +With migration paths in place +for notarization + +29 +00:01:27,921 --> 00:01:30,457 +via altool and Xcode 13, + +30 +00:01:30,457 --> 00:01:32,826 +we're announcing the sunset date +for notarization + +31 +00:01:32,826 --> 00:01:37,431 +with these older methods +to be fall of 2023. + +32 +00:01:37,431 --> 00:01:40,334 +For help moving +from altool to notarytool, + +33 +00:01:40,334 --> 00:01:42,703 +please refer +to last year’s presentation + +34 +00:01:42,703 --> 00:01:47,140 +"Faster and simpler notarization +for Mac apps." + +35 +00:01:47,140 --> 00:01:50,277 +To highlight some specifics, +the notarytool CLI + +36 +00:01:50,277 --> 00:01:53,847 +will continue to work +past the fall 2023 deadline, + +37 +00:01:53,847 --> 00:01:57,184 +including the one +bundled in Xcode 13. + +38 +00:01:57,184 --> 00:01:59,086 +As always, however, +we do encourage you + +39 +00:01:59,086 --> 00:02:02,990 +to update to receive the latest +improvements and fixes. + +40 +00:02:02,990 --> 00:02:06,393 +Uploads to the notary service +using the Xcode 13 UI + +41 +00:02:06,393 --> 00:02:09,062 +will stop working +after that deadline. + +42 +00:02:09,062 --> 00:02:11,298 +Stay tuned to hear about +some performance improvements + +43 +00:02:11,298 --> 00:02:14,368 +in Xcode 14, +but largely you can expect + +44 +00:02:14,368 --> 00:02:17,337 +your workflow +to remain unchanged. + +45 +00:02:17,337 --> 00:02:20,507 +Finally, notarization +with all forms of altool + +46 +00:02:20,507 --> 00:02:23,443 +will stop working in fall 2023. + +47 +00:02:23,443 --> 00:02:27,214 +Again, please refer +to last year's WWDC presentation + +48 +00:02:27,214 --> 00:02:30,417 +for details on migrating +to notarytool. + +49 +00:02:30,417 --> 00:02:34,888 +Next, we'll touch on changes +to notarization in Xcode 14. + +50 +00:02:34,888 --> 00:02:37,190 +We've migrated +the notarization support + +51 +00:02:37,190 --> 00:02:40,160 +built into Xcode to use +the same reliable backend + +52 +00:02:40,160 --> 00:02:44,464 +as the notarytool CLI +we introduced last year. + +53 +00:02:44,464 --> 00:02:46,700 +Because of this, +we're happy to bring the same + +54 +00:02:46,700 --> 00:02:49,970 +roughly 4x performance increase +we announced last year + +55 +00:02:49,970 --> 00:02:53,240 +for notarytool to Xcode 14. + +56 +00:02:53,240 --> 00:02:55,809 +The best part is that, +besides updating, + +57 +00:02:55,809 --> 00:02:58,412 +you don't need to change your +project settings or workflows + +58 +00:02:58,412 --> 00:03:01,481 +to receive this +performance boost. + +59 +00:03:01,481 --> 00:03:03,683 +For the final topic +of this presentation, + +60 +00:03:03,683 --> 00:03:05,952 +we're excited to announce +a new service, + +61 +00:03:05,952 --> 00:03:08,121 +a REST API for notary. + +62 +00:03:08,121 --> 00:03:10,457 +This new service allows you +to interact more flexibly + +63 +00:03:10,457 --> 00:03:14,361 +with the notary service +in even more places. + +64 +00:03:14,361 --> 00:03:16,630 +To go over some +important concepts, + +65 +00:03:16,630 --> 00:03:18,465 +this new API +is intended to allow + +66 +00:03:18,465 --> 00:03:21,735 +for a more flexible interface +to the notary service. + +67 +00:03:21,735 --> 00:03:23,770 +As a JSON-based web service, + +68 +00:03:23,770 --> 00:03:27,140 +integration should be +fairly simple in most languages. + +69 +00:03:27,140 --> 00:03:29,576 +This API allows you +to upload submissions + +70 +00:03:29,576 --> 00:03:32,379 +from any place +with an internet connection, + +71 +00:03:32,379 --> 00:03:34,614 +including continuous +integration servers -- + +72 +00:03:34,614 --> 00:03:38,452 +places where you might not +be running macOS today. + +73 +00:03:38,452 --> 00:03:41,188 +Additionally, other interactions +with the notary service + +74 +00:03:41,188 --> 00:03:43,557 +are also supported in this API, + +75 +00:03:43,557 --> 00:03:45,392 +such as retrieving +your submission history + +76 +00:03:45,392 --> 00:03:48,295 +or past submission details. + +77 +00:03:48,295 --> 00:03:50,197 +Our goal with introducing +the REST API + +78 +00:03:50,197 --> 00:03:52,799 +is to support submitting +software for notarization + +79 +00:03:52,799 --> 00:03:55,836 +from more platforms and to allow +for easier interactions + +80 +00:03:55,836 --> 00:03:58,672 +with notary +in automated systems. + +81 +00:03:58,672 --> 00:04:00,907 +This complements the current +methods of submission, + +82 +00:04:00,907 --> 00:04:04,010 +Xcode and notarytool, +where those can't be run today, + +83 +00:04:04,010 --> 00:04:07,147 +such as a Linux-based +continuous integration. + +84 +00:04:07,147 --> 00:04:09,749 +For example, imagine you want +your deployment pipeline + +85 +00:04:09,749 --> 00:04:11,485 +to submit your application +to notary + +86 +00:04:11,485 --> 00:04:13,453 +prior to distribution. + +87 +00:04:13,453 --> 00:04:15,689 +With this new API +and some basic scripts, + +88 +00:04:15,689 --> 00:04:18,925 +you can easily +automate the process. + +89 +00:04:18,925 --> 00:04:20,227 +Before I dive in, + +90 +00:04:20,227 --> 00:04:22,896 +one important topic +is authentication. + +91 +00:04:22,896 --> 00:04:26,366 +You can authenticate with +the API using JSON Web Tokens, + +92 +00:04:26,366 --> 00:04:30,604 +or JWTs, just like other +App Store Connect APIs. + +93 +00:04:30,604 --> 00:04:32,339 +For more details +on authentication + +94 +00:04:32,339 --> 00:04:34,307 +or the code I'm about to show, + +95 +00:04:34,307 --> 00:04:38,211 +please visit the REST API +documentation linked below. + +96 +00:04:38,211 --> 00:04:41,248 +In these snippets, +I'll assume you have a valid JWT + +97 +00:04:41,248 --> 00:04:44,284 +passed into the functions +as the token variable. + +98 +00:04:44,284 --> 00:04:46,953 +Let's walk through an example +of submitting files + +99 +00:04:46,953 --> 00:04:49,022 +to notary in Python. + +100 +00:04:49,022 --> 00:04:50,690 +This same basic flow +is applicable + +101 +00:04:50,690 --> 00:04:53,360 +in other programming languages. + +102 +00:04:53,360 --> 00:04:57,130 +There are two major steps +for uploading files to notary. + +103 +00:04:57,130 --> 00:04:58,932 +The first step +is to inform notary + +104 +00:04:58,932 --> 00:05:01,301 +that you wish to upload a file. + +105 +00:05:01,301 --> 00:05:04,671 +Included in this is some +basic information about the file + +106 +00:05:04,671 --> 00:05:07,674 +like name and SHA-256. + +107 +00:05:07,674 --> 00:05:09,776 +The response contains +information necessary + +108 +00:05:09,776 --> 00:05:13,079 +to upload the file and an ID +to track your submission + +109 +00:05:13,079 --> 00:05:15,415 +through our pipeline. + +110 +00:05:15,415 --> 00:05:18,251 +The second step actually uploads +the file for notarization + +111 +00:05:18,251 --> 00:05:21,054 +via Amazon S3. + +112 +00:05:21,054 --> 00:05:23,657 +You'll need to grab +your favorite S3 SDK. + +113 +00:05:23,657 --> 00:05:24,724 +For this example, + +114 +00:05:24,724 --> 00:05:27,727 +I'm going to be using +the boto3 library. + +115 +00:05:27,727 --> 00:05:29,496 +Here we use +the temporary credentials + +116 +00:05:29,496 --> 00:05:31,498 +returned in the previous call + +117 +00:05:31,498 --> 00:05:34,167 +to authenticate +and create a client. + +118 +00:05:34,167 --> 00:05:37,037 +We then use the client to upload +the file to the bucket + +119 +00:05:37,037 --> 00:05:40,807 +and object specified +in the first step's response. + +120 +00:05:40,807 --> 00:05:43,043 +Once uploaded, +the submission will proceed + +121 +00:05:43,043 --> 00:05:45,412 +though the +notarization pipeline. + +122 +00:05:45,412 --> 00:05:47,981 +This process should complete +in under 15 minutes + +123 +00:05:47,981 --> 00:05:50,417 +for most submissions. + +124 +00:05:50,417 --> 00:05:52,752 +After upload, you should confirm +the notary service + +125 +00:05:52,752 --> 00:05:54,621 +has successfully processed +your submission + +126 +00:05:54,621 --> 00:05:56,756 +prior to distribution. + +127 +00:05:56,756 --> 00:05:59,993 +There are, broadly speaking, +two approaches to this. + +128 +00:05:59,993 --> 00:06:01,661 +The first, and the simplest, + +129 +00:06:01,661 --> 00:06:04,598 +is checking the result +through the same API. + +130 +00:06:04,598 --> 00:06:06,600 +The other option is via +the webhook support + +131 +00:06:06,600 --> 00:06:08,969 +introduced with notarytool. + +132 +00:06:08,969 --> 00:06:12,505 +First, let's look at +the API approach. + +133 +00:06:12,505 --> 00:06:14,674 +Checking the status +of a submission to notary + +134 +00:06:14,674 --> 00:06:17,010 +is pretty straightforward, + +135 +00:06:17,010 --> 00:06:19,079 +You can make a request +with the submission ID + +136 +00:06:19,079 --> 00:06:22,315 +received during +the upload process + +137 +00:06:22,315 --> 00:06:24,417 +Part of the response +is the current status + +138 +00:06:24,417 --> 00:06:27,153 +of the submission, +which will remain "In Progress" + +139 +00:06:27,153 --> 00:06:29,956 +until notary is finished +processing it. + +140 +00:06:29,956 --> 00:06:32,225 +The status will then transition +to the final state + +141 +00:06:32,225 --> 00:06:36,896 +of your submission, +such as Accepted or Invalid. + +142 +00:06:36,896 --> 00:06:38,565 +Once the submission +is complete, + +143 +00:06:38,565 --> 00:06:39,933 +you can then use the API + +144 +00:06:39,933 --> 00:06:43,236 +to retrieve the notarization log +for this upload. + +145 +00:06:43,236 --> 00:06:46,072 +Please refer to the +Notary REST API documentation + +146 +00:06:46,072 --> 00:06:49,409 +for more details +on these endpoints. + +147 +00:06:49,409 --> 00:06:51,511 +Next, we'll talk about +the second approach + +148 +00:06:51,511 --> 00:06:53,913 +to getting your status: +a webhook. + +149 +00:06:53,913 --> 00:06:57,350 +In the webhook workflow, +the process is largely the same, + +150 +00:06:57,350 --> 00:06:59,886 +but this time you'll provide +a webhook URL + +151 +00:06:59,886 --> 00:07:02,422 +in the initial request +to upload. + +152 +00:07:02,422 --> 00:07:04,991 +Details on the format can be +found in the documentation + +153 +00:07:04,991 --> 00:07:07,961 +for the notary REST API. + +154 +00:07:07,961 --> 00:07:10,363 +As before, this will trigger +the notary service + +155 +00:07:10,363 --> 00:07:12,966 +to analyze your submission. + +156 +00:07:12,966 --> 00:07:16,403 +As the automated analysis +concludes, tickets are created, + +157 +00:07:16,403 --> 00:07:19,039 +and the final status is saved. + +158 +00:07:19,039 --> 00:07:21,641 +Once complete, +the notary service will call out + +159 +00:07:21,641 --> 00:07:23,843 +to the webhook URL provided. + +160 +00:07:23,843 --> 00:07:26,546 +The contents of this call +include the submission ID, + +161 +00:07:26,546 --> 00:07:32,085 +the team ID, and a signature +to verify it came from us. + +162 +00:07:32,085 --> 00:07:35,955 +On receiving that notification, +you can choose what to do next. + +163 +00:07:35,955 --> 00:07:39,025 +For example, you might notify +the original submitter + +164 +00:07:39,025 --> 00:07:42,495 +or begin an automated +distribution pipeline. + +165 +00:07:42,495 --> 00:07:44,197 +Compared to waiting +with notarytool, + +166 +00:07:44,197 --> 00:07:47,200 +this allows you to decouple +the system that uploads the file + +167 +00:07:47,200 --> 00:07:51,905 +from the system that automates +actions after notarization. + +168 +00:07:51,905 --> 00:07:54,774 +We're excited to see this new +REST API open the doors + +169 +00:07:54,774 --> 00:07:57,944 +to more integrations with +continuous integration systems + +170 +00:07:57,944 --> 00:08:01,281 +and other tools +to build software for macOS. + +171 +00:08:01,281 --> 00:08:03,750 +To wrap up, +as one more reminder, + +172 +00:08:03,750 --> 00:08:07,487 +the deadline to migrate +to using Xcode 14, notarytool, + +173 +00:08:07,487 --> 00:08:11,458 +or the REST API +directly is fall of 2023. + +174 +00:08:11,458 --> 00:08:14,194 +Finally, if you haven't yet +been able to use notarytool + +175 +00:08:14,194 --> 00:08:15,929 +in your deployment pipelines, + +176 +00:08:15,929 --> 00:08:18,031 +this is your chance +to jumpstart your automation + +177 +00:08:18,031 --> 00:08:20,734 +by trying the notary +REST API today. + +178 +00:08:20,734 --> 00:08:23,336 +You can find a link +to the documentation below. + +179 +00:08:23,336 --> 00:08:26,973 +Thank you, and I hope you enjoy +the rest of WWDC22 + +180 +00:08:26,973 --> 00:08:30,844 +♪ + diff --git a/eng/2022 Session 10110 Build global apps - Localization by example en.srt b/eng/2022 Session 10110 Build global apps - Localization by example en.srt new file mode 100644 index 0000000..cdf9b7f --- /dev/null +++ b/eng/2022 Session 10110 Build global apps - Localization by example en.srt @@ -0,0 +1,1966 @@ +1 +00:00:09,776 --> 00:00:12,112 +Andreas: Hello, and welcome to WWDC. + +2 +00:00:12,145 --> 00:00:14,481 +I'm Andreas from the localization team +at Apple + +3 +00:00:14,515 --> 00:00:16,917 +and today I would like to share with +you some examples + +4 +00:00:16,950 --> 00:00:19,820 +about how to build high-quality, +localized apps. + +5 +00:00:20,988 --> 00:00:23,524 +Internationalization means +preparing your app + +6 +00:00:23,557 --> 00:00:26,126 +to run on devices all across the world. + +7 +00:00:26,159 --> 00:00:29,696 +When localization is done well, +everybody gets to enjoy the same + +8 +00:00:29,730 --> 00:00:33,867 +great experience and utility– +regardless of the language they speak. + +9 +00:00:33,901 --> 00:00:36,303 +Using the APIs that Apple offers, + +10 +00:00:36,336 --> 00:00:39,239 +most parts of your app +are internationalization friendly + +11 +00:00:39,273 --> 00:00:41,341 +right out of the box. + +12 +00:00:41,375 --> 00:00:43,744 +In this talk, you will learn +from our experience + +13 +00:00:43,777 --> 00:00:46,480 +making Apple's apps appealing +to a diverse audience, + +14 +00:00:46,513 --> 00:00:49,249 +including some challenges +and how we solved them. + +15 +00:00:49,283 --> 00:00:53,153 +I will start with declaring +and loading localized text. + +16 +00:00:53,187 --> 00:00:57,558 +It's easy to include formatted dates, +times, and more in our strings. + +17 +00:00:57,591 --> 00:01:02,563 +I will highlight some options, and we will +take a look at a sophisticated example. + +18 +00:01:02,596 --> 00:01:05,766 +Your Swift Package +might include localized text, too, + +19 +00:01:05,799 --> 00:01:09,970 +and you will learn about improvements +to the localization workflow. + +20 +00:01:10,003 --> 00:01:14,408 +Finally, I will talk about layout +and great new additions to SwiftUI. + +21 +00:01:14,441 --> 00:01:18,345 +At Apple, we make sure that our apps +are providing a great experience + +22 +00:01:18,378 --> 00:01:20,113 +to our international audience. + +23 +00:01:20,147 --> 00:01:22,783 +And the Weather app +is one example of this. + +24 +00:01:22,816 --> 00:01:26,486 +Millions of users open it up every day +to check the forecast– + +25 +00:01:26,520 --> 00:01:30,457 +and this is what the app looks like to +them, wherever they are in the world. + +26 +00:01:30,490 --> 00:01:34,094 +Notice how everything in the UI +is adjusted to their preferences. + +27 +00:01:34,127 --> 00:01:37,064 +We localize descriptions +of the current weather conditions + +28 +00:01:37,097 --> 00:01:39,199 +and we format numbers. + +29 +00:01:39,233 --> 00:01:41,568 +The UI is also adapted appropriately + +30 +00:01:41,602 --> 00:01:45,305 +depending on whether the language +is left-to-right or right-to-left. + +31 +00:01:45,339 --> 00:01:48,909 +Let's take a closer look +at one of the things we customize + +32 +00:01:48,942 --> 00:01:50,611 +by starting with translation. + +33 +00:01:50,644 --> 00:01:54,348 +This view here says "Wind is making +it feel cooler" in English. + +34 +00:01:54,381 --> 00:01:57,317 +And this is what it looks like +in other languages. + +35 +00:01:57,351 --> 00:02:00,120 +To support them properly, +all we have to do is declare + +36 +00:02:00,153 --> 00:02:02,489 +the string using String(localized). + +37 +00:02:02,523 --> 00:02:05,125 +Xcode discovers it +when exporting for localization, + +38 +00:02:05,158 --> 00:02:09,296 +and we can send the result +over our translators. + +39 +00:02:09,329 --> 00:02:11,598 +I will use the Mail app +on my Mac to do so. + +40 +00:02:11,632 --> 00:02:13,867 +And while we're there, +I want to show you something. + +41 +00:02:13,901 --> 00:02:16,136 +If I open the context menu of an email, + +42 +00:02:16,170 --> 00:02:19,273 +I can move it +to a special folder called "Archive.” + +43 +00:02:19,306 --> 00:02:22,509 +It is located in my sidebar. + +44 +00:02:22,543 --> 00:02:25,812 +Notice how both words +are "Archive" in English. + +45 +00:02:25,846 --> 00:02:28,682 +Other languages like Spanish, however, +have different words + +46 +00:02:28,715 --> 00:02:31,018 +for the action and the folder name. + +47 +00:02:31,051 --> 00:02:33,020 +Even though the English words +are the same, + +48 +00:02:33,053 --> 00:02:35,088 +when they appear in different contexts, + +49 +00:02:35,122 --> 00:02:37,824 +other languages might use different words. + +50 +00:02:37,858 --> 00:02:40,561 +You should use two strings in code +in this case. + +51 +00:02:40,594 --> 00:02:45,832 +And to do that, we added new API +to the string initializer this year. + +52 +00:02:45,866 --> 00:02:50,037 +It now takes a default value, +which we can use for our English string. + +53 +00:02:50,070 --> 00:02:52,539 +Then, we modify the localized string's key + +54 +00:02:52,573 --> 00:02:55,542 +to make the distinction clear +to translators. + +55 +00:02:55,576 --> 00:02:58,812 +This way, the same word is shown +when running the app in English, + +56 +00:02:58,846 --> 00:03:02,349 +and Spanish translators are able +to provide different words. + +57 +00:03:02,382 --> 00:03:04,918 +Last year's talk +"Streamline your localized strings" + +58 +00:03:04,952 --> 00:03:07,721 +helps you understanding the basics +of managing strings, + +59 +00:03:07,754 --> 00:03:11,458 +and it goes further +into the localization process. + +60 +00:03:11,491 --> 00:03:15,929 +I want you to take away from this example +that sometimes the same English word, + +61 +00:03:15,963 --> 00:03:20,267 +or even an entire sentence, +is shown in different contexts in the UI. + +62 +00:03:20,300 --> 00:03:25,339 +In these instances, make sure to use +two different strings in your code. + +63 +00:03:25,372 --> 00:03:27,708 +Weather is not just about the app. + +64 +00:03:27,741 --> 00:03:30,477 +It is also well integrated +into the system. + +65 +00:03:30,511 --> 00:03:33,313 +Here, we see a user activity, +suggesting to open the app + +66 +00:03:33,347 --> 00:03:36,316 +to check the weather +at the current location. + +67 +00:03:36,350 --> 00:03:39,520 +Let's take a look +at how that might be implemented. + +68 +00:03:39,553 --> 00:03:41,889 +The string could be declared +and loaded like this, + +69 +00:03:41,922 --> 00:03:45,659 +using String Interpolation +to insert any location name. + +70 +00:03:45,692 --> 00:03:50,163 +And this name could be a city +or a term for the current location. + +71 +00:03:50,197 --> 00:03:51,932 +The result works well in English: + +72 +00:03:51,965 --> 00:03:53,433 +"Show weather in Cupertino" + +73 +00:03:53,467 --> 00:03:56,970 +and "Show weather in my location,” +respectively. + +74 +00:03:57,004 --> 00:04:00,741 +In other languages however, +we might run into grammatical issues. + +75 +00:04:00,774 --> 00:04:04,244 +In German, for example, +the preposition works for a city name, + +76 +00:04:04,278 --> 00:04:07,814 +but is wrong when inserting a term +for the current location. + +77 +00:04:07,848 --> 00:04:10,651 +We need to have +a different translation instead. + +78 +00:04:10,684 --> 00:04:13,153 +The solution here is simple: + +79 +00:04:13,187 --> 00:04:15,556 +just use two different strings. + +80 +00:04:15,589 --> 00:04:17,925 +Inserting a city name +is fine in the first one, + +81 +00:04:17,958 --> 00:04:21,061 +and for the current location +we use another string. + +82 +00:04:21,094 --> 00:04:24,231 +This ensures that translators +are able to use the correct grammar + +83 +00:04:24,264 --> 00:04:25,666 +for their language. + +84 +00:04:25,699 --> 00:04:28,435 +And it works well in English, and German. + +85 +00:04:28,468 --> 00:04:32,439 +I made this example to show you +that inserting a variable had an impact + +86 +00:04:32,472 --> 00:04:34,374 +on the entire sentence. + +87 +00:04:34,408 --> 00:04:38,478 +Joining strings might have surprising +consequences in other languages: + +88 +00:04:38,512 --> 00:04:43,050 +they might need to inflect the grammar or +could have troubles with capitalization, + +89 +00:04:43,083 --> 00:04:46,353 +but knowing that beforehand +when writing the code is difficult. + +90 +00:04:46,386 --> 00:04:48,789 +Having people who speak the language +testing the app + +91 +00:04:48,822 --> 00:04:51,491 +is a substantial part of the workflow. + +92 +00:04:51,525 --> 00:04:55,295 +Keep that in mind when you're tempted +to construct a string programmatically. + +93 +00:04:57,297 --> 00:05:00,634 +Now that we share a good understanding +of how strings are declared in code, + +94 +00:05:00,667 --> 00:05:03,270 +let's talk about their comments. + +95 +00:05:03,303 --> 00:05:07,774 +Here's the string from our previous +example again, with a proper comment. + +96 +00:05:07,808 --> 00:05:10,277 +A comment is really, really important +for translators. + +97 +00:05:10,310 --> 00:05:13,647 +You should make sure to give them +the context they need to translate it, + +98 +00:05:13,680 --> 00:05:17,851 +keeping the same intention as you had +when declaring the string. + +99 +00:05:17,885 --> 00:05:21,421 +A great comment explains which interface +element the string is shown in, + +100 +00:05:21,455 --> 00:05:23,824 +like a label or a button. + +101 +00:05:23,857 --> 00:05:28,629 +It also explains the context of the UI +element and where it is shown on screen. + +102 +00:05:28,662 --> 00:05:33,567 +That could be a section header, +a context menu, or a user activity. + +103 +00:05:33,600 --> 00:05:35,002 +If the string contains variables, + +104 +00:05:35,035 --> 00:05:37,604 +make sure to explain their value +at runtime. + +105 +00:05:37,638 --> 00:05:40,407 +This is very important +for matching the grammar of the sentence, + +106 +00:05:40,440 --> 00:05:42,543 +as we have seen in the example. + +107 +00:05:42,576 --> 00:05:45,779 +Remember that translators +might not see the app at runtime + +108 +00:05:45,812 --> 00:05:47,514 +when translating your content. + +109 +00:05:47,548 --> 00:05:50,417 +But with these tips you should be able +to create a shared understanding + +110 +00:05:50,450 --> 00:05:53,253 +between declaration and translation +of a string + +111 +00:05:53,287 --> 00:05:56,256 +and which role it plays in your app. + +112 +00:05:56,290 --> 00:05:57,858 +Now, it might have never +occurred to you, + +113 +00:05:57,891 --> 00:06:00,827 +but the Weather app doesn't +actually control the weather. + +114 +00:06:00,861 --> 00:06:03,664 +Instead, +the data is downloaded from a server. + +115 +00:06:03,697 --> 00:06:05,866 +It can be located anywhere in the world + +116 +00:06:05,899 --> 00:06:09,636 +and it might not even know what language +to send the content in. + +117 +00:06:09,670 --> 00:06:11,772 +When content is downloaded +to a user's device, + +118 +00:06:11,805 --> 00:06:15,843 +it should always be presented +in the language that the user prefers. + +119 +00:06:15,876 --> 00:06:19,780 +Having just some parts of an app localized +can be very confusing. + +120 +00:06:20,747 --> 00:06:23,717 +Here, the Weather app shows +a severe weather alert, + +121 +00:06:23,750 --> 00:06:26,286 +which has been loaded from a server. + +122 +00:06:26,320 --> 00:06:27,621 +This looks really serious, + +123 +00:06:27,654 --> 00:06:31,992 +and if it was not translated into my +language, I might get into trouble later. + +124 +00:06:32,025 --> 00:06:34,595 +Let's take a look at what you can do +to make sure that your users + +125 +00:06:34,628 --> 00:06:37,231 +are always able to read remote content. + +126 +00:06:38,999 --> 00:06:42,336 +Your server can send a list +of supported languages to the app. + +127 +00:06:42,369 --> 00:06:44,738 +This should be an array of language IDs, + +128 +00:06:44,771 --> 00:06:48,308 +and the device has all the knowledge +about which languages the user prefers, + +129 +00:06:48,342 --> 00:06:51,445 +so you don't have to check +an compare them yourselves. + +130 +00:06:51,478 --> 00:06:55,415 +You can leverage Apple's Frameworks by +calling 'Bundle.preferredLocalizations'. + +131 +00:06:55,449 --> 00:06:57,818 +And this will do the match for you. + +132 +00:06:57,851 --> 00:07:00,053 +It returns an array +of candidate languages, + +133 +00:07:00,087 --> 00:07:03,857 +sorted by how closely they match +the user's language choices. + +134 +00:07:03,891 --> 00:07:08,962 +And the first one is usually the best fit, +so you will use this one. + +135 +00:07:08,996 --> 00:07:13,300 +That language then should be used +for any subsequent requests to the server. + +136 +00:07:13,333 --> 00:07:16,403 +It uses it to generate a response +with content in the language + +137 +00:07:16,436 --> 00:07:19,273 +that your user +will be able to understand. + +138 +00:07:19,306 --> 00:07:21,041 +With this technique you can be confident + +139 +00:07:21,074 --> 00:07:24,711 +that strings coming from the server +are ready for updating the UI + +140 +00:07:24,745 --> 00:07:27,948 +and for showing alerts to the user. + +141 +00:07:27,981 --> 00:07:30,951 +So to save your users +from a storm of frustration + +142 +00:07:30,984 --> 00:07:34,388 +when displaying remote content, +download the available languages, + +143 +00:07:34,421 --> 00:07:36,490 +match that against the user's preferences, + +144 +00:07:36,523 --> 00:07:40,594 +and use the result for any requests +that load user-facing content. + +145 +00:07:40,627 --> 00:07:42,729 +But let's come back to nicer weather now. + +146 +00:07:42,763 --> 00:07:45,465 +Rain or shine, +the Weather app is very rich in data + +147 +00:07:45,499 --> 00:07:48,802 +and many aspects of it +contain numbers and counts. + +148 +00:07:48,836 --> 00:07:51,238 +Let's focus on one of them. + +149 +00:07:51,271 --> 00:07:56,443 +Under "Precipitation" +it says "0 mm in last 6 hours.” + +150 +00:07:56,476 --> 00:07:58,979 +Let's assume that you want to build +something similar, + +151 +00:07:59,012 --> 00:08:01,648 +but spelling out "one hour" here. + +152 +00:08:01,682 --> 00:08:04,017 +This is how you can declare +the string in code. + +153 +00:08:04,051 --> 00:08:06,286 +In English, +you will need to use the plural form + +154 +00:08:06,320 --> 00:08:08,655 +if the number of hours is larger than one: + +155 +00:08:08,689 --> 00:08:11,692 +one hour, but two hours. + +156 +00:08:11,725 --> 00:08:13,794 +The rules when another variant +should be used + +157 +00:08:13,827 --> 00:08:16,330 +are even more complicated in Ukrainian. + +158 +00:08:16,363 --> 00:08:19,099 +You do not want to implement +that logic in your code, + +159 +00:08:19,132 --> 00:08:22,703 +and this is why you leverage +Apple's frameworks. + +160 +00:08:22,736 --> 00:08:25,138 +All you have to do is to declare +the string in code + +161 +00:08:25,172 --> 00:08:29,543 +and provide a stringsdict file, +which encodes the plural rule. + +162 +00:08:29,576 --> 00:08:33,413 +Another option is to make use +of Automatic Grammar Agreement. + +163 +00:08:33,447 --> 00:08:35,549 +You can learn more +about these two techniques + +164 +00:08:35,582 --> 00:08:39,219 +in last year's talk +"Streamline your localized strings.” + +165 +00:08:39,253 --> 00:08:42,356 +Even though it is easy, +you should not always apply + +166 +00:08:42,389 --> 00:08:44,992 +a plural rule to all of your strings. + +167 +00:08:45,025 --> 00:08:47,561 +For example, +if your sentence doesn't count anything, + +168 +00:08:47,594 --> 00:08:51,999 +and does not include a number, +you should not use a plural rule for it. + +169 +00:08:52,032 --> 00:08:54,201 +Here, "Remove this city +from your favorites" + +170 +00:08:54,234 --> 00:08:56,537 +doesn't need one +because there is no number, + +171 +00:08:56,570 --> 00:09:00,007 +and the same applies to multiple cities. + +172 +00:09:00,040 --> 00:09:02,009 +But if the string does include a number, + +173 +00:09:02,042 --> 00:09:05,179 +you should consider +having variations for plural. + +174 +00:09:05,212 --> 00:09:09,316 +The string of the previous example counted +how much rain will fall in the next hours, + +175 +00:09:09,349 --> 00:09:14,154 +and we just learned how easy it is to +make it adapt for numbers larger than one. + +176 +00:09:14,188 --> 00:09:17,491 +However, if there is a unit +in the sentence, like a duration, + +177 +00:09:17,524 --> 00:09:21,595 +a time, or percentage, +you should consider using a formatter. + +178 +00:09:21,628 --> 00:09:24,364 +So let's talk about formatters now. + +179 +00:09:24,398 --> 00:09:28,368 +Weather displays the current humidity +in percent in this view. + +180 +00:09:28,402 --> 00:09:32,239 +To do this in SwiftUI, it's just a matter +of a single line of code. + +181 +00:09:32,272 --> 00:09:35,609 +You just wrap your value in Text() +and specify how you would like + +182 +00:09:35,642 --> 00:09:37,444 +your number to be formatted. + +183 +00:09:37,477 --> 00:09:39,479 +And the equivalent Swift code +is simple too. + +184 +00:09:39,513 --> 00:09:42,115 +You just call .formatted on your value. + +185 +00:09:43,417 --> 00:09:45,085 +That really is all you need to do, + +186 +00:09:45,118 --> 00:09:48,222 +and the Formatter takes care +of everything else. + +187 +00:09:48,255 --> 00:09:50,624 +It does not only place +the percent sign in front of + +188 +00:09:50,657 --> 00:09:52,960 +or after the number and add spaces, + +189 +00:09:52,993 --> 00:09:56,630 +it also accommodates +for the user's preferred numbering system, + +190 +00:09:56,663 --> 00:10:00,601 +and that is something +that Arabic and Hindi users expect. + +191 +00:10:00,634 --> 00:10:04,972 +But that's really only the beginning +of what types of data you can format. + +192 +00:10:05,005 --> 00:10:07,474 +There are formatters +for almost everything, + +193 +00:10:07,508 --> 00:10:11,912 +and I encourage you to recap the session: +"Formatters: Make data human-friendly.” + +194 +00:10:14,715 --> 00:10:17,417 +As we have seen, +the weather is not always sunny, + +195 +00:10:17,451 --> 00:10:19,686 +and some days will have rain. + +196 +00:10:19,720 --> 00:10:23,357 +Of course, this highlight can't be missing +from Weather app. + +197 +00:10:23,390 --> 00:10:28,462 +Under "Rainfall" it says, +"50 mm expected in next 24 hours," + +198 +00:10:28,495 --> 00:10:32,699 +and I'm really glad that it is not +that much where I am right now. + +199 +00:10:32,733 --> 00:10:34,535 +In English, the case is simple. + +200 +00:10:34,568 --> 00:10:38,705 +We say "50 millimeters expected +in next 24 hours.” + +201 +00:10:38,739 --> 00:10:41,241 +In Spanish however, +the matter is more complicated. + +202 +00:10:41,275 --> 00:10:44,444 +We need to vary the translation +when the amount of precipitation + +203 +00:10:44,478 --> 00:10:47,114 +is singular or plural. + +204 +00:10:47,147 --> 00:10:51,652 +We can solve this by combining +both a Formatter and a plural rule. + +205 +00:10:51,685 --> 00:10:54,354 +The string "2 mm" is produced +by a Formatter, + +206 +00:10:54,388 --> 00:10:59,560 +and it is embedded in a sentence that +needs to be varied for plural in Spanish. + +207 +00:10:59,593 --> 00:11:03,463 +All right, let's take a look +at how to do this in code. + +208 +00:11:03,497 --> 00:11:06,500 +We start by declaring a function +that takes a parameter + +209 +00:11:06,533 --> 00:11:10,170 +about how much the precipitation +will be in millimeters. + +210 +00:11:10,204 --> 00:11:13,807 +Probably it was downloaded from a server. + +211 +00:11:13,841 --> 00:11:16,376 +First, we ask the system +for a UnitLength, + +212 +00:11:16,410 --> 00:11:18,512 +which encodes the user's configuration, + +213 +00:11:18,545 --> 00:11:23,217 +and it will pick the right one +for our the case of showing rainfall. + +214 +00:11:23,250 --> 00:11:26,253 +If the user has not configured +their system to use metrics, + +215 +00:11:26,286 --> 00:11:29,890 +the Measurement type can be +easily converted to the preferred unit. + +216 +00:11:31,692 --> 00:11:35,162 +Next, the formatting API allows us +to produce a formatted string + +217 +00:11:35,195 --> 00:11:38,031 +for the value in a single line of code. + +218 +00:11:38,065 --> 00:11:40,667 +The preferredUnit already +has the information that we want + +219 +00:11:40,701 --> 00:11:42,102 +to display rainfall. + +220 +00:11:42,135 --> 00:11:45,939 +So when formatting, +we set the usage to asProvided. + +221 +00:11:45,973 --> 00:11:49,276 +If more than 1 millimeter or inches +of rain will fall, + +222 +00:11:49,309 --> 00:11:51,512 +we want to use the plural case. + +223 +00:11:51,545 --> 00:11:55,082 +We convert the value into an integer +so that we can check for that. + +224 +00:11:55,115 --> 00:11:58,085 +Next, we load a localized String +with a given key, + +225 +00:11:58,118 --> 00:12:00,988 +and we provide a default value, too. + +226 +00:12:01,021 --> 00:12:04,258 +There, we use String Interpolation +to include the integerValue, + +227 +00:12:04,291 --> 00:12:07,294 +the formattedValue, and the number 24. + +228 +00:12:07,327 --> 00:12:11,365 +The number is defined in code here, +because it will be always 24 hours. + +229 +00:12:11,398 --> 00:12:13,734 +Using String Interpolation +automatically makes sure + +230 +00:12:13,767 --> 00:12:17,671 +that the correct numbering system is used. + +231 +00:12:17,704 --> 00:12:19,673 +The key is declared in a stringsdict file. + +232 +00:12:19,706 --> 00:12:22,442 +Let's take a look at that. + +233 +00:12:22,476 --> 00:12:26,780 +The stringsdict starts with the key +that we have just used in our code. + +234 +00:12:26,813 --> 00:12:29,449 +In English, we don't need to vary +the string for plural, + +235 +00:12:29,483 --> 00:12:33,086 +so we use the category of "Other" for it. + +236 +00:12:33,120 --> 00:12:36,690 +The first parameter defines +which category is chosen at runtime. + +237 +00:12:36,723 --> 00:12:39,493 +Remember, it was the integer value. + +238 +00:12:39,526 --> 00:12:43,230 +Parameter number two and three +are present in the formatted string. + +239 +00:12:43,263 --> 00:12:47,034 +This defines what the sentence +will look like at runtime. + +240 +00:12:47,067 --> 00:12:49,837 +The Spanish stringsdict +has the same structure, + +241 +00:12:49,870 --> 00:12:54,007 +except that we provide a translation +in both singular and plural. + +242 +00:12:56,143 --> 00:13:00,180 +We have now formatted the data in code +and placed it in a sentence. + +243 +00:13:00,214 --> 00:13:02,449 +A stringsdict file +contains the plural rule, + +244 +00:13:02,482 --> 00:13:05,886 +so that the Spanish translation +is using the correct grammar. + +245 +00:13:05,919 --> 00:13:08,989 +Sometimes it's challenging +to provide a fully localized UI + +246 +00:13:09,022 --> 00:13:11,491 +that is working well for all languages. + +247 +00:13:11,525 --> 00:13:15,128 +Again, you learned that joining strings +can work for English + +248 +00:13:15,162 --> 00:13:18,732 +but might have surprising consequences +in other languages. + +249 +00:13:18,765 --> 00:13:21,401 +This might require +some comprehensive code to do, + +250 +00:13:21,435 --> 00:13:24,938 +but now you know how you can make it right +for all your users. + +251 +00:13:24,972 --> 00:13:28,242 +Sometimes +your strings are in a dependency, + +252 +00:13:28,275 --> 00:13:30,978 +or in a module that your app uses. + +253 +00:13:31,011 --> 00:13:34,081 +Or maybe you distribute your own code +to other developers, too, + +254 +00:13:34,114 --> 00:13:36,083 +using Swift Packages. + +255 +00:13:36,116 --> 00:13:39,019 +Let's take a look +at what's new for localization. + +256 +00:13:39,052 --> 00:13:41,922 +For defining a Swift Package +you declare the structure + +257 +00:13:41,955 --> 00:13:45,025 +and build configuration +by using Swift itself. + +258 +00:13:45,058 --> 00:13:48,061 +If you have user-facing content, +you can use the parameter + +259 +00:13:48,095 --> 00:13:51,331 +defaultLocalization to declare +that the content is using English + +260 +00:13:51,365 --> 00:13:53,267 +as primary language. + +261 +00:13:53,300 --> 00:13:58,272 +That is similar to specifying the +development language of an app project. + +262 +00:13:58,305 --> 00:14:01,875 +Xcode now reads that parameter +and recognizes that you are interested + +263 +00:14:01,909 --> 00:14:05,445 +in providing a localized experience. + +264 +00:14:05,479 --> 00:14:10,250 +Because of that, it will add the option to +Export Localizations to the Product menu. + +265 +00:14:10,284 --> 00:14:13,120 +You're probably used to using this feature +for your main app, + +266 +00:14:13,153 --> 00:14:16,123 +and now it also works for Swift Packages. + +267 +00:14:16,156 --> 00:14:21,161 +If you click "Export,” Xcode reads +your code and extracts all your strings. + +268 +00:14:21,195 --> 00:14:25,532 +They are placed in .xcloc files, +that you send to translators. + +269 +00:14:25,566 --> 00:14:28,702 +And to import your localized content +back into your package, + +270 +00:14:28,735 --> 00:14:30,404 +use Import Localizations, + +271 +00:14:30,437 --> 00:14:34,675 +and Xcode will place the files +at the correct file path in your package. + +272 +00:14:34,708 --> 00:14:39,580 +The workflow of localizing a Swift Package +is now identical to localizing your app. + +273 +00:14:41,615 --> 00:14:44,451 +But remember, +loading a string in a Swift Package + +274 +00:14:44,484 --> 00:14:47,387 +requires that you specify +the 'bundle' argument. + +275 +00:14:47,421 --> 00:14:50,357 +You can learn more about that +in the talk "Swift package: + +276 +00:14:50,390 --> 00:14:54,328 +resources and localization.” + +277 +00:14:54,361 --> 00:14:58,432 +If you are the author of a library +which is distributed as a Swift Package, + +278 +00:14:58,465 --> 00:15:01,635 +you now have an easy way +of keeping your project updated + +279 +00:15:01,668 --> 00:15:05,572 +and making localization +a regular part of your workflow. + +280 +00:15:05,606 --> 00:15:08,442 +You put a great amount of effort +and care into your project, + +281 +00:15:08,475 --> 00:15:12,513 +and having it localized is +a huge time-saver for all of your clients. + +282 +00:15:12,546 --> 00:15:14,581 +It can really make it stand out. + +283 +00:15:14,615 --> 00:15:16,750 +Make people aware +that you are going the extra mile + +284 +00:15:16,783 --> 00:15:19,486 +to provide the best experience +with your software, + +285 +00:15:19,520 --> 00:15:21,221 +so go ahead and tell them! + +286 +00:15:21,255 --> 00:15:25,192 +Be open about which languages +you support out of the box. + +287 +00:15:25,225 --> 00:15:27,928 +As an app developer, +you put special considerations + +288 +00:15:27,961 --> 00:15:29,162 +into your dependencies, + +289 +00:15:29,196 --> 00:15:32,065 +not only from a code quality perspective. + +290 +00:15:32,099 --> 00:15:34,935 +Components that you use +should support the same languages + +291 +00:15:34,968 --> 00:15:39,072 +and high-quality translations +as the rest of your app. + +292 +00:15:39,106 --> 00:15:42,943 +In the case that third-party code is not +localized to your required languages, + +293 +00:15:42,976 --> 00:15:45,546 +you can still create +a local copy of the package + +294 +00:15:45,579 --> 00:15:48,448 +and update the localizations there. + +295 +00:15:48,482 --> 00:15:53,120 +Make sure to test all parts of your app +in the languages that it supports. + +296 +00:15:53,153 --> 00:15:56,023 +This way you can make sure +that there will be no UI elements + +297 +00:15:56,056 --> 00:15:58,792 +that are not adapted +to the user's language. + +298 +00:15:58,825 --> 00:16:00,894 +Most of the time a translated string + +299 +00:16:00,928 --> 00:16:03,497 +is longer or shorter +than the English equivalent, + +300 +00:16:03,530 --> 00:16:06,400 +and that always affects +the layout of your app. + +301 +00:16:06,433 --> 00:16:08,836 +Let's look at what this means +for the Weather app. + +302 +00:16:08,869 --> 00:16:11,071 +This is the app running in English, + +303 +00:16:11,104 --> 00:16:14,408 +and on the right side +you can see it running in Arabic. + +304 +00:16:14,441 --> 00:16:17,778 +It is apparent that not only translations +are adapted to the language, + +305 +00:16:17,811 --> 00:16:22,616 +also the layout follows +the appropriate directionality. + +306 +00:16:22,649 --> 00:16:25,319 +If you want to learn more +about how to create a layout + +307 +00:16:25,352 --> 00:16:28,021 +that works for all languages, +which types of symbols + +308 +00:16:28,055 --> 00:16:30,023 +provide a localized alternative, + +309 +00:16:30,057 --> 00:16:32,492 +and what else to consider +for right-to-left languages, + +310 +00:16:32,526 --> 00:16:36,363 +make sure to watch the talk +"Get it right... to left.” + +311 +00:16:37,731 --> 00:16:40,000 +Here, the app is running in Hindi +on the right side. + +312 +00:16:40,033 --> 00:16:42,302 +Let's zoom in. + +313 +00:16:42,336 --> 00:16:45,639 +The script of that language +tends to be taller in general. + +314 +00:16:45,672 --> 00:16:48,342 +And if you look closely, +you see that the height of the labels + +315 +00:16:48,375 --> 00:16:50,944 +are adjusted to accommodate for that. + +316 +00:16:50,978 --> 00:16:52,946 +The system does this automatically. + +317 +00:16:52,980 --> 00:16:54,615 +All you have to do is to make sure + +318 +00:16:54,648 --> 00:16:57,784 +that you don't give UI elements +a fixed height. + +319 +00:16:57,818 --> 00:17:00,621 +Don't assume that everything +will fit within 44 points + +320 +00:17:00,654 --> 00:17:03,690 +just because it's tall enough +to fit the English string. + +321 +00:17:03,724 --> 00:17:07,661 +Please always expect your text to +be taller according to the circumstances. + +322 +00:17:09,062 --> 00:17:11,398 +Coming back to the main view +and scrolling it up, + +323 +00:17:11,431 --> 00:17:13,333 +Weather has a 10-day forecast view + +324 +00:17:13,367 --> 00:17:15,736 +which is great +for checking out the next week. + +325 +00:17:17,304 --> 00:17:20,374 +What stands out on this screen +is how it dynamically adjusts + +326 +00:17:20,407 --> 00:17:24,044 +the position of elements +according to the longest label. + +327 +00:17:24,077 --> 00:17:28,215 +In English, "Today" is longer +than all of the abbreviated weekday names. + +328 +00:17:28,248 --> 00:17:31,552 +In Spanish, however, +all of them are three characters wide, + +329 +00:17:31,585 --> 00:17:36,123 +and in Greek, the translation for "Today" +is almost double the size. + +330 +00:17:36,156 --> 00:17:40,227 +In all languages, though, the weather icons +are aligned vertically with each other. + +331 +00:17:40,260 --> 00:17:43,697 +Meaning they do not have fixed spacing +to their neighbor elements, + +332 +00:17:43,730 --> 00:17:47,534 +but flow according to +the longest weekday label. + +333 +00:17:47,568 --> 00:17:51,104 +When it comes to creating a layout +that works well with internationalization, + +334 +00:17:51,138 --> 00:17:55,008 +you should always keep in mind +that labels need to be flexible. + +335 +00:17:55,042 --> 00:17:58,745 +You have just seen how important it is +to make them flexible vertically, + +336 +00:17:58,779 --> 00:18:03,283 +but also expect labels to grow +horizontally with a longer translation. + +337 +00:18:03,317 --> 00:18:06,286 +It can be a challenge to accommodate +for that in certain layouts, + +338 +00:18:06,320 --> 00:18:07,921 +such as in this example, + +339 +00:18:07,955 --> 00:18:10,591 +but this year, +SwiftUI adds support for Grid, + +340 +00:18:10,624 --> 00:18:15,195 +which is a new view that helps you +to build this kind of layout more easily. + +341 +00:18:15,229 --> 00:18:18,999 +Let's take a closer look +at how to use Grid. + +342 +00:18:19,032 --> 00:18:22,002 +You start by declaring the Grid +with a leading alignment. + +343 +00:18:22,035 --> 00:18:25,405 +That means that UI elements start +on the left side of the screen + +344 +00:18:25,439 --> 00:18:26,940 +in a left-to-right language + +345 +00:18:26,974 --> 00:18:31,512 +and on the right side of the screen +in a right-to-left language. + +346 +00:18:31,545 --> 00:18:35,382 +Then, for each horizontal group, +you add a GridRow. + +347 +00:18:35,415 --> 00:18:38,752 +And lastly +you declare the content of the rows. + +348 +00:18:38,785 --> 00:18:41,622 +That's all it takes +to create this rather advanced layout. + +349 +00:18:41,655 --> 00:18:43,557 +When the label needs more space, + +350 +00:18:43,590 --> 00:18:48,061 +the Capsule can shrink in size +because it's the most flexible element. + +351 +00:18:48,095 --> 00:18:50,964 +SwiftUI does all the heavy lifting, +such as measuring, + +352 +00:18:50,998 --> 00:18:55,569 +sizing, and positioning the views– +completely automatic. + +353 +00:18:55,602 --> 00:18:58,672 +Another challenge is to make a view +with a longer translation + +354 +00:18:58,705 --> 00:19:02,576 +work with a limited amount of space, +like on Apple Watch. + +355 +00:19:02,609 --> 00:19:05,045 +Here, the German translation +of "Tip Function" + +356 +00:19:05,078 --> 00:19:07,581 +is too long to fit in one row. + +357 +00:19:07,614 --> 00:19:11,685 +To fix this, we do not remove the icon +next to the text to make more room. + +358 +00:19:11,718 --> 00:19:15,556 +The solution is rather to use +two or more lines of text if needed, + +359 +00:19:15,589 --> 00:19:17,958 +which is the default behavior. + +360 +00:19:17,991 --> 00:19:19,660 +We do not encourage you to change that + +361 +00:19:19,693 --> 00:19:22,896 +and hiding interface elements +if there is not enough space. + +362 +00:19:22,930 --> 00:19:25,365 +Usually there is a way +to adjust the layout, + +363 +00:19:25,399 --> 00:19:28,902 +so that it can accommodate +for the needs of the language. + +364 +00:19:28,936 --> 00:19:31,205 +The Mail app does this in a creative way. + +365 +00:19:32,206 --> 00:19:37,077 +In the sheet presentation, there are four +buttons to take action on this email. + +366 +00:19:37,110 --> 00:19:40,247 +When a translation +of one of the button titles is too long, + +367 +00:19:40,280 --> 00:19:43,050 +we do not clip the text +or wrap it onto a new line. + +368 +00:19:43,083 --> 00:19:45,819 +This would make the view look imbalanced. + +369 +00:19:45,853 --> 00:19:49,089 +Instead, the entire layout is transitioned +from a horizontal stack, + +370 +00:19:49,122 --> 00:19:51,758 +to a vertical stack of two rows. + +371 +00:19:53,060 --> 00:19:55,662 +This year, SwiftUI adds another great tool + +372 +00:19:55,696 --> 00:19:58,098 +that makes creating +this dynamic layout easier: + +373 +00:19:58,131 --> 00:20:00,033 +ViewThatFits. + +374 +00:20:00,067 --> 00:20:02,469 +In essence, +it lets you provide alternative layouts + +375 +00:20:02,503 --> 00:20:05,939 +if the space is constrained +and the view would not fit. + +376 +00:20:07,608 --> 00:20:10,277 +You simply declare your views +independently of each other, + +377 +00:20:10,310 --> 00:20:12,980 +and place them in ViewThatFits. + +378 +00:20:13,013 --> 00:20:16,383 +SwiftUI automatically detects +if a view does not fit without clipping, + +379 +00:20:16,416 --> 00:20:19,486 +and transitions to the next one provided. + +380 +00:20:19,520 --> 00:20:22,389 +Keep in mind that you should +only switch out the layouts. + +381 +00:20:22,422 --> 00:20:26,627 +Hiding a view just because the translation +is too long is a bad practice. + +382 +00:20:26,660 --> 00:20:30,430 +That makes it harder for the users +to orient themselves in the UI. + +383 +00:20:30,464 --> 00:20:33,400 +Try to make room for +all the interface elements first + +384 +00:20:33,433 --> 00:20:34,868 +by having a flexible layout. + +385 +00:20:36,303 --> 00:20:38,672 +This is not only helpful for localization. + +386 +00:20:38,705 --> 00:20:41,875 +This layout also works great +when the user prefers to have smaller + +387 +00:20:41,909 --> 00:20:45,412 +or larger text, +and uses different devices. + +388 +00:20:45,445 --> 00:20:48,882 +To learn more about the great new +layout features of SwiftUI this year, + +389 +00:20:48,916 --> 00:20:53,787 +I recommend you to watch the talk +"Compose custom layouts with SwiftUI.” + +390 +00:20:53,820 --> 00:20:56,123 +Having different accessibility preferences + +391 +00:20:56,156 --> 00:20:59,560 +and localized text can be a challenge +for your layout. + +392 +00:20:59,593 --> 00:21:02,496 +Interface elements can be +taller and wider. + +393 +00:21:02,529 --> 00:21:05,599 +Adapting the layout to accommodate +for that can be a challenge, + +394 +00:21:05,632 --> 00:21:08,335 +but with SwiftUI +it gets a lot easier this year. + +395 +00:21:09,770 --> 00:21:13,273 +I want you to take away from this talk +that constructing a string in code + +396 +00:21:13,307 --> 00:21:16,143 +can be challenging +when supporting other languages. + +397 +00:21:16,176 --> 00:21:18,612 +Listen to the feedback +that your international users + +398 +00:21:18,645 --> 00:21:23,183 +and testers give you to make sure +it works great for everybody. + +399 +00:21:23,217 --> 00:21:25,252 +Formatting values in Swift is easy + +400 +00:21:25,285 --> 00:21:27,921 +and it often just takes +a single line of code. + +401 +00:21:27,955 --> 00:21:30,290 +And doing that, +your formatted values respect + +402 +00:21:30,324 --> 00:21:32,426 +the user's preferences automatically. + +403 +00:21:33,727 --> 00:21:35,529 +When you are offering a Swift Package, + +404 +00:21:35,562 --> 00:21:38,165 +make use the new +Xcode localization workflow + +405 +00:21:38,198 --> 00:21:42,336 +to provide a fully localized experience +to your clients. + +406 +00:21:42,369 --> 00:21:44,538 +Now, with or without using SwiftUI, + +407 +00:21:44,571 --> 00:21:47,407 +your layout should be able +to accommodate for translated text + +408 +00:21:47,441 --> 00:21:49,476 +and accessibility settings. + +409 +00:21:49,510 --> 00:21:52,312 +Use your layout tools +to make the layout flexible, + +410 +00:21:52,346 --> 00:21:55,315 +without hiding interface elements. + +411 +00:21:55,349 --> 00:21:57,818 +In the end, +your users will be grateful for that + +412 +00:21:57,851 --> 00:22:00,954 +because they expect your app +to fit into their lives, + +413 +00:22:00,988 --> 00:22:03,857 +and that includes +respecting their languages. + +414 +00:22:03,891 --> 00:22:06,093 +Now, I'm looking forward to a very sunny week. + +415 +00:22:06,126 --> 00:22:07,728 +Enjoy the rest of WWDC, + +416 +00:22:07,761 --> 00:22:09,630 +and thank you for watching. + diff --git "a/eng/2022 Session 10115 What\342\200\231s new in CloudKit Console en.srt" "b/eng/2022 Session 10115 What\342\200\231s new in CloudKit Console en.srt" new file mode 100644 index 0000000..6e518a1 --- /dev/null +++ "b/eng/2022 Session 10115 What\342\200\231s new in CloudKit Console en.srt" @@ -0,0 +1,707 @@ +1 +00:00:00,000 --> 00:00:03,036 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,036 --> 00:00:10,143 +♪ + +3 +00:00:10,143 --> 00:00:12,045 +Hi, I'm Alex Young, +and I'm an engineer + +4 +00:00:12,045 --> 00:00:15,515 +on the iCloud developer +experience team. + +5 +00:00:15,515 --> 00:00:18,418 +CloudKit is an easy way +to create great experiences + +6 +00:00:18,418 --> 00:00:21,321 +by allowing users +to effortlessly synchronize data + +7 +00:00:21,321 --> 00:00:23,624 +between devices. + +8 +00:00:23,624 --> 00:00:26,460 +CloudKit Console helps to make +this even easier by providing + +9 +00:00:26,460 --> 00:00:30,731 +tools that let you work with +your app's schema and data. + +10 +00:00:30,731 --> 00:00:33,600 +I'm going to share several +updates to CloudKit Console + +11 +00:00:33,600 --> 00:00:35,369 +that will help you +understand and debug + +12 +00:00:35,369 --> 00:00:38,171 +your application's +schema and data. + +13 +00:00:38,171 --> 00:00:41,909 +First, I'll walk through +how to use hidden containers, + +14 +00:00:41,909 --> 00:00:44,311 +then dive into a new way +you can view records + +15 +00:00:44,311 --> 00:00:46,713 +using iCloud accounts, + +16 +00:00:46,713 --> 00:00:48,415 +and finally, +there are some great updates + +17 +00:00:48,415 --> 00:00:51,151 +for working with shared zones. + +18 +00:00:51,151 --> 00:00:54,054 +It's common to create lots of +containers while in development, + +19 +00:00:54,054 --> 00:00:56,657 +and only focus +on a few later on. + +20 +00:00:56,657 --> 00:00:57,891 +You can better organize this + +21 +00:00:57,891 --> 00:01:00,694 +with the new +hidden containers feature. + +22 +00:01:00,694 --> 00:01:02,629 +In CloudKit Console, +you can choose + +23 +00:01:02,629 --> 00:01:05,499 +which containers +are hidden or visible. + +24 +00:01:05,499 --> 00:01:07,234 +Hidden containers +aren't just hidden + +25 +00:01:07,234 --> 00:01:09,403 +from the lists +inside the Console, + +26 +00:01:09,403 --> 00:01:11,538 +but also across +all developer tools, + +27 +00:01:11,538 --> 00:01:13,740 +including Xcode. + +28 +00:01:13,740 --> 00:01:16,009 +Even better, +when a container is hidden, + +29 +00:01:16,009 --> 00:01:17,878 +it applies at the team level, + +30 +00:01:17,878 --> 00:01:20,681 +which means all of your test +and prototype containers + +31 +00:01:20,681 --> 00:01:24,084 +no longer clutter +your teammates' views. + +32 +00:01:24,084 --> 00:01:28,655 +I'll open the Console and use +this new functionality now. + +33 +00:01:28,655 --> 00:01:30,424 +Once you're signed in +to the Console, + +34 +00:01:30,424 --> 00:01:34,428 +open the container selector +from the top-level navigation. + +35 +00:01:34,428 --> 00:01:36,830 +Then click Manage Containers. + +36 +00:01:36,830 --> 00:01:39,466 +This displays a new menu +that allows you to toggle + +37 +00:01:39,466 --> 00:01:43,704 +the visibility of every +container in the current team. + +38 +00:01:43,704 --> 00:01:46,773 +For people with several teams, +you can switch between them + +39 +00:01:46,773 --> 00:01:49,576 +using the account menu +in the top right. + +40 +00:01:49,576 --> 00:01:51,378 +Then you can go on +to hide containers + +41 +00:01:51,378 --> 00:01:54,381 +in your other teams as well. + +42 +00:01:54,381 --> 00:01:57,351 +Here's the container +from WWDC21, + +43 +00:01:57,351 --> 00:01:59,052 +which isn't needed anymore. + +44 +00:01:59,052 --> 00:02:01,655 +I can hide it, like this. + +45 +00:02:01,655 --> 00:02:04,691 +And if you refresh +the container list in Xcode, + +46 +00:02:04,691 --> 00:02:08,962 +you'll see the containers +are now hidden there too. + +47 +00:02:08,962 --> 00:02:11,565 +I'll tell you about +a new addition to the Console + +48 +00:02:11,565 --> 00:02:13,900 +that lets you view data +as iCloud accounts + +49 +00:02:13,900 --> 00:02:16,236 +instead of your +developer account. + +50 +00:02:16,236 --> 00:02:19,806 +This is called Act As iCloud. + +51 +00:02:19,806 --> 00:02:23,010 +Sometimes, it can be difficult +to understand why certain users + +52 +00:02:23,010 --> 00:02:27,814 +are experiencing trouble with +data in their private databases. + +53 +00:02:27,814 --> 00:02:29,649 +Now you have the ability +to sign in + +54 +00:02:29,649 --> 00:02:34,087 +as a separate iCloud account +to view private data. + +55 +00:02:34,087 --> 00:02:36,156 +When you're logged in +with an iCloud account, + +56 +00:02:36,156 --> 00:02:39,226 +CloudKit Console's query tools +work as you'd expect + +57 +00:02:39,226 --> 00:02:42,229 +from the perspective +of that account. + +58 +00:02:42,229 --> 00:02:43,497 +This helps you debug + +59 +00:02:43,497 --> 00:02:47,868 +development and production +issues on behalf of your users. + +60 +00:02:47,868 --> 00:02:49,503 +I'll demonstrate +this in the Console + +61 +00:02:49,503 --> 00:02:52,572 +to illustrate how it works. + +62 +00:02:52,572 --> 00:02:55,709 +This query was performed +using my developer account. + +63 +00:02:55,709 --> 00:02:57,778 +I'm going to switch +to an iCloud account + +64 +00:02:57,778 --> 00:03:01,014 +to show how +the query results change. + +65 +00:03:01,014 --> 00:03:05,052 +First, navigate to the +Act As iCloud account menu item. + +66 +00:03:05,052 --> 00:03:07,254 +This will open a dialog +that allows you to sign in + +67 +00:03:07,254 --> 00:03:09,256 +as an iCloud account. + +68 +00:03:09,256 --> 00:03:13,393 +When I sign in, the context of +the Console is going to change. + +69 +00:03:13,393 --> 00:03:15,729 +Now you'll see a banner +at the top of the page. + +70 +00:03:15,729 --> 00:03:17,964 +All subsequent operations +will be performed + +71 +00:03:17,964 --> 00:03:22,402 +using the iCloud account instead +of your developer account. + +72 +00:03:22,402 --> 00:03:24,704 +You can query records +and view zones, + +73 +00:03:24,704 --> 00:03:28,909 +but you cannot perform +schema operations. + +74 +00:03:28,909 --> 00:03:30,911 +As you can see, +the results of this query + +75 +00:03:30,911 --> 00:03:34,881 +are for my iCloud account, +not my developer account. + +76 +00:03:34,881 --> 00:03:36,950 +Switching to another +container or environment + +77 +00:03:36,950 --> 00:03:39,586 +will stop acting +as the iCloud account. + +78 +00:03:39,586 --> 00:03:41,555 +You can also click +the button in the banner + +79 +00:03:41,555 --> 00:03:45,325 +to stop the Act As session. + +80 +00:03:45,325 --> 00:03:48,228 +Remember, acting as an iCloud +account applies to records, + +81 +00:03:48,228 --> 00:03:50,597 +not schema, +so this feature only works + +82 +00:03:50,597 --> 00:03:53,100 +in the data parts +of the Console. + +83 +00:03:53,100 --> 00:03:57,370 +Attempting to access schema +will halt the Act As session. + +84 +00:03:57,370 --> 00:03:59,272 +This feature helps you +to better understand + +85 +00:03:59,272 --> 00:04:01,374 +how your data +is used in production + +86 +00:04:01,374 --> 00:04:06,279 +and investigate data-related +bugs more effectively. + +87 +00:04:06,279 --> 00:04:08,615 +Encrypted fields +remain unreadable to you + +88 +00:04:08,615 --> 00:04:10,851 +when acting as another account. + +89 +00:04:10,851 --> 00:04:14,721 +Only the original user who +owns the data can decrypt it. + +90 +00:04:14,721 --> 00:04:16,490 +This is by design +and helps iCloud + +91 +00:04:16,490 --> 00:04:19,192 +maintain a high level +of security and privacy + +92 +00:04:19,192 --> 00:04:23,163 +and ensures +that sensitive data stays safe. + +93 +00:04:23,163 --> 00:04:27,234 +Next, I'll show you how +to share zones in the Console. + +94 +00:04:27,234 --> 00:04:29,302 +CloudKit sharing is a safe way + +95 +00:04:29,302 --> 00:04:33,006 +to securely share records +between users of your app. + +96 +00:04:33,006 --> 00:04:34,808 +This is sharing done securely, + +97 +00:04:34,808 --> 00:04:36,576 +using permissions +that allow your app + +98 +00:04:36,576 --> 00:04:40,914 +to determine a participant's +read and write access. + +99 +00:04:40,914 --> 00:04:42,883 +Participant access +is further controlled + +100 +00:04:42,883 --> 00:04:47,888 +by defining shares +as public or private. + +101 +00:04:47,888 --> 00:04:52,192 +Zone sharing works by applying +rules to every record in a zone. + +102 +00:04:52,192 --> 00:04:54,861 +A shared zone +has a CloudKit share record + +103 +00:04:54,861 --> 00:04:57,430 +that contains the options +for accessing each record + +104 +00:04:57,430 --> 00:04:59,599 +in the zone. + +105 +00:04:59,599 --> 00:05:03,603 +Shared zones cannot have +existing shared records. + +106 +00:05:03,603 --> 00:05:06,239 +The standard public +and private zone sharing options + +107 +00:05:06,239 --> 00:05:10,210 +are supported by shared zones. + +108 +00:05:10,210 --> 00:05:13,113 +A public shared zone +makes every record visible + +109 +00:05:13,113 --> 00:05:15,615 +to everyone who has +the share code. + +110 +00:05:15,615 --> 00:05:19,019 +Anyone with the short share code +can join this share. + +111 +00:05:19,019 --> 00:05:21,688 +This is ideal for records +that should be easy to share + +112 +00:05:21,688 --> 00:05:25,458 +between users +without strict access control. + +113 +00:05:25,458 --> 00:05:28,595 +Private shared zones have +an additional layer of security + +114 +00:05:28,595 --> 00:05:30,897 +because members must be +in the participant list + +115 +00:05:30,897 --> 00:05:33,667 +to join the share. + +116 +00:05:33,667 --> 00:05:36,036 +CloudKit Console +has several new tools + +117 +00:05:36,036 --> 00:05:38,004 +for creating and viewing +zone shares, + +118 +00:05:38,004 --> 00:05:40,240 +to help you +better understand shared zones + +119 +00:05:40,240 --> 00:05:43,843 +and work with them during +development and production. + +120 +00:05:43,843 --> 00:05:48,582 +Let's create a zone share now +so you can learn how it works. + +121 +00:05:48,582 --> 00:05:52,185 +Navigate to Zones, then select +a zone that you want to share. + +122 +00:05:52,185 --> 00:05:54,588 +Click the Configure zone +wide sharing... button + +123 +00:05:54,588 --> 00:05:56,356 +in the details view. + +124 +00:05:56,356 --> 00:05:58,091 +This shows a form +that lets you decide + +125 +00:05:58,091 --> 00:06:01,461 +if you want a public +or private share. + +126 +00:06:01,461 --> 00:06:03,964 +Public zones have +an additional permission option + +127 +00:06:03,964 --> 00:06:07,300 +for creating read-only +or read/write shares. + +128 +00:06:07,300 --> 00:06:09,402 +For private shares, +the permissions are defined + +129 +00:06:09,402 --> 00:06:11,504 +at a participant level. + +130 +00:06:11,504 --> 00:06:14,507 +I'm going to create +a private share, like this. + +131 +00:06:16,509 --> 00:06:19,145 +Once the zone is shared, +you will see a short unique ID + +132 +00:06:19,145 --> 00:06:23,950 +that can be sent to participants +to join the share. + +133 +00:06:23,950 --> 00:06:26,152 +Shared zones can be joined +in the console + +134 +00:06:26,152 --> 00:06:28,555 +by using the Accept Shared +Record menu option + +135 +00:06:28,555 --> 00:06:31,057 +found in the Records page. + +136 +00:06:31,057 --> 00:06:32,859 +Now any record created +in this zone + +137 +00:06:32,859 --> 00:06:35,996 +will be automatically shared. + +138 +00:06:35,996 --> 00:06:38,765 +Now that you've seen these +updates to CloudKit Console, + +139 +00:06:38,765 --> 00:06:42,269 +go and tidy up your container +lists by hiding containers, + +140 +00:06:42,269 --> 00:06:44,938 +try out Act As iCloud +so you can see records + +141 +00:06:44,938 --> 00:06:48,341 +with different iCloud accounts, +and try out zone sharing + +142 +00:06:48,341 --> 00:06:51,945 +for easier sharing +of multiple records. + +143 +00:06:51,945 --> 00:06:54,581 +We hope these tools +enable you to better understand + +144 +00:06:54,581 --> 00:06:57,517 +your schema, +debug issues in your apps, + +145 +00:06:57,517 --> 00:07:01,187 +and make CloudKit +a little easier to work with. + +146 +00:07:01,187 --> 00:07:03,623 +Thanks, and have a great WWDC. + +147 +00:07:03,623 --> 00:07:07,661 +♪ + diff --git a/eng/2022 Session 10116 Meet CKTool JS en.srt b/eng/2022 Session 10116 Meet CKTool JS en.srt new file mode 100644 index 0000000..4a33d30 --- /dev/null +++ b/eng/2022 Session 10116 Meet CKTool JS en.srt @@ -0,0 +1,1331 @@ +1 +00:00:00,367 --> 00:00:06,373 +[upbeat music] + +2 +00:00:09,309 --> 00:00:13,447 +- Hi! I'm Kent and I'm an +engineer on the CloudKit team. + +3 +00:00:13,480 --> 00:00:16,650 +I'm excited to introduce +a new library that you can use + +4 +00:00:16,683 --> 00:00:18,285 +to access CloudKit. + +5 +00:00:18,318 --> 00:00:21,188 +First, I'll cover +how to configure this new library. + +6 +00:00:21,221 --> 00:00:23,323 +And then you'll learn +how to manage your schema, + +7 +00:00:23,357 --> 00:00:27,127 +as well as how to access +user data with CKTool JS. + +8 +00:00:27,160 --> 00:00:28,595 +Let's begin! + +9 +00:00:29,830 --> 00:00:31,732 +CloudKit is a persistence technology + +10 +00:00:31,765 --> 00:00:35,469 +that lets you store your app's data +in iCloud within containers. + +11 +00:00:35,502 --> 00:00:39,072 +By using CloudKit in your app, +you can also have your data + +12 +00:00:39,106 --> 00:00:41,742 +stay up to date across devices +and on the web. + +13 +00:00:42,576 --> 00:00:44,044 +For building your apps, + +14 +00:00:44,077 --> 00:00:48,348 +you can access iCloud storage using +the CloudKit framework on Apple platforms + +15 +00:00:48,382 --> 00:00:51,185 +or CloudKit JS on the web. + +16 +00:00:51,218 --> 00:00:56,990 +To implement automation and tooling, +Xcode provides cktool for use on macOS. + +17 +00:00:57,024 --> 00:01:00,260 +And now you have a new way to automate +changes and interact with iCloud, + +18 +00:01:00,294 --> 00:01:02,129 +using CKTool JS. + +19 +00:01:03,597 --> 00:01:06,366 +CKTool JS lets you perform +the same operations + +20 +00:01:06,400 --> 00:01:10,337 +as the cktool command-line utility +introduced in Xcode 13 + +21 +00:01:10,370 --> 00:01:13,140 +and supports similar use cases. + +22 +00:01:13,173 --> 00:01:17,377 +In fact, CKTool JS is used to +implement features in CloudKit Console + +23 +00:01:17,411 --> 00:01:20,080 +such as adding record types +and querying records. + +24 +00:01:21,215 --> 00:01:23,817 +With CKTool JS, +you can manage your app containers + +25 +00:01:23,851 --> 00:01:25,886 +and perform schema operations, + +26 +00:01:25,919 --> 00:01:29,256 +such as resetting and applying +updates to your schema. + +27 +00:01:29,289 --> 00:01:31,725 +This is something that you couldn't +do from JavaScript before. + +28 +00:01:33,393 --> 00:01:37,664 +CKTool JS lets you fetch existing +records using their unique identifier + +29 +00:01:37,698 --> 00:01:39,566 +or through complex queries. + +30 +00:01:39,600 --> 00:01:43,237 +And it lets you create new records +and update them. + +31 +00:01:43,270 --> 00:01:47,140 +CKTool JS ships with strict +type definitions for TypeScript. + +32 +00:01:47,174 --> 00:01:49,810 +These type definitions enable +compile-time checking + +33 +00:01:49,843 --> 00:01:52,546 +that flags incorrect +usage of the client library + +34 +00:01:52,579 --> 00:01:55,782 +and it enables code completion +in supported IDEs. + +35 +00:01:55,816 --> 00:01:58,886 +You'll find editing CKTool JS +code easier because of this. + +36 +00:02:00,587 --> 00:02:02,656 +Additionally, +this new library ships with support + +37 +00:02:02,689 --> 00:02:05,325 +for both Node.js +and browsers out of the box. + +38 +00:02:05,359 --> 00:02:09,429 +CKTool JS is distributed +as a set of npm packages, + +39 +00:02:09,463 --> 00:02:13,934 +which allows you to integrate it as +part of your JavaScript build pipeline. + +40 +00:02:13,967 --> 00:02:17,971 +Doing so enables features such +as tree-shaking and bundling. + +41 +00:02:18,005 --> 00:02:20,908 +You can also +track updates to these packages + +42 +00:02:20,941 --> 00:02:24,578 +because their release history +is transparently available from npm. + +43 +00:02:26,013 --> 00:02:29,816 +The following packages are part +of the CKTool JS distribution. + +44 +00:02:29,850 --> 00:02:30,684 +Note that these packages +are within the @apple scope + +45 +00:02:33,153 --> 00:02:38,825 +and follow the convention of using +cktool. at the start of the name. + +46 +00:02:38,859 --> 00:02:42,896 +The main package that you'll use +is cktool.database. + +47 +00:02:42,930 --> 00:02:44,798 +To enable communication with iCloud, + +48 +00:02:44,831 --> 00:02:48,535 +you'll also need to use one other package +for your target platform, + +49 +00:02:48,569 --> 00:02:52,172 +cktool.target.nodejs +for Node.js + +50 +00:02:52,206 --> 00:02:56,143 +or cktool.target.browser +for web browsers. + +51 +00:02:57,144 --> 00:03:01,949 +cktool.database automatically +pulls in three more packages-- + +52 +00:03:01,982 --> 00:03:06,353 +cktool.core, cktool.api.base, + +53 +00:03:06,386 --> 00:03:10,490 +and cktool.api.database. + +54 +00:03:10,524 --> 00:03:15,362 +Since CKTool JS communicates directly +with iCloud, it must first be authorized. + +55 +00:03:15,395 --> 00:03:18,165 +Depending on the operation +that you want to call, + +56 +00:03:18,198 --> 00:03:21,368 +you'll either need +a management token or a user token. + +57 +00:03:21,401 --> 00:03:24,404 +Both kinds of tokens are +obtainable from CloudKit Console. + +58 +00:03:25,772 --> 00:03:28,876 +Management tokens are used to +access management operations + +59 +00:03:28,909 --> 00:03:31,712 +and are scoped to a team and user. + +60 +00:03:31,745 --> 00:03:36,717 +Such operations include enabling schema +import and export, schema validation, + +61 +00:03:36,750 --> 00:03:39,353 +and resetting the container to production. + +62 +00:03:39,386 --> 00:03:42,856 +User tokens are scoped to teams +and containers and enable access + +63 +00:03:42,890 --> 00:03:45,993 +to private user data +within those containers. + +64 +00:03:46,026 --> 00:03:49,196 +To learn how to obtain these +authorization tokens as well as + +65 +00:03:49,229 --> 00:03:52,866 +continuous integration with CloudKit, +check out + +66 +00:03:52,900 --> 00:03:56,770 +"Automate CloudKit tests with cktool and +declarative schema" from WWDC21. + +67 +00:03:58,472 --> 00:04:01,575 +Any time you want to use +CKTool JS in your scripts, + +68 +00:04:01,608 --> 00:04:03,944 +you'll first need to configure it for use. + +69 +00:04:03,977 --> 00:04:06,713 +But before +I dive into configuring CKTool JS, + +70 +00:04:06,747 --> 00:04:09,917 +I'll do a quick review of what +makes up a CloudKit schema. + +71 +00:04:09,950 --> 00:04:13,086 +In CloudKit, +data is stored in a structured way. + +72 +00:04:13,120 --> 00:04:17,991 +Data that has the same kinds of +values are stored together as records. + +73 +00:04:18,025 --> 00:04:20,894 +Records are instances of record types, + +74 +00:04:20,928 --> 00:04:23,597 +and the properties of a record +that a record type describes + +75 +00:04:23,630 --> 00:04:25,365 +are known as fields. + +76 +00:04:25,399 --> 00:04:29,269 +In addition to your user-defined fields, +CloudKit adds system fields + +77 +00:04:29,303 --> 00:04:32,005 +such as recordName, +which is the ID of the record. + +78 +00:04:32,039 --> 00:04:35,876 +I'll use examples from a coin collection +app I've been working on. + +79 +00:04:35,909 --> 00:04:39,179 +I want to store a collection of countries, +so I have a record type + +80 +00:04:39,213 --> 00:04:42,516 +to describe what kinds of properties +I need to store for them. + +81 +00:04:42,549 --> 00:04:45,052 +I'm storing names and ISO codes, + +82 +00:04:45,085 --> 00:04:47,554 +and I'm naming the record type, +"Countries." + +83 +00:04:47,588 --> 00:04:49,489 +ISO codes uniquely identify a country, + +84 +00:04:49,523 --> 00:04:52,025 +so it's important +to include them in my record type. + +85 +00:04:53,160 --> 00:04:55,796 +I create some records of +type Countries to store this information + +86 +00:04:55,829 --> 00:04:57,397 +along with their names. + +87 +00:04:58,732 --> 00:05:01,335 +I also have a record type +for coins of particular countries, + +88 +00:05:01,368 --> 00:05:03,704 +and I want to relate them to one another. + +89 +00:05:03,737 --> 00:05:08,175 +The Coins record type stores the +relationship from a coin to its country. + +90 +00:05:10,244 --> 00:05:13,514 +Record types and relationships +combine to make a schema. + +91 +00:05:13,547 --> 00:05:15,816 +I can consider the current +state of these elements + +92 +00:05:15,849 --> 00:05:18,685 +to be the current version of my schema. + +93 +00:05:18,719 --> 00:05:21,321 +As you develop your apps, +you'll evolve your schema, + +94 +00:05:21,355 --> 00:05:24,825 +and over the lifetime of your app, +you'll likely have several versions of it. + +95 +00:05:26,326 --> 00:05:29,129 +While my app's schema describes +the structure of the data + +96 +00:05:29,162 --> 00:05:33,767 +I want to store in iCloud, my app +container is where that data is stored. + +97 +00:05:33,800 --> 00:05:38,739 +A container has a unique identifier +and is associated with a developer team. + +98 +00:05:38,772 --> 00:05:43,243 +There are two environments to keep +in mind when working with CloudKit. + +99 +00:05:43,277 --> 00:05:46,013 +The development environment +is a safe place to make changes + +100 +00:05:46,046 --> 00:05:47,881 +without disrupting users. + +101 +00:05:47,915 --> 00:05:51,552 +This is where you should be testing +and developing changes to your schema. + +102 +00:05:51,585 --> 00:05:53,720 +When users interact with your app, + +103 +00:05:53,754 --> 00:05:56,323 +they'll be interacting with +the production environment. + +104 +00:05:56,356 --> 00:05:59,693 +The production environment +contains the live data for your app. + +105 +00:05:59,726 --> 00:06:02,729 +Now that I've reviewed +how CloudKit stores data, + +106 +00:06:02,763 --> 00:06:05,465 +I'll cover how to configure CKTool JS. + +107 +00:06:05,499 --> 00:06:07,901 +Because CKTool JS talks with iCloud, + +108 +00:06:07,935 --> 00:06:09,870 +you'll need to gather +a few pieces of information + +109 +00:06:09,903 --> 00:06:12,306 +so that it knows how to work +with the right container + +110 +00:06:12,339 --> 00:06:14,708 +and that your script +is authorized to do so. + +111 +00:06:15,909 --> 00:06:18,378 +You'll need your team ID +and the container ID + +112 +00:06:18,412 --> 00:06:20,614 +for the container you want to work with. + +113 +00:06:20,647 --> 00:06:23,417 +You'll need a management token +in order to work with schemas, + +114 +00:06:23,450 --> 00:06:26,987 +and if your script will access data, +you'll need a user token as well. + +115 +00:06:27,020 --> 00:06:30,724 +All these values can be +obtained from CloudKit Console. + +116 +00:06:30,757 --> 00:06:34,294 +You'll also need to specify which +environment, development or production, + +117 +00:06:34,328 --> 00:06:35,762 +your script will run in. + +118 +00:06:35,796 --> 00:06:38,665 +I'll use development +as an example going forward. + +119 +00:06:38,699 --> 00:06:43,437 +Anytime you configure CKTool JS for use, +you'll need these values. + +120 +00:06:43,470 --> 00:06:46,640 +For my examples, +I'm writing scripts for Node.js. + +121 +00:06:46,673 --> 00:06:51,512 +You import objects and functions +from CKTool JS in order to use them. + +122 +00:06:51,545 --> 00:06:56,250 +In this case, you can import these +symbols using CommonJS require statements. + +123 +00:06:56,283 --> 00:06:59,086 +Once you've gathered +your configuration information, + +124 +00:06:59,119 --> 00:07:02,089 +you'll create objects +to hold that information. + +125 +00:07:02,122 --> 00:07:04,291 +To store your auth tokens, +you create an object + +126 +00:07:04,324 --> 00:07:08,462 +to hold your management token and, +if you have one, your user token. + +127 +00:07:08,495 --> 00:07:12,199 +Since teamId, containerId +and environment are common values + +128 +00:07:12,232 --> 00:07:17,304 +that are passed to CKTool JS, you can +create an object to hold these values. + +129 +00:07:17,337 --> 00:07:20,908 +You instantiate a Configuration +object that tells CKTool JS + +130 +00:07:20,941 --> 00:07:26,280 +how to talk with iCloud by using the +createConfiguration factory function. + +131 +00:07:26,313 --> 00:07:28,815 +createConfiguration is platform-specific. + +132 +00:07:28,849 --> 00:07:32,052 +In this case, it'll return an +appropriate configuration for Node.js, + +133 +00:07:32,085 --> 00:07:35,589 +since that's the function that +was imported from the target package. + +134 +00:07:35,622 --> 00:07:40,227 +You then pass the configuration object +and the security object declared earlier + +135 +00:07:40,260 --> 00:07:42,696 +to initialize an API object. + +136 +00:07:42,729 --> 00:07:47,334 +API objects contain asynchronous methods +that allow you to talk to iCloud. + +137 +00:07:47,367 --> 00:07:51,104 +You've now completed the steps +to use CKTool JS in your scripts. + +138 +00:07:51,138 --> 00:07:54,508 +Let's learn about how you +can use CKTool JS + +139 +00:07:54,541 --> 00:07:56,944 +to manage your container's schema. + +140 +00:07:56,977 --> 00:08:00,214 +In my app, +I want to store information such as + +141 +00:08:00,247 --> 00:08:02,616 +an American dime issued in 2007. + +142 +00:08:02,649 --> 00:08:05,285 +This coin +is composed of copper and nickel + +143 +00:08:05,319 --> 00:08:08,889 +and the value stamped on it +is 1/10th of an American dollar. + +144 +00:08:08,922 --> 00:08:11,258 +After thinking +about how to store this data, + +145 +00:08:11,291 --> 00:08:12,860 +I decided to store information + +146 +00:08:12,893 --> 00:08:14,461 +about the coin's composition + +147 +00:08:14,494 --> 00:08:16,129 +as records separate from + +148 +00:08:16,163 --> 00:08:18,332 +the other details about the coin. + +149 +00:08:18,365 --> 00:08:20,367 +So I store the copper percentage +for the dime + +150 +00:08:20,400 --> 00:08:22,002 +and its nickel percentage + +151 +00:08:22,035 --> 00:08:23,804 +in separate records. + +152 +00:08:24,371 --> 00:08:26,173 +I identified two record types + +153 +00:08:26,206 --> 00:08:28,308 +that I want in my container's schema. + +154 +00:08:28,342 --> 00:08:31,111 +Coins, which stores its country reference, + +155 +00:08:31,144 --> 00:08:33,080 +issue year, and nominal value. + +156 +00:08:33,113 --> 00:08:35,148 +And a Components record type + +157 +00:08:35,182 --> 00:08:37,451 +that stores a reference to a coin +it describes + +158 +00:08:37,484 --> 00:08:40,220 +and the material +and its percentage in the coin. + +159 +00:08:40,254 --> 00:08:43,023 +Now that I've determined +the schema for my app, + +160 +00:08:43,056 --> 00:08:46,827 +I can create a text file in +CloudKit Schema Language to describe it. + +161 +00:08:46,860 --> 00:08:50,931 +The convention is to use the +.ckdb extension for your schema file. + +162 +00:08:52,099 --> 00:08:54,368 +For more information about +CloudKit Schema Language, + +163 +00:08:54,401 --> 00:08:59,540 +refer to "Integrating a Text-Based Schema +into Your Workflow" documentation article. + +164 +00:09:00,941 --> 00:09:05,712 +The schema file you create for your +container can be applied using CKTool JS. + +165 +00:09:05,746 --> 00:09:07,581 +Before you apply a new schema, + +166 +00:09:07,614 --> 00:09:10,184 +you'll typically reset the +container's development schema + +167 +00:09:10,217 --> 00:09:12,386 +to match the one in production. + +168 +00:09:12,419 --> 00:09:15,289 +You can do this with the +resetToProduction method. + +169 +00:09:15,322 --> 00:09:18,525 +You call this method by passing +the defaultArgs object + +170 +00:09:18,559 --> 00:09:20,861 +that you declared earlier. + +171 +00:09:20,894 --> 00:09:24,665 +If your schema isn't in production, +all record types are deleted. + +172 +00:09:24,698 --> 00:09:26,800 +Otherwise, +this will revert the development schema + +173 +00:09:26,834 --> 00:09:29,536 +to the state +of the production environment. + +174 +00:09:29,570 --> 00:09:33,607 +Note that this is an asynchronous call, +so this method returns a promise object. + +175 +00:09:35,509 --> 00:09:37,611 +CKTool JS has methods that let you + +176 +00:09:37,644 --> 00:09:39,980 +export and import your container's schema. + +177 +00:09:40,013 --> 00:09:43,250 +The exportSchema and importSchema +methods let you do this + +178 +00:09:43,283 --> 00:09:46,253 +and are named from +the perspective of the container. + +179 +00:09:46,286 --> 00:09:48,889 +So you download a schema to be exported + +180 +00:09:48,922 --> 00:09:51,425 +from the container using exportSchema, + +181 +00:09:51,458 --> 00:09:53,894 +and you upload a schema to be imported + +182 +00:09:53,927 --> 00:09:56,597 +into the container using importSchema. + +183 +00:09:56,630 --> 00:09:58,265 +Together, these allow you + +184 +00:09:58,298 --> 00:10:00,400 +to manage your schema's evolution. + +185 +00:10:01,668 --> 00:10:05,405 +You can create a help function +to apply the schema to the container. + +186 +00:10:05,439 --> 00:10:08,876 +First, import the File object +from CKTool JS, + +187 +00:10:08,909 --> 00:10:12,479 +then import the fs and +path modules from Node.js. + +188 +00:10:12,513 --> 00:10:16,149 +Now define an asynchronous +function that will do the following: + +189 +00:10:16,183 --> 00:10:19,786 +It reads the schema file's +contents into a Node.js buffer. + +190 +00:10:19,820 --> 00:10:23,390 +It creates +a CKTool JS File instance for upload. + +191 +00:10:23,423 --> 00:10:28,395 +Finally, it uploads the file's contents +to the server using importSchema. + +192 +00:10:28,428 --> 00:10:31,231 +Note that the defaultArgs object +that was declared earlier + +193 +00:10:31,265 --> 00:10:33,901 +is passed to importSchema. + +194 +00:10:33,934 --> 00:10:35,502 +Now you can put it together. + +195 +00:10:35,536 --> 00:10:39,072 +Because resetToProduction and the +helper function used to import a schema + +196 +00:10:39,106 --> 00:10:42,943 +are asynchronous, you need to ensure +that they run in the correct order. + +197 +00:10:42,976 --> 00:10:45,345 +To do that you chain the promises. + +198 +00:10:45,379 --> 00:10:48,482 +If an error occurs, +the promise will reject. + +199 +00:10:48,515 --> 00:10:52,286 +In addition to the management +capabilities that CKTool JS has, + +200 +00:10:52,319 --> 00:10:55,789 +it also allows you to work +with reading and writing data. + +201 +00:10:55,822 --> 00:10:59,593 +Field values that are used in +CKTool JS records + +202 +00:10:59,626 --> 00:11:01,929 +are type and ranged checked +on the client side + +203 +00:11:01,962 --> 00:11:03,864 +before they're sent to the server. + +204 +00:11:03,897 --> 00:11:07,000 +If the value passed in +is not the right kind of value + +205 +00:11:07,034 --> 00:11:09,369 +or is outside of +the allowed range of the value, + +206 +00:11:09,403 --> 00:11:11,271 +an exception will be thrown. + +207 +00:11:11,305 --> 00:11:14,808 +For large numbers that can't be +represented natively in JavaScript, + +208 +00:11:14,842 --> 00:11:18,412 +there are CKTool JS types +that are used instead. + +209 +00:11:18,445 --> 00:11:22,683 +For example, +to coerce a number to a CKTool JS Int64, + +210 +00:11:22,716 --> 00:11:25,986 +you use the toInt64 function. + +211 +00:11:26,019 --> 00:11:28,455 +To coerce a number to +a Double floating point value, + +212 +00:11:28,488 --> 00:11:30,924 +you use the toDouble function. + +213 +00:11:30,958 --> 00:11:33,594 +If you're writing TypeScript, +the compiler will flag + +214 +00:11:33,627 --> 00:11:37,064 +incorrect value type usages if these +coercion functions are not used. + +215 +00:11:38,332 --> 00:11:40,868 +Field values in CKTool JS records + +216 +00:11:40,901 --> 00:11:44,171 +are created using field +value factory functions. + +217 +00:11:44,204 --> 00:11:47,708 +For a coin issued in 2007, +I'd pass that value + +218 +00:11:47,741 --> 00:11:51,979 +to the makeRecordFieldValue.int64 +factory function + +219 +00:11:52,012 --> 00:11:56,049 +in order to create a record field value +that contains an Int64. + +220 +00:11:56,083 --> 00:11:59,620 +In general, if a factory function +can't create a record field value + +221 +00:11:59,653 --> 00:12:02,489 +from the value passed in, +it'll throw an exception. + +222 +00:12:03,790 --> 00:12:06,627 +Here, I've created an object +to hold common values + +223 +00:12:06,660 --> 00:12:09,263 +that I send to methods +that work with records. + +224 +00:12:09,296 --> 00:12:12,866 +Since containerId, environment, +databaseType and zoneName + +225 +00:12:12,900 --> 00:12:17,271 +are often required, I'm including those +in this databaseArgs object. + +226 +00:12:17,304 --> 00:12:21,275 +To query for records, +I use the queryRecords method. + +227 +00:12:21,308 --> 00:12:24,444 +To make this easier, +I create a helper function + +228 +00:12:24,478 --> 00:12:28,949 +that finds a country matching +its unique 3 character ISO code. + +229 +00:12:28,982 --> 00:12:32,519 +In this case, I pass the contents +of the databaseArgs object, + +230 +00:12:32,553 --> 00:12:35,956 +in addition to +a body that contains the query. + +231 +00:12:35,989 --> 00:12:39,793 +For the query object, +I'm specifying the recordType value + +232 +00:12:39,826 --> 00:12:42,496 +as well as a single filter object. + +233 +00:12:42,529 --> 00:12:46,433 +The filter object describes a +query where the country's isoCode3 + +234 +00:12:46,466 --> 00:12:50,037 +is equal to the one +this function is seeking. + +235 +00:12:50,070 --> 00:12:52,639 +If successful, +the collection of found records + +236 +00:12:52,673 --> 00:12:56,410 +will be in the +response.result.records property. + +237 +00:12:56,443 --> 00:12:58,679 +I return the +first object from this collection. + +238 +00:12:59,847 --> 00:13:04,351 +To make converting raw values into +field values that createRecord can use, + +239 +00:13:04,384 --> 00:13:08,689 +I have a helper function called +makeCoinFieldValues to do this. + +240 +00:13:08,722 --> 00:13:12,993 +For each raw property for my coin +that I want to convert to field values, + +241 +00:13:13,026 --> 00:13:16,830 +I call the appropriate +RecordFieldValue factory function. + +242 +00:13:16,864 --> 00:13:20,234 +For the country field, however, +I need to create a reference. + +243 +00:13:20,267 --> 00:13:23,070 +I use the passed-in country +record name to make a reference + +244 +00:13:23,103 --> 00:13:26,340 +from this coin record +to the corresponding country record. + +245 +00:13:27,608 --> 00:13:31,612 +Here, I create a helper function +that takes coin record field values + +246 +00:13:31,645 --> 00:13:34,982 +and sends the +createRecord request to the server. + +247 +00:13:35,015 --> 00:13:37,918 +In this function, +I'm passing the content of databaseArgs + +248 +00:13:37,951 --> 00:13:40,354 +declared earlier and a body. + +249 +00:13:40,387 --> 00:13:43,957 +The body dictionary contains +the recordType and field values. + +250 +00:13:43,991 --> 00:13:47,694 +If successful, +response.result.record is returned. + +251 +00:13:48,862 --> 00:13:51,999 +Before calling the helper function, +I need to fetch the correct + +252 +00:13:52,032 --> 00:13:55,435 +country record that will be +referenced from this coin. + +253 +00:13:55,469 --> 00:13:58,772 +I use the country query function +defined earlier. + +254 +00:13:58,805 --> 00:14:03,810 +I then call coinCreateRecord by +passing it a field values dictionary + +255 +00:14:03,844 --> 00:14:06,880 +which is created with the +makeCoinFieldValues helper function + +256 +00:14:06,914 --> 00:14:08,682 +that I wrote earlier. + +257 +00:14:08,715 --> 00:14:11,952 +The raw coin values are passed +to that helper function. + +258 +00:14:11,985 --> 00:14:15,622 +This will asynchronously create +the record and return the new record. + +259 +00:14:17,157 --> 00:14:20,327 +To update a record, +use the updateRecord method. + +260 +00:14:20,360 --> 00:14:24,431 +I create a helper function that +updates a coin matching the record name + +261 +00:14:24,464 --> 00:14:27,201 +with the fields passed to this helper. + +262 +00:14:27,234 --> 00:14:32,139 +I then call updateRecord with the contents +of the databaseArgs object, recordName, + +263 +00:14:32,172 --> 00:14:36,977 +and a body that contains the record type +and the new record's field values. + +264 +00:14:37,010 --> 00:14:39,346 +If successful, +the updated record will be in the + +265 +00:14:39,379 --> 00:14:44,084 +response.result.record property, +which I return from the helper function. + +266 +00:14:45,319 --> 00:14:49,056 +To update the coin record I created +earlier, I call this helper function + +267 +00:14:49,089 --> 00:14:52,759 +passing in its record name +and field values to update. + +268 +00:14:52,793 --> 00:14:55,696 +The field values are created +with makeCoinFieldValues. + +269 +00:14:57,030 --> 00:15:02,002 +To delete a record, I call the async +deleteRecord method on the API object. + +270 +00:15:02,035 --> 00:15:04,338 +I pass in the contents of +the databaseArgs object + +271 +00:15:04,371 --> 00:15:07,040 +as well as the recordName +of the record to delete. + +272 +00:15:07,074 --> 00:15:10,310 +I hope you've enjoyed +getting to know CKTool JS. + +273 +00:15:10,344 --> 00:15:12,145 +Try it out for yourself: + +274 +00:15:12,179 --> 00:15:16,016 +Configure CKTool JS for your automation +and tooling purposes. + +275 +00:15:16,049 --> 00:15:17,818 +Reset and import your schemas + +276 +00:15:17,851 --> 00:15:20,487 +as well as read and write +your data using JavaScript. + +277 +00:15:20,521 --> 00:15:24,157 +For usage of CKTool JS in +continuous integration scenarios, + +278 +00:15:24,191 --> 00:15:27,060 +check out +the CloudKit sample repo on GitHub. + +279 +00:15:27,094 --> 00:15:32,633 +And for more detailed documentation, check +out CKTool JS on developer.apple.com. + +280 +00:15:32,666 --> 00:15:37,037 +Thanks for joining me today, +and enjoy the rest of WWDC22. + diff --git a/eng/2022 Session 10117 Enhance voice communication with Push to Talk en.srt b/eng/2022 Session 10117 Enhance voice communication with Push to Talk en.srt new file mode 100644 index 0000000..a46cbb1 --- /dev/null +++ b/eng/2022 Session 10117 Enhance voice communication with Push to Talk en.srt @@ -0,0 +1,2172 @@ +1 +00:00:00,000 --> 00:00:03,403 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,403 --> 00:00:09,476 +♪ + +3 +00:00:09,476 --> 00:00:11,111 +Kevin Ferrell: Hi, +my name is Kevin, + +4 +00:00:11,111 --> 00:00:14,348 +and I'm an engineer working on +the new PushToTalk framework, + +5 +00:00:14,348 --> 00:00:16,950 +which enables a walkie-talkie +system experience + +6 +00:00:16,950 --> 00:00:19,052 +for apps on iOS. + +7 +00:00:19,052 --> 00:00:21,188 +Later I'll be joined +by my colleague Trevor + +8 +00:00:21,188 --> 00:00:24,057 +to talk about how you can +enhance voice communication + +9 +00:00:24,057 --> 00:00:27,261 +in your apps +with this new framework. + +10 +00:00:27,261 --> 00:00:29,496 +First, I'll introduce +the PushToTalk framework + +11 +00:00:29,496 --> 00:00:32,432 +and explain how it fits +into your app. + +12 +00:00:32,432 --> 00:00:34,935 +Next, we'll go over +how to configure your app + +13 +00:00:34,935 --> 00:00:37,237 +for PushToTalk. + +14 +00:00:37,237 --> 00:00:38,972 +After that, +Trevor will walk through + +15 +00:00:38,972 --> 00:00:43,010 +how to transmit and receive +audio using the framework. + +16 +00:00:43,010 --> 00:00:46,013 +Finally, Trevor will wrap up +with best practices + +17 +00:00:46,013 --> 00:00:48,582 +for enhancing +the Push To Talk user experience + +18 +00:00:48,582 --> 00:00:52,486 +while preserving battery life +for your users. + +19 +00:00:52,486 --> 00:00:54,588 +I'll get started +by introducing key features + +20 +00:00:54,588 --> 00:00:57,457 +of the new PushToTalk framework. + +21 +00:00:57,457 --> 00:00:59,693 +The PushToTalk framework +enables you to build + +22 +00:00:59,693 --> 00:01:03,196 +a new class of audio +communication app on iOS + +23 +00:01:03,196 --> 00:01:07,768 +that provides a walkie-talkie +style experience for your users. + +24 +00:01:07,768 --> 00:01:09,603 +Push To Talk apps have many uses + +25 +00:01:09,603 --> 00:01:12,739 +in fields where rapid +communication is essential + +26 +00:01:12,739 --> 00:01:15,976 +such as health care +and emergency services. + +27 +00:01:15,976 --> 00:01:18,412 +To provide a great +Push To Talk experience, + +28 +00:01:18,412 --> 00:01:20,347 +users need a way +to quickly access + +29 +00:01:20,347 --> 00:01:23,784 +audio transmission features +while also being able to see + +30 +00:01:23,784 --> 00:01:26,186 +who is responding to them. + +31 +00:01:26,186 --> 00:01:28,221 +At the same time, +a Push To Talk app + +32 +00:01:28,221 --> 00:01:30,824 +must be power efficient +to ensure that users + +33 +00:01:30,824 --> 00:01:35,195 +can maintain all-day battery +life while using the app. + +34 +00:01:35,195 --> 00:01:38,098 +The PushToTalk framework +provides you with APIs + +35 +00:01:38,098 --> 00:01:39,833 +to utilize a system UI + +36 +00:01:39,833 --> 00:01:42,436 +that users can access +anywhere on the system + +37 +00:01:42,436 --> 00:01:45,572 +without having to directly +launch your app. + +38 +00:01:45,572 --> 00:01:48,508 +The system UI allows a user +to quickly activate + +39 +00:01:48,508 --> 00:01:51,111 +an audio transmission, +which will launch your app + +40 +00:01:51,111 --> 00:01:55,615 +in the background to record +and stream audio to your server. + +41 +00:01:55,615 --> 00:01:58,251 +The system provides transparency +to users + +42 +00:01:58,251 --> 00:01:59,586 +by showing who's speaking + +43 +00:01:59,586 --> 00:02:02,522 +when your app plays audio +from your server. + +44 +00:02:02,522 --> 00:02:04,624 +The PushToTalk framework +accomplishes this + +45 +00:02:04,624 --> 00:02:07,627 +by introducing +a new push notification type + +46 +00:02:07,627 --> 00:02:11,965 +that notifies your app when new +audio is available for playback. + +47 +00:02:11,965 --> 00:02:14,368 +When your app receives +this notification, + +48 +00:02:14,368 --> 00:02:15,902 +it is launched in the background + +49 +00:02:15,902 --> 00:02:19,272 +so that it can stream +and play audio. + +50 +00:02:19,272 --> 00:02:22,075 +The PushToTalk framework +is designed to be compatible + +51 +00:02:22,075 --> 00:02:25,012 +with existing end-to-end +communication solutions + +52 +00:02:25,012 --> 00:02:27,147 +and backend infrastructure. + +53 +00:02:27,147 --> 00:02:28,648 +If you've already implemented + +54 +00:02:28,648 --> 00:02:30,851 +a Push To Talk workflow +in your app, + +55 +00:02:30,851 --> 00:02:32,386 +it should be easy for you +to integrate + +56 +00:02:32,386 --> 00:02:36,590 +the PushToTalk framework +into your existing code. + +57 +00:02:36,590 --> 00:02:38,525 +The framework allows +your app to implement + +58 +00:02:38,525 --> 00:02:41,261 +its own audio encoding +and streaming process + +59 +00:02:41,261 --> 00:02:43,897 +to transmit audio between users. + +60 +00:02:43,897 --> 00:02:46,400 +This provides flexibility +in how audio transmission + +61 +00:02:46,400 --> 00:02:49,536 +is handled by your app +and enables compatibility + +62 +00:02:49,536 --> 00:02:51,905 +with other platforms. + +63 +00:02:51,905 --> 00:02:53,907 +Finally, many Push To Talk apps + +64 +00:02:53,907 --> 00:02:56,043 +rely on wireless +Bluetooth accessories + +65 +00:02:56,043 --> 00:02:59,146 +to trigger audio recording +and transmission. + +66 +00:02:59,146 --> 00:03:02,015 +Your apps can continue to +integrate with these accessories + +67 +00:03:02,015 --> 00:03:04,151 +using the CoreBluetooth +framework + +68 +00:03:04,151 --> 00:03:07,654 +and can trigger audio recording +in PushToTalk. + +69 +00:03:07,654 --> 00:03:10,123 +If you are building your first +Push To Talk app, + +70 +00:03:10,123 --> 00:03:12,426 +keep these integration +considerations in mind + +71 +00:03:12,426 --> 00:03:15,262 +as you begin architecting +your code. + +72 +00:03:15,262 --> 00:03:17,164 +Before we begin +walking through the code + +73 +00:03:17,164 --> 00:03:18,999 +for the new +PushToTalk framework, + +74 +00:03:18,999 --> 00:03:21,435 +we want to demonstrate +how the Push To Talk experience + +75 +00:03:21,435 --> 00:03:24,771 +can work in your app. + +76 +00:03:24,771 --> 00:03:26,239 +Trevor and I have built +a demo app + +77 +00:03:26,239 --> 00:03:28,508 +to show how PushToTalk works. + +78 +00:03:28,508 --> 00:03:30,343 +To start, +I'll tap the join button + +79 +00:03:30,343 --> 00:03:32,179 +to connect to +a Push To Talk session, + +80 +00:03:32,179 --> 00:03:36,316 +which we call a channel. + +81 +00:03:36,316 --> 00:03:37,617 +Once I'm joined to the channel, + +82 +00:03:37,617 --> 00:03:41,588 +I can transmit and receive audio +to other members of the channel. + +83 +00:03:41,588 --> 00:03:42,989 +Trevor and a few +of our colleagues + +84 +00:03:42,989 --> 00:03:44,658 +have joined the same channel + +85 +00:03:44,658 --> 00:03:47,627 +so that we can communicate +throughout the day. + +86 +00:03:47,627 --> 00:03:49,963 +I can transmit audio +directly from the app + +87 +00:03:49,963 --> 00:03:52,566 +using the microphone button, +but the PushToTalk framework + +88 +00:03:52,566 --> 00:03:54,868 +allows me to access +the transmit feature + +89 +00:03:54,868 --> 00:03:59,072 +from anywhere in the system. + +90 +00:03:59,072 --> 00:04:00,974 +When there is an active +Push To Talk channel, + +91 +00:04:00,974 --> 00:04:03,643 +a blue pill will appear +in the status bar. + +92 +00:04:03,643 --> 00:04:08,949 +Tapping that pill +shows the system UI. + +93 +00:04:08,949 --> 00:04:11,918 +The system UI displays the name +of the Push To Talk channel + +94 +00:04:11,918 --> 00:04:14,721 +that I've joined +and an image provided by the app + +95 +00:04:14,721 --> 00:04:18,892 +to help users +quickly identify the channel. + +96 +00:04:18,892 --> 00:04:20,560 +I can transmit audio +to the channel + +97 +00:04:20,560 --> 00:04:22,729 +by pressing and holding +the Talk button + +98 +00:04:22,729 --> 00:04:24,998 +and then waiting for +the system chime to indicate + +99 +00:04:24,998 --> 00:04:27,267 +that I can begin speaking. + +100 +00:04:29,069 --> 00:04:32,072 +[CHIME] +Hey, Trevor. + +101 +00:04:32,072 --> 00:04:37,444 +Are you ready to cover +your WWDC slides? Over. + +102 +00:04:37,444 --> 00:04:39,746 +Trevor Sheridan: When my device +received Kevin's message, + +103 +00:04:39,746 --> 00:04:42,716 +it displayed a notice that +contained his name and image, + +104 +00:04:42,716 --> 00:04:48,788 +providing transparency into +who I'm receiving messages from. + +105 +00:04:48,788 --> 00:04:50,724 +Once I launch the system UI, + +106 +00:04:50,724 --> 00:04:52,859 +I can quickly respond +to Kevin's message + +107 +00:04:52,859 --> 00:04:56,196 +or leave the channel without +having to stop what I'm doing. + +108 +00:04:56,196 --> 00:05:01,001 +I don't want to leave Kevin +waiting, so I'll reply now. + +109 +00:05:01,001 --> 00:05:04,104 +[CHIME] Hey, Kevin. +I'll be ready in a few minutes. + +110 +00:05:04,104 --> 00:05:07,240 +Over. + +111 +00:05:07,240 --> 00:05:09,809 +Kevin: The PushToTalk system UI +can also be accessed + +112 +00:05:09,809 --> 00:05:13,213 +from the Lock Screen so a user +can receive and respond + +113 +00:05:13,213 --> 00:05:16,316 +to messages without +having to unlock their device. + +114 +00:05:21,288 --> 00:05:27,160 +[CHIME] OK, see you soon! +Over. [CHIME] + +115 +00:05:27,160 --> 00:05:29,429 +Now that we've discussed +how PushToTalk works, + +116 +00:05:29,429 --> 00:05:33,099 +we'll review how to integrate +the framework in your own app. + +117 +00:05:33,099 --> 00:05:35,335 +There are a few modifications +that you need to make + +118 +00:05:35,335 --> 00:05:39,439 +to your Xcode project to support +the PushToTalk framework. + +119 +00:05:39,439 --> 00:05:43,176 +First, you need to add the new +Push To Talk background mode. + +120 +00:05:43,176 --> 00:05:45,378 +This enables your app +to run in the background + +121 +00:05:45,378 --> 00:05:48,982 +when responding +to Push To Talk events. + +122 +00:05:48,982 --> 00:05:52,018 +Next, you must also add +the Push To Talk capability + +123 +00:05:52,018 --> 00:05:55,689 +to your app to enable +the framework features. + +124 +00:05:55,689 --> 00:05:59,292 +The push notification capability +is required to allow APNS + +125 +00:05:59,292 --> 00:06:01,027 +to wake your app +in the background + +126 +00:06:01,027 --> 00:06:04,097 +to play received audio. + +127 +00:06:04,097 --> 00:06:06,833 +Finally, your app must request +recording permission + +128 +00:06:06,833 --> 00:06:10,136 +from the user and include +a microphone purpose string + +129 +00:06:10,136 --> 00:06:12,973 +in its Info.plist file. + +130 +00:06:12,973 --> 00:06:16,076 +Now we're ready to begin +integrating the code. + +131 +00:06:16,076 --> 00:06:18,211 +The first step in +the Push To Talk workflow + +132 +00:06:18,211 --> 00:06:19,946 +is to join a channel. + +133 +00:06:19,946 --> 00:06:21,881 +The channel represents +and describes + +134 +00:06:21,881 --> 00:06:24,784 +the Push To Talk session +to the system. + +135 +00:06:24,784 --> 00:06:28,388 +Your app interacts with channels +through a channel manager. + +136 +00:06:28,388 --> 00:06:31,424 +The channel manager is the +primary interface for your app + +137 +00:06:31,424 --> 00:06:33,860 +to join channels +and perform actions + +138 +00:06:33,860 --> 00:06:37,230 +like transmitting +and receiving audio. + +139 +00:06:37,230 --> 00:06:39,866 +When you join a channel, +the Push To Talk system UI + +140 +00:06:39,866 --> 00:06:43,903 +becomes available and your app +receives an APNS device token + +141 +00:06:43,903 --> 00:06:47,207 +that can be used throughout +the life of the channel. + +142 +00:06:47,207 --> 00:06:49,943 +You must join a channel before +you can begin transmitting + +143 +00:06:49,943 --> 00:06:52,579 +and receiving audio. + +144 +00:06:52,579 --> 00:06:54,881 +The first step is to create +a channel manager + +145 +00:06:54,881 --> 00:06:57,450 +using the class initializer. + +146 +00:06:57,450 --> 00:06:59,452 +This initializer requires +that you provide + +147 +00:06:59,452 --> 00:07:04,057 +a channel manager delegate and +a channel restoration delegate. + +148 +00:07:04,057 --> 00:07:05,992 +Multiple calls +to the initializer + +149 +00:07:05,992 --> 00:07:08,795 +result in the same shared +instance being returned, + +150 +00:07:08,795 --> 00:07:11,131 +but we recommend that +you store the channel manager + +151 +00:07:11,131 --> 00:07:13,233 +in an instance variable. + +152 +00:07:13,233 --> 00:07:15,635 +It is important to initialize +your channel manager + +153 +00:07:15,635 --> 00:07:17,871 +as soon as possible +during app start up + +154 +00:07:17,871 --> 00:07:19,572 +in your ApplicationDelegate's + +155 +00:07:19,572 --> 00:07:22,776 +didFinishLaunchingWithOptions +method. + +156 +00:07:22,776 --> 00:07:26,212 +This ensures that the channel +manager is initialized quickly + +157 +00:07:26,212 --> 00:07:28,415 +so that existing channels +can be restored + +158 +00:07:28,415 --> 00:07:30,750 +and push notifications +will be delivered to your app + +159 +00:07:30,750 --> 00:07:33,653 +when it launches +in the background. + +160 +00:07:33,653 --> 00:07:35,722 +Now we're ready +to join a channel. + +161 +00:07:35,722 --> 00:07:37,557 +When someone joins +a channel from your app, + +162 +00:07:37,557 --> 00:07:40,527 +you must provide a UUID +to identify the channel + +163 +00:07:40,527 --> 00:07:44,297 +and a descriptor that describes +the channel to the system. + +164 +00:07:44,297 --> 00:07:47,701 +The same UUID will be used when +interacting with the manager + +165 +00:07:47,701 --> 00:07:50,103 +throughout the life +of this channel. + +166 +00:07:50,103 --> 00:07:53,340 +The descriptor includes +a name and an image. + +167 +00:07:53,340 --> 00:07:56,142 +Providing a unique image +to represent the channel + +168 +00:07:56,142 --> 00:07:58,878 +makes it easier for your users +to identify the channel + +169 +00:07:58,878 --> 00:08:01,614 +when interacting +with the system. + +170 +00:08:01,614 --> 00:08:04,818 +Your app joins a channel by +calling the requestJoin method + +171 +00:08:04,818 --> 00:08:06,720 +on the channel manager. + +172 +00:08:06,720 --> 00:08:09,022 +Note that it is only possible +to join a channel + +173 +00:08:09,022 --> 00:08:12,125 +when your app is running +in the foreground. + +174 +00:08:12,125 --> 00:08:15,028 +When your app joins a channel, +the channel manager delegate's + +175 +00:08:15,028 --> 00:08:18,198 +didJoinChannel method +will be called. + +176 +00:08:18,198 --> 00:08:20,367 +This delegate method +is your indication + +177 +00:08:20,367 --> 00:08:23,069 +that your app +has joined the channel. + +178 +00:08:23,069 --> 00:08:24,471 +In addition, +the delegate's + +179 +00:08:24,471 --> 00:08:27,607 +receivedEphemeralPushToken +method will be called + +180 +00:08:27,607 --> 00:08:30,276 +with the APNS push token +that can be used + +181 +00:08:30,276 --> 00:08:34,180 +to send Push To Talk +notifications to this device. + +182 +00:08:34,180 --> 00:08:35,849 +This token will only be active + +183 +00:08:35,849 --> 00:08:38,685 +for the life of +the Push To Talk channel. + +184 +00:08:38,685 --> 00:08:41,888 +Keep in mind that APNS +push tokens are variable length + +185 +00:08:41,888 --> 00:08:45,859 +and that you should not hardcode +their length into your app. + +186 +00:08:45,859 --> 00:08:49,129 +It is possible for the +channel join request to fail, + +187 +00:08:49,129 --> 00:08:51,030 +such as when attempting +to join a channel + +188 +00:08:51,030 --> 00:08:53,833 +when another channel +is already active. + +189 +00:08:53,833 --> 00:08:56,636 +If this occurs, +the error handler will be called + +190 +00:08:56,636 --> 00:09:00,373 +and the error will indicate +the reason for the failure. + +191 +00:09:00,373 --> 00:09:01,941 +When the user leaves a channel, + +192 +00:09:01,941 --> 00:09:04,944 +the delegate's didLeaveChannel +method will be called. + +193 +00:09:04,944 --> 00:09:07,313 +Your user may leave the channel +as a result of either + +194 +00:09:07,313 --> 00:09:10,250 +your app requesting to leave +programmatically + +195 +00:09:10,250 --> 00:09:12,552 +or the user can tap +the Leave Channel button + +196 +00:09:12,552 --> 00:09:14,320 +in the system UI. + +197 +00:09:14,320 --> 00:09:16,656 +The channel manager delegate +has an associated + +198 +00:09:16,656 --> 00:09:19,626 +LeaveChannel error-handling +method that will be called + +199 +00:09:19,626 --> 00:09:23,229 +if the request to leave +the channel fails. + +200 +00:09:23,229 --> 00:09:25,632 +PushToTalk supports +restoring previous channels + +201 +00:09:25,632 --> 00:09:28,201 +whenever your app is relaunched +after being terminated + +202 +00:09:28,201 --> 00:09:30,770 +or after a device reboot. + +203 +00:09:30,770 --> 00:09:33,006 +In order for the system +to accomplish this, + +204 +00:09:33,006 --> 00:09:37,076 +you must provide a channel +descriptor to update the system. + +205 +00:09:37,076 --> 00:09:39,145 +Here we have a helper method +that will fetch + +206 +00:09:39,145 --> 00:09:43,516 +our cached channel descriptor +in our restoration delegate. + +207 +00:09:43,516 --> 00:09:45,852 +In order to keep the system +responsive, + +208 +00:09:45,852 --> 00:09:48,721 +you should return from this +method as quickly as possible + +209 +00:09:48,721 --> 00:09:51,891 +and should not perform any +long-running or blocking tasks + +210 +00:09:51,891 --> 00:09:53,359 +such as a network request + +211 +00:09:53,359 --> 00:09:56,229 +to retrieve +your channel descriptor. + +212 +00:09:56,229 --> 00:09:59,165 +Throughout the lifecycle +of your Push To Talk session, + +213 +00:09:59,165 --> 00:10:01,067 +you should provide updates +to the descriptor + +214 +00:10:01,067 --> 00:10:04,904 +whenever information +about the channel changes. + +215 +00:10:04,904 --> 00:10:06,973 +You should also inform +the system about changes + +216 +00:10:06,973 --> 00:10:09,576 +to your network connection +or server availability + +217 +00:10:09,576 --> 00:10:12,312 +using the service status object. + +218 +00:10:12,312 --> 00:10:15,548 +Here we're updating +the descriptor for the channel. + +219 +00:10:15,548 --> 00:10:16,716 +You can call this method + +220 +00:10:16,716 --> 00:10:20,653 +whenever you need to update +the channel name or image. + +221 +00:10:20,653 --> 00:10:22,856 +In this example, +we are providing an update + +222 +00:10:22,856 --> 00:10:25,258 +to the system to indicate +that the app's connection + +223 +00:10:25,258 --> 00:10:28,461 +to its sever is in +a reconnecting state. + +224 +00:10:28,461 --> 00:10:30,730 +This updates +the system UI accordingly + +225 +00:10:30,730 --> 00:10:32,832 +and prevents the user +from transmitting audio + +226 +00:10:32,832 --> 00:10:37,103 +if the service status is +connecting or disconnected. + +227 +00:10:37,103 --> 00:10:38,938 +Once a connection +is reestablished, + +228 +00:10:38,938 --> 00:10:42,242 +you should update +the service status to "ready." + +229 +00:10:42,242 --> 00:10:44,844 +Now let's review +how to send and receive audio + +230 +00:10:44,844 --> 00:10:46,779 +using PushToTalk. + +231 +00:10:46,779 --> 00:10:49,582 +[CHIME] Trevor, are you ready +to walk through + +232 +00:10:49,582 --> 00:10:51,451 +the rest of the API? + +233 +00:10:51,451 --> 00:10:52,652 +Over. + +234 +00:10:55,088 --> 00:10:59,325 +[CHIME] Trevor: Yep. +Send them over. Over. + +235 +00:10:59,325 --> 00:11:00,326 +[CHIME] + +236 +00:11:01,861 --> 00:11:03,463 +Now that we've seen +how to configure + +237 +00:11:03,463 --> 00:11:05,098 +the PushToTalk framework, + +238 +00:11:05,098 --> 00:11:09,669 +let's explore how to transmit +and receive audio. + +239 +00:11:09,669 --> 00:11:12,171 +A core capability +of the PushToTalk framework + +240 +00:11:12,171 --> 00:11:16,209 +is to allow your users +to quickly transmit audio. + +241 +00:11:16,209 --> 00:11:18,044 +Users can begin +audio transmission + +242 +00:11:18,044 --> 00:11:19,579 +from within your app, + +243 +00:11:19,579 --> 00:11:22,615 +or from the system +Push To Talk UI. + +244 +00:11:22,615 --> 00:11:25,151 +If your app supports +Bluetooth accessories + +245 +00:11:25,151 --> 00:11:26,753 +through CoreBluetooth, + +246 +00:11:26,753 --> 00:11:29,656 +you can also begin transmission +in the background + +247 +00:11:29,656 --> 00:11:33,927 +in response to a peripheral's +characteristic change. + +248 +00:11:33,927 --> 00:11:36,663 +When transmitting, +the PushToTalk framework + +249 +00:11:36,663 --> 00:11:39,032 +unlocks the device's microphone + +250 +00:11:39,032 --> 00:11:41,668 +and activates +your app's audio session + +251 +00:11:41,668 --> 00:11:44,771 +to enable audio recording +in the background. + +252 +00:11:44,771 --> 00:11:48,107 +Let's review this process +in detail. + +253 +00:11:48,107 --> 00:11:51,311 +To begin transmission from +within your app, you can call + +254 +00:11:51,311 --> 00:11:54,814 +the requestBeginTransmitting +function. + +255 +00:11:54,814 --> 00:11:57,951 +This can be called whenever your +app is running in the foreground + +256 +00:11:57,951 --> 00:11:59,719 +or when reacting +to a change + +257 +00:11:59,719 --> 00:12:03,489 +of a Bluetooth peripheral's +characteristic. + +258 +00:12:03,489 --> 00:12:06,426 +If the system is not able +to begin transmitting, + +259 +00:12:06,426 --> 00:12:07,660 +the delegate's + +260 +00:12:07,660 --> 00:12:11,197 +failedToBeginTransmitting +InChannel method will be called + +261 +00:12:11,197 --> 00:12:13,466 +with the reason +for the failure. + +262 +00:12:13,466 --> 00:12:17,737 +For example, if the user has +an ongoing cellular call active, + +263 +00:12:17,737 --> 00:12:22,408 +they will not be able to begin +a Push To Talk transmission. + +264 +00:12:22,408 --> 00:12:23,943 +To stop transmitting, + +265 +00:12:23,943 --> 00:12:28,247 +call the channel manager's +stopTransmitting method. + +266 +00:12:28,247 --> 00:12:31,784 +To handle failures when +attempting to stop transmitting, + +267 +00:12:31,784 --> 00:12:34,887 +such as when the user +was not in a transmitting state, + +268 +00:12:34,887 --> 00:12:37,757 +the channel manager delegate +has an associated + +269 +00:12:37,757 --> 00:12:41,527 +failedToStopTransmitting +InChannel method. + +270 +00:12:41,527 --> 00:12:44,897 +Whether you begin transmission +from within your app + +271 +00:12:44,897 --> 00:12:48,067 +or if the user starts +from the system UI, + +272 +00:12:48,067 --> 00:12:50,470 +your channel manager delegate +will receive + +273 +00:12:50,470 --> 00:12:53,673 +a "Did begin transmitting" +callback. + +274 +00:12:53,673 --> 00:12:56,576 +The transmission source +will be passed to the method + +275 +00:12:56,576 --> 00:12:58,544 +and indicate whether +the transmission + +276 +00:12:58,544 --> 00:13:00,346 +was started +from the system UI, + +277 +00:13:00,346 --> 00:13:04,684 +the programmatic API, +or a hardware button event. + +278 +00:13:04,684 --> 00:13:06,486 +Once transmission begins, + +279 +00:13:06,486 --> 00:13:10,123 +the system will activate +the audio session for your app. + +280 +00:13:10,123 --> 00:13:13,660 +This is your signal that +you can now begin recording. + +281 +00:13:13,660 --> 00:13:19,432 +You should not start or stop +your own audio session. + +282 +00:13:19,432 --> 00:13:23,102 +When transmission ends, +your channel manager delegate + +283 +00:13:23,102 --> 00:13:25,171 +will receive +the end transmission + +284 +00:13:25,171 --> 00:13:29,108 +and audio session +deactivation events. + +285 +00:13:29,108 --> 00:13:32,311 +Keep in mind that while +your transmission is active, + +286 +00:13:32,311 --> 00:13:36,015 +your audio session may be +interrupted by other sources, + +287 +00:13:36,015 --> 00:13:38,851 +such as phone +and FaceTime calls + +288 +00:13:38,851 --> 00:13:42,455 +for which you need to handle +within your app. + +289 +00:13:42,455 --> 00:13:45,224 +The PushToTalk framework +also allows your app + +290 +00:13:45,224 --> 00:13:48,061 +to receive and play audio +from other users + +291 +00:13:48,061 --> 00:13:49,962 +while in the background. + +292 +00:13:49,962 --> 00:13:53,766 +This process relies on a new +Apple Push Notification type + +293 +00:13:53,766 --> 00:13:56,936 +that is specific +to Push To Talk apps. + +294 +00:13:56,936 --> 00:13:59,739 +When your Push To Talk server +has new audio + +295 +00:13:59,739 --> 00:14:02,642 +for a user to receive, +it should send the user + +296 +00:14:02,642 --> 00:14:06,446 +a Push To Talk notification +using the device push token + +297 +00:14:06,446 --> 00:14:09,582 +you received +when joining the channel. + +298 +00:14:09,582 --> 00:14:12,952 +When the push notification +is received by your app, + +299 +00:14:12,952 --> 00:14:16,422 +it must report an active speaker +to the framework, + +300 +00:14:16,422 --> 00:14:18,191 +which will cause the system +to activate + +301 +00:14:18,191 --> 00:14:23,296 +your app's audio session +and allow it to begin playback. + +302 +00:14:23,296 --> 00:14:25,031 +The new Push To Talk +notification + +303 +00:14:25,031 --> 00:14:28,568 +is similar to other +notification types on iOS + +304 +00:14:28,568 --> 00:14:31,437 +and there are specific +attributes that you must set + +305 +00:14:31,437 --> 00:14:34,941 +to enable delivery +to your Push To Talk app. + +306 +00:14:34,941 --> 00:14:39,612 +First, the APNS push type +must be set to "pushtotalk" + +307 +00:14:39,612 --> 00:14:41,948 +in the request header. + +308 +00:14:41,948 --> 00:14:45,351 +Next, the APNS topic header +must be set + +309 +00:14:45,351 --> 00:14:47,553 +to your app's bundle identifier + +310 +00:14:47,553 --> 00:14:52,291 +with a ".voip-ptt" suffix +appended to the end. + +311 +00:14:52,291 --> 00:14:55,128 +The push payload +can contain custom keys + +312 +00:14:55,128 --> 00:14:57,296 +that are relevant to your app, + +313 +00:14:57,296 --> 00:14:59,398 +such the name +of an active speaker + +314 +00:14:59,398 --> 00:15:02,335 +or an indication +that the session has ended + +315 +00:15:02,335 --> 00:15:05,538 +and the app should leave +the Push To Talk channel. + +316 +00:15:05,538 --> 00:15:10,009 +The body of the "aps" property +can be left blank. + +317 +00:15:10,009 --> 00:15:13,012 +Additionally, like other +communication-related + +318 +00:15:13,012 --> 00:15:14,213 +push types, + +319 +00:15:14,213 --> 00:15:18,618 +Push To Talk payloads should +have an APNS priority of 10 + +320 +00:15:18,618 --> 00:15:20,820 +to request immediate delivery + +321 +00:15:20,820 --> 00:15:25,625 +and an APNS expiration of zero +to prevent older pushes + +322 +00:15:25,625 --> 00:15:29,562 +that are no longer relevant +from being delivered later. + +323 +00:15:29,562 --> 00:15:32,698 +When your server sends +a Push To Talk notification, + +324 +00:15:32,698 --> 00:15:35,134 +your app will be started +in the background + +325 +00:15:35,134 --> 00:15:39,172 +and the incoming push +delegate method will be called. + +326 +00:15:39,172 --> 00:15:41,107 +When you receive +a push payload, + +327 +00:15:41,107 --> 00:15:43,943 +you will need to construct +a push result type + +328 +00:15:43,943 --> 00:15:46,479 +to indicate what action +should be performed + +329 +00:15:46,479 --> 00:15:50,016 +as a result +of the push notification. + +330 +00:15:50,016 --> 00:15:52,819 +To indicate that +a remote user is speaking, + +331 +00:15:52,819 --> 00:15:54,554 +return a push result +that includes + +332 +00:15:54,554 --> 00:15:57,123 +the active participant's +information, + +333 +00:15:57,123 --> 00:16:01,127 +including their name +and an optional image. + +334 +00:16:01,127 --> 00:16:02,428 +This will cause the system + +335 +00:16:02,428 --> 00:16:05,097 +to set the active participant +on the channel + +336 +00:16:05,097 --> 00:16:08,734 +and indicate that the channel +is in receive mode. + +337 +00:16:08,734 --> 00:16:11,571 +The system will then activate +your audio session, + +338 +00:16:11,571 --> 00:16:13,906 +and call the +didActivateaudioSession + +339 +00:16:13,906 --> 00:16:15,341 +delegate method. + +340 +00:16:15,341 --> 00:16:17,543 +You should wait for this method +to be called + +341 +00:16:17,543 --> 00:16:19,846 +before beginning playback. + +342 +00:16:19,846 --> 00:16:21,848 +If your server decides +that a user + +343 +00:16:21,848 --> 00:16:24,283 +should no longer +be joined to a channel, + +344 +00:16:24,283 --> 00:16:27,053 +it may indicate this +in the push payload, + +345 +00:16:27,053 --> 00:16:31,557 +for which you can return +a leaveChannel push result. + +346 +00:16:31,557 --> 00:16:33,593 +It's important to note +that you should return + +347 +00:16:33,593 --> 00:16:36,028 +a PTPushResult from this method +as quickly as possible + +348 +00:16:36,028 --> 00:16:40,433 +and not block the thread. + +349 +00:16:40,433 --> 00:16:43,202 +If you are attempting to set +the active remote participant + +350 +00:16:43,202 --> 00:16:45,872 +and do not have their image +stored locally, + +351 +00:16:45,872 --> 00:16:48,608 +you can return +an activeRemoteParticipant + +352 +00:16:48,608 --> 00:16:50,910 +with only the speaker's name. + +353 +00:16:50,910 --> 00:16:53,746 +Then download their image +on a separate thread, + +354 +00:16:53,746 --> 00:16:55,781 +and once the image is retrieved, + +355 +00:16:55,781 --> 00:16:57,950 +update the +activeRemoteParticipant + +356 +00:16:57,950 --> 00:17:00,786 +by calling +setActiveRemoteParticipant + +357 +00:17:00,786 --> 00:17:03,322 +on the channel manager. + +358 +00:17:03,322 --> 00:17:05,725 +When the remote participant +has finished speaking, + +359 +00:17:05,725 --> 00:17:08,694 +you should set the +activeRemoteParticipant to nil. + +360 +00:17:08,694 --> 00:17:11,664 +This indicates to the system +that you are no longer + +361 +00:17:11,664 --> 00:17:13,766 +receiving audio +on the channel + +362 +00:17:13,766 --> 00:17:17,770 +and that the system should +deactivate your audio session. + +363 +00:17:17,770 --> 00:17:20,940 +This will also update +the system Push To Talk UI + +364 +00:17:20,940 --> 00:17:24,277 +and allow the user +to transmit again. + +365 +00:17:24,277 --> 00:17:25,711 +Now that we've covered +the basics + +366 +00:17:25,711 --> 00:17:28,781 +of how to integrate PushToTalk +into your app, + +367 +00:17:28,781 --> 00:17:30,783 +let's review some best practices + +368 +00:17:30,783 --> 00:17:32,818 +for optimizing +the user experience + +369 +00:17:32,818 --> 00:17:35,488 +and preserving battery life. + +370 +00:17:37,290 --> 00:17:39,992 +The PushToTalk framework +provides a system UI + +371 +00:17:39,992 --> 00:17:42,395 +for users to begin +a transmission + +372 +00:17:42,395 --> 00:17:46,065 +and leave a channel from +anywhere within the system. + +373 +00:17:46,065 --> 00:17:50,002 +Additionally, it is flexible +and allows you to implement + +374 +00:17:50,002 --> 00:17:52,672 +your own custom +Push To Talk UI + +375 +00:17:52,672 --> 00:17:56,142 +when your app +is in the foreground. + +376 +00:17:56,142 --> 00:17:57,343 +The PushToTalk framework + +377 +00:17:57,343 --> 00:18:00,146 +utilizes shared system +resources. + +378 +00:18:00,146 --> 00:18:02,548 +Only one Push To Talk app +can be active + +379 +00:18:02,548 --> 00:18:04,383 +on the system at a time, + +380 +00:18:04,383 --> 00:18:07,620 +and Push To Talk communication +will be superseded + +381 +00:18:07,620 --> 00:18:11,757 +by cellular, FaceTime, +and VoIP calls. + +382 +00:18:11,757 --> 00:18:14,460 +Your app should handle +PushToTalk failures gracefully + +383 +00:18:14,460 --> 00:18:18,831 +and respond accordingly. + +384 +00:18:18,831 --> 00:18:21,200 +As mentioned earlier, +the PushToTalk framework + +385 +00:18:21,200 --> 00:18:23,336 +handles activating +and deactivating + +386 +00:18:23,336 --> 00:18:25,638 +your audio session for you. + +387 +00:18:25,638 --> 00:18:27,707 +However, +you should still configure + +388 +00:18:27,707 --> 00:18:30,943 +your audio session's category +to play and record + +389 +00:18:30,943 --> 00:18:33,980 +when your app launches. + +390 +00:18:33,980 --> 00:18:36,382 +The system provides +built-in sound effects + +391 +00:18:36,382 --> 00:18:39,618 +to alert the user that +the microphone is activated + +392 +00:18:39,618 --> 00:18:42,388 +and deactivated +when transmitting. + +393 +00:18:42,388 --> 00:18:44,190 +You should not provide +your own sound effects + +394 +00:18:44,190 --> 00:18:46,692 +for these events. + +395 +00:18:46,692 --> 00:18:49,395 +It is also important +for your app to monitor + +396 +00:18:49,395 --> 00:18:53,132 +and respond to AVAudioSession +notifications, + +397 +00:18:53,132 --> 00:18:54,900 +such as session interruptions, + +398 +00:18:54,900 --> 00:18:57,470 +route changes, +and failures. + +399 +00:18:57,470 --> 00:18:59,805 +Your Push To Talk app +can be affected + +400 +00:18:59,805 --> 00:19:01,774 +by these audio session events + +401 +00:19:01,774 --> 00:19:05,611 +just like any other audio app +on the system. + +402 +00:19:05,611 --> 00:19:07,613 +It's important +to optimize your app + +403 +00:19:07,613 --> 00:19:09,582 +to preserve battery life. + +404 +00:19:09,582 --> 00:19:11,784 +The PushToTalk framework +provides your app + +405 +00:19:11,784 --> 00:19:13,886 +with background runtime +when needed, + +406 +00:19:13,886 --> 00:19:17,189 +such as when transmitting +and receiving audio. + +407 +00:19:17,189 --> 00:19:19,458 +When your app is not being used +by the user, + +408 +00:19:19,458 --> 00:19:24,030 +it will be suspended by the +system to preserve battery life. + +409 +00:19:24,030 --> 00:19:26,632 +You should not activate +or deactivate + +410 +00:19:26,632 --> 00:19:28,567 +your own audio sessions. + +411 +00:19:28,567 --> 00:19:32,038 +The system will handle +audio session activation for you + +412 +00:19:32,038 --> 00:19:34,340 +at the appropriate times. + +413 +00:19:34,340 --> 00:19:36,142 +This ensures +that your audio session + +414 +00:19:36,142 --> 00:19:38,944 +has the proper priority +within the system + +415 +00:19:38,944 --> 00:19:43,382 +and can be suspended +when it is not being used. + +416 +00:19:43,382 --> 00:19:45,084 +Your Push To Talk server + +417 +00:19:45,084 --> 00:19:47,620 +should use the new +push notification type + +418 +00:19:47,620 --> 00:19:51,157 +to alert your app that there is +new audio to be played, + +419 +00:19:51,157 --> 00:19:54,560 +or that the Push To Talk +session has ended. + +420 +00:19:54,560 --> 00:19:56,529 +For more information +about improving + +421 +00:19:56,529 --> 00:19:59,065 +the battery life in your app, +refer to the + +422 +00:19:59,065 --> 00:20:03,536 +"Power down: Improve battery +consumption" session. + +423 +00:20:03,536 --> 00:20:05,905 +When your Push To Talk app +is in the background + +424 +00:20:05,905 --> 00:20:09,275 +and the app is not +transmitting or receiving audio, + +425 +00:20:09,275 --> 00:20:11,944 +it will be suspended +by the system. + +426 +00:20:11,944 --> 00:20:13,479 +When your app is suspended, + +427 +00:20:13,479 --> 00:20:17,249 +any network connections +will be disconnected. + +428 +00:20:17,249 --> 00:20:19,785 +You should consider adopting +Network.framework + +429 +00:20:19,785 --> 00:20:22,688 +and QUIC to reduce +the steps needed + +430 +00:20:22,688 --> 00:20:25,558 +to establish a secure +TLS connection + +431 +00:20:25,558 --> 00:20:28,894 +and improve initial +connection speed. + +432 +00:20:28,894 --> 00:20:31,864 +Network.framework +has built-in support for QUIC. + +433 +00:20:31,864 --> 00:20:32,898 +Check out the + +434 +00:20:32,898 --> 00:20:36,902 +"Reduce networking delays for +a more responsive app" session + +435 +00:20:36,902 --> 00:20:40,673 +for more information +about how to use QUIC. + +436 +00:20:40,673 --> 00:20:43,042 +The PushToTalk framework +enables you to build + +437 +00:20:43,042 --> 00:20:44,877 +robust and power-efficient + +438 +00:20:44,877 --> 00:20:47,513 +walkie-talkie style +communication experiences + +439 +00:20:47,513 --> 00:20:49,448 +within your apps. + +440 +00:20:49,448 --> 00:20:51,650 +If you already have an app +that implements + +441 +00:20:51,650 --> 00:20:54,787 +a walk-talkie style experience +on iOS, + +442 +00:20:54,787 --> 00:20:57,123 +you should begin updating +your existing app + +443 +00:20:57,123 --> 00:20:59,892 +to use the new API. + +444 +00:20:59,892 --> 00:21:02,094 +If you're implementing +a new walkie-talkie app, + +445 +00:21:02,094 --> 00:21:05,998 +you should use +the PushToTalk framework now. + +446 +00:21:05,998 --> 00:21:07,967 +Finally, +please submit feedback + +447 +00:21:07,967 --> 00:21:10,035 +as you begin testing +the new framework + +448 +00:21:10,035 --> 00:21:12,638 +and integrating it +with your app. + +449 +00:21:12,638 --> 00:21:15,808 +Thank you +and have a great WWDC! + +450 +00:21:15,808 --> 00:21:17,343 +Over and out! + +451 +00:21:17,343 --> 00:21:21,814 +♪ + diff --git a/eng/2022 Session 10119 Optimize your use of Core Data and CloudKit en.srt b/eng/2022 Session 10119 Optimize your use of Core Data and CloudKit en.srt new file mode 100644 index 0000000..5bd9f9d --- /dev/null +++ b/eng/2022 Session 10119 Optimize your use of Core Data and CloudKit en.srt @@ -0,0 +1,1973 @@ +1 +00:00:01,401 --> 00:00:07,407 +[spacey music] + +2 +00:00:09,309 --> 00:00:13,580 +Hi, I'm Nick Gillett, an engineer +here at Apple on the Core Data team. + +3 +00:00:14,147 --> 00:00:18,218 +In this session, I'll show you how +to use our developer tools to learn more + +4 +00:00:18,252 --> 00:00:21,722 +about your applications that use +NSPersistentCloudKitContainer. + +5 +00:00:21,755 --> 00:00:25,492 +We'll begin with a detailed look +at how to explore applications + +6 +00:00:25,526 --> 00:00:27,761 +in a productive and educational way. + +7 +00:00:28,795 --> 00:00:33,800 +Then, we'll use some of my favorite tools +to analyze how applications behave. + +8 +00:00:33,834 --> 00:00:37,037 +And finally, we'll look +at how you can provide detailed, + +9 +00:00:37,070 --> 00:00:41,408 +actionable feedback about your experiences +with NSPersistentCloudKitContainer. + +10 +00:00:42,509 --> 00:00:46,313 +I like to think of engineering +a bit like the water cycle. + +11 +00:00:46,346 --> 00:00:49,983 +Typically, I begin working on a feature +by exploring the space + +12 +00:00:50,017 --> 00:00:51,852 +that feature exists in. + +13 +00:00:51,885 --> 00:00:57,057 +Then, based on the things I learn, +I use a combination of tools and tests + +14 +00:00:57,090 --> 00:00:59,993 +to analyze my work +in a reproducible environment. + +15 +00:01:00,794 --> 00:01:05,098 +Finally, I review the results +with my peers and coworkers + +16 +00:01:05,132 --> 00:01:06,400 +and collect their feedback. + +17 +00:01:07,134 --> 00:01:10,938 +The goal of this cycle +is to durably capture the things I learn + +18 +00:01:10,971 --> 00:01:12,506 +as I work. + +19 +00:01:12,539 --> 00:01:16,877 +Apple platforms include a great selection +of tools like Xcode, + +20 +00:01:16,910 --> 00:01:21,949 +Instruments, and XCTest +that I use to capture what I learn. + +21 +00:01:21,982 --> 00:01:24,885 +Those tools also make it possible +to collect a wealth + +22 +00:01:24,918 --> 00:01:28,989 +of diagnostic information I can use +to provide actionable feedback. + +23 +00:01:30,858 --> 00:01:34,628 +This session references a lot of knowledge +from years past. + +24 +00:01:34,661 --> 00:01:37,731 +I've discussed +NSPersistentCloudKitContainer + +25 +00:01:37,764 --> 00:01:41,168 +and the Core Data CloudKit Sample +application I'll be showing today + +26 +00:01:41,201 --> 00:01:46,840 +in detail in the sessions "Build Apps that +share data through CloudKit and Core Data" + +27 +00:01:46,874 --> 00:01:51,211 +and in the session +"Using Core Data with CloudKit." + +28 +00:01:51,245 --> 00:01:56,483 +I'll also demonstrate how to use +Xcode and Instruments to run tests + +29 +00:01:56,517 --> 00:02:00,254 +and the Device organizer +to capture data from devices. + +30 +00:02:00,287 --> 00:02:03,490 +If you need to, +I recommend you review the sessions + +31 +00:02:03,524 --> 00:02:05,392 +"Getting Started With Instruments" + +32 +00:02:05,425 --> 00:02:09,763 +and "Diagnose performance issues +with the Xcode Organizer" + +33 +00:02:09,796 --> 00:02:14,735 +to learn more about these two important +pieces of the toolchain. + +34 +00:02:14,768 --> 00:02:19,873 +All right, let's get started with +the first part of the cycle, exploration. + +35 +00:02:19,907 --> 00:02:24,178 +For me, the primary goal +of exploration is to learn. + +36 +00:02:24,211 --> 00:02:27,814 +I want to challenge and verify +all of the assumptions I have + +37 +00:02:27,848 --> 00:02:29,816 +about how an application will work. + +38 +00:02:30,617 --> 00:02:34,087 +I might ask: +What happens if I tap this button? + +39 +00:02:34,121 --> 00:02:39,493 +Does NSPersistentCloudKitContainer sync +when I save data to a persistent store? + +40 +00:02:39,526 --> 00:02:43,630 +Does an application run out of memory +when working with a large data set? + +41 +00:02:44,498 --> 00:02:48,669 +From Core Data's perspective, +all of these questions are influenced + +42 +00:02:48,702 --> 00:02:51,038 +by the data an application works with. + +43 +00:02:51,071 --> 00:02:56,743 +For example, the Core Data CloudKit +sample application uses this data model. + +44 +00:02:58,245 --> 00:03:03,383 +It manages a set of posts which have +some text fields for a title and content. + +45 +00:03:04,284 --> 00:03:07,688 +Posts can be related to attachments, +generally images, + +46 +00:03:07,721 --> 00:03:08,889 +which can be quite large. + +47 +00:03:10,324 --> 00:03:14,728 +The ImageData is therefore stored +across a to-one relationship + +48 +00:03:14,761 --> 00:03:17,698 +so that it can be loaded on demand. + +49 +00:03:17,731 --> 00:03:21,768 +And I'm going to focus +my exploration on that data set, + +50 +00:03:21,802 --> 00:03:24,872 +specifically what happens +to the sample application + +51 +00:03:24,905 --> 00:03:29,443 +as I change the shape, +structure, and variance of that data. + +52 +00:03:30,677 --> 00:03:36,216 +Since its release, the sample application +has included a built-in way to explore it. + +53 +00:03:36,250 --> 00:03:40,487 +The Generate 1000 Posts button +does exactly what it says on the label. + +54 +00:03:41,355 --> 00:03:44,157 +When tapped, it generates +a sample data set + +55 +00:03:44,191 --> 00:03:47,060 +of 1,000 posts with a short title. + +56 +00:03:47,094 --> 00:03:50,931 +The Posts table view easily handles +this level of data. + +57 +00:03:50,964 --> 00:03:54,835 +So the next question I would ask is, +how can I explore a data set + +58 +00:03:54,868 --> 00:03:58,138 +of a different shape or size +in this application? + +59 +00:03:59,006 --> 00:04:02,376 +The Generate 1000 Posts button +runs what I like to call + +60 +00:04:02,409 --> 00:04:05,078 +an algorithmic data generator. + +61 +00:04:05,112 --> 00:04:08,982 +Algorithmic data generators +follow a set of predetermined rules + +62 +00:04:09,016 --> 00:04:11,852 +like "insert 1000 objects" + +63 +00:04:11,885 --> 00:04:17,224 +or "make sure that every field has +a value, or that no fields have values." + +64 +00:04:17,858 --> 00:04:21,395 +We, as it turns out, +are also data generators. + +65 +00:04:21,428 --> 00:04:25,499 +We can hand-craft +specific data sets in code, in SQL, + +66 +00:04:25,532 --> 00:04:28,869 +or by interacting +directly with an application, + +67 +00:04:28,902 --> 00:04:31,705 +and these generated data sets +can be preserved + +68 +00:04:31,738 --> 00:04:33,740 +for later use or analysis. + +69 +00:04:34,508 --> 00:04:39,213 +To explore a larger data set, +I can define a new data generator, + +70 +00:04:39,246 --> 00:04:42,850 +the LargeDataGenerator, +and give it a single method, + +71 +00:04:42,883 --> 00:04:46,086 +generateData, to build my new data set. + +72 +00:04:46,119 --> 00:04:49,890 +With just two for loops, +I can generate a set of 60 posts + +73 +00:04:49,923 --> 00:04:54,094 +that each have 11 image attachments +associated with them. + +74 +00:04:54,127 --> 00:04:57,231 +That's 660 images in total. + +75 +00:04:57,264 --> 00:05:00,601 +At an average size +of 10-20 megabytes per image, + +76 +00:05:00,634 --> 00:05:06,073 +the generated data set +consumes almost 10GB of data. + +77 +00:05:06,106 --> 00:05:10,143 +With such a simple interface, +data generators are easily invoked + +78 +00:05:10,177 --> 00:05:12,246 +in tests like this one. + +79 +00:05:12,279 --> 00:05:17,184 +This single line of code generates +over 10GB of representative data + +80 +00:05:17,217 --> 00:05:18,685 +for this test to use. + +81 +00:05:20,721 --> 00:05:24,391 +Additionally, we can build +validation methods in the tests + +82 +00:05:24,424 --> 00:05:27,828 +that verify the data generator +behaves correctly, + +83 +00:05:27,861 --> 00:05:31,899 +like asserting that each post +does indeed get 11 image attachments. + +84 +00:05:33,433 --> 00:05:37,604 +Of course, this wouldn't be a talk +about NSPersistentCloudKitContainer + +85 +00:05:37,638 --> 00:05:39,673 +if we didn't sync this data. + +86 +00:05:39,706 --> 00:05:42,409 +So let's craft a new test to do just that. + +87 +00:05:43,911 --> 00:05:49,183 +The first thing I'll need is an instance +of NSPersistentCloudKitContainer to use. + +88 +00:05:49,216 --> 00:05:52,753 +I've created a helper method +to make that easy. + +89 +00:05:52,786 --> 00:05:55,422 +Next, I use the LargeDataGenerator + +90 +00:05:55,455 --> 00:05:58,559 +to populate the container +with my desired data set. + +91 +00:05:59,526 --> 00:06:03,997 +And finally, I wait for the container +to finish exporting the data. + +92 +00:06:04,031 --> 00:06:07,467 +In this specific test, +I wait for up to 20 minutes + +93 +00:06:07,501 --> 00:06:09,736 +to give the large data set time to upload. + +94 +00:06:11,338 --> 00:06:15,108 +The eagle eyed among you may have noticed +that this test appears + +95 +00:06:15,142 --> 00:06:18,378 +to be doing a lot of waiting +for different types of events. + +96 +00:06:18,412 --> 00:06:21,381 +Here, when I create the container, + +97 +00:06:21,415 --> 00:06:23,750 +I wait for the container +to finish setting up. + +98 +00:06:24,551 --> 00:06:29,323 +And here, I use a helper method +I wrote to create XCTestExpectations + +99 +00:06:29,356 --> 00:06:31,558 +for an export event from the container. + +100 +00:06:32,159 --> 00:06:33,493 +Let's look at that in detail. + +101 +00:06:34,494 --> 00:06:38,165 +This method takes a desired event type +and an instance + +102 +00:06:38,198 --> 00:06:41,568 +of NSPersistentCloudKitContainer +as an argument. + +103 +00:06:41,602 --> 00:06:45,939 +It creates one expectation +for each persistent store in the container + +104 +00:06:45,973 --> 00:06:50,511 +using XCTestCase's +expectationForNotification method + +105 +00:06:50,544 --> 00:06:55,749 +to observe NSPersistentCloudKitContainer's +eventChanged notification. + +106 +00:06:55,782 --> 00:06:57,951 +In the notification handler block, + +107 +00:06:57,985 --> 00:06:59,953 +I verify that the incoming event + +108 +00:06:59,987 --> 00:07:01,388 +is of the correct type + +109 +00:07:01,421 --> 00:07:04,358 +for the specific store +this expectation is for, + +110 +00:07:04,391 --> 00:07:06,727 +and that it's finished +by checking endDate + +111 +00:07:06,760 --> 00:07:08,195 +is not equal to nil. + +112 +00:07:08,962 --> 00:07:10,731 +By using this technique, + +113 +00:07:10,764 --> 00:07:14,501 +we can strongly associate +points of control in our tests + +114 +00:07:14,535 --> 00:07:17,471 +with events +from NSPersistentCloudKitContainer. + +115 +00:07:17,504 --> 00:07:24,011 +Back in my test, I add a new container +to import the data that was just exported. + +116 +00:07:24,044 --> 00:07:26,446 +This technique uses a trick. + +117 +00:07:26,480 --> 00:07:27,848 +It creates a new instance + +118 +00:07:27,881 --> 00:07:31,919 +of NSPersistentCloudKitContainer +with empty store files. + +119 +00:07:31,952 --> 00:07:33,954 +This allows the test to take advantage + +120 +00:07:33,987 --> 00:07:37,157 +of NSPersistentCloudKitContainer's +first-time import + +121 +00:07:37,191 --> 00:07:42,296 +to explore what happens when all +of this data is downloaded by a device. + +122 +00:07:42,329 --> 00:07:45,966 +Now, tests are great, +but sometimes I want to feel + +123 +00:07:45,999 --> 00:07:48,702 +how a data set behaves in an application. + +124 +00:07:48,735 --> 00:07:52,806 +To do that, I can bind data generators +to a user interface, + +125 +00:07:52,840 --> 00:07:55,375 +as we have done in the sample application. + +126 +00:07:56,076 --> 00:07:59,479 +When I tap the Generate Large Data +button, I can watch + +127 +00:07:59,513 --> 00:08:02,482 +the data generator populate the data set. + +128 +00:08:02,516 --> 00:08:06,186 +On a second device, I can watch +the table view populate + +129 +00:08:06,220 --> 00:08:12,226 +as NSPersistentCloudKitContainer makes +progress downloading the generated data. + +130 +00:08:12,259 --> 00:08:16,697 +Tapping on an individual post +allows me to see the attachments download + +131 +00:08:16,730 --> 00:08:18,799 +and populate incrementally, + +132 +00:08:18,832 --> 00:08:22,102 +just as they would +for a user of this application. + +133 +00:08:22,669 --> 00:08:26,440 +This specific user interface +is driven by an alert controller. + +134 +00:08:27,107 --> 00:08:30,644 +The LargeDataGenerator's simple +interface makes it easy + +135 +00:08:30,677 --> 00:08:34,781 +to add a new alert action +with just these two lines of code. + +136 +00:08:34,815 --> 00:08:38,018 +It's clear, concise, +and easily understood. + +137 +00:08:39,753 --> 00:08:43,290 +In this section, we've explored +the behavior of an application + +138 +00:08:43,323 --> 00:08:45,726 +using the concept of a data generator. + +139 +00:08:46,593 --> 00:08:51,098 +Data generators can be driven in +our applications any way we choose, + +140 +00:08:51,131 --> 00:08:55,102 +whether that be by tests +or custom UI, as I've demonstrated, + +141 +00:08:55,135 --> 00:08:57,571 +or by something +like a command line argument, + +142 +00:08:57,604 --> 00:09:01,375 +or anything else that happens to work +for your specific use case. + +143 +00:09:01,408 --> 00:09:05,112 +Now that we know how to populate +an application with data, + +144 +00:09:05,145 --> 00:09:08,849 +we're ready to analyze how +that changes application behavior. + +145 +00:09:08,882 --> 00:09:12,586 +In this section, we'll learn +about some tools and techniques + +146 +00:09:12,619 --> 00:09:14,555 +to analyze how an application behaves + +147 +00:09:14,588 --> 00:09:15,822 +with a large data set. + +148 +00:09:16,890 --> 00:09:21,562 +Specifically, we'll use Instruments +to analyze the time and memory complexity + +149 +00:09:21,595 --> 00:09:24,665 +of the data set +created by the LargeDataGenerator. + +150 +00:09:25,465 --> 00:09:29,069 +Then, we'll look at the wealth +of information available to us + +151 +00:09:29,102 --> 00:09:30,571 +in the system logs. + +152 +00:09:30,604 --> 00:09:35,075 +There we can find a record of activity +from NSPersistentCloudKitContainer, + +153 +00:09:35,108 --> 00:09:39,713 +CloudKit, the system scheduler, +and from push notifications. + +154 +00:09:39,746 --> 00:09:42,449 +Let's get stared with Instruments. + +155 +00:09:42,482 --> 00:09:46,420 +One reason I love tests is +that Xcode makes it easy + +156 +00:09:46,453 --> 00:09:49,156 +to analyze the behavior of a test. + +157 +00:09:49,189 --> 00:09:52,860 +In my test case, I can right-click +on the test disclosure + +158 +00:09:52,893 --> 00:09:55,229 +in the gutter and select Profile. + +159 +00:09:55,262 --> 00:09:59,433 +Xcode will build the tests +and then automatically launch instruments. + +160 +00:10:00,234 --> 00:10:02,769 +I can double-click +the Time Profiler instrument + +161 +00:10:02,803 --> 00:10:05,939 +to examine where my test spends time +doing work. + +162 +00:10:07,508 --> 00:10:11,612 +When I click the record button, +Instruments will launch the application + +163 +00:10:11,645 --> 00:10:14,681 +and execute the selected test. + +164 +00:10:14,715 --> 00:10:18,785 +This test appears to be taking +quite a while to run. + +165 +00:10:18,819 --> 00:10:20,787 +Let's skip ahead and see why. + +166 +00:10:21,688 --> 00:10:24,758 +Instruments has already selected +the main thread, + +167 +00:10:24,791 --> 00:10:28,795 +and on the right side, I can see +the heaviest stack trace of the test run. + +168 +00:10:30,063 --> 00:10:31,865 +Let's make that a little easier to read. + +169 +00:10:36,203 --> 00:10:38,172 +There we go. + +170 +00:10:38,205 --> 00:10:42,876 +Now, if I scroll to the bottom, +I can see the LargeDataGenerator is + +171 +00:10:42,910 --> 00:10:45,546 +spending a lot of time +generating thumbnails. + +172 +00:10:45,579 --> 00:10:48,582 +How would we decide +if this is a bug or a feature? + +173 +00:10:50,050 --> 00:10:53,687 +In the LargeDataGenerator, +I have this line of code that generates + +174 +00:10:53,720 --> 00:10:56,623 +a new thumbnail for each attachment. + +175 +00:10:56,657 --> 00:11:01,361 +However, I know from the application's +data model that thumbnails are special. + +176 +00:11:01,395 --> 00:11:05,432 +They're computed on demand +from the related imageData. + +177 +00:11:05,465 --> 00:11:09,803 +That means this line is unnecessary, +and my data generator is wasting + +178 +00:11:09,837 --> 00:11:11,605 +a lot of time on them. + +179 +00:11:11,638 --> 00:11:14,074 +So I can just remove it. + +180 +00:11:14,107 --> 00:11:16,643 +Let's see how that changes +the performance of the test. + +181 +00:11:17,578 --> 00:11:20,781 +After rebuilding the app +with the updated data generator, + +182 +00:11:20,814 --> 00:11:23,617 +I can rerun the test in Instruments. + +183 +00:11:23,650 --> 00:11:29,223 +And honestly I don't see much of change, + +184 +00:11:29,256 --> 00:11:32,326 +but after a few more seconds, +the test completes. + +185 +00:11:32,359 --> 00:11:35,195 +That's a lot faster than the previous run. + +186 +00:11:35,229 --> 00:11:37,998 +Let's see +where the test spent most of its time. + +187 +00:11:43,504 --> 00:11:46,907 +In the right drawer, I now see +that the heaviest stack trace + +188 +00:11:46,940 --> 00:11:49,977 +is saving images to the persistent store, + +189 +00:11:50,010 --> 00:11:54,081 +and that's exactly what I would expect +for a test that manages this much data. + +190 +00:11:55,582 --> 00:11:59,720 +That one change reduced the runtime +of the generateData test + +191 +00:11:59,753 --> 00:12:02,322 +from this to this. + +192 +00:12:02,356 --> 00:12:05,959 +It executes in one tenth the time. + +193 +00:12:05,993 --> 00:12:09,496 +Analyzing tests in this way +doesn't always uncover bugs, + +194 +00:12:09,530 --> 00:12:11,198 +Sometimes we just learn more + +195 +00:12:11,231 --> 00:12:13,400 +about where an application +is spending time + +196 +00:12:13,433 --> 00:12:16,003 +when working with a specific data set. + +197 +00:12:16,036 --> 00:12:18,472 +But either way, though, +it's valuable learning. + +198 +00:12:19,473 --> 00:12:23,043 +So that's how the Time Profiler instrument +can help explore + +199 +00:12:23,076 --> 00:12:26,413 +where an application +spends time with a data set. + +200 +00:12:26,446 --> 00:12:31,618 +Now, because of the size of this data set, +I'm also curious how much memory + +201 +00:12:31,652 --> 00:12:33,720 +the test uses. + +202 +00:12:33,754 --> 00:12:37,558 +So let's give it a run +using the Allocations instrument. + +203 +00:12:37,591 --> 00:12:41,328 +I'll use Xcode to launch +Instruments to profile my test. + +204 +00:12:42,062 --> 00:12:44,765 +Instead of selecting +the Time Profiler instrument, + +205 +00:12:44,798 --> 00:12:47,301 +I'll double-click Allocations... + +206 +00:12:51,205 --> 00:12:52,873 +And then click Record. + +207 +00:13:02,916 --> 00:13:05,719 +Even though +this test is executing quickly, + +208 +00:13:05,752 --> 00:13:10,057 +it’s using a lot of memory, +over 10GB, in fact. + +209 +00:13:10,090 --> 00:13:13,026 +This tells me that nearly +the entire data set + +210 +00:13:13,060 --> 00:13:15,495 +is being kept in memory +during the test run. + +211 +00:13:15,529 --> 00:13:17,264 +Let's find out why. + +212 +00:13:19,199 --> 00:13:22,102 +I can select +a range of allocations to look at. + +213 +00:13:22,135 --> 00:13:26,573 +In the bottom pane, I can see that +there are a number of large allocations. + +214 +00:13:27,407 --> 00:13:31,211 +I can dig into those +by clicking this disclosure, + +215 +00:13:31,245 --> 00:13:33,647 +and then click +on one of the large data blobs + +216 +00:13:33,680 --> 00:13:36,250 +that's been allocated for the test. + +217 +00:13:36,283 --> 00:13:41,288 +This specific blob was allocated +but not freed for almost two seconds. + +218 +00:13:41,321 --> 00:13:45,292 +That's an eternity in test time. +Why was it alive so long? + +219 +00:13:46,894 --> 00:13:49,930 +I can explore that by expanding +the stack trace on the right. + +220 +00:13:53,834 --> 00:13:57,404 +From experience, the allocation +and deallocation stack trace + +221 +00:13:57,437 --> 00:14:00,807 +tell me that this object +was faulted by CoreData + +222 +00:14:00,841 --> 00:14:05,145 +and then released when the managed object +context finished its work. + +223 +00:14:05,179 --> 00:14:09,283 +That's usually an indication that +the object was retained by a fetch, + +224 +00:14:09,316 --> 00:14:12,486 +an autoreleasepool, +or an object in the test. + +225 +00:14:15,155 --> 00:14:19,126 +The problematic section of code +is here in my verifier. + +226 +00:14:19,159 --> 00:14:22,563 +I load an image +from an attachment and verify it. + +227 +00:14:22,596 --> 00:14:26,600 +However, this keeps the attachment +and the associated image data + +228 +00:14:26,633 --> 00:14:28,969 +registered +with the managed object context. + +229 +00:14:29,736 --> 00:14:33,106 +There are a number of ways +we could try to resolve this. + +230 +00:14:33,140 --> 00:14:37,177 +For example, in a table view, +we could use a batched fetch + +231 +00:14:37,211 --> 00:14:41,615 +to free the images +as the table scrolls over the posts. + +232 +00:14:41,648 --> 00:14:46,286 +However, this test is executing +far too quickly for that to be effective. + +233 +00:14:46,320 --> 00:14:48,922 +I need to change my approach. + +234 +00:14:48,956 --> 00:14:54,228 +Instead of verifying by fetching posts, +I can fetch attachments instead. + +235 +00:14:54,261 --> 00:14:57,931 +If I also fetch only the objectIDs, +the managed object context + +236 +00:14:57,965 --> 00:15:01,368 +won't capture any +of the loaded objects until I ask it to. + +237 +00:15:03,237 --> 00:15:07,040 +I can use NSManagedObjectContext's +objectWithID method + +238 +00:15:07,074 --> 00:15:11,178 +to fetch the attachments +as I go for validation. + +239 +00:15:11,211 --> 00:15:16,250 +Finally, for every 10 attachments +I validate, I reset the context, + +240 +00:15:16,283 --> 00:15:19,653 +freeing all of the cached state +and the associated memory. + +241 +00:15:21,555 --> 00:15:25,459 +If I rerun the test with this change, +I can see that it results + +242 +00:15:25,492 --> 00:15:29,229 +in a much more predictable and tunable +level of memory consumption. + +243 +00:15:30,063 --> 00:15:32,900 +In fact, the verifier +uses even less memory + +244 +00:15:32,933 --> 00:15:34,868 +than the LargeDataGenerator does + +245 +00:15:34,902 --> 00:15:36,770 +when it inserts these objects. + +246 +00:15:38,272 --> 00:15:41,141 +Let's drill down +into a specific allocation + +247 +00:15:41,175 --> 00:15:42,976 +to learn how the fix works. + +248 +00:15:44,344 --> 00:15:47,681 +First, I'll select a range +of allocations to work with. + +249 +00:15:47,714 --> 00:15:50,951 +Then, I'll select +a specific size to examine, + +250 +00:15:54,755 --> 00:16:00,227 +I need to enable destroyed objects to find +the ones that were freed during this time, + +251 +00:16:00,260 --> 00:16:03,530 +and then I can select +a specific allocation to examine. + +252 +00:16:05,732 --> 00:16:10,237 +On the right side, Instruments +shows me an allocation stack trace, + +253 +00:16:10,270 --> 00:16:15,742 +but I want to know where it was freed, +so I'll select the deallocation event. + +254 +00:16:15,776 --> 00:16:18,011 +I happen to know +that this stack trace means + +255 +00:16:18,045 --> 00:16:21,849 +that NSManagedObjectContext +is asynchronously deallocating + +256 +00:16:21,882 --> 00:16:26,320 +the object that retained this blob, +freeing the consumed memory. + +257 +00:16:27,054 --> 00:16:31,191 +This technique allows me to establish +a high water mark for the test, + +258 +00:16:31,225 --> 00:16:33,994 +enabling it +to run on systems with less memory. + +259 +00:16:35,028 --> 00:16:38,932 +By combining tests with Instruments, +I've been able to discover + +260 +00:16:38,966 --> 00:16:43,637 +that this specific test had +some less-than-desirable behavior. + +261 +00:16:43,670 --> 00:16:46,940 +I made targeted changes to address +that behavior directly + +262 +00:16:46,974 --> 00:16:49,610 +and then verify the results. + +263 +00:16:49,643 --> 00:16:52,713 +Additionally, the system logs also contain + +264 +00:16:52,746 --> 00:16:55,682 +a wealth of information +about an application + +265 +00:16:55,716 --> 00:16:57,918 +and the system services it depends on, + +266 +00:16:57,951 --> 00:17:01,922 +like CloudKit, +scheduling, and push notifications. + +267 +00:17:02,890 --> 00:17:08,295 +I'm going to sync a single post +between my MacBook Pro and my iPhone. + +268 +00:17:08,328 --> 00:17:12,399 +When I insert a new post on my Mac, +give a short title, + +269 +00:17:12,432 --> 00:17:14,801 +and let it upload to iCloud, + +270 +00:17:14,835 --> 00:17:17,237 +the system logs capture +a number of events. + +271 +00:17:18,338 --> 00:17:21,375 +When it syncs to my iPhone, +sometimes even capturing + +272 +00:17:21,408 --> 00:17:23,076 +intermediate state, + +273 +00:17:23,110 --> 00:17:27,181 +the system logs capture +a corresponding set of events. + +274 +00:17:27,214 --> 00:17:31,618 +On the MacBook Pro, +NSPersistentCloudKitContainer does work + +275 +00:17:31,652 --> 00:17:37,357 +inside of the application process, +in this case, CoreDataCloudKitDemo. + +276 +00:17:37,391 --> 00:17:39,826 +When data is written +to a persistent store, + +277 +00:17:39,860 --> 00:17:43,330 +it asks a system service called DASD + +278 +00:17:43,363 --> 00:17:47,234 +if now is a good time +to export that data to CloudKit. + +279 +00:17:47,267 --> 00:17:51,605 +If it is, DASD will tell +NSPersistentCloudKitContainer + +280 +00:17:51,638 --> 00:17:53,607 +to run an activity. + +281 +00:17:53,640 --> 00:17:57,044 +NSPersistentCloudKitContainer +will then schedule work + +282 +00:17:57,077 --> 00:17:59,046 +with a process called cloudd + +283 +00:17:59,079 --> 00:18:02,216 +to export the changed objects to CloudKit. + +284 +00:18:02,249 --> 00:18:07,020 +We can observe logs from each +of these processes using the Console app. + +285 +00:18:07,721 --> 00:18:11,792 +For application logs, we simply look +for the application process, + +286 +00:18:11,825 --> 00:18:13,994 +CoreDataCloudKitDemo. + +287 +00:18:14,027 --> 00:18:18,131 +Here, I've selected one +that shows an export completing. + +288 +00:18:18,165 --> 00:18:23,237 +For scheduling logs, we want +to look at logs from the process dasd + +289 +00:18:23,270 --> 00:18:26,707 +and from the application's specific store. + +290 +00:18:26,740 --> 00:18:29,977 +Here, I've selected the start +of an export activity + +291 +00:18:30,010 --> 00:18:32,613 +for the application's private store. + +292 +00:18:32,646 --> 00:18:35,215 +Let's examine +this log in a bit more detail. + +293 +00:18:36,116 --> 00:18:39,653 +Activities created +by NSPersistentCloudKitContainer + +294 +00:18:39,686 --> 00:18:43,257 +with dasd follow a specific format. + +295 +00:18:43,290 --> 00:18:47,127 +The activity identifier +is composed of a specific prefix + +296 +00:18:47,160 --> 00:18:52,266 +that NSPersistentCloudKitContainer uses +along with the store identifier + +297 +00:18:52,299 --> 00:18:55,369 +for the store the activity belongs to. + +298 +00:18:55,402 --> 00:18:59,506 +The dasd logs include information +about how the service decides + +299 +00:18:59,540 --> 00:19:01,675 +if an activity can run. + +300 +00:19:01,708 --> 00:19:05,312 +Policies that affect +the application's ability to do work + +301 +00:19:05,345 --> 00:19:08,949 +will be listed in the log +along with a final decision. + +302 +00:19:10,017 --> 00:19:14,555 +Finally, the process cloudd +logs information from CloudKit, + +303 +00:19:14,588 --> 00:19:16,757 +and I like to filter these logs + +304 +00:19:16,790 --> 00:19:19,860 +by the container identifier +I'm working with. + +305 +00:19:19,893 --> 00:19:23,463 +Here I've selected +the corresponding modify records operation + +306 +00:19:23,497 --> 00:19:25,499 +for the export I mentioned earlier. + +307 +00:19:26,633 --> 00:19:29,937 +When changes are imported +on a receiving device, + +308 +00:19:29,970 --> 00:19:33,440 +there is one additional process +to observe. + +309 +00:19:33,473 --> 00:19:38,278 +The process apsd is responsible +for receiving push notifications + +310 +00:19:38,312 --> 00:19:40,614 +and forwarding them to the application. + +311 +00:19:41,248 --> 00:19:44,017 +That causes NSPersistentCloudKitContainer + +312 +00:19:44,051 --> 00:19:48,856 +to initiate a series of activities +similar to the export process. + +313 +00:19:48,889 --> 00:19:52,392 +It asks dasd for time to perform an import + +314 +00:19:52,426 --> 00:19:56,530 +and then works with cloudd to fetch +all of the updated objects from CloudKit + +315 +00:19:56,563 --> 00:19:59,299 +and import them +into the local store. + +316 +00:20:00,601 --> 00:20:05,739 +Apsd logs when it receives +a push notification for an application, + +317 +00:20:05,772 --> 00:20:09,142 +and this log captures +a number of important details. + +318 +00:20:10,010 --> 00:20:13,680 +The log message includes +the container identifier here + +319 +00:20:13,714 --> 00:20:16,884 +as well as the subscription name +and zone identifier + +320 +00:20:16,917 --> 00:20:19,319 +that triggered the push notification. + +321 +00:20:19,353 --> 00:20:22,422 +These are managed +by NSPersistentCloudKitContainer + +322 +00:20:22,456 --> 00:20:27,728 +and will always start with the +prefix com.apple.coredata.cloudkit. + +323 +00:20:29,296 --> 00:20:31,498 +Now the console app is great. + +324 +00:20:31,532 --> 00:20:35,235 +But when I'm developing on my Mac, +I like to use the log stream command + +325 +00:20:35,269 --> 00:20:38,672 +in Terminal to display +these logs alongside my app. + +326 +00:20:40,007 --> 00:20:44,478 +I open one terminal window or tab +for each of the following predicates, + +327 +00:20:44,511 --> 00:20:46,947 +first the application. + +328 +00:20:46,980 --> 00:20:52,519 +Next, the logs from cloudd so I can see +what's happening with the CloudKit server. + +329 +00:20:52,553 --> 00:20:56,657 +Next, apsd for push notification logs. + +330 +00:20:56,690 --> 00:21:00,661 +And finally, dasd so I can see +what's happening with activities + +331 +00:21:00,694 --> 00:21:04,898 +that NSPersistentCloudKitContainer +schedules on my behalf. + +332 +00:21:04,932 --> 00:21:09,469 +These predicates can also be used +to guide your queries in the console app. + +333 +00:21:11,205 --> 00:21:15,475 +There's so much information +available to us on the devices we use. + +334 +00:21:15,509 --> 00:21:19,646 +The challenge, really, is knowing +what tools to use to find and analyze it. + +335 +00:21:19,680 --> 00:21:24,318 +With just Instruments, we can learn +about a host of topics like runtime + +336 +00:21:24,351 --> 00:21:27,054 +and memory performance and so much more. + +337 +00:21:27,087 --> 00:21:31,325 +The system logs capture events +that describe the work an application does + +338 +00:21:31,358 --> 00:21:33,861 +and what the system is doing +for it behind the scenes. + +339 +00:21:34,828 --> 00:21:37,531 +The last phase of my development cycle + +340 +00:21:37,564 --> 00:21:40,868 +is collecting and providing +actionable feedback. + +341 +00:21:40,901 --> 00:21:44,671 +In this section, I'll demonstrate +how to collect diagnostic information + +342 +00:21:44,705 --> 00:21:45,939 +from devices. + +343 +00:21:45,973 --> 00:21:48,275 +Our goal is to use +this information + +344 +00:21:48,308 --> 00:21:52,846 +to generate feedback that's actionable +and aligned with a specific goal. + +345 +00:21:52,880 --> 00:21:55,115 +These techniques can help +you collect feedback + +346 +00:21:55,148 --> 00:21:59,253 +from any device, whether it's one you own +or a customer device. + +347 +00:21:59,286 --> 00:22:04,391 +There are three steps to gathering +diagnostic information from a device. + +348 +00:22:04,424 --> 00:22:07,661 +First, we'll need to install +the CloudKit logging profile, + +349 +00:22:07,694 --> 00:22:10,764 +which enables logs that can be used +to identify issues + +350 +00:22:10,797 --> 00:22:13,000 +and triage them effectively. + +351 +00:22:13,033 --> 00:22:17,404 +Next, we'll collect a sysdiagnose +from the affected device. + +352 +00:22:17,437 --> 00:22:21,141 +And finally, if we have physical access +to the device, + +353 +00:22:21,175 --> 00:22:24,611 +we can also collect +the persistent store files from Xcode. + +354 +00:22:25,145 --> 00:22:29,716 +To install the the logging profile, +we simply visit the Profile and Logs page + +355 +00:22:29,750 --> 00:22:31,818 +on the developer portal. + +356 +00:22:31,852 --> 00:22:36,790 +I can search for the CloudKit profile +and tap the profile link to download it. + +357 +00:22:36,823 --> 00:22:41,328 +On some devices, a notification +will appear to install the profile. + +358 +00:22:41,361 --> 00:22:44,498 +However, here on iOS, +we'll need to install it manually + +359 +00:22:44,531 --> 00:22:45,465 +via the Settings app. + +360 +00:22:47,267 --> 00:22:52,639 +In Settings, I can navigate to tap +on the Profile Downloaded cell. + +361 +00:22:52,673 --> 00:22:56,610 +Then I can tap on the downloaded profile +to install it. + +362 +00:22:56,643 --> 00:23:00,514 +Follow the steps to complete +the installation. + +363 +00:23:00,547 --> 00:23:02,516 +After the profile is installed, + +364 +00:23:02,549 --> 00:23:05,619 +the device can be rebooted, +and it will take effect. + +365 +00:23:09,022 --> 00:23:12,459 +Once the device has rebooted, +we can reproduce the behavior + +366 +00:23:12,492 --> 00:23:15,395 +we want to capture +and then take a sysdiagnose. + +367 +00:23:16,096 --> 00:23:21,702 +Taking a sysdiagnose is done using +a keychord, a special series of buttons. + +368 +00:23:21,735 --> 00:23:25,506 +These are described +in the instructions page for the profile. + +369 +00:23:25,539 --> 00:23:29,076 +I happen to know that for an iPhone, +we hold the volume buttons + +370 +00:23:29,109 --> 00:23:31,645 +and the side button +for a couple of seconds + +371 +00:23:31,678 --> 00:23:33,480 +and then release it. + +372 +00:23:33,514 --> 00:23:37,784 +After a short while, a sysdiagnose +will be available in Settings. + +373 +00:23:37,818 --> 00:23:41,255 +The instructions for finding it +are included in the instructions file + +374 +00:23:41,288 --> 00:23:42,256 +for a profile. + +375 +00:23:43,190 --> 00:23:48,562 +In Settings I navigate +to Privacy & Security, + +376 +00:23:48,595 --> 00:23:50,564 +Analytics and Improvements, + +377 +00:23:50,597 --> 00:23:52,833 +then choose Analytics Data, + +378 +00:23:52,866 --> 00:23:56,170 +and scroll through the logs +until I find the sysdiagnose. + +379 +00:23:57,437 --> 00:24:01,308 +If I tap on the sysdiagnose +and then tap the Share button, + +380 +00:24:01,341 --> 00:24:03,310 +I can choose a number of ways to share it. + +381 +00:24:04,077 --> 00:24:08,081 +For example, I like to AirDrop them +to my Mac for analysis. + +382 +00:24:08,715 --> 00:24:11,752 +Finally, if possible, +I can collect the store files + +383 +00:24:11,785 --> 00:24:13,921 +from Xcode using the Device Organizer. + +384 +00:24:14,721 --> 00:24:16,924 +I can collect the files from this iPhone + +385 +00:24:16,957 --> 00:24:18,792 +by clicking on the Sample Application + +386 +00:24:18,825 --> 00:24:20,394 +in the installed apps list, + +387 +00:24:20,427 --> 00:24:22,262 +clicking on the disclosure button, + +388 +00:24:22,296 --> 00:24:24,164 +choosing Download Container, + +389 +00:24:24,198 --> 00:24:26,733 +and saving that to my Downloads directory. + +390 +00:24:30,470 --> 00:24:34,174 +With all of that done, +both the system logs and the store files + +391 +00:24:34,208 --> 00:24:36,343 +are now available for analysis. + +392 +00:24:36,376 --> 00:24:38,946 +We already talked +about the log stream command, + +393 +00:24:38,979 --> 00:24:40,047 +but with a sysdiagnose, + +394 +00:24:40,080 --> 00:24:44,985 +I can use the log show command +to print out logs from the sysdiagnose. + +395 +00:24:45,018 --> 00:24:49,389 +Here, I've copied the predicate +for the apsd logs we talked about earlier. + +396 +00:24:51,925 --> 00:24:56,530 +The final argument to the log show command +is the logarchive to use. + +397 +00:24:56,563 --> 00:24:59,466 +If nothing is specified, +it will display the system logs + +398 +00:24:59,499 --> 00:25:01,502 +from the machine its running on. + +399 +00:25:01,535 --> 00:25:05,105 +Here, I have specified +system_logs.logarchive + +400 +00:25:05,138 --> 00:25:08,308 +so that it reads the logs +I took from the sysdiagnose. + +401 +00:25:08,342 --> 00:25:11,912 +For example, I can specify +a precise time range + +402 +00:25:11,945 --> 00:25:15,382 +to focus on the time +when an event I'm interested in occurred. + +403 +00:25:16,884 --> 00:25:20,120 +I can also combine many of the predicates +we discussed earlier + +404 +00:25:20,153 --> 00:25:25,492 +to form a unified log of all the activity +relevant to an application, + +405 +00:25:25,526 --> 00:25:28,562 +beginning with the application logs here, + +406 +00:25:28,595 --> 00:25:33,200 +the cloudd logs here, apsd logs here, + +407 +00:25:33,233 --> 00:25:35,869 +and finally dasd logs here. + +408 +00:25:36,703 --> 00:25:40,073 +This powerful command can be included +in feedback reports + +409 +00:25:40,107 --> 00:25:41,408 +or shared with teammates + +410 +00:25:41,441 --> 00:25:45,445 +to allow everyone to focus +on a specific set of logs for analysis. + +411 +00:25:46,947 --> 00:25:50,918 +In this session, we talked about +how you can explore application behavior + +412 +00:25:50,951 --> 00:25:52,920 +with data generators, + +413 +00:25:52,953 --> 00:25:56,690 +analyze applications with instruments +and the system logs, + +414 +00:25:56,723 --> 00:25:59,193 +and provide or collect actionable feedback + +415 +00:25:59,226 --> 00:26:02,663 +from applications +that use NSPersistentCloudKitContainer. + +416 +00:26:03,964 --> 00:26:04,932 +I'm Nick Gillett, + +417 +00:26:04,965 --> 00:26:08,101 +and it's been my pleasure to bring you +this presentation. + +418 +00:26:08,135 --> 00:26:13,106 +Thanks for watching, stay active, +close your rings, and have a great WWDC. + +419 +00:26:14,408 --> 00:26:16,476 +[spacey music] + diff --git a/eng/2022 Session 10120 Evolve your Core Data schema en.srt b/eng/2022 Session 10120 Evolve your Core Data schema en.srt new file mode 100644 index 0000000..7438c16 --- /dev/null +++ b/eng/2022 Session 10120 Evolve your Core Data schema en.srt @@ -0,0 +1,1489 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,309 --> 00:00:13,113 +David Stites: Hi, and welcome +to "Evolve Your Core Data Schema." + +3 +00:00:13,146 --> 00:00:17,718 +My name is David Stites and I am +an engineer on the Core Data team. + +4 +00:00:17,751 --> 00:00:20,187 +In this session, +I am excited to talk to you + +5 +00:00:20,220 --> 00:00:24,658 +about how to update and migrate +the Core Data schema in your app. + +6 +00:00:24,691 --> 00:00:29,229 +The agenda for this session is to learn +what schema migration is + +7 +00:00:29,263 --> 00:00:33,700 +and why your app has to perform it +after updating its data model, + +8 +00:00:33,734 --> 00:00:37,070 +how to migrate the existing schema, + +9 +00:00:37,104 --> 00:00:41,275 +and how CloudKit +and schema migration interact. + +10 +00:00:41,308 --> 00:00:45,812 +First up, what is schema migration +and why your app must migrate + +11 +00:00:45,846 --> 00:00:47,681 +when you update your data model. + +12 +00:00:49,049 --> 00:00:54,788 +As your application evolves, it may become +necessary to change your data model. + +13 +00:00:54,821 --> 00:00:58,592 +Updating the data model requires +that those changes are materialized + +14 +00:00:58,625 --> 00:01:00,861 +in the underlying storage schema. + +15 +00:01:00,894 --> 00:01:02,763 +Consider this data model. + +16 +00:01:02,796 --> 00:01:06,200 +It has an Aircraft entity +with two attributes, + +17 +00:01:06,233 --> 00:01:09,136 +type and number of engines. + +18 +00:01:09,169 --> 00:01:13,440 +These attributes are reflected +in the underlying storage. + +19 +00:01:13,473 --> 00:01:16,677 +If I add +a number of passengers attribute, + +20 +00:01:16,710 --> 00:01:20,147 +I need to add the corresponding storage. + +21 +00:01:20,180 --> 00:01:25,285 +After migration, the changes are +fully reflected in the underlying storage. + +22 +00:01:25,319 --> 00:01:28,488 +Without migrating the changes +in the underlying storage, + +23 +00:01:28,522 --> 00:01:31,792 +Core Data will refuse +to open your persistent store + +24 +00:01:31,825 --> 00:01:36,897 +as the freshly-changed model doesn't +match the model used for storage. + +25 +00:01:36,930 --> 00:01:40,868 +Attempting to open an incompatible store +will result in an error + +26 +00:01:40,901 --> 00:01:45,873 +with the code NSPersistentStore- +IncompatibleVersionHashError. + +27 +00:01:45,906 --> 00:01:48,675 +If you receive this error, +it should be an indication to you + +28 +00:01:48,709 --> 00:01:51,512 +that a migration is required. + +29 +00:01:51,545 --> 00:01:54,548 +Now that I've explained +what schema migration is + +30 +00:01:54,581 --> 00:01:56,850 +and why it's essential +to evolving your app, + +31 +00:01:56,884 --> 00:01:59,553 +let me tell you +how migration is accomplished. + +32 +00:01:59,586 --> 00:02:02,523 +Core Data has built-in +data migration tools + +33 +00:02:02,556 --> 00:02:08,028 +to help keep your app's data storage +up-to-date with the current data model. + +34 +00:02:08,061 --> 00:02:12,566 +Collectively, these tools are +referred to as "lightweight migration." + +35 +00:02:14,067 --> 00:02:17,905 +Lightweight migration is +the preferred method of migration. + +36 +00:02:17,938 --> 00:02:22,643 +Lightweight migration automatically +analyzes and infers the migration + +37 +00:02:22,676 --> 00:02:27,581 +from the differences between the source +and destination managed object models. + +38 +00:02:27,614 --> 00:02:31,518 +At runtime, Core Data looks for the models +in the bundles returned + +39 +00:02:31,552 --> 00:02:36,757 +by .allBundles and .allFrameworks methods +of the NSBundle class. + +40 +00:02:36,790 --> 00:02:39,760 +Lightweight migration then generates +a mapping model + +41 +00:02:39,793 --> 00:02:44,231 +to materialize the changes you've made +in your app in your database schema. + +42 +00:02:45,966 --> 00:02:49,803 +Using lightweight migration +requires the changes to the data model + +43 +00:02:49,837 --> 00:02:52,239 +to fit an obvious migration pattern. + +44 +00:02:54,041 --> 00:02:57,110 +Lightweight operations +involving attributes include + +45 +00:02:57,144 --> 00:03:00,614 +adding an attribute, +removing an attribute, + +46 +00:03:00,647 --> 00:03:04,084 +making a non-optional attribute optional, + +47 +00:03:04,117 --> 00:03:07,087 +making an optional attribute non-optional + +48 +00:03:07,120 --> 00:03:09,423 +and defining a default value, + +49 +00:03:09,456 --> 00:03:11,391 +and renaming an attribute. + +50 +00:03:11,425 --> 00:03:15,062 +If you want to rename an attribute, +set the renaming identifier + +51 +00:03:15,095 --> 00:03:16,864 +in the destination model to the name + +52 +00:03:16,897 --> 00:03:19,366 +of the corresponding attribute +in the source model. + +53 +00:03:20,534 --> 00:03:22,236 +The renaming identifier is found + +54 +00:03:22,269 --> 00:03:25,973 +in the Xcode Data Model Editor's +property inspector. + +55 +00:03:26,006 --> 00:03:28,575 +For example, you can rename + +56 +00:03:28,609 --> 00:03:32,713 +the Aircraft entity color attribute +to paintColor. + +57 +00:03:32,746 --> 00:03:35,749 +The renaming identifier +creates a canonical name, + +58 +00:03:35,782 --> 00:03:38,719 +so set the renaming identifier +to the name of the attribute + +59 +00:03:38,752 --> 00:03:43,490 +in the source model, unless that attribute +already has a renaming identifier. + +60 +00:03:43,524 --> 00:03:47,661 +This means you can rename +an attribute in version 2 of a model, + +61 +00:03:47,694 --> 00:03:50,497 +and then rename it again in version 3. + +62 +00:03:50,531 --> 00:03:55,002 +The renaming will work correctly +going from version 2 to version 3, + +63 +00:03:55,035 --> 00:03:58,539 +or from version 1 to version 3. + +64 +00:04:00,240 --> 00:04:03,544 +Lightweight migration can also +handle changes to the relationships + +65 +00:04:03,577 --> 00:04:05,479 +without breaking a sweat. + +66 +00:04:05,512 --> 00:04:10,150 +You can add a new relationship +or delete an existing relationship. + +67 +00:04:10,184 --> 00:04:13,787 +You can also rename a relationship +by using a renaming identifier, + +68 +00:04:13,820 --> 00:04:15,989 +just like an attribute. + +69 +00:04:16,023 --> 00:04:19,259 +In addition, +you can change relationship cardinality, + +70 +00:04:19,293 --> 00:04:23,630 +for example, +migrating from a to-one to a to-many, + +71 +00:04:23,664 --> 00:04:27,668 +or a non-ordered to-many +to an ordered to-many, and vice versa. + +72 +00:04:29,236 --> 00:04:32,539 +If you guessed that entities are +also eligible for lightweight migration, + +73 +00:04:32,573 --> 00:04:33,974 +you're right. + +74 +00:04:34,007 --> 00:04:38,145 +You can add a new entity, +remove an existing entity, + +75 +00:04:38,178 --> 00:04:40,547 +and rename entities. + +76 +00:04:40,581 --> 00:04:43,750 +You can also create +a new parent or child entity + +77 +00:04:43,784 --> 00:04:48,488 +and move attributes up and down +within the entity hierarchy. + +78 +00:04:48,522 --> 00:04:53,594 +You can move entities +into or out of a hierarchy. + +79 +00:04:53,627 --> 00:04:56,530 +You cannot, +however, merge entity hierarchies. + +80 +00:04:56,563 --> 00:05:00,534 +if two existing entities do not +share a common parent in the source, + +81 +00:05:00,567 --> 00:05:03,237 +they cannot share a common parent +in the destination. + +82 +00:05:03,270 --> 00:05:07,841 +Lightweight migration is controlled +by two options keys: + +83 +00:05:07,875 --> 00:05:11,545 +NSMigratePersistent- +StoresAutomaticallyOption + +84 +00:05:11,578 --> 00:05:16,383 +and +NSInferMappingModelAutomaticallyOption. + +85 +00:05:16,416 --> 00:05:19,386 +The presence of these two keys +set to a true value + +86 +00:05:19,419 --> 00:05:23,056 +when the store is added to the persistent +coordinator will cause Core Data + +87 +00:05:23,090 --> 00:05:26,727 +to perform lightweight migration +automatically if it detects + +88 +00:05:26,760 --> 00:05:31,031 +the persistent store no longer matches +the current model. + +89 +00:05:31,064 --> 00:05:33,667 +If you're using NSPersistentContainer + +90 +00:05:33,700 --> 00:05:36,203 +or NSPersistentStoreDescription, + +91 +00:05:36,236 --> 00:05:38,605 +these options are set +automatically for you + +92 +00:05:38,639 --> 00:05:41,275 +and you don't need to do anything. + +93 +00:05:41,308 --> 00:05:43,443 +If you're using an alternative API + +94 +00:05:43,477 --> 00:05:47,648 +such as NSPersistentStoreCoordinator +.addPersistentStore + +95 +00:05:47,681 --> 00:05:50,918 +(type:configuration:at:options:), + +96 +00:05:50,951 --> 00:05:56,857 +lightweight migration can be requested by +setting and passing an options dictionary + +97 +00:05:56,890 --> 00:06:02,496 +with the keys set NSMigratePersistent- +StoresAutomaticallyOption + +98 +00:06:02,529 --> 00:06:07,968 +and NSInferMappingModelAutomaticallyOption +to a value of YES. + +99 +00:06:08,001 --> 00:06:11,138 +Core Data will perform +lightweight migration automatically + +100 +00:06:11,171 --> 00:06:15,676 +if it detects the persistent store +no longer matches the current model. + +101 +00:06:16,910 --> 00:06:19,379 +Here’s how this works in code. + +102 +00:06:19,413 --> 00:06:23,684 +First, I'll import CoreData +and create a managed object model. + +103 +00:06:23,717 --> 00:06:27,020 +Then, I'll create +a persistent store coordinator + +104 +00:06:27,054 --> 00:06:29,923 +by using the model I just created. + +105 +00:06:29,957 --> 00:06:33,527 +Note the options dictionary +I created and that I'll pass + +106 +00:06:33,560 --> 00:06:36,663 +when I add the store +to the persistent coordinator. + +107 +00:06:36,697 --> 00:06:39,333 +Lastly, I'll add the store +to the coordinator + +108 +00:06:39,366 --> 00:06:43,604 +where migration will occur +automatically if necessary. + +109 +00:06:43,637 --> 00:06:48,876 +Regardless of whatever API you use, +the changes to your data model can be made + +110 +00:06:48,909 --> 00:06:52,579 +directly in the same model +that is shipping with the application. + +111 +00:06:52,613 --> 00:06:56,617 +There is no need to create a new version +of the model to make changes. + +112 +00:06:56,650 --> 00:07:00,087 +If you want to determine +in advance whether Core Data can + +113 +00:07:00,120 --> 00:07:04,157 +infer the mapping model between the source +and destination models + +114 +00:07:04,191 --> 00:07:06,593 +without actually doing the work +of migration, + +115 +00:07:06,627 --> 00:07:11,465 +you can use NSMappingModel +.inferredMappingModel method. + +116 +00:07:11,498 --> 00:07:16,036 +The method returns the inferred model +if Core Data was able to create it. + +117 +00:07:16,069 --> 00:07:18,071 +Otherwise, it returns nil. + +118 +00:07:19,339 --> 00:07:22,176 +Sometimes, combined changes to the schema + +119 +00:07:22,209 --> 00:07:25,212 +may exceed the capabilities +of lightweight migration. + +120 +00:07:25,245 --> 00:07:28,282 +I’m going to describe to you +how to deal with that problem + +121 +00:07:28,315 --> 00:07:30,751 +and still use lightweight migration. + +122 +00:07:31,518 --> 00:07:36,089 +Returning to our previous example model, +suppose that we've previously added + +123 +00:07:36,123 --> 00:07:42,095 +an attribute called “flightData” that uses +external storage for binary data, + +124 +00:07:42,129 --> 00:07:46,233 +indicated by the file path +stored in FLIGHT_DATA. + +125 +00:07:46,266 --> 00:07:49,503 +Further, suppose there is a need +to change that attribute + +126 +00:07:49,536 --> 00:07:53,707 +to store data internally +and remove the external storage. + +127 +00:07:53,740 --> 00:07:57,177 +Checking to see if this migration +fits any of the capabilities + +128 +00:07:57,211 --> 00:08:00,948 +of lightweight migration, +it is discovered that it doesn't. + +129 +00:08:00,981 --> 00:08:03,317 +On the face of it, +it appears that we're stuck, + +130 +00:08:03,350 --> 00:08:04,985 +unable to make this change. + +131 +00:08:05,018 --> 00:08:06,453 +However, fear not! + +132 +00:08:06,486 --> 00:08:10,591 +Lightweight migration can still be used +to perform more complex, + +133 +00:08:10,624 --> 00:08:14,761 +non-conforming migrations, +albeit in multiple steps. + +134 +00:08:16,330 --> 00:08:20,434 +The goal becomes +to decompose the migration tasks + +135 +00:08:20,467 --> 00:08:23,070 +that aren't eligible +for lightweight migration + +136 +00:08:23,103 --> 00:08:28,242 +into a minimum series of migrations that +are eligible for lightweight migration. + +137 +00:08:28,275 --> 00:08:33,981 +Generally, if the original model is A +and the objective model is B, + +138 +00:08:34,014 --> 00:08:36,817 +but model B has changes +that aren't eligible + +139 +00:08:36,850 --> 00:08:40,721 +for lightweight migration, +a bridge can be created by introducing + +140 +00:08:40,754 --> 00:08:45,158 +one or more model versions +that decompose those changes. + +141 +00:08:46,827 --> 00:08:51,331 +Each of the models introduced +will have one or more operations + +142 +00:08:51,365 --> 00:08:55,802 +that is within the capabilities +that compose the non-conforming changes. + +143 +00:08:55,836 --> 00:08:58,205 +This results in a series of migrations + +144 +00:08:58,238 --> 00:09:01,575 +where each model is now +lightweight migrateable + +145 +00:09:01,608 --> 00:09:05,279 +but equivalent +to the non-conforming migration. + +146 +00:09:05,312 --> 00:09:07,781 +Returning to my example +that wasn't eligible + +147 +00:09:07,814 --> 00:09:11,752 +for lightweight migration, +our original model is model A. + +148 +00:09:11,785 --> 00:09:16,957 +I will start decomposing the task +by introducing a new model version, A prime, + +149 +00:09:16,990 --> 00:09:20,794 +and add a new attribute +"tmpStorage" that will be used + +150 +00:09:20,827 --> 00:09:25,199 +temporarily to store data +that is imported from the external files. + +151 +00:09:26,333 --> 00:09:29,303 +Next, I will import the data +from the external files + +152 +00:09:29,336 --> 00:09:31,371 +into our new attribute. + +153 +00:09:31,405 --> 00:09:33,674 +The code to import this data is separate + +154 +00:09:33,707 --> 00:09:37,010 +from the functionality provided +by Core Data. + +155 +00:09:37,044 --> 00:09:41,715 +The execution of this import +is interposed between migrations. + +156 +00:09:42,683 --> 00:09:45,185 +Once the data has been safely imported, + +157 +00:09:45,219 --> 00:09:50,657 +I'll create another new version +of the model A double-prime from A prime. + +158 +00:09:50,691 --> 00:09:55,028 +In A double-prime, I will delete +the old external storage attribute + +159 +00:09:55,062 --> 00:09:58,232 +while simultaneously renaming +the new attribute. + +160 +00:09:58,265 --> 00:10:03,470 +Each of these steps described is within +the capabilities of lightweight migration. + +161 +00:10:04,438 --> 00:10:09,076 +Intuitively, an event loop could be built +that opens the persistent store + +162 +00:10:09,109 --> 00:10:11,478 +with the lightweight migration options set + +163 +00:10:11,512 --> 00:10:16,216 +and iteratively steps through each +unprocessed model in a serial order, + +164 +00:10:16,250 --> 00:10:19,253 +and Core Data will migrate the store. + +165 +00:10:19,286 --> 00:10:22,723 +If you perform app-specific logic +during your migrations, + +166 +00:10:22,756 --> 00:10:26,927 +such as how I imported data from +external files in the previous example, + +167 +00:10:26,960 --> 00:10:31,832 +that logic must be "restartable" +in the event the migration is interrupted + +168 +00:10:31,865 --> 00:10:33,800 +due to the process terminating. + +169 +00:10:34,668 --> 00:10:36,904 +If your app uses Core Data and CloudKit, + +170 +00:10:36,937 --> 00:10:39,373 +there are some important points +you should keep in mind + +171 +00:10:39,406 --> 00:10:42,142 +when designing your data model +in Core Data. + +172 +00:10:42,176 --> 00:10:46,914 +To pass records between a Core Data store +and a CloudKit database, + +173 +00:10:46,947 --> 00:10:51,285 +they require a shared understanding +of the data model. + +174 +00:10:51,318 --> 00:10:55,389 +You define this model +in the Core Data model editor. + +175 +00:10:55,422 --> 00:10:59,860 +That model is subsequently used +to generate the CloudKit schema. + +176 +00:10:59,893 --> 00:11:04,264 +The generated schema is created initially +in the Development environment, + +177 +00:11:04,298 --> 00:11:07,067 +and then promoted to Production. + +178 +00:11:07,100 --> 00:11:10,504 +You should be aware that CloudKit +doesn't support all the features + +179 +00:11:10,537 --> 00:11:12,406 +of a Core Data model. + +180 +00:11:12,439 --> 00:11:16,109 +As you design your model, +be aware of the following limitations + +181 +00:11:16,143 --> 00:11:18,612 +and create a compatible data model. + +182 +00:11:18,645 --> 00:11:23,417 +For example, unique constraints +on entities aren't supported. + +183 +00:11:23,450 --> 00:11:28,856 +Undefined and objectID attribute type +aren't supported as attribute types. + +184 +00:11:28,889 --> 00:11:34,461 +And relationships must be optional +and have an inverse relationship. + +185 +00:11:34,494 --> 00:11:38,899 +In addition, CloudKit does not support +the deny deletion rule. + +186 +00:11:38,932 --> 00:11:43,937 +As you're developing your app, you'll be +using the Development environment. + +187 +00:11:43,971 --> 00:11:48,475 +The CloudKit schema can be +modified freely in this environment. + +188 +00:11:48,509 --> 00:11:51,612 +However, after you promote +your schema to Production, + +189 +00:11:51,645 --> 00:11:54,882 +the record types +and their fields are immutable. + +190 +00:11:54,915 --> 00:11:58,919 +While lightweight migration handles +many different scenarios, + +191 +00:11:58,952 --> 00:12:02,523 +CloudKit is more restricted +in what it supports. + +192 +00:12:02,556 --> 00:12:07,561 +Many of the lightweight operations +I described earlier are unsupported. + +193 +00:12:07,594 --> 00:12:10,998 +Specifically, +what is supported in CloudKit is + +194 +00:12:11,031 --> 00:12:16,203 +adding new fields to existing record types +and adding new record types. + +195 +00:12:16,236 --> 00:12:21,441 +You cannot modify or delete +existing record types or fields. + +196 +00:12:21,475 --> 00:12:25,245 +Consider these restrictions +when modifying the model schema. + +197 +00:12:26,680 --> 00:12:28,949 +When it comes time to update +your data model, + +198 +00:12:28,982 --> 00:12:33,153 +keep in mind that lightweight migration +only materializes schema changes + +199 +00:12:33,187 --> 00:12:35,155 +in the local store file. + +200 +00:12:35,189 --> 00:12:38,859 +Regardless of whether a particular store +is used with CloudKit, + +201 +00:12:38,892 --> 00:12:42,162 +migration will only change +the store on disk + +202 +00:12:42,196 --> 00:12:45,799 +and does not make changes +to the CloudKit schema. + +203 +00:12:45,832 --> 00:12:48,001 +You still need +to materialize those changes + +204 +00:12:48,035 --> 00:12:52,172 +in the Development database +by running the schema initializer + +205 +00:12:52,206 --> 00:12:55,042 +and then promoting +those changes in Development + +206 +00:12:55,075 --> 00:12:58,212 +to Production using the CloudKit Console. + +207 +00:12:58,245 --> 00:13:02,216 +Keep in mind that users of your app +will be using old versions + +208 +00:13:02,249 --> 00:13:04,384 +as well as new versions. + +209 +00:13:04,418 --> 00:13:06,954 +The latest version of the app +will of course know + +210 +00:13:06,987 --> 00:13:09,823 +about any new additions to the schema. + +211 +00:13:09,857 --> 00:13:14,127 +Old versions of the app won't know +about the new fields or record types. + +212 +00:13:15,863 --> 00:13:18,866 +Since CloudKit schema is +essentially additive, + +213 +00:13:18,899 --> 00:13:22,069 +give consideration to the effects +of schema migration + +214 +00:13:22,102 --> 00:13:24,938 +to devices running +older versions of your app. + +215 +00:13:24,972 --> 00:13:29,743 +For example, one common pitfall +is forgetting to update old fields + +216 +00:13:29,776 --> 00:13:33,747 +that the older versions of your app use +but newer versions don't. + +217 +00:13:33,780 --> 00:13:37,885 +Here are some strategies +to migrate CloudKit schema. + +218 +00:13:37,918 --> 00:13:43,090 +The first option is to incrementally add +new fields to existing record types. + +219 +00:13:43,123 --> 00:13:47,227 +If you adopt this approach, older versions +of your app will have access + +220 +00:13:47,261 --> 00:13:50,964 +to every record a user creates, +but not every field. + +221 +00:13:52,332 --> 00:13:55,102 +A second option is +to version your entities + +222 +00:13:55,135 --> 00:13:59,006 +by including a version attribute, +and then use a fetch request to select + +223 +00:13:59,039 --> 00:14:03,110 +only the records that are compatible +with the current version of the app. + +224 +00:14:04,077 --> 00:14:07,714 +If you adopt this approach, +older versions of your app + +225 +00:14:07,748 --> 00:14:11,752 +won't fetch records that a user creates +with a more recent version, + +226 +00:14:11,785 --> 00:14:15,122 +effectively hiding them on that device. + +227 +00:14:15,155 --> 00:14:18,525 +The last strategy is to create +a completely new container, + +228 +00:14:18,559 --> 00:14:21,595 +using +NSPersistentCloudKitContainerOptions, + +229 +00:14:21,628 --> 00:14:25,032 +to associate the new store +with a new container. + +230 +00:14:25,065 --> 00:14:28,068 +Be aware that if the user has +a large data set, + +231 +00:14:28,101 --> 00:14:33,106 +uploading the data set to iCloud +could take an extended period of time. + +232 +00:14:33,140 --> 00:14:37,244 +Whatever method you use, +take care in designing your data model. + +233 +00:14:37,277 --> 00:14:40,948 +Be sure to consider +cross-version compatibility issues + +234 +00:14:40,981 --> 00:14:44,518 +and test different versions +of your data model together. + +235 +00:14:44,551 --> 00:14:47,020 +Now that we've +thoroughly discussed data models, + +236 +00:14:47,054 --> 00:14:51,124 +migration, and CloudKit, +I am going to demonstrate this in action. + +237 +00:14:51,158 --> 00:14:54,161 +As you may have guessed, I'm a pilot. + +238 +00:14:54,194 --> 00:14:57,264 +I've created a small app +to log my flight time. + +239 +00:14:57,297 --> 00:14:59,499 +Here is the data model for that app. + +240 +00:14:59,533 --> 00:15:04,037 +I have a single entity called “LogEntry” +and have added a number of attributes, + +241 +00:15:04,071 --> 00:15:09,443 +such as aircraft type, +flight duration, origin, destination, + +242 +00:15:09,476 --> 00:15:14,414 +and tail number to allow me to log +the required experience information. + +243 +00:15:14,448 --> 00:15:16,817 +When I run this application +for the first time, + +244 +00:15:16,850 --> 00:15:21,455 +Core Data will create the store +and materialize the schema in that store. + +245 +00:15:21,488 --> 00:15:23,190 +Before I run the application, + +246 +00:15:23,223 --> 00:15:28,128 +I am going to turn on +the com.apple.CoreData.SQLDebug + +247 +00:15:28,161 --> 00:15:34,001 +and the com.apple.CoreData.MigrationDebug +environment variables. + +248 +00:15:34,034 --> 00:15:37,704 +This will cause Core Data +to log the steps it is taking. + +249 +00:15:37,738 --> 00:15:40,574 +With these arguments in place, +I will run the app. + +250 +00:15:42,342 --> 00:15:46,180 +As the app launches, Core Data is +logging the steps that it is taking: + +251 +00:15:46,213 --> 00:15:49,716 +creating the file, +creating the metadata for the store, + +252 +00:15:49,750 --> 00:15:52,019 +and materializing the schema. + +253 +00:15:52,052 --> 00:15:56,623 +SQLite created the table ZLOGENTRY +with our schema in it. + +254 +00:15:56,657 --> 00:15:59,526 +This can also be confirmed +by looking at the store file + +255 +00:15:59,560 --> 00:16:02,262 +using the sqlite3 command line tool. + +256 +00:16:02,296 --> 00:16:07,201 +Here, I have the LogEntry table, +and it has the corresponding columns + +257 +00:16:07,234 --> 00:16:09,970 +to the attributes I created +in the data model. + +258 +00:16:10,003 --> 00:16:12,973 +Now I'm going to make +some lightweight changes. + +259 +00:16:14,341 --> 00:16:18,445 +I'm adding some new entities, +Aircraft, Pilot, and Airport. + +260 +00:16:18,478 --> 00:16:21,081 +This will help me normalize the schema. + +261 +00:16:21,114 --> 00:16:26,086 +I am changing some of the attributes in +the LogEntry entity to be relationships. + +262 +00:16:26,119 --> 00:16:30,958 +For example, destination and origin move +from being string attributes + +263 +00:16:30,991 --> 00:16:34,061 +to being an Airport to-one relationship. + +264 +00:16:34,094 --> 00:16:37,164 +The Airport entity +also has two new attributes, + +265 +00:16:37,197 --> 00:16:40,534 +icaoIdentifier and faaIdentifier. + +266 +00:16:40,567 --> 00:16:44,304 +The type attribute is promoted +to a new entity; Aircraft + +267 +00:16:44,338 --> 00:16:49,343 +and I am adding two new attributes, +tailNumber and registrationNumber. + +268 +00:16:49,376 --> 00:16:55,249 +On LogEntry, I am creating a to-one +relationship to an Aircraft from LogEntry. + +269 +00:16:56,416 --> 00:17:01,522 +Lastly, I added a Pilot entity +that has name and certificate ID. + +270 +00:17:02,589 --> 00:17:06,393 +Each log entry will be +related to a Pilot entity. + +271 +00:17:06,426 --> 00:17:09,296 +Now that I've completed my changes +to the data model, + +272 +00:17:09,329 --> 00:17:10,998 +I'm going to run the app again. + +273 +00:17:15,235 --> 00:17:19,039 +Oop! I received an error running the app. + +274 +00:17:19,072 --> 00:17:20,774 +Inspecting the code, it is + +275 +00:17:20,807 --> 00:17:24,845 +NSPersistentStore- +IncompatibleVersionHashError. + +276 +00:17:24,878 --> 00:17:28,448 +That error means that my current model +no longer matches the schema + +277 +00:17:28,482 --> 00:17:30,184 +for the model in the store. + +278 +00:17:30,217 --> 00:17:32,719 +I need to migrate the store schema. + +279 +00:17:32,753 --> 00:17:35,355 +I can do that in one of three ways. + +280 +00:17:35,389 --> 00:17:40,761 +Using the first method, I can convert +my code to use an NSPersistentContainer + +281 +00:17:40,794 --> 00:17:45,799 +as the lightweight migration options +are automatically set for me. + +282 +00:17:45,832 --> 00:17:50,337 +Using the second method, +I can use NSPersistentStoreDescription, + +283 +00:17:50,370 --> 00:17:55,375 +as, again, the lightweight migrations +options are automatically set for me. + +284 +00:17:55,409 --> 00:17:58,812 +Lastly, using the third method, +I can manually set + +285 +00:17:58,846 --> 00:18:02,115 +the lightweight migration options +on an options dictionary + +286 +00:18:02,149 --> 00:18:06,119 +and pass that dictionary +to the coordinator when opening the store. + +287 +00:18:06,987 --> 00:18:11,225 +I think I'll go with the first option, +using an NSPersistentContainer. + +288 +00:18:11,258 --> 00:18:15,295 +Now that I have converted the code +to use an NSPersistentContainer, + +289 +00:18:15,329 --> 00:18:17,431 +I will launch the app and again observe + +290 +00:18:17,464 --> 00:18:20,467 +that Core Data is migrating the schema +in the store file. + +291 +00:18:32,012 --> 00:18:36,850 +Again, this can be confirmed +using the sqlite3 command line tool. + +292 +00:18:36,884 --> 00:18:40,621 +Note the new schema was materialized +by Core Data automatically, + +293 +00:18:40,654 --> 00:18:42,256 +using lightweight migration. + +294 +00:18:42,289 --> 00:18:44,525 +What could be easier? + +295 +00:18:44,558 --> 00:18:48,562 +Before ending my demo, +I wanted to show option number 3. + +296 +00:18:48,595 --> 00:18:50,931 +Recall in this option, +I am manually setting + +297 +00:18:50,964 --> 00:18:54,168 +the lightweight migration options +on an options dictionary + +298 +00:18:54,201 --> 00:18:58,739 +and then passing that dictionary +to the coordinator when opening the store. + +299 +00:18:58,772 --> 00:19:00,073 +The end result is the same + +300 +00:19:00,107 --> 00:19:02,976 +in that the store is migrated +to the new schema. + +301 +00:19:03,010 --> 00:19:07,748 +When you make changes to your data model, +use lightweight migration to help you. + +302 +00:19:07,781 --> 00:19:11,385 +Lightweight migration is very flexible +and easy to use + +303 +00:19:11,418 --> 00:19:14,788 +for the vast majority +of data model changes. + +304 +00:19:14,821 --> 00:19:18,692 +If you have more complex data models, +break that model down + +305 +00:19:18,725 --> 00:19:22,062 +into ones that are composed +of lightweight changes. + +306 +00:19:22,095 --> 00:19:24,965 +Lastly, if you use CloudKit with your app, + +307 +00:19:24,998 --> 00:19:29,303 +carefully consider the implications +of the data model changes. + +308 +00:19:29,336 --> 00:19:32,506 +Thoroughly test any data model changes. + +309 +00:19:32,539 --> 00:19:34,808 +I hope you've found +this information useful + +310 +00:19:34,842 --> 00:19:37,611 +and that you'll consider updating +the model in your project + +311 +00:19:37,644 --> 00:19:40,047 +to build some awesome new features. + +312 +00:19:40,080 --> 00:19:43,884 +Thanks for flying with me, +and have a great WWDC. + diff --git a/eng/2022 Session 10121 Meet Focus filters en.srt b/eng/2022 Session 10121 Meet Focus filters en.srt new file mode 100644 index 0000000..b247613 --- /dev/null +++ b/eng/2022 Session 10121 Meet Focus filters en.srt @@ -0,0 +1,1404 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,943 +♪ + +3 +00:00:09,943 --> 00:00:14,748 +Hello, I am Teja, an engineer on +the iOS System Experience team, + +4 +00:00:14,748 --> 00:00:18,318 +and in this session, you'll get +to meet Focus filters. + +5 +00:00:18,318 --> 00:00:24,291 +Focus was introduced in iOS 15, +macOS Monterey and watchOS 8. + +6 +00:00:24,291 --> 00:00:27,527 +It is a way for people to +concentrate on what's important + +7 +00:00:27,527 --> 00:00:31,531 +by configuring system behavior +for a period of time. + +8 +00:00:31,531 --> 00:00:34,968 +Focus is enabled by simply +going into Control Center + +9 +00:00:34,968 --> 00:00:39,806 +and selecting from either a +system Focus or a custom Focus. + +10 +00:00:39,806 --> 00:00:42,342 +During the time +that a Focus is enabled, + +11 +00:00:42,342 --> 00:00:45,078 +notification behavior +can be customized. + +12 +00:00:45,078 --> 00:00:48,482 +For example, during Work Focus, +someone may want + +13 +00:00:48,482 --> 00:00:51,919 +to only allow notifications +from their coworkers + +14 +00:00:51,919 --> 00:00:55,355 +or only allow notifications +for a select few apps + +15 +00:00:55,355 --> 00:00:57,524 +that are relevant to work. + +16 +00:00:57,524 --> 00:01:01,094 +For each Focus, system behavior +can be configured + +17 +00:01:01,094 --> 00:01:04,264 +and scheduled in Settings. + +18 +00:01:04,264 --> 00:01:08,669 +iOS 16 and macOS Ventura +enhance the Focus feature + +19 +00:01:08,669 --> 00:01:10,704 +with Focus filters. + +20 +00:01:10,704 --> 00:01:13,273 +I'll start by introducing you +to Focus filters + +21 +00:01:13,273 --> 00:01:15,475 +and how they behave. + +22 +00:01:15,475 --> 00:01:20,814 +Then, I'll go over how to define +a Focus filter in your app. + +23 +00:01:20,814 --> 00:01:25,285 +After that, I'll cover what it +means to act on a Focus filter. + +24 +00:01:25,285 --> 00:01:28,155 +And finally, I'll cover how +your app can provide + +25 +00:01:28,155 --> 00:01:31,825 +additional context +back to the system. + +26 +00:01:31,825 --> 00:01:34,027 +Focus filters +are a way for someone + +27 +00:01:34,027 --> 00:01:39,666 +to customize app behavior based +on the currently enabled Focus. + +28 +00:01:39,666 --> 00:01:41,902 +There are some great examples +of system apps + +29 +00:01:41,902 --> 00:01:44,938 +that have adopted Focus filters. + +30 +00:01:44,938 --> 00:01:46,940 +The Calendar app allows people + +31 +00:01:46,940 --> 00:01:50,510 +to filter which calendars +should be visible by default + +32 +00:01:50,510 --> 00:01:53,146 +when a Focus is enabled. + +33 +00:01:53,146 --> 00:01:55,882 +This is what my calendar +normally looks like. + +34 +00:01:55,882 --> 00:01:57,551 +And as you can see, +I have a mix + +35 +00:01:57,551 --> 00:02:01,488 +of work and personal +calendar events. + +36 +00:02:01,488 --> 00:02:04,324 +I can configure +a Focus filter for Calendar, + +37 +00:02:04,324 --> 00:02:08,895 +during the Personal Focus, to +only show my personal calendar. + +38 +00:02:08,895 --> 00:02:10,731 +After setting up +the Focus filter, + +39 +00:02:10,731 --> 00:02:13,300 +this is what my calendar +looks like. + +40 +00:02:13,300 --> 00:02:16,670 +Calendar has indicated +that this is filtered by Focus + +41 +00:02:16,670 --> 00:02:19,740 +and provided a way +to toggle the filtering. + +42 +00:02:19,740 --> 00:02:22,709 +Now I won't be overwhelmed +with my work calendar + +43 +00:02:22,709 --> 00:02:26,279 +when trying to enjoy +some personal time. + +44 +00:02:26,279 --> 00:02:28,648 +The Mail app's inbox +can be filtered + +45 +00:02:28,648 --> 00:02:32,853 +to show only relevant mailboxes +during a Focus. + +46 +00:02:32,853 --> 00:02:35,422 +Mail notifications +are also filtered + +47 +00:02:35,422 --> 00:02:40,027 +to show only the relevant +notifications prominently. + +48 +00:02:40,027 --> 00:02:42,162 +This means +that I can set up Mail + +49 +00:02:42,162 --> 00:02:45,265 +to only deliver work-related +Mail notifications + +50 +00:02:45,265 --> 00:02:48,235 +during the Work Focus +and prevent + +51 +00:02:48,235 --> 00:02:52,439 +personal mail notifications +from interrupting me. + +52 +00:02:52,439 --> 00:02:53,507 +There are many reasons + +53 +00:02:53,507 --> 00:02:56,510 +why your app may want +to implement Focus filters. + +54 +00:02:56,510 --> 00:02:59,179 +Perhaps your app +manages multiple accounts, + +55 +00:02:59,179 --> 00:03:01,615 +and it's appropriate +to associate a Focus + +56 +00:03:01,615 --> 00:03:04,084 +with a particular account. + +57 +00:03:04,084 --> 00:03:07,654 +Apps with large amount of data +may need to filter content + +58 +00:03:07,654 --> 00:03:09,656 +for the Focus. + +59 +00:03:09,656 --> 00:03:11,324 +If you would like +to help your users + +60 +00:03:11,324 --> 00:03:13,794 +avoid getting distracted +while focused, + +61 +00:03:13,794 --> 00:03:16,196 +you can do this +by reducing badge counts, + +62 +00:03:16,196 --> 00:03:18,632 +in-app alerts, +and notifications + +63 +00:03:18,632 --> 00:03:23,303 +to what is salient +for the enabled Focus. + +64 +00:03:23,303 --> 00:03:26,673 +Regarding appearance, your app +may want to surface a theme + +65 +00:03:26,673 --> 00:03:30,110 +or a layout based +on the enabled Focus. + +66 +00:03:30,110 --> 00:03:34,281 +Fundamentally, if your app +can surface different content + +67 +00:03:34,281 --> 00:03:38,151 +based on context, you may be +able to employ Focus filters + +68 +00:03:38,151 --> 00:03:41,388 +to enhance user experience. + +69 +00:03:41,388 --> 00:03:44,491 +Let me explain +how Focus filters work. + +70 +00:03:44,491 --> 00:03:49,596 +Your app defines what can be +customized by a user per Focus, + +71 +00:03:49,596 --> 00:03:52,732 +and this is done +using an AppIntent. + +72 +00:03:52,732 --> 00:03:57,237 +The system will expose what +can be configured per Focus. + +73 +00:03:57,237 --> 00:04:00,774 +UI to configure properties +defined by your AppIntent + +74 +00:04:00,774 --> 00:04:05,579 +will be exposed in Focus +settings as a Focus filter. + +75 +00:04:05,579 --> 00:04:09,216 +Users can configure your app +to behave a certain way + +76 +00:04:09,216 --> 00:04:13,320 +by navigating to Focus settings +and configuring Focus filters + +77 +00:04:13,320 --> 00:04:15,589 +for your app. + +78 +00:04:15,589 --> 00:04:17,924 +Now, I'm going to go over +how you can incorporate + +79 +00:04:17,924 --> 00:04:21,128 +Focus filters +into your codebase. + +80 +00:04:21,128 --> 00:04:24,164 +There are a few parts +to defining your Focus filter. + +81 +00:04:24,164 --> 00:04:28,168 +The first is implementing +SetFocusFilterIntent. + +82 +00:04:28,168 --> 00:04:30,537 +This indicates to the system +that your app + +83 +00:04:30,537 --> 00:04:35,008 +is interested in having +custom settings per Focus. + +84 +00:04:35,008 --> 00:04:38,678 +The second step is defining +your app's parameters. + +85 +00:04:38,678 --> 00:04:42,516 +These will represent what can +be configured within your app + +86 +00:04:42,516 --> 00:04:44,217 +by the user. + +87 +00:04:44,217 --> 00:04:47,787 +The final step is to set +the display representation, + +88 +00:04:47,787 --> 00:04:50,857 +so your Focus filter +appears in system settings + +89 +00:04:50,857 --> 00:04:52,893 +with the correct content. + +90 +00:04:52,893 --> 00:04:57,397 +This way, users are aware +of what is configured. + +91 +00:04:57,397 --> 00:04:59,232 +I'll dive into some code. + +92 +00:04:59,232 --> 00:05:02,836 +The first thing you need to do +is import AppIntents + +93 +00:05:02,836 --> 00:05:07,541 +and define a struct that +implements SetFocusFilterIntent. + +94 +00:05:07,541 --> 00:05:10,110 +This is your Focus filter. + +95 +00:05:10,110 --> 00:05:12,179 +Setting the title +and description + +96 +00:05:12,179 --> 00:05:17,651 +will help users discover +what your Focus is all about. + +97 +00:05:17,651 --> 00:05:22,088 +Focus filters appear +in a grid view in Settings. + +98 +00:05:22,088 --> 00:05:24,691 +Before your Focus filter +has been configured, + +99 +00:05:24,691 --> 00:05:28,528 +it will be surfaced +to the user with this look. + +100 +00:05:28,528 --> 00:05:31,665 +The icon here +is your app's icon, + +101 +00:05:31,665 --> 00:05:34,534 +the primary text +is your app's name, + +102 +00:05:34,534 --> 00:05:37,704 +and the secondary text +will match the title variable + +103 +00:05:37,704 --> 00:05:40,874 +that you set +in your Focus filter. + +104 +00:05:40,874 --> 00:05:44,211 +When the user taps in +to configure your filter, + +105 +00:05:44,211 --> 00:05:46,846 +the same content is displayed. + +106 +00:05:46,846 --> 00:05:50,517 +This time, the system also +includes the description string + +107 +00:05:50,517 --> 00:05:54,554 +that you've provided, +for additional context. + +108 +00:05:54,554 --> 00:05:57,657 +Both the title and description +strings are static, + +109 +00:05:57,657 --> 00:05:59,326 +and they are read by the system + +110 +00:05:59,326 --> 00:06:02,562 +at the time that your app +is installed. + +111 +00:06:02,562 --> 00:06:05,832 +When defining your Focus filter, +you'll have to specify + +112 +00:06:05,832 --> 00:06:08,802 +what a person can customize +by providing a series + +113 +00:06:08,802 --> 00:06:13,006 +of properties that are decorated +as parameters. + +114 +00:06:13,006 --> 00:06:16,009 +When specifying a parameter, +you must give it a name + +115 +00:06:16,009 --> 00:06:17,911 +and a data type. + +116 +00:06:17,911 --> 00:06:19,980 +Parameters can be +of a standard data type + +117 +00:06:19,980 --> 00:06:23,683 +such as Bool, string, +float, etcetera. + +118 +00:06:23,683 --> 00:06:25,619 +If you have a custom data +type that you would like + +119 +00:06:25,619 --> 00:06:28,922 +to have configured, +you can make it an entity, + +120 +00:06:28,922 --> 00:06:32,392 +which will allow you +to decorate it as a parameter. + +121 +00:06:32,392 --> 00:06:35,228 +To learn more about entities +and App Intents, + +122 +00:06:35,228 --> 00:06:38,632 +watch the +"Dive into App Intents" session. + +123 +00:06:38,632 --> 00:06:41,768 +When defining your Focus filter, +you will only specify + +124 +00:06:41,768 --> 00:06:44,771 +the data type and name +for each parameter. + +125 +00:06:44,771 --> 00:06:48,775 +It is up to users to configure +the value of the parameter + +126 +00:06:48,775 --> 00:06:52,145 +that would apply +during each Focus. + +127 +00:06:52,145 --> 00:06:54,381 +Parameters can be +marked as optional, + +128 +00:06:54,381 --> 00:06:57,717 +which means that they do not +have to be configured. + +129 +00:06:57,717 --> 00:07:03,123 +Parameters that are not optional +should provide default values. + +130 +00:07:03,123 --> 00:07:06,326 +In code, you specify +a parameter -- + +131 +00:07:06,326 --> 00:07:09,596 +or an optional parameter -- +by defining a variable + +132 +00:07:09,596 --> 00:07:12,499 +of the type you want +in your Focus filter + +133 +00:07:12,499 --> 00:07:15,435 +and decorating it +as a parameter. + +134 +00:07:15,435 --> 00:07:19,205 +Here, I've created a required +Bool parameter that represents + +135 +00:07:19,205 --> 00:07:23,376 +whether my Focus filter +should always use Dark Mode. + +136 +00:07:23,376 --> 00:07:25,779 +I've set its default to false. + +137 +00:07:25,779 --> 00:07:29,115 +I've also created +an optional string parameter + +138 +00:07:29,115 --> 00:07:33,987 +that represents a user's status +message during this Focus. + +139 +00:07:33,987 --> 00:07:37,524 +Lastly, I've included +an optional account parameter + +140 +00:07:37,524 --> 00:07:40,527 +that is an entity +defined by my app; + +141 +00:07:40,527 --> 00:07:44,931 +it contains information +about a particular account. + +142 +00:07:44,931 --> 00:07:48,635 +The title, which is set on all +three of these parameters, + +143 +00:07:48,635 --> 00:07:51,805 +is displayed in Settings +to describe the parameter + +144 +00:07:51,805 --> 00:07:54,274 +to the user. + +145 +00:07:54,274 --> 00:07:55,508 +In Focus settings, + +146 +00:07:55,508 --> 00:07:58,345 +once a user configures +your app's Focus filter, + +147 +00:07:58,345 --> 00:08:00,780 +it'll be presented +in a similar grid + +148 +00:08:00,780 --> 00:08:02,582 +to what I showed earlier. + +149 +00:08:02,582 --> 00:08:04,050 +But this time, +because the filter + +150 +00:08:04,050 --> 00:08:07,954 +has already been configured, +the content is dynamic + +151 +00:08:07,954 --> 00:08:11,624 +in order to reflect +what has been configured. + +152 +00:08:11,624 --> 00:08:14,794 +The icon here is +still your app's icon. + +153 +00:08:14,794 --> 00:08:17,330 +The primary text +and the secondary text + +154 +00:08:17,330 --> 00:08:21,534 +can be customized using the +display representation property + +155 +00:08:21,534 --> 00:08:24,471 +on your FocusFilterIntent. + +156 +00:08:24,471 --> 00:08:27,574 +The primary text should +represent what parameters + +157 +00:08:27,574 --> 00:08:31,277 +have been configured, +such as Select Account, + +158 +00:08:31,277 --> 00:08:34,180 +Set Status, etcetera. + +159 +00:08:34,180 --> 00:08:37,417 +The secondary text should +represent what the parameters + +160 +00:08:37,417 --> 00:08:43,556 +have been configured to, +such as Work Account or Working. + +161 +00:08:43,556 --> 00:08:46,526 +In my code, I set +the display representation + +162 +00:08:46,526 --> 00:08:49,229 +to be generated dynamically. + +163 +00:08:49,229 --> 00:08:53,366 +Since account and status +are optional parameters, + +164 +00:08:53,366 --> 00:08:56,035 +they only get included +in the dynamic primary + +165 +00:08:56,035 --> 00:08:59,839 +and secondary texts +if they are actually set. + +166 +00:08:59,839 --> 00:09:03,343 +Since alwaysUseDarkMode +is a required parameter, + +167 +00:09:03,343 --> 00:09:09,115 +it is always included in +the primary and secondary texts. + +168 +00:09:09,115 --> 00:09:11,718 +OK, you have now +defined your Focus filter, + +169 +00:09:11,718 --> 00:09:13,853 +so users can go +into Focus settings + +170 +00:09:13,853 --> 00:09:17,457 +and customize certain values +for a particular Focus. + +171 +00:09:17,457 --> 00:09:20,493 +But how can your app know +what someone has customized? + +172 +00:09:20,493 --> 00:09:23,296 +And how can your app +update itself accordingly? + +173 +00:09:23,296 --> 00:09:26,666 +It has to act on a change +from the system. + +174 +00:09:26,666 --> 00:09:29,702 +When a Focus change occurs +and the system has determined + +175 +00:09:29,702 --> 00:09:32,839 +that it's important for your app +to know about this change, + +176 +00:09:32,839 --> 00:09:36,376 +it will deliver this information +to you in one of two ways. + +177 +00:09:36,376 --> 00:09:39,245 +If the app is running, +you will receive a call + +178 +00:09:39,245 --> 00:09:42,148 +to the perform method +in your FocusFilterIntent, + +179 +00:09:42,148 --> 00:09:44,017 +if you've implemented it. + +180 +00:09:44,017 --> 00:09:47,987 +If the app is not running, +you can implement an extension + +181 +00:09:47,987 --> 00:09:49,522 +that will get spun up. + +182 +00:09:49,522 --> 00:09:51,724 +Again, if you've +implemented perform + +183 +00:09:51,724 --> 00:09:55,094 +in your FocusFilterIntent, +that will get called + +184 +00:09:55,094 --> 00:09:58,731 +in your extension. + +185 +00:09:58,731 --> 00:10:01,668 +Since perform can get called +on either your app + +186 +00:10:01,668 --> 00:10:06,005 +or your extension, not every +app needs an extension. + +187 +00:10:06,005 --> 00:10:09,776 +Typically, if your app +is only updating its own view + +188 +00:10:09,776 --> 00:10:12,445 +in response +to a Focus transition, + +189 +00:10:12,445 --> 00:10:18,017 +then implementing perform +just in the app would suffice. + +190 +00:10:18,017 --> 00:10:21,221 +If your app's widget, +notifications or badges + +191 +00:10:21,221 --> 00:10:24,724 +would need to change +based on the Focus transition, + +192 +00:10:24,724 --> 00:10:29,562 +then you may want to consider +implementing an extension. + +193 +00:10:29,562 --> 00:10:31,664 +Basically, +if your app would want + +194 +00:10:31,664 --> 00:10:34,601 +to update anything +outside its own views, + +195 +00:10:34,601 --> 00:10:37,670 +you would need +to implement the extension. + +196 +00:10:37,670 --> 00:10:40,740 +For the rest of this session, +I may refer to "your app" + +197 +00:10:40,740 --> 00:10:44,511 +but that can mean either +your app or your extension + +198 +00:10:44,511 --> 00:10:47,780 +depending on this context. + +199 +00:10:47,780 --> 00:10:52,118 +To respond to a Focus filter, +implement the perform function, + +200 +00:10:52,118 --> 00:10:54,687 +access the populated values +for parameters + +201 +00:10:54,687 --> 00:10:58,525 +provided via Settings, +and then use these values + +202 +00:10:58,525 --> 00:11:02,195 +to update your app's +views and behavior. + +203 +00:11:02,195 --> 00:11:04,631 +Your implementation +of perform is called + +204 +00:11:04,631 --> 00:11:06,799 +when the system +determines that your app + +205 +00:11:06,799 --> 00:11:10,537 +needs to respond +to a Focus transition. + +206 +00:11:10,537 --> 00:11:13,806 +Perform is also called +when the system determines + +207 +00:11:13,806 --> 00:11:18,211 +that the previously delivered +values are no longer relevant. + +208 +00:11:18,211 --> 00:11:21,014 +In this case, +your Focus filter parameters + +209 +00:11:21,014 --> 00:11:24,450 +are configured +with the default values. + +210 +00:11:24,450 --> 00:11:27,387 +When perform is called +on your app's Focus filter, + +211 +00:11:27,387 --> 00:11:30,256 +the values of all the parameters +will be filled out + +212 +00:11:30,256 --> 00:11:32,859 +to match what was configured +in Settings. + +213 +00:11:32,859 --> 00:11:35,895 +The values of the named +parameters can be read + +214 +00:11:35,895 --> 00:11:39,399 +by calling +self."name of the parameter." + +215 +00:11:39,399 --> 00:11:41,634 +In this example, +at the end of perform, + +216 +00:11:41,634 --> 00:11:46,172 +I update my app +with the data I received. + +217 +00:11:46,172 --> 00:11:47,707 +Sometimes, you may need to query + +218 +00:11:47,707 --> 00:11:50,043 +the current Focus +filter parameters. + +219 +00:11:50,043 --> 00:11:51,110 +In my case, + +220 +00:11:51,110 --> 00:11:55,315 +since my filter is called +ExampleChatAppFocusFilter, + +221 +00:11:55,315 --> 00:11:59,519 +I access ExampleChatAppFocus +Filter.current. + +222 +00:12:02,121 --> 00:12:04,924 +Now that your app is able +to act on a Focus filter, + +223 +00:12:04,924 --> 00:12:08,261 +the next step is to take +the user experience further + +224 +00:12:08,261 --> 00:12:11,998 +by providing additional context +about how your app behavior + +225 +00:12:11,998 --> 00:12:14,701 +has changed back to the system. + +226 +00:12:16,569 --> 00:12:19,339 +By providing additional context, +you can influence + +227 +00:12:19,339 --> 00:12:22,909 +your app behavior +outside your app's views. + +228 +00:12:22,909 --> 00:12:26,012 +Examples of this include +notifications filtering + +229 +00:12:26,012 --> 00:12:30,249 +and setting your app's +notification badge count. + +230 +00:12:30,249 --> 00:12:32,452 +One way you can give +the system information + +231 +00:12:32,452 --> 00:12:35,088 +is via the App Context object. + +232 +00:12:35,088 --> 00:12:37,090 +This is an object +that can be returned + +233 +00:12:37,090 --> 00:12:41,027 +as part of the result +of the perform function. + +234 +00:12:41,027 --> 00:12:43,663 +Or you can return +the App Context at any time + +235 +00:12:43,663 --> 00:12:46,399 +in your Focus filter +and force the system + +236 +00:12:46,399 --> 00:12:50,603 +to get the updated value +by calling invalidate. + +237 +00:12:50,603 --> 00:12:53,806 +When a Focus filter is active, +your app may have + +238 +00:12:53,806 --> 00:12:57,810 +additional context to determine +if a particular notification + +239 +00:12:57,810 --> 00:13:00,313 +should not interrupt the user. + +240 +00:13:00,313 --> 00:13:03,616 +To pass along this information, +your app must set + +241 +00:13:03,616 --> 00:13:08,554 +the filterPredicate property +in the AppContext. + +242 +00:13:08,554 --> 00:13:11,324 +This filter predicate +works in conjunction + +243 +00:13:11,324 --> 00:13:15,294 +with a new string property +called filterCriteria + +244 +00:13:15,294 --> 00:13:17,964 +on the UNNotification. + +245 +00:13:17,964 --> 00:13:21,834 +If the filter criteria on the +notification does not match + +246 +00:13:21,834 --> 00:13:26,406 +the filter predicate, +the notification is silenced. + +247 +00:13:26,406 --> 00:13:30,009 +To set the filter predicate +from your FocusFilterIntent, + +248 +00:13:30,009 --> 00:13:32,712 +include it in your App Context. + +249 +00:13:32,712 --> 00:13:35,648 +Say the device has +the Personal Focus enabled + +250 +00:13:35,648 --> 00:13:38,651 +and the user has set it up so +that only the personal account + +251 +00:13:38,651 --> 00:13:40,453 +is selected; + +252 +00:13:40,453 --> 00:13:42,955 +in this case, I set up +the filter predicate + +253 +00:13:42,955 --> 00:13:46,092 +to be the personal account's +identifier. + +254 +00:13:46,092 --> 00:13:49,529 +If the incoming notification is +not from the personal account, + +255 +00:13:49,529 --> 00:13:53,099 +it should not interrupt +the user. + +256 +00:13:53,099 --> 00:13:56,402 +Here, when I'm configuring +this notification, + +257 +00:13:56,402 --> 00:14:00,873 +I set the filterCriteria to be +the work account's identifier. + +258 +00:14:00,873 --> 00:14:03,810 +This is because I know +this notification + +259 +00:14:03,810 --> 00:14:06,145 +is being sent +to the work account, + +260 +00:14:06,145 --> 00:14:09,248 +and I expect that this +notification would be silenced + +261 +00:14:09,248 --> 00:14:13,052 +because the account identifier +does not match the predicate + +262 +00:14:13,052 --> 00:14:14,554 +that I had just set, + +263 +00:14:14,554 --> 00:14:19,092 +which only matched with the +personal account's identifier. + +264 +00:14:19,092 --> 00:14:22,028 +This example is +for a local notification + +265 +00:14:22,028 --> 00:14:25,898 +but filter criteria can also +be set on the JSON payload + +266 +00:14:25,898 --> 00:14:29,769 +of a remote notification. + +267 +00:14:29,769 --> 00:14:32,371 +Another way to provide +the system additional context + +268 +00:14:32,371 --> 00:14:35,608 +is by updating your app's +badge count to reflect + +269 +00:14:35,608 --> 00:14:39,579 +what is important during +the currently-enabled Focus. + +270 +00:14:39,579 --> 00:14:42,615 +This prevents distractions +for your users. + +271 +00:14:42,615 --> 00:14:45,218 +There is a new API +in UserNotifications + +272 +00:14:45,218 --> 00:14:47,086 +for this purpose. + +273 +00:14:47,086 --> 00:14:52,091 +On UNUserNotificationCenter, +you simply call setBadgeCount + +274 +00:14:52,091 --> 00:14:57,163 +with an unsigned integer that +represents the new badge value. + +275 +00:14:57,163 --> 00:14:59,398 +Now, you know how +to provide additional context + +276 +00:14:59,398 --> 00:15:02,735 +to filter notifications +or set the badge count. + +277 +00:15:02,735 --> 00:15:05,371 +Remember, the goal +of this feature is to surface + +278 +00:15:05,371 --> 00:15:08,508 +what is most relevant to a user +when they are focused. + +279 +00:15:08,508 --> 00:15:12,111 +Sometimes this requires +minimizing unrelated content + +280 +00:15:12,111 --> 00:15:16,282 +to prevent distraction +when a Focus is enabled. + +281 +00:15:16,282 --> 00:15:18,951 +For next steps, I encourage you +to start considering + +282 +00:15:18,951 --> 00:15:22,655 +what parts of your app would +benefit from a Focus filter, + +283 +00:15:22,655 --> 00:15:25,558 +determine which properties +can be configured, + +284 +00:15:25,558 --> 00:15:29,028 +set up your app and your +extension to process this, + +285 +00:15:29,028 --> 00:15:30,696 +and then take it a step further + +286 +00:15:30,696 --> 00:15:34,567 +by assessing whether +to provide additional context. + +287 +00:15:34,567 --> 00:15:36,435 +That's it for Focus filters! + +288 +00:15:36,435 --> 00:15:38,171 +Thank you for joining in +on this session + +289 +00:15:38,171 --> 00:15:40,439 +and have a great rest of WWDC. + +290 +00:15:40,439 --> 00:15:44,577 +♪ + diff --git a/eng/2022 Session 10122 Enhance your Sign in with Apple experience en.srt b/eng/2022 Session 10122 Enhance your Sign in with Apple experience en.srt new file mode 100644 index 0000000..f54c95d --- /dev/null +++ b/eng/2022 Session 10122 Enhance your Sign in with Apple experience en.srt @@ -0,0 +1,2173 @@ +1 +00:00:01,468 --> 00:00:07,474 +[spacey music] + +2 +00:00:10,010 --> 00:00:12,145 +Ram: Hi. +My name is Ram. + +3 +00:00:12,179 --> 00:00:15,115 +I'm an engineer +on the account experiences team. + +4 +00:00:15,148 --> 00:00:17,150 +I'll be joined by my colleague Patrick + +5 +00:00:17,184 --> 00:00:18,919 +to talk about how you can enhance + +6 +00:00:18,952 --> 00:00:22,022 +your Sign in with Apple experience +for your app. + +7 +00:00:22,055 --> 00:00:25,492 +Since Sign in with Apple was introduced +in iOS 13, + +8 +00:00:25,526 --> 00:00:29,796 +people love the fast, easy account setup +and sign-in. + +9 +00:00:29,830 --> 00:00:32,499 +With just a few lines of code, +you can enable + +10 +00:00:32,533 --> 00:00:34,468 +quick, one-tap account setup + +11 +00:00:34,501 --> 00:00:37,070 +with no forms or passwords. + +12 +00:00:37,104 --> 00:00:39,406 +Every Sign in with Apple account +is protected + +13 +00:00:39,439 --> 00:00:42,176 +by a strong two-factor authentication + +14 +00:00:42,209 --> 00:00:46,180 +that is already used to secure +the user's Apple ID. + +15 +00:00:46,213 --> 00:00:48,448 +If you need to communicate +with your users, + +16 +00:00:48,482 --> 00:00:51,084 +Sign in with Apple provides you +with an email address + +17 +00:00:51,118 --> 00:00:52,619 +that just works. + +18 +00:00:52,653 --> 00:00:55,989 +There is no additional verification +required. + +19 +00:00:56,023 --> 00:00:58,425 +Sign in with Apple also provides +an indicator + +20 +00:00:58,458 --> 00:01:01,495 +for how likely the user is indeed real. + +21 +00:01:01,528 --> 00:01:04,498 +This can help you combat fraud. + +22 +00:01:04,531 --> 00:01:09,002 +And Sign in with Apple works everywhere, +including your managed Apple IDs + +23 +00:01:09,036 --> 00:01:11,672 +that you use for work and school. + +24 +00:01:11,705 --> 00:01:16,143 +To learn more about how you can integrate +your app with work and school accounts, + +25 +00:01:16,176 --> 00:01:20,714 +check out the session "Discover Sign in +with Apple at Work & School." + +26 +00:01:20,747 --> 00:01:24,918 +In this session, we are going to talk +about how you can enhance and streamline + +27 +00:01:24,952 --> 00:01:26,720 +your Sign in with Apple experience. + +28 +00:01:27,621 --> 00:01:31,225 +First, I will discuss how you can prevent +account duplication + +29 +00:01:31,258 --> 00:01:34,862 +by checking for existing credentials +in your app. + +30 +00:01:34,895 --> 00:01:39,867 +Next, I'll take a deep dive +into the Apple ID credential, + +31 +00:01:39,900 --> 00:01:44,004 +and then, I'll talk about some of the ways +you can monitor credential changes + +32 +00:01:44,037 --> 00:01:47,941 +and discuss how to handle scenarios +like account deletion. + +33 +00:01:47,975 --> 00:01:51,645 +And finally, Patrick will discuss +how to integrate Sign in with Apple + +34 +00:01:51,678 --> 00:01:54,248 +on the web and other platforms. + +35 +00:01:54,281 --> 00:01:56,149 +Let's get started. + +36 +00:01:56,183 --> 00:01:59,920 +Sign in with Apple is +a convenient and secure alternative + +37 +00:01:59,953 --> 00:02:03,590 +to traditional username- +and-password-based authentication, + +38 +00:02:03,624 --> 00:02:07,728 +but your users could still have accounts +that are unlocked with passwords. + +39 +00:02:07,761 --> 00:02:10,564 +So if your user already has an account +that works for them, + +40 +00:02:10,597 --> 00:02:13,934 +it is important not to create +a second account for your app. + +41 +00:02:14,902 --> 00:02:18,105 +I'll explore how you can guide someone +to make the right decision + +42 +00:02:18,138 --> 00:02:19,173 +while signing in. + +43 +00:02:20,207 --> 00:02:24,545 +This is Juice, a sample app +that uses Sign in with Apple. + +44 +00:02:24,578 --> 00:02:27,614 +You can find its source code +in the related links for this video. + +45 +00:02:28,949 --> 00:02:33,554 +You can sign in to Juice either using +traditional email and password + +46 +00:02:33,587 --> 00:02:36,490 +or using Sign in with Apple. + +47 +00:02:36,523 --> 00:02:39,426 +If your user has either +of these credentials already, + +48 +00:02:39,459 --> 00:02:43,430 +it is in your hands to help them +sign into the right account. + +49 +00:02:43,463 --> 00:02:46,567 +To start with, be sure to implement +password autofill + +50 +00:02:46,600 --> 00:02:48,702 +so that existing password credentials + +51 +00:02:48,735 --> 00:02:51,071 +are displayed on the keyboard +for your login screen. + +52 +00:02:52,139 --> 00:02:56,109 +This way, the user can autofill +the credential with just one tap. + +53 +00:02:57,244 --> 00:02:59,413 +Also, you should provide your users + +54 +00:02:59,446 --> 00:03:01,648 +to upgrade their password-based accounts + +55 +00:03:01,682 --> 00:03:03,717 +to Sign in with Apple. + +56 +00:03:03,750 --> 00:03:06,386 +Once upgraded, your users +will get an account + +57 +00:03:06,420 --> 00:03:08,222 +that has security built in, + +58 +00:03:08,255 --> 00:03:11,391 +and they have one less password +to remember. + +59 +00:03:11,425 --> 00:03:14,161 +This is implemented +using the Account Authentication + +60 +00:03:14,194 --> 00:03:16,697 +Modification Extension. + +61 +00:03:16,730 --> 00:03:21,368 +The extension-based API provides +seamless experience for your users + +62 +00:03:21,401 --> 00:03:23,070 +to upgrade the way they sign in + +63 +00:03:23,103 --> 00:03:24,371 +using Sign in with Apple. + +64 +00:03:25,372 --> 00:03:26,507 +For more information + +65 +00:03:26,540 --> 00:03:29,776 +on providing security upgrades +for your users' accounts, + +66 +00:03:29,810 --> 00:03:33,046 +check out the docs +"Get the most out of Sign in with Apple" + +67 +00:03:33,080 --> 00:03:35,215 +and "One-tap account security upgrades." + +68 +00:03:36,850 --> 00:03:38,986 +Apart from providing password autofill, + +69 +00:03:39,019 --> 00:03:40,554 +you can go a step further + +70 +00:03:40,587 --> 00:03:42,422 +by presenting existing credentials + +71 +00:03:42,456 --> 00:03:44,858 +as soon as your app launches. + +72 +00:03:44,892 --> 00:03:46,994 +This way, your users can sign in + +73 +00:03:47,027 --> 00:03:48,328 +using the right account + +74 +00:03:48,362 --> 00:03:50,163 +even before they reach the login screen. + +75 +00:03:50,764 --> 00:03:54,801 +The Authentication Services API +is really flexible this way. + +76 +00:03:54,835 --> 00:03:56,904 +Besides allowing a user to create + +77 +00:03:56,937 --> 00:03:58,839 +a Sign in with Apple credential, + +78 +00:03:58,872 --> 00:04:01,608 +the API can also present +existing credentials, + +79 +00:04:01,642 --> 00:04:03,644 +including password-based credentials. + +80 +00:04:04,711 --> 00:04:06,747 +Adopting this is really easy. + +81 +00:04:06,780 --> 00:04:10,450 +Let me take you through some code +on how to achieve this. + +82 +00:04:10,484 --> 00:04:13,620 +If you're already using +the Authentication Services API, + +83 +00:04:13,654 --> 00:04:15,622 +this code should be very familiar. + +84 +00:04:16,623 --> 00:04:20,661 +You start by creating an instance +of ASAuthorizationController + +85 +00:04:20,694 --> 00:04:24,164 +and include both +ASAuthorizationAppleIDProvider + +86 +00:04:24,198 --> 00:04:27,100 +as well as +an ASAuthorizationPasswordProvider + +87 +00:04:27,134 --> 00:04:29,670 +in the authorization request array. + +88 +00:04:29,703 --> 00:04:32,339 +Then you'll need to set up a delegate +and an object + +89 +00:04:32,372 --> 00:04:34,741 +to help with presenting the interface. + +90 +00:04:34,775 --> 00:04:37,444 +And finally, you'll need to call +performRequests + +91 +00:04:37,477 --> 00:04:40,948 +with the option +preferImmediatelyAvailableCredentials + +92 +00:04:40,981 --> 00:04:43,183 +on your authorization controller. + +93 +00:04:43,217 --> 00:04:46,420 +This option is new on iOS 16. + +94 +00:04:46,453 --> 00:04:49,223 +It tells the system +that you only want credentials + +95 +00:04:49,256 --> 00:04:52,125 +that are immediately available +on the device. + +96 +00:04:52,159 --> 00:04:54,995 +It is intended specifically to be called +on app launch. + +97 +00:04:56,463 --> 00:04:59,032 +If you want to support +previous iOS versions, + +98 +00:04:59,066 --> 00:05:01,368 +you can use performRequests. + +99 +00:05:01,401 --> 00:05:02,903 +When you do this, + +100 +00:05:02,936 --> 00:05:06,673 +you will be presented with a list +of existing credentials. + +101 +00:05:06,707 --> 00:05:07,975 +Your user can now select + +102 +00:05:08,008 --> 00:05:11,912 +either an existing +Sign in with Apple credential + +103 +00:05:11,945 --> 00:05:15,048 +or an existing password credential. + +104 +00:05:15,082 --> 00:05:17,551 +After the user has selected a credential, + +105 +00:05:17,584 --> 00:05:19,186 +the system will call + +106 +00:05:19,219 --> 00:05:21,054 +didCompleteWithAuthorization + +107 +00:05:21,088 --> 00:05:24,491 +on the ASAuthorizationController delegate. + +108 +00:05:24,525 --> 00:05:26,994 +If the user chose +a Sign in with Apple account, + +109 +00:05:27,027 --> 00:05:29,596 +you continue with the appleIDCredential. + +110 +00:05:29,630 --> 00:05:32,165 +If the user chose +a password-based account, + +111 +00:05:32,199 --> 00:05:35,402 +you sign in with the passwordCredential +returned. + +112 +00:05:35,435 --> 00:05:37,871 +If the user has no existing credential, + +113 +00:05:37,905 --> 00:05:39,806 +the API will not present the user + +114 +00:05:39,840 --> 00:05:42,109 +to create a Sign in with Apple account. + +115 +00:05:42,142 --> 00:05:46,547 +Instead, the system will call +didCompleteWithError. + +116 +00:05:46,580 --> 00:05:49,950 +You should fall back to showing +standard login flows in such scenarios. + +117 +00:05:50,884 --> 00:05:54,121 +By the way, the same +Authentication Services API + +118 +00:05:54,154 --> 00:05:57,324 +also works seamlessly for passkeys. + +119 +00:05:57,357 --> 00:05:59,059 +To learn more about passkeys, + +120 +00:05:59,092 --> 00:06:03,163 +a next-generation authentication +technology to replace passwords, + +121 +00:06:03,197 --> 00:06:05,732 +check out the session "Meet passkeys." + +122 +00:06:05,766 --> 00:06:07,668 +With just a few lines of code, + +123 +00:06:07,701 --> 00:06:08,969 +you can take full advantage + +124 +00:06:09,002 --> 00:06:11,038 +of the sign-in experience. + +125 +00:06:11,071 --> 00:06:13,640 +You can now help your users +select the right account, + +126 +00:06:13,674 --> 00:06:16,210 +and hopefully, this prevents +duplicate accounts + +127 +00:06:16,243 --> 00:06:18,312 +from being created in your system. + +128 +00:06:19,346 --> 00:06:22,983 +Next, I'd like to take you on a deep dive +on Apple ID credential. + +129 +00:06:24,451 --> 00:06:28,088 +As you know, the response you get +after a successful authorization + +130 +00:06:28,121 --> 00:06:29,690 +using Sign in with Apple + +131 +00:06:29,723 --> 00:06:33,994 +is an ASAuthorizationAppleIDCredential +object. + +132 +00:06:34,027 --> 00:06:36,396 +It contains values like user, + +133 +00:06:36,430 --> 00:06:39,867 +fullName, email, realUserStatus, + +134 +00:06:39,900 --> 00:06:42,402 +identityToken, and authorizationCode. + +135 +00:06:43,270 --> 00:06:45,806 +I'll cover each of them briefly. + +136 +00:06:45,839 --> 00:06:49,510 +User is a unique and stable identifier. + +137 +00:06:49,543 --> 00:06:51,912 +It is the same identifier +across all the apps + +138 +00:06:51,945 --> 00:06:53,213 +in your developer team. + +139 +00:06:54,281 --> 00:06:58,252 +Use this to uniquely identify +users in your system. + +140 +00:06:58,285 --> 00:07:01,088 +You should ask for fullName +only if you need it. + +141 +00:07:01,121 --> 00:07:05,492 +If requested, your users can share +any name they want. + +142 +00:07:05,526 --> 00:07:07,427 +If you want to communicate +with your users, + +143 +00:07:07,461 --> 00:07:09,663 +you should ask for the email. + +144 +00:07:09,696 --> 00:07:12,299 +When requested, your users +have two options + +145 +00:07:12,332 --> 00:07:14,701 +to share their email address. + +146 +00:07:14,735 --> 00:07:19,173 +One option is to share the email +associated with their Apple ID. + +147 +00:07:19,206 --> 00:07:23,277 +The other option is to use +the "hide my email" feature. + +148 +00:07:23,310 --> 00:07:27,114 +This creates a hidden email address +that routes to their inbox. + +149 +00:07:27,981 --> 00:07:30,751 +It is a two-way relay, +so it can handle replies, too. + +150 +00:07:31,919 --> 00:07:34,221 +Regardless of which option is chosen, + +151 +00:07:34,254 --> 00:07:36,890 +the email address +has been previously verified by Apple, + +152 +00:07:36,924 --> 00:07:38,592 +and it is ready to use. + +153 +00:07:38,625 --> 00:07:42,262 +Also, not all accounts have +an associated email, + +154 +00:07:42,296 --> 00:07:44,198 +so be prepared to handle scenarios + +155 +00:07:44,231 --> 00:07:45,999 +where there is no value for email, + +156 +00:07:46,033 --> 00:07:47,334 +even if you requested it. + +157 +00:07:48,368 --> 00:07:51,071 +RealUserStatus is +a high-confidence indicator + +158 +00:07:51,104 --> 00:07:53,807 +on how likely the user is real. + +159 +00:07:53,841 --> 00:07:56,577 +It is calculated +using on-device machine learning, + +160 +00:07:56,610 --> 00:07:59,479 +account history, and hardware attestation + +161 +00:07:59,513 --> 00:08:01,815 +whilst preserving the user's privacy. + +162 +00:08:01,849 --> 00:08:04,518 +There's three types for realUserStatus. + +163 +00:08:04,551 --> 00:08:08,755 +"Likely real" means the user appears +to be a real person. + +164 +00:08:08,789 --> 00:08:10,891 +Provide this user +with the best experience, + +165 +00:08:10,924 --> 00:08:13,393 +such as skipping +additional fraud verification checks + +166 +00:08:13,427 --> 00:08:15,696 +like CAPTCHAs. + +167 +00:08:15,729 --> 00:08:17,998 +"Unknown" is when the system +hasn't determined + +168 +00:08:18,031 --> 00:08:20,400 +whether the user is a real person. + +169 +00:08:20,434 --> 00:08:24,071 +Trust this user as you would +for any account with limited information + +170 +00:08:24,104 --> 00:08:27,207 +that requires +additional verification steps. + +171 +00:08:27,241 --> 00:08:29,710 +The user could still be real, +so do not block them + +172 +00:08:29,743 --> 00:08:30,744 +from using your app. + +173 +00:08:31,745 --> 00:08:35,115 +And finally, "unsupported" +means the system is not capable + +174 +00:08:35,148 --> 00:08:36,717 +of this determination. + +175 +00:08:37,284 --> 00:08:39,887 +I should take a moment to call out +that the properties + +176 +00:08:39,920 --> 00:08:43,190 +like fullName, email, +and realUserStatus + +177 +00:08:43,223 --> 00:08:46,627 +are only returned when an account +is created for the very first time. + +178 +00:08:47,761 --> 00:08:50,430 +They're not returned +upon subsequent sign-ins, + +179 +00:08:50,464 --> 00:08:54,635 +so make sure to securely cache +properties like fullName and email + +180 +00:08:54,668 --> 00:08:58,205 +until you can verify that an account +has been created in your system. + +181 +00:08:59,339 --> 00:09:02,109 +The identityToken is a JSON web token + +182 +00:09:02,142 --> 00:09:03,610 +that contains most of the data + +183 +00:09:03,644 --> 00:09:05,112 +that the app server needs, + +184 +00:09:05,145 --> 00:09:07,581 +including the user information. + +185 +00:09:07,614 --> 00:09:10,317 +This is an industry-standard approach +to authentication. + +186 +00:09:11,084 --> 00:09:15,822 +The JSON web token, or JWT, +consists of three parts: + +187 +00:09:15,856 --> 00:09:19,459 +a base-64 URL encoded header, + +188 +00:09:19,493 --> 00:09:23,197 +a base-64 URL encoded payload, + +189 +00:09:23,230 --> 00:09:25,232 +and a signature signed by Apple. + +190 +00:09:26,033 --> 00:09:28,702 +You should verify the signature +with Apple's public key + +191 +00:09:28,735 --> 00:09:31,872 +to ensure that the response +has not been tampered with + +192 +00:09:31,905 --> 00:09:35,008 +and is indeed from Apple ID servers. + +193 +00:09:35,042 --> 00:09:36,577 +It is also equally important + +194 +00:09:36,610 --> 00:09:40,047 +that your app server checks +the validity of the token. + +195 +00:09:40,080 --> 00:09:41,882 +Once you decode the payload, + +196 +00:09:41,915 --> 00:09:46,453 +you should verify the issuer is +appleid.apple.com, + +197 +00:09:46,486 --> 00:09:51,558 +verify the audience field +is your app's bundle identifier, + +198 +00:09:51,592 --> 00:09:55,462 +then make sure the expiry timestamp +is greater than the current time + +199 +00:09:55,495 --> 00:09:58,332 +so that you know the token is valid. + +200 +00:09:58,365 --> 00:10:01,802 +Subject will be your user identifier. + +201 +00:10:01,835 --> 00:10:04,438 +If you requested +for the user's email address, + +202 +00:10:04,471 --> 00:10:06,440 +it will also be included. + +203 +00:10:06,473 --> 00:10:09,176 +You can also find the realUserStatus. + +204 +00:10:09,209 --> 00:10:11,545 +The value will be 0 for "unsupported," + +205 +00:10:11,578 --> 00:10:15,249 +1 for "unknown," 2 for "likely real." + +206 +00:10:15,282 --> 00:10:17,851 +And finally, verify that the nonce +is the same + +207 +00:10:17,885 --> 00:10:21,421 +as the one generated prior to the creation +of the authorization request. + +208 +00:10:22,656 --> 00:10:24,324 +For more information on nonce + +209 +00:10:24,358 --> 00:10:26,927 +and how to secure +your authorization process + +210 +00:10:26,960 --> 00:10:28,595 +to mitigate replay attacks, + +211 +00:10:28,629 --> 00:10:31,498 +check out the session "Get the most +out of Sign in with Apple." + +212 +00:10:32,599 --> 00:10:36,236 +AuthorizationCode is +a short-lived, single-use token + +213 +00:10:36,270 --> 00:10:38,272 +that you can provide Apple ID servers + +214 +00:10:38,305 --> 00:10:40,107 +in exchange for refresh tokens. + +215 +00:10:40,974 --> 00:10:43,410 +If your systems already use open standards + +216 +00:10:43,443 --> 00:10:44,511 +like OAuth 2.0, + +217 +00:10:44,545 --> 00:10:46,079 +this might be familiar for you. + +218 +00:10:46,813 --> 00:10:48,649 +To generate a refresh token, + +219 +00:10:48,682 --> 00:10:52,486 +you should send a post request +to the auth/token endpoint. + +220 +00:10:53,287 --> 00:10:56,123 +You pass on the client ID +and the client secret + +221 +00:10:56,156 --> 00:10:59,092 +along with the authorization code +that you just received. + +222 +00:10:59,126 --> 00:11:02,196 +A detailed description on how to create +the client secret + +223 +00:11:02,229 --> 00:11:05,065 +is available +in the Apple Developer documentation. + +224 +00:11:05,098 --> 00:11:06,633 +In the response, + +225 +00:11:06,667 --> 00:11:09,636 +you will get a refresh token, +an access token, + +226 +00:11:09,670 --> 00:11:11,471 +and a new identity token + +227 +00:11:11,505 --> 00:11:14,374 +similar to the one you received earlier. + +228 +00:11:14,408 --> 00:11:16,610 +If you have an expired access token, + +229 +00:11:16,643 --> 00:11:18,078 +you can use the refresh token + +230 +00:11:18,111 --> 00:11:19,813 +to obtain a new access token + +231 +00:11:19,847 --> 00:11:22,316 +using the same endpoint. + +232 +00:11:22,349 --> 00:11:24,818 +You can also continue to use +the same refresh token + +233 +00:11:24,852 --> 00:11:27,554 +until it gets invalidated. + +234 +00:11:27,588 --> 00:11:29,690 +The refresh token could get invalidated + +235 +00:11:29,723 --> 00:11:31,658 +if the token verification fails + +236 +00:11:31,692 --> 00:11:34,628 +or if there are changes +around your user's session. + +237 +00:11:35,762 --> 00:11:37,331 +Speaking of user session, + +238 +00:11:37,364 --> 00:11:40,300 +next, I’ll talk about handling +and monitoring changes + +239 +00:11:40,334 --> 00:11:42,269 +around the credential state. + +240 +00:11:42,936 --> 00:11:44,972 +After verifying the identity token, + +241 +00:11:45,005 --> 00:11:47,808 +your app is responsible for managing +the user session. + +242 +00:11:48,942 --> 00:11:52,913 +There are various scenarios +that can trigger user session changes. + +243 +00:11:52,946 --> 00:11:57,918 +For example, a user could stop using +Apple ID with your app from Settings, + +244 +00:11:57,951 --> 00:12:00,754 +or they could have been signed out +of the device. + +245 +00:12:01,455 --> 00:12:03,690 +To handle session changes gracefully, + +246 +00:12:03,724 --> 00:12:06,827 +call the getCredentialState(forUserID:) + +247 +00:12:06,860 --> 00:12:10,364 +on the ASAuthorizationAppleIDProvider. + +248 +00:12:10,397 --> 00:12:12,566 +This API is recommended to be called + +249 +00:12:12,599 --> 00:12:14,535 +as soon as your app launches + +250 +00:12:14,568 --> 00:12:18,372 +or at any point where you wish to check +the state. + +251 +00:12:18,405 --> 00:12:21,808 +You should also observe +for credentialRevokedNotification + +252 +00:12:21,842 --> 00:12:25,179 +so that your app is notified +when the credential is revoked. + +253 +00:12:25,212 --> 00:12:27,481 +If you observe any change in the state, + +254 +00:12:27,514 --> 00:12:30,083 +you should assume that a different user +has signed in + +255 +00:12:30,117 --> 00:12:32,853 +and sign the current user out of the app. + +256 +00:12:32,886 --> 00:12:34,421 +If you have an app server, + +257 +00:12:34,454 --> 00:12:37,891 +you should subscribe +to server-to-server notifications. + +258 +00:12:37,925 --> 00:12:39,927 +Your server will receive important updates + +259 +00:12:39,960 --> 00:12:42,563 +about your users and their accounts. + +260 +00:12:42,596 --> 00:12:45,132 +Notifications are sent to each group +of apps + +261 +00:12:45,165 --> 00:12:47,134 +for the following scenarios: + +262 +00:12:47,167 --> 00:12:51,805 +when the user disables or enables +their mail forwarding preference; + +263 +00:12:51,839 --> 00:12:55,309 +when the user stops using their Apple ID +with your app; + +264 +00:12:55,342 --> 00:12:58,378 +or when the user permanently deletes +their Apple ID. + +265 +00:12:59,179 --> 00:13:01,448 +To start receiving notifications, + +266 +00:13:01,481 --> 00:13:03,784 +you should first register an endpoint URL + +267 +00:13:03,817 --> 00:13:06,220 +in the Apple Developer portal. + +268 +00:13:06,253 --> 00:13:08,856 +All events arrive +at the same endpoint URL. + +269 +00:13:09,756 --> 00:13:14,127 +The events are sent as JSON web tokens +signed by Apple. + +270 +00:13:14,161 --> 00:13:16,430 +If mail forwarding is disabled, + +271 +00:13:16,463 --> 00:13:18,398 +in the payload of the JWT, + +272 +00:13:18,432 --> 00:13:20,934 +you will receive an email-disabled event. + +273 +00:13:23,070 --> 00:13:26,206 +When the user stops using +their Apple ID with your app, + +274 +00:13:26,240 --> 00:13:28,308 +you will get a consent-revoked event. + +275 +00:13:29,276 --> 00:13:32,212 +It is important to invalidate +any active user session + +276 +00:13:32,246 --> 00:13:33,347 +when you get this event. + +277 +00:13:34,515 --> 00:13:36,850 +And if the user deletes their Apple ID, + +278 +00:13:36,884 --> 00:13:39,353 +you will get an account-delete event. + +279 +00:13:39,386 --> 00:13:42,990 +Again, be sure to invalidate +any active user session + +280 +00:13:43,023 --> 00:13:45,392 +and update their accounts +according to your process. + +281 +00:13:46,527 --> 00:13:49,663 +Now I'd like to focus on account deletion. + +282 +00:13:49,696 --> 00:13:52,065 +Accounts are part of our identities, + +283 +00:13:52,099 --> 00:13:55,836 +and we use them to manage some +of our most personal and private data. + +284 +00:13:55,869 --> 00:13:58,038 +Someone might want to delete +their account, + +285 +00:13:58,071 --> 00:14:00,140 +and you need to support this +in your app. + +286 +00:14:01,241 --> 00:14:03,610 +You should provide a way +to initiate account deletion + +287 +00:14:03,644 --> 00:14:04,678 +from your app, + +288 +00:14:04,711 --> 00:14:06,346 +and it is your responsibility + +289 +00:14:06,380 --> 00:14:08,315 +to manage the entire deletion process. + +290 +00:14:09,516 --> 00:14:10,984 +If you have an app server + +291 +00:14:11,018 --> 00:14:13,020 +that stores user information, + +292 +00:14:13,053 --> 00:14:15,055 +typically, the app notifies the server + +293 +00:14:15,088 --> 00:14:17,191 +to delete user accounts. + +294 +00:14:17,224 --> 00:14:19,393 +Now, you can include Sign in with Apple as + +295 +00:14:19,426 --> 00:14:22,296 +part of your deletion process. + +296 +00:14:22,329 --> 00:14:24,698 +This is done using a new REST endpoint + +297 +00:14:24,731 --> 00:14:26,333 +that your server can use + +298 +00:14:26,366 --> 00:14:29,603 +to delete an account associated +with your app. + +299 +00:14:29,636 --> 00:14:31,805 +Let me briefly take you through this API. + +300 +00:14:32,673 --> 00:14:34,675 +In order to delete an account, + +301 +00:14:34,708 --> 00:14:37,344 +you must have either a valid refresh token + +302 +00:14:37,377 --> 00:14:40,380 +or a valid access token for the user. + +303 +00:14:40,414 --> 00:14:42,316 +If you don't have either of the tokens, + +304 +00:14:42,349 --> 00:14:46,253 +you can generate using +the auth/token endpoint. + +305 +00:14:46,286 --> 00:14:48,155 +Once you have either of the tokens, + +306 +00:14:48,188 --> 00:14:50,924 +you can use the auth/revoke endpoint + +307 +00:14:50,958 --> 00:14:53,126 +with the required parameters. + +308 +00:14:53,160 --> 00:14:54,795 +When using the refresh token, + +309 +00:14:54,828 --> 00:14:57,564 +set the token type to REFRESH_TOKEN. + +310 +00:14:58,765 --> 00:15:01,168 +If you want to delete +using the access token, + +311 +00:15:01,201 --> 00:15:04,004 +set the token type to ACCESS_TOKEN. + +312 +00:15:04,838 --> 00:15:06,740 +If you get a successful response, + +313 +00:15:06,773 --> 00:15:09,376 +the tokens and the user's active sessions + +314 +00:15:09,409 --> 00:15:11,078 +will be instantly invalidated. + +315 +00:15:12,079 --> 00:15:14,982 +Once deleted, +the user returning to your app + +316 +00:15:15,015 --> 00:15:16,783 +and using Sign in with Apple + +317 +00:15:16,817 --> 00:15:18,252 +will have an experience similar + +318 +00:15:18,285 --> 00:15:20,354 +to when they first created the account +with the app. + +319 +00:15:21,522 --> 00:15:23,090 +With that, I will sign out + +320 +00:15:23,123 --> 00:15:25,259 +and pass it on to my colleague Patrick + +321 +00:15:25,292 --> 00:15:27,561 +to discuss how you can use +Sign in with Apple + +322 +00:15:27,594 --> 00:15:29,363 +on the web and other platforms. + +323 +00:15:30,731 --> 00:15:31,965 +Patrick: Thank you, Ram! + +324 +00:15:31,999 --> 00:15:34,801 +People love how Sign in with Apple +works seamlessly + +325 +00:15:34,835 --> 00:15:37,137 +across all of Apple's platforms. + +326 +00:15:37,171 --> 00:15:38,939 +But it doesn’t stop there. + +327 +00:15:38,972 --> 00:15:43,510 +Sign in with Apple also works seamlessly +on the web and other platforms. + +328 +00:15:43,544 --> 00:15:47,047 +Let’s discuss how you can enhance +your Sign in with Apple experience + +329 +00:15:47,080 --> 00:15:49,449 +to support the web and other platforms. + +330 +00:15:50,918 --> 00:15:55,956 +We have an existing iOS app that Ram +introduced to you earlier, called Juice. + +331 +00:15:55,989 --> 00:15:58,425 +We’d like to bring Juice +to even more users + +332 +00:15:58,458 --> 00:16:00,694 +by expanding to the web. + +333 +00:16:00,727 --> 00:16:03,063 +Let’s begin by discussing how to group + +334 +00:16:03,096 --> 00:16:04,665 +our similar apps together. + +335 +00:16:05,832 --> 00:16:08,702 +It is recommended that you group +related apps together + +336 +00:16:08,735 --> 00:16:11,405 +to streamline the user experience. + +337 +00:16:11,438 --> 00:16:13,173 +By grouping related apps together, + +338 +00:16:13,207 --> 00:16:15,943 +your users only need to provide +their consent once + +339 +00:16:15,976 --> 00:16:17,811 +to share their information with your app. + +340 +00:16:18,846 --> 00:16:22,549 +For example, your app may be available +on iOS and macOS + +341 +00:16:22,583 --> 00:16:25,519 +but use different bundle identifiers +for each platform. + +342 +00:16:26,220 --> 00:16:28,555 +It’s recommended that you group +these apps together. + +343 +00:16:29,156 --> 00:16:31,525 +When using Sign in with Apple, your users + +344 +00:16:31,558 --> 00:16:33,894 +will see the App Icon for the app you set + +345 +00:16:33,927 --> 00:16:35,095 +as the primary app. + +346 +00:16:35,796 --> 00:16:38,966 +Let’s discover how you can configure +a Services ID + +347 +00:16:38,999 --> 00:16:41,635 +to support Sign in with Apple +on your website. + +348 +00:16:42,436 --> 00:16:45,839 +To begin, log into the Apple Developer Portal + +349 +00:16:45,873 --> 00:16:49,376 +and navigate to "Certificates, +Identifiers & Profiles." + +350 +00:16:51,011 --> 00:16:53,914 +Select the radio button +next to Services IDs + +351 +00:16:53,947 --> 00:16:55,315 +and then click "Continue." + +352 +00:16:56,884 --> 00:16:58,719 +Enter a description for your Service. + +353 +00:16:59,987 --> 00:17:02,589 +Enter a unique identifier for your Service + +354 +00:17:02,623 --> 00:17:03,590 +and click Continue. + +355 +00:17:04,825 --> 00:17:07,528 +Click on the checkbox +next to Sign in with Apple + +356 +00:17:07,561 --> 00:17:09,363 +and then click the Configure button. + +357 +00:17:10,264 --> 00:17:13,300 +On the Web Authentication Configuration +screen, + +358 +00:17:13,333 --> 00:17:16,370 +select a Primary App ID +from the drop-down menu. + +359 +00:17:17,504 --> 00:17:20,440 +Next, input the domains and subdomains + +360 +00:17:20,474 --> 00:17:24,211 +your website will use to support +Sign in with Apple. + +361 +00:17:24,244 --> 00:17:27,080 +Last, enter a redirect URL for Apple + +362 +00:17:27,114 --> 00:17:30,250 +to redirect your user back to your app +or website + +363 +00:17:30,284 --> 00:17:32,686 +after a successful authorization. + +364 +00:17:32,719 --> 00:17:33,854 +That’s it! + +365 +00:17:33,887 --> 00:17:35,489 +You’ve configured a Services ID + +366 +00:17:35,522 --> 00:17:37,958 +to support Sign in with Apple +on your website. + +367 +00:17:38,692 --> 00:17:40,727 +You’ll need a button to show users + +368 +00:17:40,761 --> 00:17:42,563 +your website supports Sign in with Apple. + +369 +00:17:43,530 --> 00:17:46,900 +Apple provides +a highly configurable button API + +370 +00:17:46,934 --> 00:17:49,536 +to generate Sign in with Apple +button images. + +371 +00:17:50,537 --> 00:17:53,507 +Use this to customize +and embed your button of choice + +372 +00:17:53,540 --> 00:17:56,176 +in your app or website. + +373 +00:17:56,210 --> 00:17:59,680 +Sign in with Apple JS +is a simple Javascript framework + +374 +00:17:59,713 --> 00:18:03,417 +that makes integrating Sign in with Apple +even easier on the web. + +375 +00:18:04,117 --> 00:18:06,253 +In your application or website, + +376 +00:18:06,286 --> 00:18:08,655 +start by including the Sign in with Apple + +377 +00:18:08,689 --> 00:18:10,324 +JavaScript framework. + +378 +00:18:10,357 --> 00:18:13,660 +This simple API will allow you +to authenticate your users + +379 +00:18:13,694 --> 00:18:16,196 +and obtain different assets, + +380 +00:18:16,230 --> 00:18:18,365 +like the Sign in with Apple button + +381 +00:18:18,398 --> 00:18:21,735 +that you can create +with just one simple DIV. + +382 +00:18:21,768 --> 00:18:25,105 +You can also customize the button to fit +your app or website + +383 +00:18:25,138 --> 00:18:27,107 +by modifying the properties of the button. + +384 +00:18:28,108 --> 00:18:31,278 +For example, with the current properties, + +385 +00:18:31,311 --> 00:18:33,647 +you’ll get a white +"Sign in with Apple" button + +386 +00:18:33,680 --> 00:18:36,116 +with a border and a default corner radius. + +387 +00:18:37,084 --> 00:18:39,353 +By changing the data-color property, + +388 +00:18:39,386 --> 00:18:41,855 +we can choose a different background color +for the button. + +389 +00:18:42,789 --> 00:18:45,459 +If we change the data type to "continue", + +390 +00:18:45,492 --> 00:18:47,327 +the button text will update to show + +391 +00:18:47,361 --> 00:18:48,729 +"Continue with Apple." + +392 +00:18:49,763 --> 00:18:52,599 +Alternatively, you can create +a logo-only button + +393 +00:18:52,633 --> 00:18:55,502 +by setting the data mode property +to logo-only. + +394 +00:18:56,403 --> 00:18:59,173 +The Sign in with Apple Javascript +Button API + +395 +00:18:59,206 --> 00:19:02,442 +provides even more customizable +properties. + +396 +00:19:02,476 --> 00:19:05,179 +You can refer +to the Sign in with Apple button resource + +397 +00:19:05,212 --> 00:19:07,314 +to easily configure these many options. + +398 +00:19:08,515 --> 00:19:10,417 +If you’d prefer to use a REST API + +399 +00:19:10,450 --> 00:19:12,519 +to generate +your Sign in with Apple button, + +400 +00:19:12,553 --> 00:19:14,855 +you can use one +of the Apple ID button endpoints + +401 +00:19:14,888 --> 00:19:16,623 +to generate a button. + +402 +00:19:16,657 --> 00:19:19,193 +There are separate endpoints +for center-aligned, + +403 +00:19:19,226 --> 00:19:21,128 +left-aligned, and logo buttons. + +404 +00:19:22,062 --> 00:19:25,365 +You can customize the button by using +query parameters. + +405 +00:19:25,399 --> 00:19:26,967 +In this example request, + +406 +00:19:27,000 --> 00:19:29,436 +I customize +a white Sign in with Apple button + +407 +00:19:29,469 --> 00:19:30,804 +with a border. + +408 +00:19:30,838 --> 00:19:34,908 +I receive a response +with the customized button as a PNG image. + +409 +00:19:34,942 --> 00:19:38,178 +Now that you have personalized +your Sign in with Apple button, + +410 +00:19:38,212 --> 00:19:40,380 +it's time to authenticate your user. + +411 +00:19:41,215 --> 00:19:43,450 +You’ll need to send +an authorization request + +412 +00:19:43,483 --> 00:19:46,620 +with the required parameters to Apple. + +413 +00:19:46,653 --> 00:19:48,255 +These are the parameters you need + +414 +00:19:48,288 --> 00:19:50,791 +in order to successfully log in a user. + +415 +00:19:52,059 --> 00:19:55,896 +Since you have already implemented +Sign in with Apple on an Apple platform, + +416 +00:19:55,929 --> 00:19:57,798 +these parameters are very familiar. + +417 +00:19:59,032 --> 00:20:01,802 +First, we’ll need to set the “clientID”. + +418 +00:20:01,835 --> 00:20:03,504 +This will be the Services ID + +419 +00:20:03,537 --> 00:20:05,806 +that you created +on the Apple Developer Portal + +420 +00:20:05,839 --> 00:20:07,074 +for your app or website. + +421 +00:20:08,075 --> 00:20:12,012 +Next, if your app or website requires +email or name, + +422 +00:20:12,045 --> 00:20:13,914 +fill in the "scope" parameter. + +423 +00:20:13,947 --> 00:20:18,519 +If you are requesting multiple scopes, +use a space to separate each scope. + +424 +00:20:19,419 --> 00:20:22,823 +It is important that you only request +the data you need. + +425 +00:20:23,891 --> 00:20:26,593 +The “redirectURI” parameter +is where you will add + +426 +00:20:26,627 --> 00:20:28,729 +the URL you registered previously + +427 +00:20:28,762 --> 00:20:30,831 +on the Apple Developer Portal + +428 +00:20:30,864 --> 00:20:34,201 +and informs Apple where to direct the user +to on your website. + +429 +00:20:35,369 --> 00:20:38,739 +You can also add a “state” +and “nonce” to secure your request. + +430 +00:20:40,040 --> 00:20:42,776 +And finally, +with the "usePopup" parameter, + +431 +00:20:42,809 --> 00:20:44,945 +you can choose to display the login screen + +432 +00:20:44,978 --> 00:20:46,747 +in a separate pop-up window + +433 +00:20:46,780 --> 00:20:48,815 +or have the existing window + +434 +00:20:48,849 --> 00:20:50,817 +redirect to the Apple Sign in website. + +435 +00:20:51,418 --> 00:20:53,187 +If someone is using Safari, + +436 +00:20:53,220 --> 00:20:55,956 +they’ll see a native screen like this one +providing them + +437 +00:20:55,989 --> 00:20:58,926 +a first-class experience to sign into +your website. + +438 +00:20:59,660 --> 00:21:03,530 +After the Apple ID server +processes the authorization request, + +439 +00:21:03,564 --> 00:21:04,965 +you’ll receive a DOM event + +440 +00:21:04,998 --> 00:21:07,434 +containing the results +of the authorization. + +441 +00:21:08,268 --> 00:21:11,538 +To handle a success response, +add an event listener + +442 +00:21:11,572 --> 00:21:14,708 +for "AppleIDSignInOnSuccess." + +443 +00:21:16,176 --> 00:21:19,112 +To handle a failure response, +add an event listener + +444 +00:21:19,146 --> 00:21:22,216 +for "AppleIDSignInOnFailure." + +445 +00:21:23,784 --> 00:21:25,719 +If the authorization was a success, + +446 +00:21:25,752 --> 00:21:29,156 +you’ll receive a response that contains +the authorization code, + +447 +00:21:29,189 --> 00:21:30,724 +the identity token, + +448 +00:21:30,757 --> 00:21:33,393 +and the user information if requested. + +449 +00:21:33,427 --> 00:21:36,029 +This is similar to the response +you are already used to + +450 +00:21:36,063 --> 00:21:37,831 +on Apple platforms. + +451 +00:21:37,865 --> 00:21:40,834 +If you’d prefer to use a REST API +to integrate directly + +452 +00:21:40,868 --> 00:21:42,369 +with Apple ID servers, + +453 +00:21:42,402 --> 00:21:45,539 +direct the authorization request +to the authorize endpoint + +454 +00:21:45,572 --> 00:21:47,841 +with the required parameters. + +455 +00:21:47,875 --> 00:21:49,877 +If the authorization is successful, + +456 +00:21:49,910 --> 00:21:52,746 +you’ll get a response that contains +the authorization code, + +457 +00:21:52,779 --> 00:21:54,181 +the identity token, + +458 +00:21:54,214 --> 00:21:56,283 +and the user information. + +459 +00:21:56,316 --> 00:21:57,484 +This is very similar + +460 +00:21:57,518 --> 00:22:00,954 +to the response you're already used to +on Apple platforms. + +461 +00:22:00,988 --> 00:22:01,922 +That’s it! + +462 +00:22:01,955 --> 00:22:04,725 +You’ve successfully adopted +Sign in with Apple on your website! + +463 +00:22:05,392 --> 00:22:09,563 +To wrap up, I’d like to highlight +some important things to keep in mind + +464 +00:22:09,596 --> 00:22:11,398 +while implementing Sign in with Apple. + +465 +00:22:12,533 --> 00:22:16,036 +Unless your app requires +significant account-based features, + +466 +00:22:16,069 --> 00:22:19,039 +let people use your app without a login. + +467 +00:22:19,072 --> 00:22:21,141 +For example, you may allow a user + +468 +00:22:21,175 --> 00:22:23,243 +to purchase an item using Apple Pay + +469 +00:22:23,277 --> 00:22:26,880 +and then optionally offer them to tie +their purchase to an account + +470 +00:22:26,914 --> 00:22:28,448 +after the purchase is complete. + +471 +00:22:29,249 --> 00:22:33,520 +Offer existing users the ability +to upgrade the security of their account + +472 +00:22:33,554 --> 00:22:37,024 +by switching away from username +and password authentication + +473 +00:22:37,057 --> 00:22:38,258 +to Sign in with Apple. + +474 +00:22:39,226 --> 00:22:42,563 +If you just need a unique identifier +to identify the user, + +475 +00:22:42,596 --> 00:22:44,298 +don't collect name or email. + +476 +00:22:44,831 --> 00:22:47,668 +And if you do collect email +through Sign In with Apple, + +477 +00:22:47,701 --> 00:22:50,103 +make sure that you respect +the user's choice. + +478 +00:22:50,671 --> 00:22:52,906 +You should not prompt +for an additional email. + +479 +00:22:54,041 --> 00:22:56,109 +It’s important to implement +Sign in with Apple + +480 +00:22:56,143 --> 00:22:59,580 +across all of the platforms your app +or website is available. + +481 +00:23:00,214 --> 00:23:02,482 +Your users may use multiple platforms, + +482 +00:23:02,516 --> 00:23:05,385 +and they will expect to use +Sign in with Apple everywhere. + +483 +00:23:06,153 --> 00:23:09,823 +We’re really excited to see what you do +with Sign in with Apple in your apps, + +484 +00:23:09,857 --> 00:23:11,959 +and we look forward to your feedback. + +485 +00:23:11,992 --> 00:23:14,828 +Thank you for watching, +and enjoy the rest of WWDC! + +486 +00:23:14,862 --> 00:23:17,831 +[music] + diff --git a/eng/2022 Session 10126 Discover ARKit 6 en.srt b/eng/2022 Session 10126 Discover ARKit 6 en.srt new file mode 100644 index 0000000..735cd6f --- /dev/null +++ b/eng/2022 Session 10126 Discover ARKit 6 en.srt @@ -0,0 +1,1421 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,778 +Christian: Hi, my name is Christian. + +3 +00:00:11,812 --> 00:00:14,214 +I'm an engineer on the ARKit team, + +4 +00:00:14,248 --> 00:00:18,452 +and I would like to welcome you +to our session, Discover ARKit 6. + +5 +00:00:18,485 --> 00:00:21,688 +You'll learn how to make use +of the latest advancements + +6 +00:00:21,722 --> 00:00:24,691 +in our Augmented Reality framework. + +7 +00:00:24,725 --> 00:00:28,996 +We are delighted to see what you have been +creating over the last several years + +8 +00:00:29,029 --> 00:00:30,497 +with ARKit. + +9 +00:00:30,531 --> 00:00:33,967 +We are seeing some amazing apps +in interior design, + +10 +00:00:34,001 --> 00:00:36,103 +travel, virtual exhibitions, + +11 +00:00:36,136 --> 00:00:38,372 +games, and so many others. + +12 +00:00:38,405 --> 00:00:42,009 +Our team here at Apple has +paid close attention to your feedback, + +13 +00:00:42,042 --> 00:00:45,546 +and we have incorporated a lot of it +into ARKit 6. + +14 +00:00:45,579 --> 00:00:46,647 +Let's have a look. + +15 +00:00:46,680 --> 00:00:51,618 +We are introducing a new 4K video mode +that lets you run the camera stream + +16 +00:00:51,652 --> 00:00:54,421 +in the highest image resolution yet. + +17 +00:00:54,454 --> 00:00:58,392 +After that, I'll talk about some +additional camera enhancements we made + +18 +00:00:58,425 --> 00:01:01,762 +that give you more control +of the video backdrop. + +19 +00:01:01,795 --> 00:01:05,566 +We also have updates +on the behavior of plane anchors, + +20 +00:01:05,599 --> 00:01:08,435 +additions to the Motion Capture API, + +21 +00:01:08,468 --> 00:01:12,739 +and finally share new cities +where location anchors will be supported. + +22 +00:01:14,441 --> 00:01:17,477 +Let's start with 4K video. + +23 +00:01:17,511 --> 00:01:20,347 +Over the course of the past several years, + +24 +00:01:20,380 --> 00:01:24,184 +we saw a lot of demand +for high resolution content, + +25 +00:01:24,218 --> 00:01:28,055 +especially those apps which leverage +the power of Augmented Reality + +26 +00:01:28,088 --> 00:01:32,492 +for filmmaking, +are ever hungry for more pixels. + +27 +00:01:32,526 --> 00:01:36,530 +Let me show you how images are captured +and processed for ARKit. + +28 +00:01:36,563 --> 00:01:39,933 +This is the camera module +of an iPhone 13 Pro. + +29 +00:01:39,967 --> 00:01:43,036 +If we open it up, we can see its setup. + +30 +00:01:43,070 --> 00:01:46,406 +Let us talk about the Wide +and the Ultrawide camera. + +31 +00:01:46,440 --> 00:01:50,844 +Both these cameras can be used +for different computer vision tasks, + +32 +00:01:50,878 --> 00:01:55,415 +such as world tracking, +motion capture, or person segmentation. + +33 +00:01:55,449 --> 00:01:58,719 +The wide camera +has a special place in our heart, + +34 +00:01:58,752 --> 00:02:02,489 +since it delivers the images +for the rendering backdrop. + +35 +00:02:02,523 --> 00:02:06,326 +It's important to understand +how images are processed for rendering, + +36 +00:02:06,360 --> 00:02:09,129 +so let me zoom in to the sensor level. + +37 +00:02:13,100 --> 00:02:17,838 +When capturing images for ARKit, +we use a large part of the image sensor. + +38 +00:02:17,871 --> 00:02:23,343 +To be more precise, +it's an area of 3840x2880 pixels + +39 +00:02:23,377 --> 00:02:25,546 +on this particular model. + +40 +00:02:25,579 --> 00:02:28,815 +After capture, +we use a process called binning. + +41 +00:02:28,849 --> 00:02:31,852 +It works as follows: + +42 +00:02:31,885 --> 00:02:36,123 +Binning takes a region of 2x2 pixels, + +43 +00:02:36,156 --> 00:02:38,525 +averages the pixel values, + +44 +00:02:38,559 --> 00:02:41,895 +and writes back a single pixel. + +45 +00:02:41,929 --> 00:02:44,731 +This has two significant advantages. + +46 +00:02:44,765 --> 00:02:48,535 +First, image dimensions are reduced +by a factor of two, + +47 +00:02:48,569 --> 00:02:53,240 +in this case, +it downscales to 1920x1440 pixels. + +48 +00:02:53,273 --> 00:02:58,579 +As a result of this, each frame consumes +way less memory and processing power. + +49 +00:02:58,612 --> 00:03:02,883 +This allows the device to run the camera +at up to 60 frames per second + +50 +00:03:02,916 --> 00:03:05,819 +and frees up resources for rendering. + +51 +00:03:05,853 --> 00:03:09,022 +Secondly, +this process offers an advantage + +52 +00:03:09,056 --> 00:03:10,724 +in low light environments, + +53 +00:03:10,757 --> 00:03:15,062 +where the averaging of pixel values +reduces the effects of sensor noise. + +54 +00:03:15,796 --> 00:03:21,869 +We end up with a camera stream +that provides an image at HD resolution + +55 +00:03:21,902 --> 00:03:24,972 +roughly every 17 milliseconds. + +56 +00:03:25,005 --> 00:03:29,042 +After using the current frame +for various computer vision tasks, + +57 +00:03:29,076 --> 00:03:33,347 +ARKit surfaces the current frame +for rendering. + +58 +00:03:33,380 --> 00:03:35,983 +In case you are writing +your own Metal renderer, + +59 +00:03:36,016 --> 00:03:41,088 +you have access to it via +ARSession's currentFrame.capturedImage. + +60 +00:03:42,623 --> 00:03:47,227 +If you are using RealityKit, the image +is automatically processed further + +61 +00:03:47,261 --> 00:03:49,129 +for use as a backdrop. + +62 +00:03:49,162 --> 00:03:55,302 +It is scaled on-device to match +the screen width of 2532 pixels + +63 +00:03:55,335 --> 00:04:00,374 +and is cropped to match +the display aspect ratio. + +64 +00:04:00,407 --> 00:04:05,379 +RealityKit performs the task of rendering +and compositing the virtual content, + +65 +00:04:05,412 --> 00:04:06,847 +like this pirate ship, + +66 +00:04:06,880 --> 00:04:10,817 +on top of the frame and displays +the final result on screen. + +67 +00:04:10,851 --> 00:04:13,453 +Now, with the power +of our latest hardware, + +68 +00:04:13,487 --> 00:04:17,891 +we enable full 4K video mode in ARKit. + +69 +00:04:17,925 --> 00:04:21,461 +Your app can now take advantage +of a higher resolution image + +70 +00:04:21,495 --> 00:04:27,701 +by skipping the binning step and directly +accessing it in full 4K resolution. + +71 +00:04:27,734 --> 00:04:34,241 +In 4K mode, +an image area of 3840x2160 pixels is used + +72 +00:04:34,274 --> 00:04:37,878 +and you can capture video +at 30 frames per second. + +73 +00:04:37,911 --> 00:04:41,515 +Apart from these changes, your app +will work the same way as before. + +74 +00:04:41,548 --> 00:04:44,685 +If you use RealityKit, +it performs scaling, cropping, + +75 +00:04:44,718 --> 00:04:46,420 +and rendering for you. + +76 +00:04:49,723 --> 00:04:54,528 +You can enable the 4K mode +using a few simple steps. + +77 +00:04:54,561 --> 00:04:56,964 +Let's see how that looks in code. + +78 +00:04:58,866 --> 00:05:02,236 +'ARConfiguration' has a new convenience +function + +79 +00:05:02,269 --> 00:05:08,675 +'recommendedVideoFormatFor4KResolution' +that returns a 4K video format + +80 +00:05:08,709 --> 00:05:11,812 +if that mode is supported +on your device. + +81 +00:05:11,845 --> 00:05:17,317 +If the device or configuration do not +support 4K, this function will return nil. + +82 +00:05:17,351 --> 00:05:20,854 +You can then assign this video format +to your configuration, + +83 +00:05:20,888 --> 00:05:24,291 +then you run your session +with that adjusted configuration. + +84 +00:05:25,826 --> 00:05:30,464 +The 4K video mode is available +on iPhone 11 and up + +85 +00:05:30,497 --> 00:05:34,034 +and on any iPad Pro with an M1 chip. + +86 +00:05:34,067 --> 00:05:40,807 +The resolution is 3840x2160 pixels +at 30 frames per second. + +87 +00:05:40,841 --> 00:05:43,544 +The aspect ratio is 16:9, + +88 +00:05:43,577 --> 00:05:47,748 +for iPad that means that images have to be +cropped at the sides + +89 +00:05:47,781 --> 00:05:52,286 +for full screen display and the final +render might look zoomed in. + +90 +00:05:53,453 --> 00:05:57,658 +When using ARKit, +especially in the new 4K resolution, + +91 +00:05:57,691 --> 00:06:01,728 +it's important to follow some +best practices for optimal results. + +92 +00:06:01,762 --> 00:06:04,531 +Do not hold on to an ARFrame for too long. + +93 +00:06:04,565 --> 00:06:08,402 +This might prevent the system +from freeing up memory, + +94 +00:06:08,435 --> 00:06:12,406 +which might further stop ARKit +from surfacing newer frames to you. + +95 +00:06:12,439 --> 00:06:15,876 +This will become visible through +frame drops in your rendering. + +96 +00:06:15,909 --> 00:06:20,714 +Ultimately, the ARCamera's tracking state +might fall back to limited. + +97 +00:06:20,747 --> 00:06:23,817 +Check for console warnings +to make sure you do not retain + +98 +00:06:23,851 --> 00:06:26,753 +too many images at any given time. + +99 +00:06:26,787 --> 00:06:32,693 +Also consider if the new 4K video format +is indeed the right option for your app. + +100 +00:06:32,726 --> 00:06:36,697 +Apps that benefit from high resolution +video are good candidates, + +101 +00:06:36,730 --> 00:06:40,868 +such as video, filmmaking, +and virtual production apps. + +102 +00:06:40,901 --> 00:06:45,205 +Dealing with higher resolution images +takes up additional system resources, + +103 +00:06:45,239 --> 00:06:49,009 +so for games and other apps +that rely on a high refresh rate, + +104 +00:06:49,042 --> 00:06:54,047 +we still suggest using full HD video +at 60 frames per second. + +105 +00:06:55,048 --> 00:06:58,685 +On top of the new 4K mode, +there are some additional enhancements + +106 +00:06:58,719 --> 00:07:03,090 +that allow you to get more control +over your camera. + +107 +00:07:03,123 --> 00:07:08,161 +I will start by introducing the hi-res +background photo API + +108 +00:07:08,195 --> 00:07:12,599 +and show how to enable the new HDR mode. + +109 +00:07:12,633 --> 00:07:15,836 +Further, I will demonstrate how to get access + +110 +00:07:15,869 --> 00:07:20,807 +to the underlying AVCaptureDevice +for more fine grained control + +111 +00:07:20,841 --> 00:07:24,678 +and show you how to read EXIF tags +in ARKit. + +112 +00:07:24,711 --> 00:07:29,283 +Let's jump into the new +hi-res background photo API. + +113 +00:07:30,317 --> 00:07:31,985 +While running an ARSession, + +114 +00:07:32,019 --> 00:07:35,923 +you still get access +to the video stream as usual. + +115 +00:07:35,956 --> 00:07:41,628 +In addition, ARKit lets you request +the capture of single photos on demand + +116 +00:07:41,662 --> 00:07:42,996 +in the background, + +117 +00:07:43,030 --> 00:07:46,133 +while the video stream is running +uninterrupted. + +118 +00:07:46,166 --> 00:07:50,571 +Those single photo frames take +full advantage of your camera sensor. + +119 +00:07:50,604 --> 00:07:55,843 +On my iPhone 13 that means +the full 12 megapixels of the wide camera. + +120 +00:07:55,876 --> 00:07:58,212 +When preparing for WWDC, + +121 +00:07:58,245 --> 00:08:01,548 +we at ARKit had a fun idea +for a Photography app + +122 +00:08:01,582 --> 00:08:06,153 +that highlights what this new API +can help you create. + +123 +00:08:06,186 --> 00:08:09,423 +In our example, +we take you back in time + +124 +00:08:09,456 --> 00:08:13,227 +to April 1st, 2016, +when the famous pirate flag + +125 +00:08:13,260 --> 00:08:16,430 +was flying over +the Apple Infinite Loop Campus. + +126 +00:08:16,463 --> 00:08:19,600 +I asked Tommy, +the original photographer, + +127 +00:08:19,633 --> 00:08:23,036 +where exactly he shot that photo +six years ago. + +128 +00:08:25,005 --> 00:08:29,376 +Based on this coordinate, +we can create a location anchor + +129 +00:08:29,409 --> 00:08:35,148 +that guides you to the exact same spot +where Tommy stood in April 2016, + +130 +00:08:35,182 --> 00:08:38,785 +as indicated by the big blue dot. + +131 +00:08:38,819 --> 00:08:42,956 +Upon reaching that spot, +it helps you frame that perfect picture + +132 +00:08:42,990 --> 00:08:45,292 +by showing a focus square. + +133 +00:08:45,325 --> 00:08:51,265 +Finally, the app lets your take a photo +by tapping the screen. + +134 +00:08:51,298 --> 00:08:54,434 +That photo can be taken +in native camera resolution + +135 +00:08:54,468 --> 00:08:56,737 +while the current ARKit session +is running, + +136 +00:08:56,770 --> 00:09:00,607 +without the need to spin up +another AVCapture session. + +137 +00:09:00,641 --> 00:09:05,345 +We're excited to see which ideas you have +that combine the power of AR + +138 +00:09:05,379 --> 00:09:06,613 +and photography. + +139 +00:09:06,647 --> 00:09:11,118 +Another use case that will greatly benefit +by this API + +140 +00:09:11,151 --> 00:09:14,688 +is the creation of 3D models +using Object Capture. + +141 +00:09:15,489 --> 00:09:19,893 +Object capture takes in photos +of a real world object, + +142 +00:09:19,927 --> 00:09:21,395 +like this running shoe, + +143 +00:09:21,428 --> 00:09:24,831 +and using our latest photogrammetry +algorithms, + +144 +00:09:24,865 --> 00:09:30,103 +it turns them into a 3D model +ready for deployment in your AR app. + +145 +00:09:30,137 --> 00:09:35,008 +With ARKit you can overlay a 3D UI +on top of a physical object + +146 +00:09:35,042 --> 00:09:37,277 +and provide better capture guidance. + +147 +00:09:37,311 --> 00:09:41,448 +And now with the new high resolution +background image API, + +148 +00:09:41,481 --> 00:09:44,418 +you can take higher-resolution photos +of the object + +149 +00:09:44,451 --> 00:09:47,754 +and create even more realistic 3D models. + +150 +00:09:47,788 --> 00:09:49,756 +I'm a big fan of photogrammetry, + +151 +00:09:49,790 --> 00:09:53,126 +so I'd highly recommend +that you check out this year's + +152 +00:09:53,160 --> 00:09:56,296 +"Bring your world to augmented reality" +session. + +153 +00:09:56,330 --> 00:10:01,034 +Let me show you how you can enable +high-resolution photo captures in code. + +154 +00:10:01,935 --> 00:10:07,574 +First, we check for a video format +that supports hiResCapture. + +155 +00:10:07,608 --> 00:10:09,510 +We can use the convenience function + +156 +00:10:09,543 --> 00:10:15,582 +'recommendedVideoFormatForHighResolution +FrameCapturing' for that. + +157 +00:10:15,616 --> 00:10:18,619 +After we make sure +that the format is supported, + +158 +00:10:18,652 --> 00:10:22,456 +we can set the new video format +and run the session. + +159 +00:10:22,489 --> 00:10:27,294 +We further have to tell ARKit +when to capture a hi-res image. + +160 +00:10:27,327 --> 00:10:29,062 +In our earlier example, + +161 +00:10:29,096 --> 00:10:32,733 +the capture of a photo is triggered +by a tap on the screen. + +162 +00:10:32,766 --> 00:10:36,436 +In your own application, you might want +to react to different events + +163 +00:10:36,470 --> 00:10:39,206 +that trigger high-resolution +frame captures. + +164 +00:10:39,239 --> 00:10:42,075 +It really depends on your use case. + +165 +00:10:42,109 --> 00:10:47,414 +The ARSession has a new function +called captureHighResolutionFrame. + +166 +00:10:47,447 --> 00:10:51,351 +Calling this function +triggers an out-of-band capture + +167 +00:10:51,385 --> 00:10:54,087 +of a high-resolution image. + +168 +00:10:54,121 --> 00:10:57,658 +You get access to an ARFrame +containing the high-resolution image + +169 +00:10:57,691 --> 00:11:00,227 +and all other frame properties + +170 +00:11:00,260 --> 00:11:03,163 +asynchronously in the completion handler. + +171 +00:11:03,197 --> 00:11:05,766 +You should check +if the frame capture was successful + +172 +00:11:05,799 --> 00:11:08,168 +before accessing its contents. + +173 +00:11:08,202 --> 00:11:11,672 +In this example +we store the frame to disk. + +174 +00:11:11,705 --> 00:11:15,209 +Also keep in mind our best practices +on image retention + +175 +00:11:15,242 --> 00:11:16,543 +that I mentioned earlier, + +176 +00:11:16,577 --> 00:11:22,616 +especially since these images use +the full resolution of the image sensor. + +177 +00:11:22,649 --> 00:11:26,386 +Next, let's talk about HDR. + +178 +00:11:26,420 --> 00:11:29,389 +High Dynamic Range captures +a wider range of colors + +179 +00:11:29,423 --> 00:11:31,792 +and maps those to your display. + +180 +00:11:31,825 --> 00:11:35,896 +This is most visible in environments +with high contrast. + +181 +00:11:35,929 --> 00:11:38,599 +Here's a good example from my backyard. + +182 +00:11:38,632 --> 00:11:41,668 +This scene features both very dark areas– + +183 +00:11:41,702 --> 00:11:43,470 +for example, on the wooden fence– + +184 +00:11:43,504 --> 00:11:47,541 +and some very bright areas +like the clouds in the sky. + +185 +00:11:47,574 --> 00:11:50,377 +When turning on the HDR mode, +as on the right, + +186 +00:11:50,410 --> 00:11:52,613 +you can see that details in these regions, + +187 +00:11:52,646 --> 00:11:58,051 +like the fluffiness in the clouds, +are preserved much better in HDR. + +188 +00:11:58,085 --> 00:12:01,321 +Let's see how HDR is turned on in code. + +189 +00:12:01,355 --> 00:12:05,325 +You can query any video format +if it supports HDR + +190 +00:12:05,359 --> 00:12:09,162 +through its +'isVideoHDRSupported' property. + +191 +00:12:09,196 --> 00:12:13,700 +Currently, only non-binned video formats +support HDR. + +192 +00:12:13,734 --> 00:12:18,939 +If HDR is supported, set videoHDRAllowed +in the config to true + +193 +00:12:18,972 --> 00:12:22,309 +and run your session +with that configuration. + +194 +00:12:22,342 --> 00:12:25,712 +Turning on HDR +will have a performance impact, + +195 +00:12:25,746 --> 00:12:29,283 +so make sure to only use it +when there is a need for it. + +196 +00:12:29,316 --> 00:12:31,919 +In use cases where you prefer +manual control + +197 +00:12:31,952 --> 00:12:35,489 +over settings such as exposure +or white balance, + +198 +00:12:35,522 --> 00:12:40,227 +there is now convenient way +to directly access an AVCaptureDevice + +199 +00:12:40,260 --> 00:12:42,996 +and change any of its settings. + +200 +00:12:43,030 --> 00:12:44,498 +In our code example, + +201 +00:12:44,531 --> 00:12:48,702 +call 'configurableCaptureDevice +ForPrimaryCamera' + +202 +00:12:48,735 --> 00:12:54,308 +of your configuration to get access +to the underlying 'AVCaptureDevice'. + +203 +00:12:54,341 --> 00:12:58,779 +Use this capability to create custom looks +for your ARKit app, + +204 +00:12:58,812 --> 00:13:03,083 +but keep in mind that the image is +not only used as a rendering backdrop, + +205 +00:13:03,116 --> 00:13:07,321 +but is also actively used by ARKit +to analyze the scene. + +206 +00:13:07,354 --> 00:13:11,725 +So any changes like strong overexposure +might have a negative effect + +207 +00:13:11,758 --> 00:13:14,595 +on the output quality of ARKit. + +208 +00:13:14,628 --> 00:13:19,566 +You can also perform some advanced +operations, like triggering focus events. + +209 +00:13:19,600 --> 00:13:23,437 +For more information on how to configure +AVCaptureSessions, + +210 +00:13:23,470 --> 00:13:29,009 +please refer to the AVCapture +documentation on developer.apple.com. + +211 +00:13:29,042 --> 00:13:33,313 +Finally, ARKit exposes EXIF tags +to your app. + +212 +00:13:33,347 --> 00:13:36,984 +They are now available with every ARFrame. + +213 +00:13:37,017 --> 00:13:40,420 +EXIF tags contain useful information +about white balance, + +214 +00:13:40,454 --> 00:13:45,259 +exposure, and other settings +that can be valuable for post-processing. + +215 +00:13:45,292 --> 00:13:49,062 +That concludes all updates +on the image capture pipeline. + +216 +00:13:49,096 --> 00:13:51,932 +Let's see which changes we have +for Plane Anchors. + +217 +00:13:53,200 --> 00:13:58,805 +Plane anchors have been a popular feature +since the very first version of ARKit. + +218 +00:13:58,839 --> 00:14:03,577 +Many of you expressed the need to have +a cleaner separation of plane anchors + +219 +00:14:03,610 --> 00:14:05,879 +and the underlying plane geometry. + +220 +00:14:05,913 --> 00:14:08,348 +For that reason, we are announcing updates + +221 +00:14:08,382 --> 00:14:12,786 +on the behavior of the plane anchor +and the geometry of the plane. + +222 +00:14:12,819 --> 00:14:17,925 +This is an example of a typical +plane anchor in iOS 15. + +223 +00:14:17,958 --> 00:14:20,961 +At the beginning of the AR session, +it fits the plane + +224 +00:14:20,994 --> 00:14:24,198 +to this well-textured notebook +on the table. + +225 +00:14:24,231 --> 00:14:27,301 +When running the session, +the plane is gradually updated + +226 +00:14:27,334 --> 00:14:31,238 +to account for new parts of the table +that come into view. + +227 +00:14:31,271 --> 00:14:33,740 +Every time the plane geometry is updated, + +228 +00:14:33,774 --> 00:14:36,243 +the anchor rotation is updated as well + +229 +00:14:36,276 --> 00:14:39,213 +to reflect the new orientation +of the plane. + +230 +00:14:39,246 --> 00:14:44,218 +With iOS 16, we introduce a cleaner +separation between plane anchors + +231 +00:14:44,251 --> 00:14:45,986 +and their plane geometry. + +232 +00:14:47,154 --> 00:14:52,659 +Plane anchor and geometry updates +are now fully decoupled. + +233 +00:14:52,693 --> 00:14:56,230 +While the plane is extending +and updating its geometry + +234 +00:14:56,263 --> 00:14:58,465 +as the full table comes into view, + +235 +00:14:58,498 --> 00:15:02,236 +the anchor rotation itself +remains constant. + +236 +00:15:06,807 --> 00:15:09,943 +When contrasting with the old behavior +on the left hand side, + +237 +00:15:09,977 --> 00:15:13,680 +you can see that the plane anchor +in iOS 16 has stayed + +238 +00:15:13,714 --> 00:15:16,850 +at the same orientation, +aligned to the notebook, + +239 +00:15:16,884 --> 00:15:19,653 +throughout the whole AR Session. + +240 +00:15:21,421 --> 00:15:24,224 +All information on plane geometry + +241 +00:15:24,258 --> 00:15:28,529 +is now contained in a class +called ARPlaneExtent. + +242 +00:15:28,562 --> 00:15:33,800 +Rotation updates are no longer expressed +through rotating the plane anchor itself. + +243 +00:15:33,834 --> 00:15:39,339 +Instead, ARPlaneExtent contains +a new property, rotationOnYAxis, + +244 +00:15:39,373 --> 00:15:42,176 +that represents the angle of rotation. + +245 +00:15:42,209 --> 00:15:44,444 +In addition to this new property, + +246 +00:15:44,478 --> 00:15:48,348 +planes are fully defined +by width and height, + +247 +00:15:48,382 --> 00:15:52,252 +as well as the center coordinate +of the PlaneAnchor. + +248 +00:15:52,286 --> 00:15:56,423 +Let me show you how to create this +plane visualization in code. + +249 +00:15:58,525 --> 00:16:02,396 +First, we generate an entity +based on a plane mesh + +250 +00:16:02,429 --> 00:16:06,500 +according to the specified +width and height. + +251 +00:16:06,533 --> 00:16:11,672 +Then we set the entities transform +according to the rotation on y axis + +252 +00:16:11,705 --> 00:16:16,176 +and also offset it by the value +of the center property. + +253 +00:16:16,210 --> 00:16:21,081 +Every time the plane is updated, we have +to account for the fact that width, + +254 +00:16:21,114 --> 00:16:26,787 +height, and center coordinate and +the new rotationOnYAxis might change. + +255 +00:16:26,820 --> 00:16:31,391 +To make use of this new behavior, +set your deployment target to iOS 16 + +256 +00:16:31,425 --> 00:16:34,194 +in your Xcode settings. + +257 +00:16:34,228 --> 00:16:37,097 +The next update is on MotionCapture, + +258 +00:16:37,130 --> 00:16:42,202 +our machine learning masterminds worked +hard to make further improvements. + +259 +00:16:42,236 --> 00:16:44,338 +There is a whole suite of updates, + +260 +00:16:44,371 --> 00:16:49,176 +both for the 2D skeleton, +as well as for the 3D joints. + +261 +00:16:49,209 --> 00:16:52,946 +For the 2D skeleton +we are tracking two new joints: + +262 +00:16:52,980 --> 00:16:55,449 +the left and the right ear. + +263 +00:16:55,482 --> 00:16:59,086 +We also improved +the overall pose detection. + +264 +00:16:59,119 --> 00:17:02,856 +On iPhone 12 and up, +as well as on the latest iPad Pro + +265 +00:17:02,890 --> 00:17:05,325 +and iPad Air models with the M1 chip, + +266 +00:17:05,359 --> 00:17:09,096 +the 3D skeleton, as shown in red, +has been improved. + +267 +00:17:09,129 --> 00:17:14,535 +You will experience less jitter +and overall more temporal consistency. + +268 +00:17:14,568 --> 00:17:18,472 +Tracking is also more stable +if parts of the person are occluded + +269 +00:17:18,505 --> 00:17:21,308 +or when walking up close to the camera. + +270 +00:17:21,341 --> 00:17:26,180 +To make use of the improved MotionCapture, +set your deployment target to iOS 16 + +271 +00:17:26,213 --> 00:17:29,249 +in your Xcode settings. + +272 +00:17:29,283 --> 00:17:33,820 +Next, I would also like to announce +new cities and countries + +273 +00:17:33,854 --> 00:17:35,989 +where LocationAnchors will be supported. + +274 +00:17:36,023 --> 00:17:40,060 +As a small reminder, +Apple Maps uses the LocationAnchor API + +275 +00:17:40,093 --> 00:17:42,496 +to power its pedestrian walking +instructions. + +276 +00:17:42,529 --> 00:17:47,167 +In this example you can see that it can +lead you through the streets of London, + +277 +00:17:47,201 --> 00:17:50,938 +thanks to the power of LocationAnchors. + +278 +00:17:50,971 --> 00:17:54,041 +LocationAnchors are already available +in a growing number of cities + +279 +00:17:54,074 --> 00:17:57,311 +in the United States and in London, UK. + +280 +00:17:57,344 --> 00:18:01,415 +Starting today, they will be available +in the Canadian cities of Vancouver, + +281 +00:18:01,448 --> 00:18:03,483 +Toronto, and Montreal. + +282 +00:18:03,517 --> 00:18:06,086 +We are also enabling them in Singapore, + +283 +00:18:06,119 --> 00:18:10,157 +and in seven metropolitan areas in Japan, +including Tokyo. + +284 +00:18:10,190 --> 00:18:14,294 +As well as in Melbourne and Sydney, +Australia. + +285 +00:18:14,328 --> 00:18:17,431 +Later this year, +we will make them available in + +286 +00:18:17,464 --> 00:18:19,233 +Auckland, New Zealand, + +287 +00:18:19,266 --> 00:18:22,970 +Tel Aviv, Israel, +and Paris, France + +288 +00:18:23,003 --> 00:18:26,073 +If you are interested to know +if LocationAnchors are supported + +289 +00:18:26,106 --> 00:18:27,941 +at a particular coordinate, + +290 +00:18:27,975 --> 00:18:33,146 +just use the checkAvailability method +of ARGeoTrackingConfiguration. + +291 +00:18:33,981 --> 00:18:37,284 +And those were all the updates to ARKit 6. + +292 +00:18:37,317 --> 00:18:43,223 +To summarize, I presented how to run ARKit +in the new 4K video format. + +293 +00:18:43,257 --> 00:18:47,427 +For advanced use cases, +I demonstrated how to enable HDR + +294 +00:18:47,461 --> 00:18:51,698 +or get manual control +over the AVCaptureDevice. + +295 +00:18:51,732 --> 00:18:54,134 +For even more pixel hungry applications, + +296 +00:18:54,168 --> 00:18:59,173 +I demonstrated how to get high resolution +photos from an ARKit session. + +297 +00:18:59,206 --> 00:19:01,942 +We talked about the new behavior +of Plane Anchors, + +298 +00:19:01,975 --> 00:19:03,977 +and I presented the new ear joints + +299 +00:19:04,011 --> 00:19:07,080 +and other improvements in MotionCapture. + +300 +00:19:07,114 --> 00:19:10,284 +You also got to know +in which countries LocationAnchors + +301 +00:19:10,317 --> 00:19:12,986 +will be available later this year. + +302 +00:19:14,321 --> 00:19:15,656 +Thanks for tuning in. + +303 +00:19:15,689 --> 00:19:20,093 +Have a great WWDC 2022! + diff --git a/eng/2022 Session 10127 Create parametric 3D room scans with RoomPlan en.srt b/eng/2022 Session 10127 Create parametric 3D room scans with RoomPlan en.srt new file mode 100644 index 0000000..6f138b9 --- /dev/null +++ b/eng/2022 Session 10127 Create parametric 3D room scans with RoomPlan en.srt @@ -0,0 +1,1430 @@ +1 +00:00:00,167 --> 00:00:03,237 +♪ (Mellow instrumental +hip-hop music) ♪ + +2 +00:00:03,237 --> 00:00:09,810 +♪ + +3 +00:00:09,810 --> 00:00:11,712 +Praveen Sharma: Hi. +My name is Praveen, + +4 +00:00:11,712 --> 00:00:14,181 +and I'm from the Prototyping +team here at Apple. + +5 +00:00:14,181 --> 00:00:15,649 +Kai Kang: Hi. My name is Kai + +6 +00:00:15,649 --> 00:00:18,185 +and I'm from +the Video Engineering team. + +7 +00:00:18,185 --> 00:00:20,354 +Praveen: Over the past few years +Apple has enabled + +8 +00:00:20,354 --> 00:00:24,858 +powerful new ways for people to +bring the world into their apps. + +9 +00:00:24,858 --> 00:00:28,462 +Last year, +we introduced Object Capture, + +10 +00:00:28,462 --> 00:00:31,365 +which takes in photos +of real-world objects, + +11 +00:00:31,365 --> 00:00:34,668 +and using the Photogrammetry API +in RealityKit, + +12 +00:00:34,668 --> 00:00:39,873 +turns them into a 3D model +ready for use in your app. + +13 +00:00:39,873 --> 00:00:41,775 +Previous to Object Capture, + +14 +00:00:41,775 --> 00:00:44,811 +we released +the Scene Reconstruction API + +15 +00:00:44,811 --> 00:00:46,914 +which gives you +a coarse understanding + +16 +00:00:46,914 --> 00:00:49,149 +of the geometric structure +of your space + +17 +00:00:49,149 --> 00:00:52,252 +and enables brand-new +augmented reality use cases + +18 +00:00:52,252 --> 00:00:54,421 +in your apps. + +19 +00:00:54,421 --> 00:00:57,090 +This year, we are +very excited to announce + +20 +00:00:57,090 --> 00:01:01,895 +a brand-new framework called +RoomPlan. + +21 +00:01:01,895 --> 00:01:04,631 +RoomPlan allows you +to scan your room + +22 +00:01:04,631 --> 00:01:07,801 +using your LiDAR-enabled +iPhone or iPad. + +23 +00:01:07,801 --> 00:01:11,371 +It generates a parametric 3D +model of the room + +24 +00:01:11,371 --> 00:01:15,075 +and its room-defining objects +which you can use in your app. + +25 +00:01:15,075 --> 00:01:18,211 +Let's take a look at what +a RoomPlan scanning experience + +26 +00:01:18,211 --> 00:01:21,648 +looks like. + +27 +00:01:21,648 --> 00:01:25,218 +RoomPlan uses sophisticated +machine learning algorithms + +28 +00:01:25,218 --> 00:01:31,091 +powered by ARKit to detect +walls, windows, openings, + +29 +00:01:31,091 --> 00:01:34,227 +and doors, as well as +room-defining objects + +30 +00:01:34,227 --> 00:01:40,200 +like fireplaces, couches, +tables, and cabinets. + +31 +00:01:40,200 --> 00:01:42,336 +With our RoomCaptureView API, + +32 +00:01:42,336 --> 00:01:47,040 +which uses RealityKit to render +scanning progress in real time, + +33 +00:01:47,040 --> 00:01:49,977 +you can easily integrate +a scanning experience + +34 +00:01:49,977 --> 00:01:52,946 +into your app. + +35 +00:01:52,946 --> 00:01:55,015 +And when you are +finished scanning, + +36 +00:01:55,015 --> 00:01:59,186 +RoomCaptureView presents +the final post-processed results + +37 +00:01:59,186 --> 00:02:04,224 +for you to use however +best fits your use case. + +38 +00:02:04,224 --> 00:02:07,694 +For the first time, without +the complexities of implementing + +39 +00:02:07,694 --> 00:02:10,497 +machine learning +and computer vision algorithms, + +40 +00:02:10,497 --> 00:02:14,634 +people can now interact with +their room in brand new ways. + +41 +00:02:14,634 --> 00:02:17,637 +For example, interior design +apps can preview + +42 +00:02:17,637 --> 00:02:20,374 +wall color changes +and accurately calculate + +43 +00:02:20,374 --> 00:02:24,011 +the amount of paint +required to repaint a room. + +44 +00:02:24,011 --> 00:02:27,280 +Architecture apps can now +easily allow someone to preview + +45 +00:02:27,280 --> 00:02:31,752 +and edit changes to their room's +layout in real time. + +46 +00:02:31,752 --> 00:02:34,855 +Real estate apps can now +seamlessly enable agents + +47 +00:02:34,855 --> 00:02:39,059 +to capture floor plans +and 3D models of a listing. + +48 +00:02:39,059 --> 00:02:41,762 +And e-commerce apps +can engage customers + +49 +00:02:41,762 --> 00:02:46,099 +through product visualization +in their physical spaces. + +50 +00:02:46,099 --> 00:02:50,203 +These are just a few examples of +applications RoomPlan enables, + +51 +00:02:50,203 --> 00:02:52,506 +and you'll be surprised +to see how simple it is + +52 +00:02:52,506 --> 00:02:56,009 +to integrate RoomPlan +into your app. + +53 +00:02:56,009 --> 00:02:57,911 +Let's take a look. + +54 +00:02:57,911 --> 00:03:01,381 +There are two main ways +you can use RoomPlan. + +55 +00:03:01,381 --> 00:03:04,684 +The first is our out-of-the-box +scanning experience + +56 +00:03:04,684 --> 00:03:06,987 +which allows you to seamlessly +integrate RoomPlan + +57 +00:03:06,987 --> 00:03:08,855 +into your app. + +58 +00:03:08,855 --> 00:03:12,492 +The second is our data API +which enables your app + +59 +00:03:12,492 --> 00:03:15,495 +to use the live +parametric data from a scan + +60 +00:03:15,495 --> 00:03:18,498 +however best suits +your use case. + +61 +00:03:18,498 --> 00:03:22,502 +With both of these APIs, +we recommend some best practices + +62 +00:03:22,502 --> 00:03:25,238 +to help you achieve +the best possible scan results, + +63 +00:03:25,238 --> 00:03:30,010 +which we'll go over in the last +section of this presentation. + +64 +00:03:30,010 --> 00:03:33,246 +First, let's talk about +the scanning experience + +65 +00:03:33,246 --> 00:03:34,881 +that you can bring into your app + +66 +00:03:34,881 --> 00:03:38,552 +using our new +RoomCaptureView API. + +67 +00:03:38,552 --> 00:03:41,354 +RoomCaptureView +is a UIView subclass + +68 +00:03:41,354 --> 00:03:43,824 +that you can easily place +in your app. + +69 +00:03:43,824 --> 00:03:48,195 +It handles the presentation of +world space scanning feedback, + +70 +00:03:48,195 --> 00:03:50,964 +real-time room model +generation, + +71 +00:03:50,964 --> 00:03:54,134 +as well as coaching +and user guidance. + +72 +00:03:54,134 --> 00:03:56,403 +Let's take a closer look +at the design elements + +73 +00:03:56,403 --> 00:04:01,241 +presented during +a RoomCaptureView-based scan. + +74 +00:04:01,241 --> 00:04:04,111 +During an active +RoomCaptureView session, + +75 +00:04:04,111 --> 00:04:08,915 +animated lines outline detected +walls, windows, openings, + +76 +00:04:08,915 --> 00:04:13,620 +doors, and room-defining objects +in real time. + +77 +00:04:13,620 --> 00:04:16,323 +The interactive 3D model +generated in real time + +78 +00:04:16,323 --> 00:04:18,525 +at the bottom +of the RoomCaptureView, + +79 +00:04:18,525 --> 00:04:23,163 +gives you an overview of your +scanning progress at a glance. + +80 +00:04:23,163 --> 00:04:25,899 +Finally, text coaching +guides you + +81 +00:04:25,899 --> 00:04:29,302 +to the best possible +scanning results. + +82 +00:04:29,302 --> 00:04:32,305 +Let's take a look at how you can +start using RoomCaptureView + +83 +00:04:32,305 --> 00:04:35,809 +in just four easy steps. + +84 +00:04:35,809 --> 00:04:38,879 +First, we create +a RoomCaptureView reference + +85 +00:04:38,879 --> 00:04:40,814 +in our ViewController. + +86 +00:04:40,814 --> 00:04:43,116 +Second, we create a reference + +87 +00:04:43,116 --> 00:04:46,753 +to our RoomCaptureSession +configuration object. + +88 +00:04:46,753 --> 00:04:49,489 +Third, we start +our scan session, + +89 +00:04:49,489 --> 00:04:51,191 +passing in our configuration + +90 +00:04:51,191 --> 00:04:54,027 +to the capture session's +run function. + +91 +00:04:54,027 --> 00:04:57,464 +And finally, our application +tells the capture session + +92 +00:04:57,464 --> 00:05:00,634 +to stop scanning. + +93 +00:05:00,634 --> 00:05:04,137 +Optionally, your app can adhere +to our RoomCaptureViewDelegate + +94 +00:05:04,137 --> 00:05:07,674 +protocol and opt out of +post-processed results + +95 +00:05:07,674 --> 00:05:09,276 +and their presentation + +96 +00:05:09,276 --> 00:05:11,878 +or handle the post-processed +scan results + +97 +00:05:11,878 --> 00:05:14,981 +once they have been presented. + +98 +00:05:14,981 --> 00:05:18,418 +For example, you can export +a USDZ of the results + +99 +00:05:18,418 --> 00:05:20,754 +by calling +the export function available + +100 +00:05:20,754 --> 00:05:25,392 +on the provided +CapturedRoom data struct. + +101 +00:05:25,392 --> 00:05:27,694 +And that's how simple it is +to integrate RoomPlan + +102 +00:05:27,694 --> 00:05:29,329 +into your app. + +103 +00:05:29,329 --> 00:05:32,599 +We are so excited to see +what you make with this API. + +104 +00:05:32,599 --> 00:05:36,503 +Now my colleague Kai will talk +about RoomCaptureSession + +105 +00:05:36,503 --> 00:05:39,506 +and RoomPlan's Data API. + +106 +00:05:39,506 --> 00:05:40,707 +Kai: Thanks, Praveen. + +107 +00:05:40,707 --> 00:05:43,743 +In this section, we will +walk you through the Data APIs + +108 +00:05:43,743 --> 00:05:46,947 +that provide you the access to +the underlying data structures + +109 +00:05:46,947 --> 00:05:51,117 +during scanning and can help +you build a custom visualization + +110 +00:05:51,117 --> 00:05:55,589 +of the scanning experience +from ground up. + +111 +00:05:55,589 --> 00:05:58,658 +The basic workflow +consists of three parts: + +112 +00:05:58,658 --> 00:06:03,296 +scan, process, and export. + +113 +00:06:03,296 --> 00:06:06,533 +For scanning, we will cover +the basics of how to set up + +114 +00:06:06,533 --> 00:06:08,401 +and start the capture session, + +115 +00:06:08,401 --> 00:06:13,340 +as well as display and monitor +the capture process. + +116 +00:06:13,340 --> 00:06:16,943 +Then we'll look at how +your scanned data is processed + +117 +00:06:16,943 --> 00:06:21,982 +and the final model is received +for presentation. + +118 +00:06:21,982 --> 00:06:25,819 +Finally, we'll discuss +how you can generate and export + +119 +00:06:25,819 --> 00:06:29,022 +the output USD file +which can also be used + +120 +00:06:29,022 --> 00:06:32,993 +in your USD workflows. + +121 +00:06:32,993 --> 00:06:36,930 +Now, let's look into +the Scan step in detail. + +122 +00:06:36,930 --> 00:06:39,132 +We will use +the RoomCaptureSession API + +123 +00:06:39,132 --> 00:06:41,901 +to set up the session +and display the progress + +124 +00:06:41,901 --> 00:06:43,970 +as we continue scanning. + +125 +00:06:43,970 --> 00:06:47,040 +Let me show you in code. + +126 +00:06:47,040 --> 00:06:50,744 +Here's a simple RealityKit app +as an example. + +127 +00:06:50,744 --> 00:06:56,583 +To start, simply import RoomPlan +into your Swift project. + +128 +00:06:56,583 --> 00:06:58,451 +In the ViewController +of your app, + +129 +00:06:58,451 --> 00:07:01,788 +you can have a custom type +to visualize the results + +130 +00:07:01,788 --> 00:07:05,859 +and to initiate +a RoomCaptureSession instance. + +131 +00:07:05,859 --> 00:07:09,129 +Additionally, RoomCaptureSession +provides a handle + +132 +00:07:09,129 --> 00:07:12,966 +to the underlying AR session so +that your apps can draw planes + +133 +00:07:12,966 --> 00:07:17,237 +and object bounding boxes +in the AR view. + +134 +00:07:17,237 --> 00:07:20,240 +RoomCaptureSession +adopts the delegate pattern. + +135 +00:07:20,240 --> 00:07:21,875 +In your ViewController class, + +136 +00:07:21,875 --> 00:07:24,144 +you can assign +the ViewController itself + +137 +00:07:24,144 --> 00:07:26,780 +as the captureSession's +delegate. + +138 +00:07:26,780 --> 00:07:28,548 +This would allow +the ViewController + +139 +00:07:28,548 --> 00:07:33,553 +to get real-time updates +from the RoomCaptureSession. + +140 +00:07:33,553 --> 00:07:36,690 +Theses updates include +3D models and instructions + +141 +00:07:36,690 --> 00:07:39,659 +in order to guide people +during the capture. + +142 +00:07:39,659 --> 00:07:42,195 +To get these updates, +your ViewController + +143 +00:07:42,195 --> 00:07:45,198 +needs to conform to the +RoomCaptureSessionDelegate + +144 +00:07:45,198 --> 00:07:48,968 +protocol and implement +two methods. + +145 +00:07:48,968 --> 00:07:50,303 +The first one is + +146 +00:07:50,303 --> 00:07:52,906 +captureSession(_ session: +didUpdate room:) method + +147 +00:07:52,906 --> 00:07:57,277 +in order to get the real-time +CapturedRoom data structure. + +148 +00:07:57,277 --> 00:08:01,281 +Your visualizer can use it to +update AR view of the 3D model, + +149 +00:08:01,281 --> 00:08:03,616 +which provides +real-time feedback to people + +150 +00:08:03,616 --> 00:08:05,819 +on the progress. + +151 +00:08:05,819 --> 00:08:08,188 +We will dive into +the CapturedRoom structure + +152 +00:08:08,188 --> 00:08:11,191 +more in a later part +of the talk. + +153 +00:08:11,191 --> 00:08:13,893 +This method will be called +when we detect updates + +154 +00:08:13,893 --> 00:08:16,162 +to the captured room. + +155 +00:08:16,162 --> 00:08:17,597 +The second method is + +156 +00:08:17,597 --> 00:08:21,034 +captureSession(_ session: +didProvide instruction:). + +157 +00:08:21,034 --> 00:08:24,537 +This method provides you +with an instruction structure + +158 +00:08:24,537 --> 00:08:27,374 +which contains real-time +feedback. + +159 +00:08:27,374 --> 00:08:30,143 +Your visualizer can use +the instruction + +160 +00:08:30,143 --> 00:08:33,146 +to guide people during the scan. + +161 +00:08:33,146 --> 00:08:34,748 +Let's go through +the instructions + +162 +00:08:34,748 --> 00:08:37,951 +that this API provides. + +163 +00:08:37,951 --> 00:08:42,889 +These instructions include +distance to the objects, + +164 +00:08:42,889 --> 00:08:47,527 +scanning speed, +lighting adjustment to the room, + +165 +00:08:47,527 --> 00:08:50,697 +as well as focusing on +specific areas of the room + +166 +00:08:50,697 --> 00:08:53,366 +that have more textures. + +167 +00:08:53,366 --> 00:08:56,236 +These instructions will be +provided during the scan + +168 +00:08:56,236 --> 00:09:01,141 +in order to guide people +with real-time feedback. + +169 +00:09:01,141 --> 00:09:04,611 +Next, we will move on +to the process part. + +170 +00:09:04,611 --> 00:09:07,580 +In this section, we will use +the RoomBuilder class + +171 +00:09:07,580 --> 00:09:13,720 +to process the scanned data and +generate the final 3D models. + +172 +00:09:13,720 --> 00:09:17,390 +To process the captured data, +the first step is to initiate + +173 +00:09:17,390 --> 00:09:21,828 +a RoomBuilder instance +in your ViewController class. + +174 +00:09:21,828 --> 00:09:24,397 +Next, in order to receive +the sensor data + +175 +00:09:24,397 --> 00:09:27,634 +after the capture process, +your app needs to implement + +176 +00:09:27,634 --> 00:09:32,305 +the captureSession(_ session: +didEndWith data: error:) method. + +177 +00:09:32,305 --> 00:09:34,908 +When the RoomCaptureSession +is stopped, by calling + +178 +00:09:34,908 --> 00:09:38,278 +the stop() function in your app, +or due to an error, + +179 +00:09:38,278 --> 00:09:42,415 +this function will be called to +return a CaptureRoomData object + +180 +00:09:42,415 --> 00:09:45,585 +and an optional error. + +181 +00:09:45,585 --> 00:09:48,288 +Finally, to process +the captured data, + +182 +00:09:48,288 --> 00:09:52,225 +we call the roomBuilder's +async roomModel(from:) method + +183 +00:09:52,225 --> 00:09:55,094 +with the await keyword. + +184 +00:09:55,094 --> 00:09:58,998 +The method runs asynchronously +to process the scanned data + +185 +00:09:58,998 --> 00:10:01,601 +and build the final 3D model. + +186 +00:10:01,601 --> 00:10:05,004 +It utilizes the Swift +async/await function + +187 +00:10:05,004 --> 00:10:09,175 +that we introduced +in last year's WWDC. + +188 +00:10:09,175 --> 00:10:12,679 +Within just a few seconds, +the model will be available + +189 +00:10:12,679 --> 00:10:17,484 +for the final presentation +in your app. + +190 +00:10:17,484 --> 00:10:19,486 +Now, let's dive into the details + +191 +00:10:19,486 --> 00:10:21,521 +of the CapturedRoom +data structure + +192 +00:10:21,521 --> 00:10:26,025 +and how you can export it +to use in your app. + +193 +00:10:26,025 --> 00:10:28,695 +At the top level, +there is CapturedRoom + +194 +00:10:28,695 --> 00:10:32,599 +which consists of +Surfaces and Objects. + +195 +00:10:32,599 --> 00:10:36,736 +Surface contains unique +attributes to represent curves + +196 +00:10:36,736 --> 00:10:40,607 +such as radius; +starting and ending angles; + +197 +00:10:40,607 --> 00:10:43,843 +four different edges +of the surface; + +198 +00:10:43,843 --> 00:10:49,649 +and architecture categories of +wall, opening, window, door. + +199 +00:10:49,649 --> 00:10:52,252 +Object contains +furniture categories + +200 +00:10:52,252 --> 00:10:56,789 +such as table, bed, +sofa, etc. + +201 +00:10:56,789 --> 00:11:00,393 +Surface and Object share +some common attributes + +202 +00:11:00,393 --> 00:11:02,462 +such as dimensions; + +203 +00:11:02,462 --> 00:11:05,832 +confidence, which gives you +three levels of confidence + +204 +00:11:05,832 --> 00:11:08,268 +for the scanned surface +or object; + +205 +00:11:08,268 --> 00:11:13,373 +the 3D transform matrix; +as well as a unique identifier. + +206 +00:11:13,373 --> 00:11:17,977 +Let's see how they are +represented in code. + +207 +00:11:17,977 --> 00:11:21,681 +The CapturedRoom structure is a +fully parametric representation + +208 +00:11:21,681 --> 00:11:23,950 +of the elements in the room. + +209 +00:11:23,950 --> 00:11:27,520 +It contains five properties +including walls, openings, + +210 +00:11:27,520 --> 00:11:31,558 +doors, windows, +and objects in the room. + +211 +00:11:31,558 --> 00:11:33,593 +For the first four elements, + +212 +00:11:33,593 --> 00:11:36,563 +they are represented +as the Surface structure + +213 +00:11:36,563 --> 00:11:41,000 +which represents 2D planar +architectural structures. + +214 +00:11:41,000 --> 00:11:43,436 +On the right, you can see +the various properties + +215 +00:11:43,436 --> 00:11:47,473 +of Surface we covered earlier. + +216 +00:11:47,473 --> 00:11:51,444 +The last property is an array +of 3D objects in the room, + +217 +00:11:51,444 --> 00:11:54,247 +and they are represented +as cuboids. + +218 +00:11:54,247 --> 00:11:59,919 +On the right, you can see the +various properties of Object. + +219 +00:11:59,919 --> 00:12:04,190 +Here is the list of object +types we support in RoomPlan. + +220 +00:12:04,190 --> 00:12:07,260 +These include a variety +of common furniture types + +221 +00:12:07,260 --> 00:12:12,899 +such as sofa, table, +chair, bed, and many more. + +222 +00:12:12,899 --> 00:12:16,336 +Finally, the export function +allows you to export + +223 +00:12:16,336 --> 00:12:20,006 +this CapturedRoom +into a USD or USDZ data + +224 +00:12:20,006 --> 00:12:24,310 +for your existing workflows. + +225 +00:12:24,310 --> 00:12:27,647 +Here is an example to show +how you can directly open + +226 +00:12:27,647 --> 00:12:30,483 +the USD output in Cinema 4D + +227 +00:12:30,483 --> 00:12:33,319 +to browse and edit +the hierarchical data structure + +228 +00:12:33,319 --> 00:12:35,922 +of the room, as well as +the dimensions and location + +229 +00:12:35,922 --> 00:12:40,326 +of each room element or object. + +230 +00:12:40,326 --> 00:12:44,230 +You can also leverage your +existing USD and USDZ workflows + +231 +00:12:44,230 --> 00:12:47,200 +to add renders +of the captured room + +232 +00:12:47,200 --> 00:12:52,105 +into a variety of applications +such as real estate, e-commerce, + +233 +00:12:52,105 --> 00:12:57,143 +utilities, and interior design. + +234 +00:12:57,143 --> 00:12:59,212 +So far, we covered +the scanning experience + +235 +00:12:59,212 --> 00:13:02,115 +and underlying RoomPlan APIs. + +236 +00:13:02,115 --> 00:13:04,550 +We'll now go through +some best practices + +237 +00:13:04,550 --> 00:13:09,022 +to help you get good results +with RoomPlan. + +238 +00:13:09,022 --> 00:13:10,923 +We will cover +the recommended conditions + +239 +00:13:10,923 --> 00:13:12,959 +that allow for a good scan, + +240 +00:13:12,959 --> 00:13:16,829 +room features to look out for +while selecting a room, + +241 +00:13:16,829 --> 00:13:19,632 +as well as a few scanning +and thermal considerations + +242 +00:13:19,632 --> 00:13:22,735 +to keep in mind. + +243 +00:13:22,735 --> 00:13:26,305 +RoomPlan API supports most +common architectural structures + +244 +00:13:26,305 --> 00:13:29,142 +and objects +in a typical household. + +245 +00:13:29,142 --> 00:13:31,711 +It works best +for a single residential room + +246 +00:13:31,711 --> 00:13:35,248 +with a maximum room size +of 30 feet by 30 feet + +247 +00:13:35,248 --> 00:13:38,284 +or around 9 by 9 meters. + +248 +00:13:38,284 --> 00:13:40,787 +Lighting is also important +for the API + +249 +00:13:40,787 --> 00:13:45,658 +to get a clear video stream and +good AR tracking performance. + +250 +00:13:45,658 --> 00:13:50,096 +A minimum 50 lux or higher is +recommended for using the API, + +251 +00:13:50,096 --> 00:13:54,233 +which is typical for +a family living room at night. + +252 +00:13:54,233 --> 00:13:57,403 +For the hardware, RoomPlan API +is supported on all + +253 +00:13:57,403 --> 00:14:02,709 +LiDAR-enabled iPhone +and iPad Pro models. + +254 +00:14:02,709 --> 00:14:04,343 +There are +some special conditions + +255 +00:14:04,343 --> 00:14:07,246 +that can present a challenge +for the API. + +256 +00:14:07,246 --> 00:14:11,184 +For example, full-height mirrors +and glass pose a challenge + +257 +00:14:11,184 --> 00:14:15,188 +for the LiDAR sensor +to produce the expected output. + +258 +00:14:15,188 --> 00:14:18,658 +Even high ceilings could exceed +the limit of the scanning range + +259 +00:14:18,658 --> 00:14:20,493 +of the LiDAR sensor. + +260 +00:14:20,493 --> 00:14:27,033 +Also, very dark surfaces could +be hard for the device to scan. + +261 +00:14:27,033 --> 00:14:31,571 +There are some considerations +to get better scanning results. + +262 +00:14:31,571 --> 00:14:36,609 +First, for applications that +have high accuracy requirements, + +263 +00:14:36,609 --> 00:14:38,778 +preparing the room +before scanning + +264 +00:14:38,778 --> 00:14:41,414 +can enhance the quality +of the scan. + +265 +00:14:41,414 --> 00:14:43,549 +For example, +opening the curtains + +266 +00:14:43,549 --> 00:14:47,620 +can let more natural light in +and reduce window occlusions, + +267 +00:14:47,620 --> 00:14:50,690 +which works best +for daytime scans. + +268 +00:14:50,690 --> 00:14:53,159 +Closing the doors +can reduce the chance + +269 +00:14:53,159 --> 00:14:57,196 +of scanning unnecessary area +outside of the room. + +270 +00:14:57,196 --> 00:15:00,700 +Following a good scanning +motion is also very important + +271 +00:15:00,700 --> 00:15:03,770 +to achieving good +scanning results with the API. + +272 +00:15:03,770 --> 00:15:07,440 +And that is why we provide the +user instruction delegate method + +273 +00:15:07,440 --> 00:15:12,044 +in order to provide feedback +on textures, distance, speed, + +274 +00:15:12,044 --> 00:15:16,682 +and lighting conditions +to people during the scans. + +275 +00:15:16,682 --> 00:15:18,284 +Another thing to keep in mind + +276 +00:15:18,284 --> 00:15:21,587 +is battery and thermals +of the device. + +277 +00:15:21,587 --> 00:15:24,657 +We have done many optimizations +on RoomPlan API + +278 +00:15:24,657 --> 00:15:27,693 +to ensure a good +scanning experience. + +279 +00:15:27,693 --> 00:15:30,997 +Nevertheless, it's best +to avoid repeated scans + +280 +00:15:30,997 --> 00:15:34,267 +or single long scans +over 5 minutes. + +281 +00:15:34,267 --> 00:15:36,235 +These could +not only cause fatigue + +282 +00:15:36,235 --> 00:15:39,305 +but also drain out the battery +and create thermal issues + +283 +00:15:39,305 --> 00:15:45,278 +which might in turn impact +the user experience of your app. + +284 +00:15:45,278 --> 00:15:47,346 +There is a lot +that we covered today. + +285 +00:15:47,346 --> 00:15:50,349 +We introduced a brand-new API, +RoomPlan. + +286 +00:15:50,349 --> 00:15:52,985 +It provides an intuitive +scanning experience + +287 +00:15:52,985 --> 00:15:54,887 +to capture your rooms, + +288 +00:15:54,887 --> 00:15:58,457 +powerful machine learning models +to understand the environment, + +289 +00:15:58,457 --> 00:16:01,861 +as well as a fully parametric +USD output format + +290 +00:16:01,861 --> 00:16:05,064 +for easy integration +in your apps. + +291 +00:16:05,064 --> 00:16:07,767 +For guidance on how to better +design and implement + +292 +00:16:07,767 --> 00:16:10,002 +your new RoomPlan experience, + +293 +00:16:10,002 --> 00:16:13,506 +please check out +the related talks below. + +294 +00:16:13,506 --> 00:16:16,342 +Praveen: It's time for you +to try RoomPlan in your app. + +295 +00:16:16,342 --> 00:16:19,779 +We can't wait to see what you +can create with this new API. + +296 +00:16:19,779 --> 00:16:21,581 +Kai: Thanks for watching! + +297 +00:16:21,581 --> 00:16:26,018 +♪ + diff --git a/eng/2022 Session 10128 Bring your world into augmented reality en.srt b/eng/2022 Session 10128 Bring your world into augmented reality en.srt new file mode 100644 index 0000000..e628859 --- /dev/null +++ b/eng/2022 Session 10128 Bring your world into augmented reality en.srt @@ -0,0 +1,2392 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,810 +♪ + +3 +00:00:09,810 --> 00:00:11,879 +Hao Tang: Hi, my name is Hao. + +4 +00:00:11,879 --> 00:00:15,115 +I'm an engineer +on the Object Capture team. + +5 +00:00:15,115 --> 00:00:18,986 +Today, my colleague Risa and I +will be showing you how to use + +6 +00:00:18,986 --> 00:00:21,989 +the Object Capture API +and RealityKit + +7 +00:00:21,989 --> 00:00:25,325 +to create 3D models +of real-world objects + +8 +00:00:25,325 --> 00:00:27,628 +and bring them into AR. + +9 +00:00:27,628 --> 00:00:29,396 +Let's get started. + +10 +00:00:29,396 --> 00:00:32,633 +First, I'll give you a recap +of Object Capture, + +11 +00:00:32,633 --> 00:00:35,502 +which we launched +as a RealityKit API + +12 +00:00:35,502 --> 00:00:38,238 +on macOS last year. + +13 +00:00:38,238 --> 00:00:40,307 +Then, I'll introduce you + +14 +00:00:40,307 --> 00:00:43,310 +to a couple of camera +enhancements in ARKit, + +15 +00:00:43,310 --> 00:00:47,047 +which allow you to capture +high-res photos of your object + +16 +00:00:47,047 --> 00:00:48,582 +and can help you +better integrate + +17 +00:00:48,582 --> 00:00:52,753 +Object Capture +into your AR applications. + +18 +00:00:52,753 --> 00:00:56,223 +After that, I will go through +the best practice guidelines + +19 +00:00:56,223 --> 00:00:58,792 +of Object Capture +so you can continue + +20 +00:00:58,792 --> 00:01:02,362 +to make the best +out of this technology. + +21 +00:01:02,362 --> 00:01:05,265 +In the last section, +Risa will take you through + +22 +00:01:05,265 --> 00:01:09,436 +an end-to-end workflow with +Object Capture in RealityKit + +23 +00:01:09,436 --> 00:01:12,506 +and demonstrate how +you can bring real-world objects + +24 +00:01:12,506 --> 00:01:15,609 +into an AR experience. + +25 +00:01:15,609 --> 00:01:19,246 +Let's start with a quick recap +of Object Capture. + +26 +00:01:19,246 --> 00:01:22,316 +Object Capture +is a computer vision technology + +27 +00:01:22,316 --> 00:01:25,018 +that you can leverage +to easily turn images + +28 +00:01:25,018 --> 00:01:29,523 +of real-world objects +into detailed 3D models. + +29 +00:01:29,523 --> 00:01:32,059 +You begin by taking photos +of your object + +30 +00:01:32,059 --> 00:01:37,030 +from various angles +with an iPhone, iPad, or DSLR. + +31 +00:01:37,030 --> 00:01:39,633 +Then, you copy +those photos to a Mac + +32 +00:01:39,633 --> 00:01:42,836 +which supports Object Capture. + +33 +00:01:42,836 --> 00:01:45,138 +Using the Photogrammetry API, + +34 +00:01:45,138 --> 00:01:49,142 +RealityKit can transform +your photos into a 3D model + +35 +00:01:49,142 --> 00:01:51,878 +in just a few minutes. + +36 +00:01:51,878 --> 00:01:55,182 +The output model includes both +a geometric mesh + +37 +00:01:55,182 --> 00:01:58,585 +as well as various material +maps, including textures, + +38 +00:01:58,585 --> 00:02:02,522 +that are automatically applied +to your model. + +39 +00:02:02,522 --> 00:02:05,492 +For more details +of the Object Capture API, + +40 +00:02:05,492 --> 00:02:07,561 +I would highly recommend +that you watch + +41 +00:02:07,561 --> 00:02:12,499 +last year's WWDC session +on Object Capture. + +42 +00:02:12,499 --> 00:02:16,269 +Many developers have created +amazing 3D capture apps + +43 +00:02:16,269 --> 00:02:21,241 +using Object Capture: +Unity, Cinema4D, Qlone, + +44 +00:02:21,241 --> 00:02:25,078 +PolyCam, PhotoCatch, +just to name a few. + +45 +00:02:25,078 --> 00:02:28,382 +In addition to this, +we have beautiful-looking models + +46 +00:02:28,382 --> 00:02:31,351 +that were created +using this API. + +47 +00:02:31,351 --> 00:02:34,688 +Here's a few models that +were created by Ethan Saadia + +48 +00:02:34,688 --> 00:02:36,957 +using the power +of Object Capture + +49 +00:02:36,957 --> 00:02:39,626 +within the PhotoCatch app. + +50 +00:02:39,626 --> 00:02:43,597 +And our friend Mikko Haapoja +from Shopify also generated + +51 +00:02:43,597 --> 00:02:48,068 +a bunch of great-looking +3D models using this API. + +52 +00:02:48,068 --> 00:02:50,704 +The detailed quality +of the output 3D models + +53 +00:02:50,704 --> 00:02:56,143 +you get with Object Capture is +highly beneficial in e-commerce. + +54 +00:02:56,143 --> 00:02:58,412 +Here's the GOAT app, +for example, + +55 +00:02:58,412 --> 00:03:02,115 +that lets you try on a variety +of shoes on your feet. + +56 +00:03:02,115 --> 00:03:04,351 +All of these shoe models +have been created + +57 +00:03:04,351 --> 00:03:07,921 +with the Object Capture API +which has been designed + +58 +00:03:07,921 --> 00:03:12,392 +to capture the finest level +of detail on them. + +59 +00:03:12,392 --> 00:03:15,195 +This can go a long way +in helping you + +60 +00:03:15,195 --> 00:03:18,331 +with your purchase decision +on a product, + +61 +00:03:18,331 --> 00:03:22,702 +or even try out an accurate fit +for an object in your space. + +62 +00:03:22,702 --> 00:03:26,106 +For example, the Plant Story app +lets you preview + +63 +00:03:26,106 --> 00:03:30,177 +real-looking 3D models +of various plants in your space, + +64 +00:03:30,177 --> 00:03:34,147 +all of which have been created +with Object Capture. + +65 +00:03:34,147 --> 00:03:36,650 +This can help you get a sense +of how much space + +66 +00:03:36,650 --> 00:03:38,819 +you may need for a plant, + +67 +00:03:38,819 --> 00:03:43,457 +or simply see them in your space +in realistic detail. + +68 +00:03:43,457 --> 00:03:45,525 +Speaking about realism, + +69 +00:03:45,525 --> 00:03:49,563 +were you able to spot +the real plant in this video? + +70 +00:03:49,563 --> 00:03:52,299 +Yes, it's the one +in the white planter + +71 +00:03:52,299 --> 00:03:55,769 +on the left-most corner +of the table. + +72 +00:03:55,769 --> 00:03:58,305 +We are very thrilled +to see such a stunning + +73 +00:03:58,305 --> 00:04:00,974 +and widespread use +of the Object Capture API + +74 +00:04:00,974 --> 00:04:04,111 +since its launch in 2021. + +75 +00:04:04,111 --> 00:04:08,381 +Now, let's talk about some +camera enhancements in ARKit, + +76 +00:04:08,381 --> 00:04:11,651 +which will greatly help +the quality of reconstruction + +77 +00:04:11,651 --> 00:04:14,087 +with Object Capture. + +78 +00:04:14,087 --> 00:04:16,389 +A great Object Capture +experience starts + +79 +00:04:16,389 --> 00:04:21,128 +with taking good photos +of objects from all sides. + +80 +00:04:21,128 --> 00:04:24,764 +To this end, you can use +any high-resolution camera, + +81 +00:04:24,764 --> 00:04:26,833 +like the iPhone or iPad, + +82 +00:04:26,833 --> 00:04:31,071 +or even a DSLR +or mirrorless camera. + +83 +00:04:31,071 --> 00:04:34,741 +If you use the Camera app +on your iPhone or iPad, + +84 +00:04:34,741 --> 00:04:37,410 +you can take high-quality +photos with depth + +85 +00:04:37,410 --> 00:04:41,081 +and gravity information which +lets the Object Capture API + +86 +00:04:41,081 --> 00:04:43,717 +automatically recover +the real-world scale + +87 +00:04:43,717 --> 00:04:47,254 +and orientation of the object. + +88 +00:04:47,254 --> 00:04:51,124 +In addition to that, +if you use an iPhone or iPad, + +89 +00:04:51,124 --> 00:04:54,461 +you can take advantage +of ARKit's tracking capabilities + +90 +00:04:54,461 --> 00:04:58,598 +to overlay a 3D guidance UI +on top of the model + +91 +00:04:58,598 --> 00:05:03,203 +to get good coverage +of the object from all sides. + +92 +00:05:03,203 --> 00:05:05,038 +Another important thing to note + +93 +00:05:05,038 --> 00:05:08,842 +is that the higher the image +resolution from your capture, + +94 +00:05:08,842 --> 00:05:11,778 +the better the quality +of the 3D model + +95 +00:05:11,778 --> 00:05:14,581 +that Object Capture can produce. + +96 +00:05:14,581 --> 00:05:15,882 +To that end, + +97 +00:05:15,882 --> 00:05:19,719 +with this year's ARKit release +we are introducing a brand-new + +98 +00:05:19,719 --> 00:05:23,423 +high-resolution +background photos API. + +99 +00:05:23,423 --> 00:05:28,061 +This API lets you capture photos +at native camera resolution + +100 +00:05:28,061 --> 00:05:31,164 +while you are still running +an ARSession. + +101 +00:05:31,164 --> 00:05:36,002 +It allows you to use your 3D +UI overlays on top of the object + +102 +00:05:36,002 --> 00:05:40,240 +while taking full advantage +of the camera sensor on device. + +103 +00:05:40,240 --> 00:05:44,044 +On an iPhone 13, that means +the full 12 megapixels + +104 +00:05:44,044 --> 00:05:47,547 +native resolution +of the Wide camera. + +105 +00:05:47,547 --> 00:05:50,250 +This API is nonintrusive. + +106 +00:05:50,250 --> 00:05:52,852 +It does not interrupt +the continuous video stream + +107 +00:05:52,852 --> 00:05:56,423 +of the current ARSession, +so your app will continue + +108 +00:05:56,423 --> 00:06:01,061 +to provide a smooth +AR experience for your users. + +109 +00:06:01,061 --> 00:06:05,165 +In addition, ARKit makes +EXIF metadata available + +110 +00:06:05,165 --> 00:06:07,767 +in the photos, +which allows your app + +111 +00:06:07,767 --> 00:06:12,038 +to read useful information +about white balance, exposure, + +112 +00:06:12,038 --> 00:06:16,910 +and other settings that can be +valuable for post-processing. + +113 +00:06:16,910 --> 00:06:22,882 +ARKit makes it extremely easy +to use this new API in your app. + +114 +00:06:22,882 --> 00:06:25,318 +You can simply query +a video format + +115 +00:06:25,318 --> 00:06:27,954 +that supports high-resolution +frame capturing + +116 +00:06:27,954 --> 00:06:33,059 +on ARWorldTrackingConfguration, +and if successful, + +117 +00:06:33,059 --> 00:06:37,897 +set the new video format +and run the ARSession. + +118 +00:06:37,897 --> 00:06:41,668 +When it comes to capturing +a high-res photo, + +119 +00:06:41,668 --> 00:06:44,137 +simply call ARSession's new + +120 +00:06:44,137 --> 00:06:47,574 +captureHighResolutionFrame API +function, + +121 +00:06:47,574 --> 00:06:50,310 +which will return to you +a high-res photo + +122 +00:06:50,310 --> 00:06:53,480 +via a completion handler +asynchronously. + +123 +00:06:53,480 --> 00:06:56,850 +It is that simple. + +124 +00:06:56,850 --> 00:06:59,686 +We have also recognized +that there are use cases + +125 +00:06:59,686 --> 00:07:03,657 +where you may prefer manual +control over the camera settings + +126 +00:07:03,657 --> 00:07:07,661 +such as focus, exposure, +or white balance. + +127 +00:07:07,661 --> 00:07:11,231 +So we are providing you +with a convenient way + +128 +00:07:11,231 --> 00:07:14,934 +to access the underlying +AVCaptureDevice directly + +129 +00:07:14,934 --> 00:07:19,139 +and change its properties +for fine-grained camera control. + +130 +00:07:19,139 --> 00:07:22,776 +As shown in this code example, +simply call + +131 +00:07:22,776 --> 00:07:25,645 +configurableCaptureDevice +ForPrimaryCamera + +132 +00:07:25,645 --> 00:07:28,515 +on your +ARWorldTrackingConfiguration + +133 +00:07:28,515 --> 00:07:33,553 +to get access to the underlying +AVCaptureDevice. + +134 +00:07:33,553 --> 00:07:36,056 +For more details +on these enhancements, + +135 +00:07:36,056 --> 00:07:38,224 +I highly recommend +you to check out + +136 +00:07:38,224 --> 00:07:44,097 +the "Discover ARKit 6 session" +from this year's WWDC. + +137 +00:07:44,097 --> 00:07:47,500 +Now, let's go through +some best practice guidelines + +138 +00:07:47,500 --> 00:07:49,669 +with Object Capture. + +139 +00:07:49,669 --> 00:07:52,806 +First things first; +we need to choose an object + +140 +00:07:52,806 --> 00:07:56,810 +with the right characteristics +for Object Capture. + +141 +00:07:56,810 --> 00:08:01,081 +A good object has +adequate texture on its surface. + +142 +00:08:01,081 --> 00:08:04,951 +If some regions of the object +are textureless or transparent, + +143 +00:08:04,951 --> 00:08:09,923 +the details in those regions +may not be reconstructed well. + +144 +00:08:09,923 --> 00:08:14,127 +A good object should also be +free of glare and reflections. + +145 +00:08:14,127 --> 00:08:16,896 +If the object does not have +a matte surface, + +146 +00:08:16,896 --> 00:08:19,399 +you can try to reduce +the specular on it + +147 +00:08:19,399 --> 00:08:21,901 +using diffuse lighting. + +148 +00:08:21,901 --> 00:08:24,070 +If you would like +to flip over the object + +149 +00:08:24,070 --> 00:08:25,872 +to capture its bottom, + +150 +00:08:25,872 --> 00:08:29,242 +please ensure +that your object stays rigid. + +151 +00:08:29,242 --> 00:08:33,947 +In other words, it should not +change its shape when flipped. + +152 +00:08:33,947 --> 00:08:37,884 +And lastly, a good object +can contain fine structure + +153 +00:08:37,884 --> 00:08:40,253 +to some degree, +but you will need to use + +154 +00:08:40,253 --> 00:08:43,223 +a high-resolution camera +and take close-up photos + +155 +00:08:43,223 --> 00:08:48,161 +to recover the fine detail +of the object. + +156 +00:08:48,161 --> 00:08:49,529 +The next important thing + +157 +00:08:49,529 --> 00:08:52,932 +is setting up an ideal +capture environment. + +158 +00:08:52,932 --> 00:08:56,269 +You will want to make sure +that your capture environment + +159 +00:08:56,269 --> 00:09:00,006 +has good, even, +and diffuse lighting. + +160 +00:09:00,006 --> 00:09:03,176 +It is important to ensure +a stable background + +161 +00:09:03,176 --> 00:09:06,413 +and have sufficient space +around the object. + +162 +00:09:06,413 --> 00:09:08,014 +If your room is dark, + +163 +00:09:08,014 --> 00:09:11,918 +you can make use +of a well-lit turntable. + +164 +00:09:11,918 --> 00:09:14,487 +Next, we'll look +at some guidelines + +165 +00:09:14,487 --> 00:09:18,691 +for capturing good photos +of your object, which in turn, + +166 +00:09:18,691 --> 00:09:22,061 +will ensure that you get +a good quality 3D model + +167 +00:09:22,061 --> 00:09:24,431 +from Object Capture. + +168 +00:09:24,431 --> 00:09:28,067 +As an example, I'll show you +how my colleague Maunesh + +169 +00:09:28,067 --> 00:09:30,370 +used his iPhone +to capture the images + +170 +00:09:30,370 --> 00:09:32,939 +of a beautiful pirate ship +that was created + +171 +00:09:32,939 --> 00:09:37,911 +by our beloved ARKit engineer, +Christian Lipski. + +172 +00:09:37,911 --> 00:09:40,380 +Maunesh begins +by placing the pirate ship + +173 +00:09:40,380 --> 00:09:43,516 +in the middle of a clean table. + +174 +00:09:43,516 --> 00:09:47,921 +This makes the ship +clearly stand out in the photos. + +175 +00:09:47,921 --> 00:09:51,491 +He holds his iPhone steadily +with two hands. + +176 +00:09:51,491 --> 00:09:54,260 +As he circles +around the ship slowly, + +177 +00:09:54,260 --> 00:09:57,997 +he captures photos +at various heights. + +178 +00:09:57,997 --> 00:10:01,901 +He makes sure that the ship +is large enough in the center + +179 +00:10:01,901 --> 00:10:03,903 +of the camera's field of view + +180 +00:10:03,903 --> 00:10:07,874 +so that he can capture +the maximum amount of detail. + +181 +00:10:07,874 --> 00:10:10,310 +He also makes sure +that he always maintains + +182 +00:10:10,310 --> 00:10:15,882 +a high degree of overlap +between any two adjacent photos. + +183 +00:10:15,882 --> 00:10:18,384 +After he takes +a good number of photos -- + +184 +00:10:18,384 --> 00:10:23,423 +about 80 in this case -- +he flips the ship on its side, + +185 +00:10:23,423 --> 00:10:27,227 +so that he can also +reconstruct its bottom. + +186 +00:10:27,227 --> 00:10:31,097 +He continues to capture +about 20 more photos of the ship + +187 +00:10:31,097 --> 00:10:34,334 +in a flipped orientation. + +188 +00:10:34,334 --> 00:10:37,270 +One thing to note is that +he is holding the iPhone + +189 +00:10:37,270 --> 00:10:39,172 +in landscape mode. + +190 +00:10:39,172 --> 00:10:42,642 +This is because +he is capturing a long object, + +191 +00:10:42,642 --> 00:10:45,278 +and in this case, +the landscape mode helps him + +192 +00:10:45,278 --> 00:10:49,916 +capture maximum amount of detail +of the object. + +193 +00:10:49,916 --> 00:10:53,286 +However, he may need to use +the iPhone in portrait mode + +194 +00:10:53,286 --> 00:10:57,357 +if he were to capture +a tall object instead. + +195 +00:10:59,359 --> 00:11:00,527 +That's it! + +196 +00:11:00,527 --> 00:11:03,963 +The final step in the process +is to copy those photos + +197 +00:11:03,963 --> 00:11:09,002 +onto a Mac and process them +using the Object Capture API. + +198 +00:11:09,002 --> 00:11:11,771 +You can choose from +four different detail levels, + +199 +00:11:11,771 --> 00:11:15,041 +which are optimized +for different use cases. + +200 +00:11:15,041 --> 00:11:18,211 +The reduced and medium detail +levels are optimized + +201 +00:11:18,211 --> 00:11:21,481 +for use in web-based +and mobile experiences, + +202 +00:11:21,481 --> 00:11:25,218 +such as viewing 3D content +in AR QuickLook. + +203 +00:11:25,218 --> 00:11:28,121 +The reconstructed models +for those detail levels + +204 +00:11:28,121 --> 00:11:31,124 +have fewer triangles +and material channels, + +205 +00:11:31,124 --> 00:11:34,294 +thereby consuming +less memory. + +206 +00:11:34,294 --> 00:11:36,496 +The full and raw detail levels + +207 +00:11:36,496 --> 00:11:39,432 +are intended for high-end +interactive use cases, + +208 +00:11:39,432 --> 00:11:44,203 +such as in computer games +or post-production workflows. + +209 +00:11:44,203 --> 00:11:47,140 +These models contain +the highest geometric detail + +210 +00:11:47,140 --> 00:11:50,176 +and give you the flexibility +to choose between baked + +211 +00:11:50,176 --> 00:11:52,412 +and unbaked materials, + +212 +00:11:52,412 --> 00:11:55,882 +but they require more memory +to reconstruct. + +213 +00:11:55,882 --> 00:11:58,585 +It is important to select +the right output level + +214 +00:11:58,585 --> 00:12:00,954 +depending on your use case. + +215 +00:12:00,954 --> 00:12:04,324 +For our pirate ship, we chose +the medium detail level, + +216 +00:12:04,324 --> 00:12:09,896 +which only took a few minutes +to process it on an M1 Mac. + +217 +00:12:09,896 --> 00:12:12,565 +The output 3D model +looked so stunning + +218 +00:12:12,565 --> 00:12:15,268 +that we actually put together +an animated clip + +219 +00:12:15,268 --> 00:12:18,905 +of the pirate ship +sailing in high seas. + +220 +00:12:18,905 --> 00:12:22,208 +And that's the power +of Object Capture for you! + +221 +00:12:22,208 --> 00:12:24,010 +Ahoy! + +222 +00:12:24,010 --> 00:12:25,945 +Now I'll hand it off to Risa, + +223 +00:12:25,945 --> 00:12:28,681 +who will be walking you through +an end-to-end workflow + +224 +00:12:28,681 --> 00:12:31,517 +with Object Capture +in RealityKit. + +225 +00:12:31,517 --> 00:12:33,186 +Risa Yoneyama: Thanks, Hao. + +226 +00:12:33,186 --> 00:12:37,056 +Now that we have gone over +the Object Capture API, + +227 +00:12:37,056 --> 00:12:41,194 +I am excited to go over an +end-to-end developer workflow, + +228 +00:12:41,194 --> 00:12:46,399 +to bring your real-life object +into AR using RealityKit. + +229 +00:12:46,399 --> 00:12:48,368 +We'll walk through +each step in detail + +230 +00:12:48,368 --> 00:12:50,236 +with an example workflow, + +231 +00:12:50,236 --> 00:12:54,007 +so let's dive +straight into a demo. + +232 +00:12:54,007 --> 00:12:56,843 +My colleague Zach +is an occasional woodworker + +233 +00:12:56,843 --> 00:13:00,780 +and recently built six oversized +wooden chess pieces -- + +234 +00:13:00,780 --> 00:13:02,949 +one for each unique piece. + +235 +00:13:02,949 --> 00:13:04,517 +Looking at these chess pieces, + +236 +00:13:04,517 --> 00:13:08,621 +I'm inspired to create +an interactive AR chess game. + +237 +00:13:08,621 --> 00:13:10,790 +Previously, +you'd need a 3D modeler + +238 +00:13:10,790 --> 00:13:13,993 +and material specialist +to create high-quality 3D models + +239 +00:13:13,993 --> 00:13:15,862 +of real-world objects. + +240 +00:13:15,862 --> 00:13:18,398 +Now, with the +Object Capture API, + +241 +00:13:18,398 --> 00:13:21,367 +we can simply capture +these chess pieces directly + +242 +00:13:21,367 --> 00:13:24,504 +and bring them +into augmented reality. + +243 +00:13:24,504 --> 00:13:27,006 +Let's start off +by capturing the rook. + +244 +00:13:27,006 --> 00:13:30,543 +My colleague Bryan will be using +this professional setup, + +245 +00:13:30,543 --> 00:13:33,179 +keeping in mind +the best practices we covered + +246 +00:13:33,179 --> 00:13:35,448 +in the previous section. + +247 +00:13:35,448 --> 00:13:39,085 +In this case, Bryan is placing +the rook on this turntable + +248 +00:13:39,085 --> 00:13:41,821 +with some professional lighting +to help avoid harsh shadows + +249 +00:13:41,821 --> 00:13:44,123 +in the final output. + +250 +00:13:44,123 --> 00:13:47,393 +You can also use the iPhone +camera with a turntable, + +251 +00:13:47,393 --> 00:13:50,029 +which provides you with +automatic scale estimation + +252 +00:13:50,029 --> 00:13:54,500 +and gravity vector information +in your output USDZ. + +253 +00:13:54,500 --> 00:13:57,870 +Please refer to the +Object Capture session from 2021 + +254 +00:13:57,870 --> 00:14:00,306 +for more details on this. + +255 +00:14:00,306 --> 00:14:03,142 +Of course, if you do not have +an elaborate setup + +256 +00:14:03,142 --> 00:14:07,613 +like Bryan does here, you can +also simply hold your iOS device + +257 +00:14:07,613 --> 00:14:12,218 +and walk around your object +to capture the images. + +258 +00:14:12,218 --> 00:14:14,987 +Now that we have all the photos +of our rook piece, + +259 +00:14:14,987 --> 00:14:17,924 +I'm going to transfer these +over to the Mac. + +260 +00:14:17,924 --> 00:14:19,292 +I'll process these photos + +261 +00:14:19,292 --> 00:14:21,427 +using the +PhotogrammetrySession API + +262 +00:14:21,427 --> 00:14:24,597 +that was introduced in 2021. + +263 +00:14:24,597 --> 00:14:26,632 +Per the best practice +guidelines, + +264 +00:14:26,632 --> 00:14:29,469 +I'll use the reduced detail +level to reconstruct, + +265 +00:14:29,469 --> 00:14:33,239 +as we want to make sure +our AR app performs well. + +266 +00:14:33,239 --> 00:14:38,911 +The final output of the API +will be a USDZ file type model. + +267 +00:14:38,911 --> 00:14:40,913 +Here is our final output +of the rook chess piece + +268 +00:14:40,913 --> 00:14:43,616 +I just reconstructed. + +269 +00:14:43,616 --> 00:14:46,319 +To save us some time, +I've gone ahead and captured + +270 +00:14:46,319 --> 00:14:50,022 +the other five pieces +ahead of time. + +271 +00:14:50,022 --> 00:14:52,825 +You may be wondering +how we will create a chess game + +272 +00:14:52,825 --> 00:14:56,262 +with only one color scheme +for the chess pieces. + +273 +00:14:56,262 --> 00:14:58,598 +Let's duplicate +our six unique pieces + +274 +00:14:58,598 --> 00:15:01,667 +and drag them +into Reality Converter. + +275 +00:15:01,667 --> 00:15:04,370 +I have inverted the colors +in the original texture + +276 +00:15:04,370 --> 00:15:08,808 +and replaced the duplicated set +with this new inverted texture. + +277 +00:15:08,808 --> 00:15:10,943 +This way, we can have +a lighter version + +278 +00:15:10,943 --> 00:15:13,479 +and a darker version +of the chess pieces, + +279 +00:15:13,479 --> 00:15:15,348 +one for each player. + +280 +00:15:15,348 --> 00:15:16,783 +I'll be exporting the models + +281 +00:15:16,783 --> 00:15:18,918 +with the compressed textures +option turned on + +282 +00:15:18,918 --> 00:15:20,987 +in the Export menu. + +283 +00:15:20,987 --> 00:15:22,822 +This will help decrease +the memory footprint + +284 +00:15:22,822 --> 00:15:24,724 +of the textures. + +285 +00:15:26,325 --> 00:15:28,795 +Now that we have +our full set of chess pieces, + +286 +00:15:28,795 --> 00:15:33,566 +we are ready to bring the models +into our Xcode project. + +287 +00:15:33,566 --> 00:15:35,802 +I've created a chessboard +using RealityKit + +288 +00:15:35,802 --> 00:15:38,738 +by scaling down +primitive cubes on the y-axis + +289 +00:15:38,738 --> 00:15:42,241 +and alternating the colors +between black and white. + +290 +00:15:42,241 --> 00:15:45,077 +Here are all the chess pieces +I reconstructed, + +291 +00:15:45,077 --> 00:15:47,246 +laid out on the chessboard. + +292 +00:15:47,246 --> 00:15:48,881 +This is already exciting to see + +293 +00:15:48,881 --> 00:15:51,751 +our real-life objects +in our application, + +294 +00:15:51,751 --> 00:15:53,152 +but let's start adding +some features + +295 +00:15:53,152 --> 00:15:56,455 +to make it an actual +interactive game. + +296 +00:15:56,455 --> 00:15:58,090 +Throughout +this part of the demo, + +297 +00:15:58,090 --> 00:16:01,160 +I would like to showcase several +different existing technologies, + +298 +00:16:01,160 --> 00:16:03,429 +so we can provide examples +of how you might want + +299 +00:16:03,429 --> 00:16:08,034 +to combine the technologies to +accomplish your desired output. + +300 +00:16:08,034 --> 00:16:10,369 +As we'll be going over +some practical use cases + +301 +00:16:10,369 --> 00:16:12,772 +of advanced topics +in RealityKit, + +302 +00:16:12,772 --> 00:16:14,574 +I would recommend checking out + +303 +00:16:14,574 --> 00:16:16,709 +the RealityKit sessions +from 2021 + +304 +00:16:16,709 --> 00:16:20,546 +if you are not already +familiar with the APIs. + +305 +00:16:20,546 --> 00:16:23,349 +I want to start by adding +a start-up animation + +306 +00:16:23,349 --> 00:16:26,352 +when we first launch +the application. + +307 +00:16:26,352 --> 00:16:28,855 +I am imagining an animation +where the checker tiles + +308 +00:16:28,855 --> 00:16:30,323 +slowly fall into place + +309 +00:16:30,323 --> 00:16:32,792 +from slightly above +its final position, + +310 +00:16:32,792 --> 00:16:36,429 +all while the chess pieces +also translate in together. + +311 +00:16:36,429 --> 00:16:38,664 +To replicate +this effect in code, + +312 +00:16:38,664 --> 00:16:41,400 +all it takes is two steps. + +313 +00:16:41,400 --> 00:16:44,136 +The first step is to translate +both our entities + +314 +00:16:44,136 --> 00:16:46,172 +up along the y-axis, + +315 +00:16:46,172 --> 00:16:50,009 +while also uniformly +scaling down the chess piece. + +316 +00:16:50,009 --> 00:16:53,145 +The second step and final step +is to animate both entities + +317 +00:16:53,145 --> 00:16:56,315 +back to its original transform. + +318 +00:16:56,315 --> 00:16:59,352 +The code for this +is quite simple. + +319 +00:16:59,352 --> 00:17:03,289 +I'll start by iterating through +the checker tile entities. + +320 +00:17:03,289 --> 00:17:04,390 +For each entity, + +321 +00:17:04,390 --> 00:17:07,159 +I'll save the current transform +of the checker tile + +322 +00:17:07,159 --> 00:17:10,763 +as this will be the final +position it lands on. + +323 +00:17:10,763 --> 00:17:16,002 +Then, I'll move each square up +10 cm on the y-axis. + +324 +00:17:16,002 --> 00:17:18,104 +We can now take advantage +of the move function + +325 +00:17:18,104 --> 00:17:22,642 +to animate this back +to our original transform. + +326 +00:17:22,642 --> 00:17:25,544 +I also happen to know +that this border USDZ + +327 +00:17:25,544 --> 00:17:29,749 +that outlines the checkerboard +has a built-in animation. + +328 +00:17:29,749 --> 00:17:31,817 +We can use the playAnimation API + +329 +00:17:31,817 --> 00:17:35,454 +to start the animation +simultaneously. + +330 +00:17:35,454 --> 00:17:38,357 +I've added the exact same +animation to the chess pieces + +331 +00:17:38,357 --> 00:17:42,495 +but also modifying the scale +as they translate. + +332 +00:17:42,495 --> 00:17:43,729 +And here we have it: + +333 +00:17:43,729 --> 00:17:47,934 +a simple startup animation +with just a few lines of code. + +334 +00:17:47,934 --> 00:17:50,970 +However, we won't actually +be able to play chess + +335 +00:17:50,970 --> 00:17:53,539 +without the ability +to move the chess pieces. + +336 +00:17:53,539 --> 00:17:55,708 +Let's work on that next. + +337 +00:17:55,708 --> 00:17:58,210 +Before we can start moving +the chess pieces, + +338 +00:17:58,210 --> 00:18:00,980 +we'll need to be able +to select one. + +339 +00:18:00,980 --> 00:18:03,649 +I've already added +a UITapGestureRecognizer + +340 +00:18:03,649 --> 00:18:05,651 +to my ARView. + +341 +00:18:05,651 --> 00:18:08,220 +When the users taps +a specific location, + +342 +00:18:08,220 --> 00:18:10,990 +we will define a ray that starts +from the camera origin + +343 +00:18:10,990 --> 00:18:14,827 +and goes through that 2D point. + +344 +00:18:14,827 --> 00:18:18,698 +We can then perform a raycast +with that ray into the 3D scene + +345 +00:18:18,698 --> 00:18:21,167 +to see if we hit any entities. + +346 +00:18:21,167 --> 00:18:24,403 +I've specified my chess piece +collision group as a mask + +347 +00:18:24,403 --> 00:18:27,206 +as I know I only want to be able +to select the chess pieces + +348 +00:18:27,206 --> 00:18:29,308 +in my scene. + +349 +00:18:29,308 --> 00:18:31,077 +Be mindful that +the raycast function + +350 +00:18:31,077 --> 00:18:33,212 +will ignore all entities +that do not have + +351 +00:18:33,212 --> 00:18:35,047 +a CollisionComponent. + +352 +00:18:35,047 --> 00:18:39,719 +If we do find a chess piece, +we can finally select it. + +353 +00:18:39,719 --> 00:18:41,754 +Now that we know +which piece is selected, + +354 +00:18:41,754 --> 00:18:43,122 +I want to add an effect + +355 +00:18:43,122 --> 00:18:47,259 +that will make the piece +look like it is glowing. + +356 +00:18:47,259 --> 00:18:50,062 +We can leverage custom materials +to achieve this; + +357 +00:18:50,062 --> 00:18:52,999 +more specifically, +surface shaders. + +358 +00:18:52,999 --> 00:18:55,001 +Surface shaders allow you +to calculate + +359 +00:18:55,001 --> 00:18:58,170 +or specify material parameters +through Metal, + +360 +00:18:58,170 --> 00:19:01,240 +which then gets called by +RealityKit's fragment shader + +361 +00:19:01,240 --> 00:19:03,809 +once per each pixel. + +362 +00:19:03,809 --> 00:19:05,177 +We can write a surface shader + +363 +00:19:05,177 --> 00:19:08,014 +that looks like +this fire effect in Metal. + +364 +00:19:08,014 --> 00:19:09,615 +Then apply a custom material, + +365 +00:19:09,615 --> 00:19:12,184 +with this surface shader +to our rectangular prism + +366 +00:19:12,184 --> 00:19:15,955 +to have the shader affect +how our entity looks. + +367 +00:19:15,955 --> 00:19:19,859 +Let's write some code +to achieve our desired effect. + +368 +00:19:19,859 --> 00:19:22,228 +I've already added +a noise texture to the project + +369 +00:19:22,228 --> 00:19:24,530 +to use in this surface shader. + +370 +00:19:24,530 --> 00:19:26,399 +We'll sample the texture twice, + +371 +00:19:26,399 --> 00:19:28,534 +once for the overall +shape of the effect + +372 +00:19:28,534 --> 00:19:30,603 +and another for detail. + +373 +00:19:30,603 --> 00:19:33,606 +We then take the RGB value +and remap it to look + +374 +00:19:33,606 --> 00:19:35,641 +just the way we want. + +375 +00:19:35,641 --> 00:19:38,144 +Then, with the processed value +we just extracted, + +376 +00:19:38,144 --> 00:19:40,946 +we'll calculate the opacity +of the sample point + +377 +00:19:40,946 --> 00:19:45,418 +by comparing its y-position +with the image value. + +378 +00:19:45,418 --> 00:19:47,053 +To give the effect +some movement, + +379 +00:19:47,053 --> 00:19:49,188 +we'll be moving through +the y-axis of the texture + +380 +00:19:49,188 --> 00:19:52,525 +as a function of time. + +381 +00:19:52,525 --> 00:19:55,194 +Additionally, we will also +use the facing angle + +382 +00:19:55,194 --> 00:19:57,296 +of each sample point +in conjunction + +383 +00:19:57,296 --> 00:19:59,965 +with the viewing direction +of the camera + +384 +00:19:59,965 --> 00:20:02,001 +to fade the effect at the sides. + +385 +00:20:02,001 --> 00:20:04,503 +This will soften the edges +and hide the regular nature + +386 +00:20:04,503 --> 00:20:07,973 +of the underlying model. + +387 +00:20:07,973 --> 00:20:11,544 +Finally, we'll set the color +and opacity we just calculated + +388 +00:20:11,544 --> 00:20:15,448 +using the surface +parameter functions. + +389 +00:20:15,448 --> 00:20:16,615 +And here are the chess pieces + +390 +00:20:16,615 --> 00:20:19,018 +with the selection shader +applied to it. + +391 +00:20:19,018 --> 00:20:22,922 +They really do look like they +are glowing from the inside. + +392 +00:20:22,922 --> 00:20:24,390 +Now, if we combine that + +393 +00:20:24,390 --> 00:20:26,892 +with three separate +translation animations, + +394 +00:20:26,892 --> 00:20:30,663 +that will result in something +that looks like this. + +395 +00:20:30,663 --> 00:20:34,867 +With the functionality to move +chess pieces implemented, + +396 +00:20:34,867 --> 00:20:38,704 +we'll also be able to capture +the opponent's pieces. + +397 +00:20:38,704 --> 00:20:41,774 +Just like surface shaders, +geometry modifiers + +398 +00:20:41,774 --> 00:20:45,511 +can be implemented +using custom materials. + +399 +00:20:45,511 --> 00:20:48,981 +They are a very powerful tool, +as you can change vertex data + +400 +00:20:48,981 --> 00:20:54,420 +such as position, normals, +texture coordinates, and more. + +401 +00:20:54,420 --> 00:20:57,523 +Each of these Metal functions +will be called once per vertex + +402 +00:20:57,523 --> 00:21:00,526 +by RealityKit's vertex shader. + +403 +00:21:00,526 --> 00:21:03,295 +These modifications +are purely transient + +404 +00:21:03,295 --> 00:21:05,598 +and do not affect +the vertex information + +405 +00:21:05,598 --> 00:21:08,667 +of the actual entity. + +406 +00:21:08,667 --> 00:21:11,137 +I'm thinking we could add +a fun geometry modifier + +407 +00:21:11,137 --> 00:21:14,473 +to squash the pieces +when they are captured. + +408 +00:21:14,473 --> 00:21:16,942 +I have this property +on my chess piece + +409 +00:21:16,942 --> 00:21:19,812 +called capturedProgress +to represent the progress + +410 +00:21:19,812 --> 00:21:24,350 +of the capturing animation +from 0 to 1. + +411 +00:21:24,350 --> 00:21:27,486 +Since capturing +is a user-initiated action, + +412 +00:21:27,486 --> 00:21:29,755 +we somehow need to tell +the geometry modifier + +413 +00:21:29,755 --> 00:21:32,725 +when to start its animation. + +414 +00:21:32,725 --> 00:21:34,593 +The good thing is +you can do this + +415 +00:21:34,593 --> 00:21:38,097 +by setting the custom property +on a customMaterial. + +416 +00:21:38,097 --> 00:21:42,735 +This allows data to be shared +between the CPU and the GPU. + +417 +00:21:42,735 --> 00:21:45,704 +We will specifically use +the custom value property here + +418 +00:21:45,704 --> 00:21:50,142 +and pass the animation progress +to the geometry modifier. + +419 +00:21:50,142 --> 00:21:53,579 +To extract the animation +progress from the Metal side, + +420 +00:21:53,579 --> 00:21:57,917 +we can use the custom parameter +on uniforms. + +421 +00:21:57,917 --> 00:22:00,186 +Since I want to scale +the object vertically, + +422 +00:22:00,186 --> 00:22:02,621 +as if it is being squashed +by another piece, + +423 +00:22:02,621 --> 00:22:06,125 +we will set the scale axis +as the y-direction. + +424 +00:22:06,125 --> 00:22:08,360 +To add some complexity +to the animation, + +425 +00:22:08,360 --> 00:22:10,796 +we will also change +the geometry in the x-axis + +426 +00:22:10,796 --> 00:22:13,265 +to create a wave effect. + +427 +00:22:13,265 --> 00:22:15,601 +The offset of the vertex +can be set using the + +428 +00:22:15,601 --> 00:22:19,271 +set_model_position_ offset +function. + +429 +00:22:19,271 --> 00:22:23,142 +Here is the final product +of our geometry modifier. + +430 +00:22:23,142 --> 00:22:26,278 +You can see that it scales up +a bit before collapsing down, + +431 +00:22:26,278 --> 00:22:30,482 +while being stretched vertically +along the x-axis. + +432 +00:22:30,482 --> 00:22:32,885 +As a chess novice myself, + +433 +00:22:32,885 --> 00:22:34,987 +I thought it might be helpful +to add a feature + +434 +00:22:34,987 --> 00:22:37,389 +to indicate where +your selected piece can move to + +435 +00:22:37,389 --> 00:22:40,292 +to help me learn the game. + +436 +00:22:40,292 --> 00:22:43,329 +Since the checker pieces +are each individual entities + +437 +00:22:43,329 --> 00:22:45,297 +with their own Model Component, + +438 +00:22:45,297 --> 00:22:48,634 +I can apply a pulsing effect +using a surface shader + +439 +00:22:48,634 --> 00:22:52,171 +to potential moves +to distinguish them from others. + +440 +00:22:52,171 --> 00:22:55,107 +Next, I'll add a post-processing +effect called "bloom" + +441 +00:22:55,107 --> 00:22:58,344 +to accentuate +the effect even more. + +442 +00:22:58,344 --> 00:23:00,713 +Again, we're using +the custom parameter here + +443 +00:23:00,713 --> 00:23:04,283 +we used in the surface shader +for the glow effect. + +444 +00:23:04,283 --> 00:23:07,853 +In this case, we are passing in +a Boolean from the CPU side + +445 +00:23:07,853 --> 00:23:11,123 +to our Metal surface shader. + +446 +00:23:11,123 --> 00:23:13,459 +If this checker +is a possible move, + +447 +00:23:13,459 --> 00:23:16,629 +I want to add a pulsing effect +by changing the color. + +448 +00:23:16,629 --> 00:23:21,100 +We'll specifically add the pulse +to the emissive color here. + +449 +00:23:21,100 --> 00:23:25,571 +Lastly, I'll add the bloom +effect to the entire view. + +450 +00:23:25,571 --> 00:23:28,440 +Bloom is a post-processing +effect that produces + +451 +00:23:28,440 --> 00:23:32,344 +feathers of light from +the borders of bright areas. + +452 +00:23:32,344 --> 00:23:35,147 +We can accomplish this effect +by taking advantage + +453 +00:23:35,147 --> 00:23:39,451 +of the render callbacks property +on ARView. + +454 +00:23:39,451 --> 00:23:42,154 +We will write the bloom effect +using the already built-in + +455 +00:23:42,154 --> 00:23:45,758 +Metal performance shader +functions. + +456 +00:23:45,758 --> 00:23:49,094 +Next, we'll simply set the +renderCallbacks.postProcess + +457 +00:23:49,094 --> 00:23:52,498 +closure as our bloom function +we just defined. + +458 +00:23:52,498 --> 00:23:55,601 +When we pulse our checkers, +we are pulsing to a white color + +459 +00:23:55,601 --> 00:23:57,536 +which will now be +further emphasized + +460 +00:23:57,536 --> 00:24:00,339 +with the bloom effect. + +461 +00:24:00,339 --> 00:24:03,142 +With the surface shader +and bloom effect together, + +462 +00:24:03,142 --> 00:24:08,480 +we can see exactly where +we can move our pieces to. + +463 +00:24:08,480 --> 00:24:11,216 +Finally, let's combine +everything we have together + +464 +00:24:11,216 --> 00:24:13,619 +to see our real-life +chess pieces come to life + +465 +00:24:13,619 --> 00:24:16,422 +in our AR app. + +466 +00:24:16,422 --> 00:24:18,490 +We can see how +all the features we added + +467 +00:24:18,490 --> 00:24:21,126 +look in our environment. + +468 +00:24:21,126 --> 00:24:22,761 +For your convenience +we have linked + +469 +00:24:22,761 --> 00:24:26,732 +the Capture Chess sample project +to the session resources. + +470 +00:24:26,732 --> 00:24:28,901 +Please download it +and try it out for yourself + +471 +00:24:28,901 --> 00:24:31,236 +to see it in your environment. + +472 +00:24:31,236 --> 00:24:32,604 +And it's that simple. + +473 +00:24:32,604 --> 00:24:36,041 +From capture to reconstruction +of the oversized chess pieces, + +474 +00:24:36,041 --> 00:24:40,112 +then into +our augmented reality app. + +475 +00:24:40,112 --> 00:24:43,082 +We've covered a lot +in this session today + +476 +00:24:43,082 --> 00:24:46,418 +so let's summarize +some of the key points. + +477 +00:24:46,418 --> 00:24:50,255 +We first started off by +recapping the Object Capture API + +478 +00:24:50,255 --> 00:24:53,459 +that we announced in 2021. + +479 +00:24:53,459 --> 00:24:56,362 +We then went over +a new API in ARKit + +480 +00:24:56,362 --> 00:24:58,997 +that enables +capturing photos on-demand + +481 +00:24:58,997 --> 00:25:03,802 +at native camera resolution +during an active ARSession. + +482 +00:25:03,802 --> 00:25:07,706 +To help you get the most out of +the Object Capture technology, + +483 +00:25:07,706 --> 00:25:11,243 +we listed types of objects that +are suited for reconstruction, + +484 +00:25:11,243 --> 00:25:14,747 +ideal environments +to get high-quality images, + +485 +00:25:14,747 --> 00:25:16,648 +and the recommended +flow to follow + +486 +00:25:16,648 --> 00:25:19,885 +while capturing your object. + +487 +00:25:19,885 --> 00:25:21,787 +For the latter part +of this session, + +488 +00:25:21,787 --> 00:25:25,591 +we walked through an example +end-to-end developer workflow. + +489 +00:25:25,591 --> 00:25:28,494 +We captured photos +of the oversized chess pieces + +490 +00:25:28,494 --> 00:25:32,431 +and used the images as input +to the PhotogrammetrySession API + +491 +00:25:32,431 --> 00:25:35,167 +to create 3D models of them. + +492 +00:25:35,167 --> 00:25:38,270 +Then, we imported the models +into Reality Converter + +493 +00:25:38,270 --> 00:25:40,739 +to replace some textures. + +494 +00:25:40,739 --> 00:25:43,575 +And finally, we slowly +built up our chess game + +495 +00:25:43,575 --> 00:25:47,946 +to see our chess pieces +in action in AR. + +496 +00:25:47,946 --> 00:25:50,015 +And that's it +for our session today. + +497 +00:25:50,015 --> 00:25:51,817 +Thank you so much for watching. + +498 +00:25:51,817 --> 00:25:53,285 +Ahoy! + +499 +00:25:53,285 --> 00:25:57,556 +♪ + diff --git a/eng/2022 Session 10129 Understand USD fundamentals en.srt b/eng/2022 Session 10129 Understand USD fundamentals en.srt new file mode 100644 index 0000000..4dc68fd --- /dev/null +++ b/eng/2022 Session 10129 Understand USD fundamentals en.srt @@ -0,0 +1,1851 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,509 +♪ + +3 +00:00:09,509 --> 00:00:12,145 +Hello, and welcome to WWDC. + +4 +00:00:12,145 --> 00:00:14,047 +My name is Kacey. + +5 +00:00:14,047 --> 00:00:16,617 +You may have come across +Universal Scene Description, + +6 +00:00:16,617 --> 00:00:18,218 +or USD, already. + +7 +00:00:18,218 --> 00:00:21,555 +You may have interacted with +USD files in augmented reality. + +8 +00:00:21,555 --> 00:00:25,058 +Or you may have converted assets +from other formats to USD. + +9 +00:00:25,058 --> 00:00:28,195 +But what's really inside +of a USD file? + +10 +00:00:28,195 --> 00:00:31,365 +USD is an important technology +with unique features, + +11 +00:00:31,365 --> 00:00:32,933 +so let's take a look +under the hood + +12 +00:00:32,933 --> 00:00:36,370 +and explore the fundamentals +of USD together. + +13 +00:00:36,370 --> 00:00:39,139 +We'll learn what USD is, + +14 +00:00:39,139 --> 00:00:42,843 +introduce some +basic USD concepts, + +15 +00:00:42,843 --> 00:00:46,113 +learn how to compose scenes +with USD, + +16 +00:00:46,113 --> 00:00:50,684 +and get an understanding +of the USD file formats. + +17 +00:00:50,684 --> 00:00:53,921 +First, what is USD? + +18 +00:00:53,921 --> 00:00:56,924 +USD was developed +by Pixar Animation Studios + +19 +00:00:56,924 --> 00:00:59,693 +to enable them to create +the complex movies we love, + +20 +00:00:59,693 --> 00:01:02,696 +and it is widely used +across the film, entertainment, + +21 +00:01:02,696 --> 00:01:04,665 +and other industries. + +22 +00:01:04,665 --> 00:01:07,200 +It's extensible by design +and is rapidly emerging + +23 +00:01:07,200 --> 00:01:10,370 +as a collaborative, +key workflow technology. + +24 +00:01:10,370 --> 00:01:12,506 +It's an open source project +built on decades + +25 +00:01:12,506 --> 00:01:14,908 +of production experience +in the film industry + +26 +00:01:14,908 --> 00:01:17,210 +and is increasingly +being adopted for games, + +27 +00:01:17,210 --> 00:01:22,416 +simulation, AR, manufacturing, +and e-commerce. + +28 +00:01:22,416 --> 00:01:24,818 +There are three +core aspects to USD: + +29 +00:01:24,818 --> 00:01:27,921 +the scene description +specification, the API, + +30 +00:01:27,921 --> 00:01:30,824 +and the rendering system. + +31 +00:01:30,824 --> 00:01:35,529 +Your application interacts +with USD using the API. + +32 +00:01:35,529 --> 00:01:36,964 +The rendering system +provides support + +33 +00:01:36,964 --> 00:01:41,468 +for visualizing the scenes +with different renderers. + +34 +00:01:41,468 --> 00:01:44,604 +Apple has multiple different +rendering systems for USD. + +35 +00:01:44,604 --> 00:01:47,341 +Check out the "Explore USD tools +and rendering" session + +36 +00:01:47,341 --> 00:01:50,677 +to learn more about it. + +37 +00:01:50,677 --> 00:01:51,878 +In this session, + +38 +00:01:51,878 --> 00:01:54,381 +we focus on the scene +description specification + +39 +00:01:54,381 --> 00:01:56,616 +which states how +scene data is described, + +40 +00:01:56,616 --> 00:01:58,018 +how it is organized, + +41 +00:01:58,018 --> 00:02:02,155 +and how it is represented +in a file format. + +42 +00:02:02,155 --> 00:02:04,958 +Fundamentally, +these USD files contain data + +43 +00:02:04,958 --> 00:02:07,627 +describing how a scene +should look. + +44 +00:02:07,627 --> 00:02:09,763 +Rendering applications +interpret the data, + +45 +00:02:09,763 --> 00:02:12,632 +and produce an image +on the screen. + +46 +00:02:12,632 --> 00:02:15,936 +For example, +at last year's WWDC session, + +47 +00:02:15,936 --> 00:02:19,940 +we created this USD scene +and rendered it in Octane. + +48 +00:02:19,940 --> 00:02:22,242 +Now let's dive into +the fundamental concepts + +49 +00:02:22,242 --> 00:02:24,811 +and learn about the data +behind the render. + +50 +00:02:24,811 --> 00:02:26,246 +To keep things simple, + +51 +00:02:26,246 --> 00:02:29,549 +we will use +a text USD representation + +52 +00:02:29,549 --> 00:02:32,386 +USD has a lot of cool features +we'd love to talk about, + +53 +00:02:32,386 --> 00:02:33,787 +but for the sake of time, + +54 +00:02:33,787 --> 00:02:35,255 +we will focus on +the essentials features + +55 +00:02:35,255 --> 00:02:36,723 +that you may +most commonly encounter + +56 +00:02:36,723 --> 00:02:41,361 +while working with USD +such as stage, prim, and layers, + +57 +00:02:41,361 --> 00:02:43,296 +just to name a few. + +58 +00:02:43,296 --> 00:02:45,832 +Let's start with the stage. + +59 +00:02:45,832 --> 00:02:48,969 +Imagine we are attending +a play in a theater. + +60 +00:02:48,969 --> 00:02:51,371 +As audience members, +we observe the stage + +61 +00:02:51,371 --> 00:02:54,341 +to watch the actors perform, +notice the environments, + +62 +00:02:54,341 --> 00:02:56,877 +lighting, and the props. + +63 +00:02:56,877 --> 00:03:01,281 +This is a good analogy +to how a stage works in USD. + +64 +00:03:01,281 --> 00:03:04,051 +A stage is a scene graph, +or a data structure, + +65 +00:03:04,051 --> 00:03:06,086 +that organizes +graphical information + +66 +00:03:06,086 --> 00:03:07,487 +in a hierarchical way, + +67 +00:03:07,487 --> 00:03:10,624 +where all the scene elements +come together. + +68 +00:03:10,624 --> 00:03:13,293 +A stage is a composition +of one or more layers, + +69 +00:03:13,293 --> 00:03:18,298 +which are typically files +containing scene information. + +70 +00:03:18,298 --> 00:03:20,767 +In general, +a stage is made up of prims, + +71 +00:03:20,767 --> 00:03:24,104 +which are the primary +container objects of a scene. + +72 +00:03:24,104 --> 00:03:25,605 +Prims can contain other prims + +73 +00:03:25,605 --> 00:03:28,108 +to create a hierarchy +of scene elements. + +74 +00:03:28,108 --> 00:03:30,844 +Let's check out an example. + +75 +00:03:30,844 --> 00:03:33,780 +On the left, +we have a sample USD layer + +76 +00:03:33,780 --> 00:03:36,650 +and on the right, we see +a preview visual representation + +77 +00:03:36,650 --> 00:03:38,351 +of the stage. + +78 +00:03:38,351 --> 00:03:43,623 +We see two prims: +a sphere and a cube. + +79 +00:03:43,623 --> 00:03:45,525 +Each prim has a certain type + +80 +00:03:45,525 --> 00:03:48,662 +that defines what it represents +on the stage. + +81 +00:03:48,662 --> 00:03:50,230 +There are many +different types of prims + +82 +00:03:50,230 --> 00:03:55,068 +that make up a stage such as +meshes, lights, and materials. + +83 +00:03:55,068 --> 00:03:56,303 +For this example, + +84 +00:03:56,303 --> 00:03:59,406 +the sphere prim +has a type called "Sphere," + +85 +00:03:59,406 --> 00:04:03,710 +and the cube prim +has a type called "Cube." + +86 +00:04:03,710 --> 00:04:06,279 +But this brings up +a good question. + +87 +00:04:06,279 --> 00:04:10,016 +How does USD know +what these prim types mean? + +88 +00:04:10,016 --> 00:04:12,419 +USD knows this +through the use of schemas. + +89 +00:04:12,419 --> 00:04:14,020 +Schemas are structured data + +90 +00:04:14,020 --> 00:04:16,957 +that define the role +of a prim on the stage. + +91 +00:04:16,957 --> 00:04:19,292 +They provide meaning +to common scene concepts + +92 +00:04:19,292 --> 00:04:22,529 +like geometry, +materials, and more. + +93 +00:04:22,529 --> 00:04:23,797 +For this example, + +94 +00:04:23,797 --> 00:04:26,466 +here is a schema definition +of a sphere. + +95 +00:04:26,466 --> 00:04:28,435 +It defines that every sphere +has a radius + +96 +00:04:28,435 --> 00:04:31,104 +and a bounding box extent. + +97 +00:04:31,104 --> 00:04:34,174 +With the existing schemas, +USD gives you a rich foundation + +98 +00:04:34,174 --> 00:04:38,011 +of built-in types +to describe your scenes. + +99 +00:04:38,011 --> 00:04:41,848 +Custom schemas enable you +to extend USD even further. + +100 +00:04:41,848 --> 00:04:44,851 +You can provide your own schemas +that represent your custom data + +101 +00:04:44,851 --> 00:04:47,420 +for your own use cases +and workflows. + +102 +00:04:47,420 --> 00:04:49,890 +Schemas don't need to have +a visual representation. + +103 +00:04:49,890 --> 00:04:52,125 +They can just be data +you want to have in the stage + +104 +00:04:52,125 --> 00:04:54,427 +in a structured, meaningful way. + +105 +00:04:54,427 --> 00:04:55,529 +For example, + +106 +00:04:55,529 --> 00:04:59,432 +here, I've created a new schema +called "WWDC." + +107 +00:04:59,432 --> 00:05:01,268 +It defines that a prim +that has a title + +108 +00:05:01,268 --> 00:05:03,370 +and a year associated with it, + +109 +00:05:03,370 --> 00:05:06,373 +and here is a prim +called "WWDC22" + +110 +00:05:06,373 --> 00:05:07,908 +that uses the schema. + +111 +00:05:07,908 --> 00:05:09,876 +It has set the year to 2022 + +112 +00:05:09,876 --> 00:05:12,612 +and the title is set +to "Call to code." + +113 +00:05:12,612 --> 00:05:14,981 +The year and the title +are called the "attributes" + +114 +00:05:14,981 --> 00:05:16,183 +of the prim. + +115 +00:05:16,183 --> 00:05:18,518 +Prims can have many +different attributes. + +116 +00:05:18,518 --> 00:05:21,621 +Each attribute has a type +and a value. + +117 +00:05:21,621 --> 00:05:24,024 +Attributes can also have +default values authored + +118 +00:05:24,024 --> 00:05:25,825 +so they don't have to be +explicitly defined + +119 +00:05:25,825 --> 00:05:28,395 +in the prim +that uses the schema. + +120 +00:05:28,395 --> 00:05:30,163 +Going back +to our Sphere schema, + +121 +00:05:30,163 --> 00:05:31,898 +you can see how we've defined +default values + +122 +00:05:31,898 --> 00:05:34,901 +for the radius +and extent attributes. + +123 +00:05:34,901 --> 00:05:37,637 +In this layer, +we have a single Sphere prim. + +124 +00:05:37,637 --> 00:05:39,839 +Since the Radius attribute +is not set, + +125 +00:05:39,839 --> 00:05:42,042 +it derives its value +from the Sphere schema: + +126 +00:05:42,042 --> 00:05:43,910 +a default radius of one. + +127 +00:05:43,910 --> 00:05:47,280 +We can be explicit though, +and set the radius attribute. + +128 +00:05:47,280 --> 00:05:48,748 +The sphere still looks the same + +129 +00:05:48,748 --> 00:05:50,951 +because this value matches +the default value of one + +130 +00:05:50,951 --> 00:05:52,686 +that is set in the schema. + +131 +00:05:52,686 --> 00:05:57,123 +Now let's add a second sphere, +and set the radius to 0.5. + +132 +00:05:57,123 --> 00:05:58,825 +We can see that +it's indeed half the size + +133 +00:05:58,825 --> 00:06:00,694 +of the other sphere. + +134 +00:06:00,694 --> 00:06:04,864 +Attributes, prims, and stages +can also contain metadata, + +135 +00:06:04,864 --> 00:06:07,234 +which are key-value pairings +of information + +136 +00:06:07,234 --> 00:06:10,604 +that can provide auxiliary data +for parts of a scene. + +137 +00:06:10,604 --> 00:06:13,807 +Metadata is set +at the level where it applies. + +138 +00:06:13,807 --> 00:06:15,809 +Metadata that affects +the whole stage + +139 +00:06:15,809 --> 00:06:19,246 +and all prims in it +is set at the stage level. + +140 +00:06:19,246 --> 00:06:21,214 +Metadata that is specific +to a single prim, + +141 +00:06:21,214 --> 00:06:23,583 +is set on the prim. + +142 +00:06:23,583 --> 00:06:26,019 +Attributes can also +have metadata. + +143 +00:06:26,019 --> 00:06:30,223 +For example, here is some +typical stage metadata. + +144 +00:06:30,223 --> 00:06:33,760 +metersPerUnit defines the scale +units for the scene. + +145 +00:06:33,760 --> 00:06:36,830 +upAxis defines which +of the X, Y, or Z axes + +146 +00:06:36,830 --> 00:06:41,167 +is considered the up direction +for the camera in the scene. + +147 +00:06:41,167 --> 00:06:43,169 +doc strings store +useful documentation + +148 +00:06:43,169 --> 00:06:46,806 +for workflow tracking purposes. + +149 +00:06:46,806 --> 00:06:48,742 +Now that we have defined +these basics, + +150 +00:06:48,742 --> 00:06:51,544 +we can already start +making use of USD. + +151 +00:06:51,544 --> 00:06:53,346 +Let's see how +the pancakes scene can be done + +152 +00:06:53,346 --> 00:06:55,849 +using just these concepts. + +153 +00:06:55,849 --> 00:06:56,916 +In our layer, + +154 +00:06:56,916 --> 00:07:00,654 +we create a transform prim +called "Pancakes." + +155 +00:07:00,654 --> 00:07:03,690 +We then add a transform prim +called "Blueberry_01" + +156 +00:07:03,690 --> 00:07:06,893 +that is a child +of the Pancakes prim. + +157 +00:07:06,893 --> 00:07:11,631 +Within the Blueberry_01 prim, +we include the Mesh prim. + +158 +00:07:11,631 --> 00:07:14,000 +The Mesh prim holds +the geometry for the blueberry + +159 +00:07:14,000 --> 00:07:17,037 +and binds to the materials. + +160 +00:07:17,037 --> 00:07:18,538 +Let's complete +the rest of the asset + +161 +00:07:18,538 --> 00:07:21,274 +by adding the other prims. + +162 +00:07:21,274 --> 00:07:23,243 +Here is our completed +pancake scene, + +163 +00:07:23,243 --> 00:07:25,245 +using just these basic concepts. + +164 +00:07:28,848 --> 00:07:30,684 +These fundamentals +can get you far, + +165 +00:07:30,684 --> 00:07:32,385 +but in production-centric +projects, + +166 +00:07:32,385 --> 00:07:34,387 +we need to collaborate +with many different team members + +167 +00:07:34,387 --> 00:07:36,289 +and organizations. + +168 +00:07:36,289 --> 00:07:38,425 +USD has a lot of features +to address this need + +169 +00:07:38,425 --> 00:07:41,661 +in what is called "composition." + +170 +00:07:41,661 --> 00:07:43,697 +Composition enables +the creation of a stage + +171 +00:07:43,697 --> 00:07:46,733 +from separate units +of scene description. + +172 +00:07:46,733 --> 00:07:48,835 +This allows for the efficient +reuse of 3D elements + +173 +00:07:48,835 --> 00:07:51,137 +in a scene +that enables collaboration + +174 +00:07:51,137 --> 00:07:53,573 +and fast iteration. + +175 +00:07:53,573 --> 00:07:56,509 +We will discuss the most common +types of composition: + +176 +00:07:56,509 --> 00:08:01,181 +layering, references, payloads, +and variantSets. + +177 +00:08:01,181 --> 00:08:03,216 +But let's do it in a fun way. + +178 +00:08:03,216 --> 00:08:05,585 +Fancy a game of chess? + +179 +00:08:05,585 --> 00:08:07,787 +Let's build out +a chess set scene. + +180 +00:08:07,787 --> 00:08:09,089 +In the scene, + +181 +00:08:09,089 --> 00:08:12,158 +we will use a catalog of assets +in a catalog layer, + +182 +00:08:12,158 --> 00:08:14,694 +which will refer +to our chess pieces. + +183 +00:08:14,694 --> 00:08:16,863 +We will then arrange these +pieces on the chessboard + +184 +00:08:16,863 --> 00:08:19,399 +in a layout layer +to get our final result, + +185 +00:08:19,399 --> 00:08:22,535 +which is viewable +with the ChessSet layer. + +186 +00:08:22,535 --> 00:08:25,572 +We'll first need to get +our chess pieces + +187 +00:08:25,572 --> 00:08:28,441 +In chess, +there are six main pieces: + +188 +00:08:28,441 --> 00:08:35,014 +the pawn, rook, bishop, +knight, queen, and king. + +189 +00:08:35,014 --> 00:08:37,584 +We will use these assets, +created with Object Capture, + +190 +00:08:37,584 --> 00:08:40,487 +to build a complete chess set. + +191 +00:08:40,487 --> 00:08:42,655 +Let's start by bringing in +our pieces to our stage + +192 +00:08:42,655 --> 00:08:45,892 +using a USD concept, +referencing. + +193 +00:08:45,892 --> 00:08:49,462 +Referencing is when a prim in +a stage refers to another prim + +194 +00:08:49,462 --> 00:08:51,931 +in either the same stage +or a different layer + +195 +00:08:51,931 --> 00:08:54,534 +without copying the data. + +196 +00:08:54,534 --> 00:08:56,903 +This helps minimize +duplication of data + +197 +00:08:56,903 --> 00:08:58,872 +and allows for the data +to be updated separately + +198 +00:08:58,872 --> 00:09:01,674 +by different people +and applications. + +199 +00:09:01,674 --> 00:09:06,379 +Let's reference our pawn asset +into our scene. + +200 +00:09:06,379 --> 00:09:10,150 +In our catalog layer, +we define a prim called "Pawn". + +201 +00:09:10,150 --> 00:09:15,555 +So far it doesn't have any data +and so our stage is still empty. + +202 +00:09:15,555 --> 00:09:19,459 +Next, we add a reference +to the Pawn.usda layer. + +203 +00:09:19,459 --> 00:09:20,994 +This brings in the pawn asset, + +204 +00:09:20,994 --> 00:09:24,097 +and we can see it +in the catalog layer. + +205 +00:09:24,097 --> 00:09:26,166 +But how does USD know +which prim it should bring in + +206 +00:09:26,166 --> 00:09:28,368 +from the file you reference? + +207 +00:09:28,368 --> 00:09:32,439 +We can specify this prim +with the defaultPrim metadata. + +208 +00:09:32,439 --> 00:09:35,408 +The defaultPrim metadata +is defined on the stage + +209 +00:09:35,408 --> 00:09:37,477 +and specifies which prim +should be referred to + +210 +00:09:37,477 --> 00:09:40,280 +when using this scene +on another stage. + +211 +00:09:40,280 --> 00:09:44,417 +We recommend always authoring +a defaultPrim for USD assets. + +212 +00:09:44,417 --> 00:09:45,718 +Let's check our pawn asset + +213 +00:09:45,718 --> 00:09:48,421 +to make sure +the defaultPrim is authored. + +214 +00:09:48,421 --> 00:09:50,690 +We can see the defaultPrim +metadata at the stage level + +215 +00:09:50,690 --> 00:09:51,891 +in our asset. + +216 +00:09:51,891 --> 00:09:53,893 +Great. + +217 +00:09:53,893 --> 00:09:57,096 +Alternatively, if the +defaultPrim is not authored + +218 +00:09:57,096 --> 00:09:58,698 +or you wish to refer to a prim + +219 +00:09:58,698 --> 00:10:00,767 +that is different +from the defaultPrim, + +220 +00:10:00,767 --> 00:10:02,435 +you can specify the path +to the prim + +221 +00:10:02,435 --> 00:10:04,337 +in the referencing +layer explicitly, + +222 +00:10:04,337 --> 00:10:08,341 +anywhere in the hierarchy. + +223 +00:10:08,341 --> 00:10:12,579 +Paths in USD are used to +identify elements on a stage. + +224 +00:10:12,579 --> 00:10:16,049 +The prim path is a unique +identifier for a prim. + +225 +00:10:16,049 --> 00:10:19,819 +For example, on this stage +we see three prims. + +226 +00:10:19,819 --> 00:10:23,323 +The World prim +has a prim path of /World. + +227 +00:10:23,323 --> 00:10:24,691 +The Pawn and Knight prims + +228 +00:10:24,691 --> 00:10:27,360 +are considered children +of the World prim. + +229 +00:10:27,360 --> 00:10:31,130 +Thus the path to the Pawn prim +would be /World/Pawn, + +230 +00:10:31,130 --> 00:10:36,736 +and the path to the Knight prim +would be /World/Knight. + +231 +00:10:36,736 --> 00:10:39,806 +With that in mind, we can +explicitly set the prim path + +232 +00:10:39,806 --> 00:10:44,210 +to the Pawn prim +in our reference here. + +233 +00:10:44,210 --> 00:10:46,179 +For larger scenes, +it can be expensive + +234 +00:10:46,179 --> 00:10:49,048 +to load all +the scene information at once. + +235 +00:10:49,048 --> 00:10:51,251 +For this, USD allows +for the deferred loading + +236 +00:10:51,251 --> 00:10:52,952 +of scene description +onto a stage + +237 +00:10:52,952 --> 00:10:56,289 +with a type of reference +called a "payload." + +238 +00:10:56,289 --> 00:10:58,258 +It is recommended to use +payloads when referencing + +239 +00:10:58,258 --> 00:11:00,827 +large data sets, +such as complex geometry, + +240 +00:11:00,827 --> 00:11:02,161 +or other large scene graphs + +241 +00:11:02,161 --> 00:11:04,163 +representing props +or characters. + +242 +00:11:07,433 --> 00:11:10,003 +We'll convert these asset +references to payloads instead, + +243 +00:11:10,003 --> 00:11:14,541 +so we can choose to defer +the loading of the chess pieces. + +244 +00:11:14,541 --> 00:11:16,576 +If we choose +not to load the payloads, + +245 +00:11:16,576 --> 00:11:20,246 +our scene appears empty +when initially opened. + +246 +00:11:20,246 --> 00:11:22,181 +Once we enable +the loading of payloads, + +247 +00:11:22,181 --> 00:11:24,517 +we can see +all of our pieces again. + +248 +00:11:24,517 --> 00:11:26,452 +Now that we have our pieces +in the scene, + +249 +00:11:26,452 --> 00:11:28,888 +we can start laying them out +on the chess board. + +250 +00:11:28,888 --> 00:11:30,423 +We can do this +on yet another layer + +251 +00:11:30,423 --> 00:11:33,259 +using the USD concept +of layering. + +252 +00:11:33,259 --> 00:11:35,929 +With layering, +layers can be stacked, + +253 +00:11:35,929 --> 00:11:38,264 +similar to popular image editing +software packages + +254 +00:11:38,264 --> 00:11:41,534 +such as Photoshop +or Affinity Photo. + +255 +00:11:41,534 --> 00:11:43,536 +Prims in a layer +that are above another layer + +256 +00:11:43,536 --> 00:11:46,539 +are considered stronger +and can add or override data + +257 +00:11:46,539 --> 00:11:49,075 +in the lower layers. + +258 +00:11:49,075 --> 00:11:50,376 +As you can see, + +259 +00:11:50,376 --> 00:11:53,179 +the pieces are currently +in their wrong places + +260 +00:11:53,179 --> 00:11:55,081 +But don't worry, +we can use layering + +261 +00:11:55,081 --> 00:11:58,718 +to make nondestructive +modifications to other layers. + +262 +00:11:58,718 --> 00:12:00,453 +This is perfect for us +to move our pieces + +263 +00:12:00,453 --> 00:12:04,090 +to the right positions without +editing our catalog layer. + +264 +00:12:04,090 --> 00:12:06,659 +Let's see how this can be done + +265 +00:12:06,659 --> 00:12:09,596 +We start by creating a new stage +called ChessSet + +266 +00:12:09,596 --> 00:12:12,131 +which will be our final scene. + +267 +00:12:12,131 --> 00:12:13,833 +It contains metadata +called "sublayers" + +268 +00:12:13,833 --> 00:12:15,802 +at the top of the layer. + +269 +00:12:15,802 --> 00:12:20,573 +Here we bring in our pieces +with the catalog layer. + +270 +00:12:20,573 --> 00:12:23,776 +Next, we create a new layer -- +Layout.usda -- + +271 +00:12:23,776 --> 00:12:27,780 +which we use to move our pieces. + +272 +00:12:27,780 --> 00:12:29,983 +This layout layer will contain +the positions + +273 +00:12:29,983 --> 00:12:31,818 +of our chess pieces. + +274 +00:12:31,818 --> 00:12:34,921 +Let's override the position +of our pieces in the scene. + +275 +00:12:34,921 --> 00:12:37,857 +We'll start with Pawn_01. + +276 +00:12:37,857 --> 00:12:39,859 +We modify +the pawn asset's position + +277 +00:12:39,859 --> 00:12:42,662 +by changing +its translation attributes. + +278 +00:12:42,662 --> 00:12:46,766 +Let's check out +how this updates our scene. + +279 +00:12:46,766 --> 00:12:50,003 +Here we have our chess set again +without our layout layer, + +280 +00:12:50,003 --> 00:12:53,406 +so the pawn is still +in its original position. + +281 +00:12:53,406 --> 00:12:55,975 +Once we add our layout layer, +the pawn has been moved + +282 +00:12:55,975 --> 00:12:59,212 +to its expected position +on the chessboard. + +283 +00:12:59,212 --> 00:13:00,947 +Let's do the same +for the other pieces + +284 +00:13:00,947 --> 00:13:03,249 +and move them +to their correct locations. + +285 +00:13:04,951 --> 00:13:06,285 +We position our other +chess pieces + +286 +00:13:06,285 --> 00:13:09,055 +the same way +we position the pawn. + +287 +00:13:09,055 --> 00:13:12,225 +Here's how we did it +for Pawn_02. + +288 +00:13:12,225 --> 00:13:16,529 +And then again +for Pawn_03, and so on. + +289 +00:13:16,529 --> 00:13:18,398 +We finished the changes +to the layout layer, + +290 +00:13:18,398 --> 00:13:21,534 +and now we have the chess pieces +set in their correct location. + +291 +00:13:23,903 --> 00:13:26,205 +We have half +of our chess set built. + +292 +00:13:26,205 --> 00:13:29,676 +We use layers to bring in our +assets with the catalog layer + +293 +00:13:29,676 --> 00:13:32,045 +and use the overrides +to place them on the board + +294 +00:13:32,045 --> 00:13:33,680 +with the layout layer. + +295 +00:13:35,715 --> 00:13:37,450 +All that is left +is setting up the pieces + +296 +00:13:37,450 --> 00:13:39,519 +for the opponent side. + +297 +00:13:39,519 --> 00:13:41,621 +One thing to note is that +our opponent's pieces + +298 +00:13:41,621 --> 00:13:43,556 +are a different color. + +299 +00:13:43,556 --> 00:13:45,525 +Instead of making +a new set of assets, + +300 +00:13:45,525 --> 00:13:47,226 +we can update +our chess piece assets + +301 +00:13:47,226 --> 00:13:50,863 +using another USD concept, +variantSets. + +302 +00:13:50,863 --> 00:13:53,232 +VariantSets allow +for the dynamic swapping + +303 +00:13:53,232 --> 00:13:56,169 +of discrete alternatives +on the stage. + +304 +00:13:56,169 --> 00:13:57,870 +The variants contained +in a variantSet + +305 +00:13:57,870 --> 00:14:00,440 +can be different materials, +geometry, + +306 +00:14:00,440 --> 00:14:03,943 +or anything that +can be represented in USD. + +307 +00:14:03,943 --> 00:14:06,079 +Switching between +different variants on a stage + +308 +00:14:06,079 --> 00:14:08,347 +is nondestructive. + +309 +00:14:08,347 --> 00:14:10,216 +We will setup variant sets +to switch between + +310 +00:14:10,216 --> 00:14:14,320 +different materials +for the chess pieces. + +311 +00:14:14,320 --> 00:14:17,657 +In our Pawn asset, we add +a variantSet called "color" + +312 +00:14:17,657 --> 00:14:21,194 +so we can switch between +different colors for the pawn. + +313 +00:14:21,194 --> 00:14:24,197 +Now we add two variants +to our variantSet: + +314 +00:14:24,197 --> 00:14:30,570 +Dark using the dark material and +Light using the light material. + +315 +00:14:30,570 --> 00:14:33,473 +Finally, we set +the default variant to specify + +316 +00:14:33,473 --> 00:14:38,244 +which one to use when we load +the pawn onto the stage. + +317 +00:14:38,244 --> 00:14:40,913 +Now, we are back +in our catalog layer. + +318 +00:14:40,913 --> 00:14:42,849 +We've set up all the pieces +on the board, + +319 +00:14:42,849 --> 00:14:45,651 +but they are all using +the light material. + +320 +00:14:45,651 --> 00:14:47,153 +That is because +the default variant + +321 +00:14:47,153 --> 00:14:49,689 +is set to the light material. + +322 +00:14:49,689 --> 00:14:51,557 +Let's change the variant +of one of the pawns + +323 +00:14:51,557 --> 00:14:54,560 +to the dark material. + +324 +00:14:54,560 --> 00:14:56,362 +We set the variant to Dark. + +325 +00:14:56,362 --> 00:14:58,531 +We can see that one of the pawns +on the opponent's side + +326 +00:14:58,531 --> 00:15:00,633 +is now dark. + +327 +00:15:00,633 --> 00:15:04,871 +Let's apply this change +to the other pieces as well. + +328 +00:15:04,871 --> 00:15:07,607 +Finally, we completed +our chess set. + +329 +00:15:09,208 --> 00:15:13,012 +There's one more USD concept we +can use to optimize our scene: + +330 +00:15:13,012 --> 00:15:15,414 +scene graph instancing. + +331 +00:15:15,414 --> 00:15:17,016 +Scene graph instancing allows us + +332 +00:15:17,016 --> 00:15:20,019 +to reuse parts of scene graph +on a stage many times + +333 +00:15:20,019 --> 00:15:21,220 +to represent anything +that contains + +334 +00:15:21,220 --> 00:15:24,690 +more than one of something +such as foliage, furniture, + +335 +00:15:24,690 --> 00:15:27,660 +or in our case, chess pieces. + +336 +00:15:27,660 --> 00:15:29,862 +Using scene graph instancing +provides both memory + +337 +00:15:29,862 --> 00:15:33,266 +and performance improvements +in your applications. + +338 +00:15:33,266 --> 00:15:35,101 +To use scene graph instancing, + +339 +00:15:35,101 --> 00:15:37,570 +we can specify the instance +metadata on a prim + +340 +00:15:37,570 --> 00:15:40,139 +or part of a scene graph. + +341 +00:15:40,139 --> 00:15:42,175 +All prims that are instanced +are candidates + +342 +00:15:42,175 --> 00:15:44,577 +to share the same scene graph. + +343 +00:15:44,577 --> 00:15:48,347 +Let's add support for prim +instancing in our scene. + +344 +00:15:48,347 --> 00:15:51,384 +In our catalog file, we will add +the metadata "instanceable" + +345 +00:15:51,384 --> 00:15:55,021 +to the chess piece prims +and set the value to true. + +346 +00:15:55,021 --> 00:15:57,924 +By adding this metadata, +USD will now treat these assets + +347 +00:15:57,924 --> 00:16:01,494 +as potentially instanceable +prims that share the same data, + +348 +00:16:01,494 --> 00:16:05,198 +rather than duplicating the data +for each prim. + +349 +00:16:05,198 --> 00:16:06,999 +Our chess set +still looks the same + +350 +00:16:06,999 --> 00:16:10,069 +but it's now more performant +and memory efficient. + +351 +00:16:10,069 --> 00:16:12,572 +And with this, we're done +with our chess set! + +352 +00:16:12,572 --> 00:16:16,008 +It looks great, and we exercised +a lot of USD features. + +353 +00:16:17,310 --> 00:16:21,214 +Layering, referencing, payloads +and variantSets + +354 +00:16:21,214 --> 00:16:25,451 +are just some of the types +of composition that USD defines. + +355 +00:16:25,451 --> 00:16:26,853 +There is a specific +strength order + +356 +00:16:26,853 --> 00:16:28,821 +in which USD composes +the scene graph + +357 +00:16:28,821 --> 00:16:32,291 +to ensure consistent +scene representation. + +358 +00:16:32,291 --> 00:16:34,126 +More information +about composition + +359 +00:16:34,126 --> 00:16:36,362 +and the order acronym LIVRPS + +360 +00:16:36,362 --> 00:16:40,466 +can be learned +in Pixar's USD documentation. + +361 +00:16:40,466 --> 00:16:41,767 +In this session, + +362 +00:16:41,767 --> 00:16:44,237 +we talked about +what's inside of USD files. + +363 +00:16:44,237 --> 00:16:46,339 +Now let's talk about +the files themselves + +364 +00:16:46,339 --> 00:16:48,908 +and how they might +appear on disk. + +365 +00:16:48,908 --> 00:16:51,744 +There are several types +of USD files. + +366 +00:16:51,744 --> 00:16:53,946 +USD files containing +readable ASCII text + +367 +00:16:53,946 --> 00:16:56,282 +had the .usda extension. + +368 +00:16:56,282 --> 00:16:59,952 +We have been using these files +the whole time in this session. + +369 +00:16:59,952 --> 00:17:02,788 +A more compact and efficient +binary representation + +370 +00:17:02,788 --> 00:17:07,226 +is the crate format, +which has the .usdc extension. + +371 +00:17:07,226 --> 00:17:10,496 +You may also have files +with the .usd extension. + +372 +00:17:10,496 --> 00:17:15,034 +These could either be ASCII text +or binary crate files. + +373 +00:17:15,034 --> 00:17:18,537 +Lastly, USD has a packaging +format which can contain + +374 +00:17:18,537 --> 00:17:21,540 +multiple USD files +and associated auxiliary files, + +375 +00:17:21,540 --> 00:17:24,110 +like textures, in an +uncompressed zip archive + +376 +00:17:24,110 --> 00:17:27,680 +with the .usdz extension. + +377 +00:17:27,680 --> 00:17:31,317 +Today we learned about some +of USD's fundamental concepts: + +378 +00:17:31,317 --> 00:17:38,357 +stage, layers, prims, schemas, +attributes, and metadata. + +379 +00:17:38,357 --> 00:17:40,426 +We used these concepts +to build a chess set + +380 +00:17:40,426 --> 00:17:44,263 +and made use of referencing, +payloads, the defaultPrim, + +381 +00:17:44,263 --> 00:17:48,200 +prim paths, layering, +and instancing. + +382 +00:17:48,200 --> 00:17:51,904 +And finally, we discussed +the different USD file formats: + +383 +00:17:51,904 --> 00:17:57,343 +.usda, .usdc, .usd, and .usdz. + +384 +00:17:57,343 --> 00:17:59,645 +We encourage you to learn more +about these concepts + +385 +00:17:59,645 --> 00:18:02,748 +with Pixar's USD documentation +and take full advantage + +386 +00:18:02,748 --> 00:18:05,918 +of USD's capabilities +in your applications. + +387 +00:18:05,918 --> 00:18:07,386 +Thank you! + +388 +00:18:07,386 --> 00:18:11,390 +♪ + diff --git a/eng/2022 Session 10131 Qualities of great AR experiences en.srt b/eng/2022 Session 10131 Qualities of great AR experiences en.srt new file mode 100644 index 0000000..c039e65 --- /dev/null +++ b/eng/2022 Session 10131 Qualities of great AR experiences en.srt @@ -0,0 +1,1461 @@ +1 +00:00:00,033 --> 00:00:03,203 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,203 --> 00:00:09,509 +♪ + +3 +00:00:09,509 --> 00:00:12,646 +Welcome to "Qualities of +great AR experiences." + +4 +00:00:12,646 --> 00:00:15,282 +I'm Alli Dryer, and I'm part of +the Apple Design team + +5 +00:00:15,282 --> 00:00:18,485 +working on augmented reality. + +6 +00:00:18,485 --> 00:00:21,688 +AR lets you deliver experiences +that blend virtual objects + +7 +00:00:21,688 --> 00:00:22,956 +with the real world, + +8 +00:00:22,956 --> 00:00:26,526 +creating the illusion that +these objects actually exist. + +9 +00:00:26,526 --> 00:00:28,629 +AR can be +surprisingly immersive, + +10 +00:00:28,629 --> 00:00:30,097 +and yet at the same time, + +11 +00:00:30,097 --> 00:00:33,267 +it's always grounded +in what's really happening. + +12 +00:00:33,267 --> 00:00:34,735 +It allows you +to visualize things + +13 +00:00:34,735 --> 00:00:37,204 +that would be impossible, +risky, or hard to do + +14 +00:00:37,204 --> 00:00:39,640 +from a practical standpoint. + +15 +00:00:39,640 --> 00:00:42,175 +Blurring the line between +imagination and reality, + +16 +00:00:42,175 --> 00:00:45,345 +an AR experience +really feels like magic. + +17 +00:00:45,345 --> 00:00:47,547 +You can completely transform +people's surroundings + +18 +00:00:47,547 --> 00:00:51,084 +through visuals and sound so +they experience something new. + +19 +00:00:51,084 --> 00:00:54,087 +You can also add new layers of +information to the environment + +20 +00:00:54,087 --> 00:00:57,724 +to enable quick and lightweight +interactions. + +21 +00:00:57,724 --> 00:01:00,494 +Designing for augmented reality +can be very different + +22 +00:01:00,494 --> 00:01:02,596 +from designing +for 2D applications, + +23 +00:01:02,596 --> 00:01:04,998 +but don't worry, +we'll break it down here. + +24 +00:01:04,998 --> 00:01:08,035 +Today I'm going to share +criteria you can use + +25 +00:01:08,035 --> 00:01:11,238 +to decide if AR is right for +the experience you're building, + +26 +00:01:11,238 --> 00:01:14,207 +along with tips for handling +some of the unique aspects + +27 +00:01:14,207 --> 00:01:16,209 +of augmented reality +when you're designing + +28 +00:01:16,209 --> 00:01:18,912 +your first AR experience. + +29 +00:01:18,912 --> 00:01:21,315 +OK. So you're designing +an app or feature + +30 +00:01:21,315 --> 00:01:24,017 +and wondering whether AR +makes sense for you. + +31 +00:01:24,017 --> 00:01:26,219 +Here are a few things +to consider. + +32 +00:01:26,219 --> 00:01:29,856 +AR can help you deliver +a true representation of things. + +33 +00:01:29,856 --> 00:01:31,091 +To show what I mean, + +34 +00:01:31,091 --> 00:01:34,361 +let's look at an example of +something AR does really well, + +35 +00:01:34,361 --> 00:01:37,230 +which is to present the +real-world size of an object. + +36 +00:01:37,230 --> 00:01:38,865 +Say you're trying +to educate people + +37 +00:01:38,865 --> 00:01:41,168 +about how big a dinosaur is. + +38 +00:01:41,168 --> 00:01:44,171 +You could just write it out, + +39 +00:01:44,171 --> 00:01:47,574 +or show a picture, + +40 +00:01:47,574 --> 00:01:51,411 +or you could unleash +a virtual T. rex on the scene. + +41 +00:01:51,411 --> 00:01:53,447 +In that case, +there's no need to imagine + +42 +00:01:53,447 --> 00:01:55,282 +how big the dinosaur is. + +43 +00:01:55,282 --> 00:01:57,050 +The AR experience +gives the viewer + +44 +00:01:57,050 --> 00:01:59,353 +an instant, +visceral sense of scale + +45 +00:01:59,353 --> 00:02:01,655 +in a way that's +truly immersive. + +46 +00:02:01,655 --> 00:02:04,091 +This example comes +from the app Monster Park, + +47 +00:02:04,091 --> 00:02:06,493 +a game that lets people +explore and control + +48 +00:02:06,493 --> 00:02:09,896 +the movement of animated +three-dimensional dinosaurs. + +49 +00:02:09,896 --> 00:02:11,398 +It's fitting that the entire app + +50 +00:02:11,398 --> 00:02:14,501 +revolves around this +large-as-life AR experience, + +51 +00:02:14,501 --> 00:02:20,007 +also emphasizing the creation of +photos and videos for sharing. + +52 +00:02:20,007 --> 00:02:23,176 +AR is at its most delightful +when it affects and responds + +53 +00:02:23,176 --> 00:02:25,312 +to what's immediately +around you, + +54 +00:02:25,312 --> 00:02:28,415 +involving physical space +in a meaningful way. + +55 +00:02:28,415 --> 00:02:30,017 +Say you're trying +to design an app + +56 +00:02:30,017 --> 00:02:32,919 +that helps with interior design, +and you want to help people + +57 +00:02:32,919 --> 00:02:35,122 +choose a paint color +for their walls. + +58 +00:02:35,122 --> 00:02:38,425 +You could use swatches +or stock photography, + +59 +00:02:38,425 --> 00:02:40,327 +but those aren't very accurate. + +60 +00:02:40,327 --> 00:02:42,095 +Even better, +you could show someone + +61 +00:02:42,095 --> 00:02:43,864 +how their very own +room would look + +62 +00:02:43,864 --> 00:02:46,166 +with freshly painted walls. + +63 +00:02:46,166 --> 00:02:48,435 +The Color Snap app +does just that. + +64 +00:02:48,435 --> 00:02:50,303 +It allows people +to choose a paint color + +65 +00:02:50,303 --> 00:02:52,973 +and then tap a wall +to preview the effect. + +66 +00:02:52,973 --> 00:02:55,308 +Because it's AR, +people can move around + +67 +00:02:55,308 --> 00:02:57,444 +to see how the new color +will affect their space + +68 +00:02:57,444 --> 00:02:59,312 +from different vantage points. + +69 +00:02:59,312 --> 00:03:02,983 +Next, augmented reality +allows you to create experiences + +70 +00:03:02,983 --> 00:03:05,919 +that visualize +virtual objects in 3D, + +71 +00:03:05,919 --> 00:03:09,256 +which makes it easier +to understand and evaluate them. + +72 +00:03:09,256 --> 00:03:12,793 +With AR, you can build features +that help people try on things + +73 +00:03:12,793 --> 00:03:15,128 +in a way that's virtual +yet realistic enough + +74 +00:03:15,128 --> 00:03:18,165 +for someone to make +a confident purchasing decision. + +75 +00:03:18,165 --> 00:03:20,067 +For example, +the Warby Parker app + +76 +00:03:20,067 --> 00:03:22,235 +lets people try on +virtual glasses + +77 +00:03:22,235 --> 00:03:24,004 +so they can place an order +knowing exactly + +78 +00:03:24,004 --> 00:03:25,605 +how their frames will fit. + +79 +00:03:25,605 --> 00:03:28,175 +It's faster to switch between +frame styles in AR + +80 +00:03:28,175 --> 00:03:31,144 +than it would be +to swap glasses in real life. + +81 +00:03:31,144 --> 00:03:32,946 +Grounding virtual objects +in the scene + +82 +00:03:32,946 --> 00:03:34,948 +is also really important. + +83 +00:03:34,948 --> 00:03:37,951 +The quality of lighting +and shadows in your experiences + +84 +00:03:37,951 --> 00:03:40,454 +can have a huge impact +on people's feeling + +85 +00:03:40,454 --> 00:03:42,055 +that things are really there, + +86 +00:03:42,055 --> 00:03:44,958 +even when an object +isn't very realistic. + +87 +00:03:44,958 --> 00:03:47,094 +The IKEA Place app +allows people to place + +88 +00:03:47,094 --> 00:03:49,429 +a piece of virtual furniture +in their home + +89 +00:03:49,429 --> 00:03:50,530 +and get a more accurate sense + +90 +00:03:50,530 --> 00:03:52,599 +of what the updated room +would look like. + +91 +00:03:52,599 --> 00:03:55,535 +IKEA Place takes advantage of +ARKit's built-in lighting + +92 +00:03:55,535 --> 00:03:57,904 +for realistic shadows +that really settle + +93 +00:03:57,904 --> 00:04:00,874 +the virtual furniture +into the room. + +94 +00:04:00,874 --> 00:04:03,143 +Visualizing in 3D +can refer to objects, + +95 +00:04:03,143 --> 00:04:05,345 +but don't forget about +using people's surroundings + +96 +00:04:05,345 --> 00:04:08,482 +as a canvas +for your experiences. + +97 +00:04:08,482 --> 00:04:11,418 +You can create new features +with advanced visual effects, + +98 +00:04:11,418 --> 00:04:14,254 +provide maps and 3D models +that fit your brand, + +99 +00:04:14,254 --> 00:04:17,157 +and even bring people's spaces +into the game you're building + +100 +00:04:17,157 --> 00:04:20,393 +with iOS's new RoomPlan API. + +101 +00:04:20,393 --> 00:04:22,696 +We've got a session +on creating 3D room scans + +102 +00:04:22,696 --> 00:04:24,397 +that goes into detail +about RoomPlan + +103 +00:04:24,397 --> 00:04:26,399 +if you want to learn more. + +104 +00:04:26,399 --> 00:04:28,535 +AR lets you streamline actions + +105 +00:04:28,535 --> 00:04:31,638 +by attaching digital +capabilities to physical things, + +106 +00:04:31,638 --> 00:04:34,407 +making your apps and features +more efficient. + +107 +00:04:34,407 --> 00:04:35,775 +You might have +already experienced + +108 +00:04:35,775 --> 00:04:38,678 +how using the real world +as input can speed things up + +109 +00:04:38,678 --> 00:04:42,482 +if you've ever scanned a code +to order at a restaurant. + +110 +00:04:42,482 --> 00:04:45,919 +You can use AR to build compact +yet incredibly useful features + +111 +00:04:45,919 --> 00:04:47,721 +that save people time. + +112 +00:04:47,721 --> 00:04:51,024 +In iOS's Measure App, +centering the camera on a person + +113 +00:04:51,024 --> 00:04:54,594 +automatically displays her +height in just the right spot. + +114 +00:04:54,594 --> 00:04:58,231 +The experience feels lightweight +because there's very little UI, + +115 +00:04:58,231 --> 00:05:00,567 +the interaction is based +on device movement, + +116 +00:05:00,567 --> 00:05:04,671 +and the measurement information +disappears after you've seen it. + +117 +00:05:04,671 --> 00:05:07,507 +This AR experience +would be too limited to offer + +118 +00:05:07,507 --> 00:05:10,210 +as an app on its own, +but it really complements + +119 +00:05:10,210 --> 00:05:13,280 +the other capabilities +of the Measure app. + +120 +00:05:13,280 --> 00:05:16,683 +So, now you know you can use +AR for your apps and features + +121 +00:05:16,683 --> 00:05:19,519 +when you want to deliver +a true representation, + +122 +00:05:19,519 --> 00:05:22,322 +involve physical space +in a meaningful way, + +123 +00:05:22,322 --> 00:05:24,925 +visualize in 3D, +and streamline actions + +124 +00:05:24,925 --> 00:05:27,961 +by making use of what's in +the environment. + +125 +00:05:27,961 --> 00:05:30,664 +And as you can see, there are +a lot of different ways + +126 +00:05:30,664 --> 00:05:33,967 +to incorporate AR into +the experiences you're creating, + +127 +00:05:33,967 --> 00:05:36,036 +whether it's as +the main focus of the app + +128 +00:05:36,036 --> 00:05:39,573 +or with a lighter hand as +a feature that reduces friction. + +129 +00:05:39,573 --> 00:05:42,809 +So, you've decided AR +is right for your purposes + +130 +00:05:42,809 --> 00:05:45,845 +and now it's time +to get started with design. + +131 +00:05:45,845 --> 00:05:47,414 +When you're designing for AR, + +132 +00:05:47,414 --> 00:05:49,482 +you'll need to consider +some of the aspects + +133 +00:05:49,482 --> 00:05:53,286 +that make it different +than traditional 2D interfaces. + +134 +00:05:53,286 --> 00:05:57,023 +AR is spatial. + +135 +00:05:57,023 --> 00:06:00,894 +AR is movement-based. + +136 +00:06:00,894 --> 00:06:05,265 +And AR is tied +to the physical environment. + +137 +00:06:05,265 --> 00:06:07,968 +You'll need to think about +how to blend virtual elements + +138 +00:06:07,968 --> 00:06:10,470 +into the scene, +about ergonomics, + +139 +00:06:10,470 --> 00:06:13,473 +and about working +with a limited field of view. + +140 +00:06:13,473 --> 00:06:15,475 +Now I'll go through tips +you can keep in mind + +141 +00:06:15,475 --> 00:06:18,712 +when designing AR experiences. + +142 +00:06:18,712 --> 00:06:20,814 +People using your app +might be in places + +143 +00:06:20,814 --> 00:06:22,649 +that are difficult for AR, + +144 +00:06:22,649 --> 00:06:24,651 +so what you can do +to help is coach them + +145 +00:06:24,651 --> 00:06:27,721 +through different ways +to get to a better experience. + +146 +00:06:27,721 --> 00:06:29,823 +The Mission to Mars app +does a great job + +147 +00:06:29,823 --> 00:06:31,691 +of getting people +set up for success + +148 +00:06:31,691 --> 00:06:33,727 +with a strong coaching sequence. + +149 +00:06:33,727 --> 00:06:37,664 +Let's take a closer look +at this example. + +150 +00:06:37,664 --> 00:06:38,898 +As you're getting started, + +151 +00:06:38,898 --> 00:06:41,234 +Mission to Mars highlights +three different requirements + +152 +00:06:41,234 --> 00:06:44,271 +you need in your environment +to have a great session. + +153 +00:06:44,271 --> 00:06:47,107 +First, there's a reminder +that AR can be immersive, + +154 +00:06:47,107 --> 00:06:50,277 +and it's wise to find +a safe place to engage with it. + +155 +00:06:50,277 --> 00:06:51,811 +This can be a particular concern + +156 +00:06:51,811 --> 00:06:54,180 +for experiences +that involve wayfinding, + +157 +00:06:54,180 --> 00:06:55,749 +where you'll need +to take extra care + +158 +00:06:55,749 --> 00:06:57,917 +not to draw people's +attention to the screen + +159 +00:06:57,917 --> 00:07:01,021 +for long periods of time. + +160 +00:07:01,021 --> 00:07:03,456 +Next, the app suggests +that AR performs best + +161 +00:07:03,456 --> 00:07:05,225 +on surfaces with texture, + +162 +00:07:05,225 --> 00:07:09,362 +rather than on glass or +smooth white featureless planes. + +163 +00:07:09,362 --> 00:07:10,630 +LiDAR can help overcome + +164 +00:07:10,630 --> 00:07:12,999 +some of these challenging +environmental conditions, + +165 +00:07:12,999 --> 00:07:15,035 +so you might be able +to omit this suggestion + +166 +00:07:15,035 --> 00:07:17,070 +for some devices. + +167 +00:07:17,070 --> 00:07:19,272 +And last, Mission to Mars +advises people + +168 +00:07:19,272 --> 00:07:21,975 +to find a bright space +because AR works best + +169 +00:07:21,975 --> 00:07:25,011 +in environments +that are properly lit. + +170 +00:07:25,011 --> 00:07:27,747 +What's great about this coaching +sequence is that it's quick, + +171 +00:07:27,747 --> 00:07:30,283 +easy to navigate, +and helps people understand + +172 +00:07:30,283 --> 00:07:33,920 +how to set up their environment +for the best AR experience. + +173 +00:07:33,920 --> 00:07:36,923 +The next tip is to take +advantage of screen space. + +174 +00:07:36,923 --> 00:07:39,225 +You'd be surprised +how large a role it can play + +175 +00:07:39,225 --> 00:07:41,394 +in AR experience design. + +176 +00:07:41,394 --> 00:07:43,596 +So, what is screen space? + +177 +00:07:43,596 --> 00:07:45,498 +Think of screen space +as a 2D layer + +178 +00:07:45,498 --> 00:07:47,767 +that sits on top of +the three-dimensional world + +179 +00:07:47,767 --> 00:07:49,969 +that's captured by +your camera view. + +180 +00:07:49,969 --> 00:07:52,472 +It's best to place text +and interactive elements + +181 +00:07:52,472 --> 00:07:54,474 +like buttons +here in screen space, + +182 +00:07:54,474 --> 00:07:56,776 +rather than in the 3D world +of the camera + +183 +00:07:56,776 --> 00:07:58,812 +to preserve legibility. + +184 +00:07:58,812 --> 00:08:01,181 +Here's a great example +from the Mission to Mars app + +185 +00:08:01,181 --> 00:08:04,818 +that illustrates how to handle +text in screen space. + +186 +00:08:04,818 --> 00:08:08,488 +High-contrast text and buttons +sit in a 2D screen space layer + +187 +00:08:08,488 --> 00:08:11,024 +on top of the 3D scene below. + +188 +00:08:11,024 --> 00:08:14,260 +The text updates as the camera +moves to help you understand + +189 +00:08:14,260 --> 00:08:17,864 +how it relates to the 3D objects +in the scene. + +190 +00:08:17,864 --> 00:08:20,667 +If you do need to lock your text +to something in the world, + +191 +00:08:20,667 --> 00:08:23,169 +try to keep it billboarded +or parallel to the screen + +192 +00:08:23,169 --> 00:08:24,838 +when possible. + +193 +00:08:24,838 --> 00:08:26,840 +Focus on increasing contrast, + +194 +00:08:26,840 --> 00:08:29,642 +bumping up type sizes +and putting text on a background + +195 +00:08:29,642 --> 00:08:32,512 +so you can maintain +accessibility. + +196 +00:08:32,512 --> 00:08:34,647 +Design for constant movement. + +197 +00:08:34,647 --> 00:08:36,249 +It's important +that people understand + +198 +00:08:36,249 --> 00:08:38,718 +how and when to move +their devices. + +199 +00:08:38,718 --> 00:08:40,754 +Sometimes they'll need +to move their bodies as well + +200 +00:08:40,754 --> 00:08:43,523 +in order to experience AR. + +201 +00:08:43,523 --> 00:08:45,291 +Try to provide +real-time feedback + +202 +00:08:45,291 --> 00:08:47,994 +with visual effects and sound +as people move, + +203 +00:08:47,994 --> 00:08:50,363 +so that even if the action +is taking place out of view, + +204 +00:08:50,363 --> 00:08:54,534 +people can still connect +with the experience. + +205 +00:08:54,534 --> 00:08:57,203 +Use simple, glanceable +instructions and animations + +206 +00:08:57,203 --> 00:08:59,272 +to coach people how to move. + +207 +00:08:59,272 --> 00:09:01,775 +It helps to place instructions +in screen space + +208 +00:09:01,775 --> 00:09:05,812 +and to show them on an as-needed +basis, rather than all up front. + +209 +00:09:05,812 --> 00:09:08,815 +It's great to leverage the +built-in coaching animations, + +210 +00:09:08,815 --> 00:09:11,551 +but you can also create your own +onboarding instructions + +211 +00:09:11,551 --> 00:09:12,886 +that relate to your app, + +212 +00:09:12,886 --> 00:09:15,088 +like this example +from the app DoodleLens + +213 +00:09:15,088 --> 00:09:17,190 +that shows an iPhone +panning back and forth + +214 +00:09:17,190 --> 00:09:19,492 +in front of a doodle. + +215 +00:09:19,492 --> 00:09:23,997 +Ergonomic considerations +are really important for AR. + +216 +00:09:23,997 --> 00:09:25,899 +It can be exhausting +to hold your arm out + +217 +00:09:25,899 --> 00:09:27,667 +for long periods of time, + +218 +00:09:27,667 --> 00:09:29,269 +and uncomfortable +to reach for buttons + +219 +00:09:29,269 --> 00:09:33,306 +that aren't positioned properly +for one-handed use. + +220 +00:09:33,306 --> 00:09:37,143 +Emphasize legibility at arm's +length for the entire interface. + +221 +00:09:37,143 --> 00:09:39,779 +Simplify interactions +so that they can be completed + +222 +00:09:39,779 --> 00:09:41,648 +with minimal effort. + +223 +00:09:41,648 --> 00:09:44,617 +Use oversized buttons +with high-contrast icons + +224 +00:09:44,617 --> 00:09:47,387 +that are easy to tap +one-handed with a thumb. + +225 +00:09:47,387 --> 00:09:49,389 +Here's a nice example +at the bottom of the screen + +226 +00:09:49,389 --> 00:09:52,091 +in DoodleLens. + +227 +00:09:52,091 --> 00:09:54,060 +People are viewing +AR experiences + +228 +00:09:54,060 --> 00:09:55,595 +with a limited field of view + +229 +00:09:55,595 --> 00:09:57,630 +offered through +a handheld device, + +230 +00:09:57,630 --> 00:09:59,766 +so there's a good chance +they might not be able to see + +231 +00:09:59,766 --> 00:10:02,168 +the full extent +of something large. + +232 +00:10:02,168 --> 00:10:04,871 +There's also a possibility +that a virtual object + +233 +00:10:04,871 --> 00:10:07,874 +might be located out of view. + +234 +00:10:07,874 --> 00:10:10,443 +Allow people to adjust +the scale of an object, + +235 +00:10:10,443 --> 00:10:12,645 +in case they're not +able to back up far enough + +236 +00:10:12,645 --> 00:10:14,747 +to see the whole thing. + +237 +00:10:14,747 --> 00:10:16,783 +This example from AR Quick Look + +238 +00:10:16,783 --> 00:10:20,086 +uses a pinch gesture +for direct manipulation. + +239 +00:10:20,086 --> 00:10:22,489 +There's also a haptic +so that you can feel it + +240 +00:10:22,489 --> 00:10:26,426 +when you scale something +past 100 percent. + +241 +00:10:26,426 --> 00:10:29,362 +When something is out of view +to the right or to the left, + +242 +00:10:29,362 --> 00:10:32,298 +use sounds and haptics +and provide simple indicators + +243 +00:10:32,298 --> 00:10:35,568 +or text instructions +in screen space. + +244 +00:10:35,568 --> 00:10:37,770 +It can also be helpful +to provide a map + +245 +00:10:37,770 --> 00:10:40,607 +or bird's-eye view +showing someone's orientation + +246 +00:10:40,607 --> 00:10:42,742 +and illustrating +that the object is located + +247 +00:10:42,742 --> 00:10:44,978 +in the opposite direction. + +248 +00:10:44,978 --> 00:10:47,347 +In this example +from the RoomPlan experience, + +249 +00:10:47,347 --> 00:10:49,983 +the small 3D model drawing +at the bottom of the view + +250 +00:10:49,983 --> 00:10:51,885 +helps you preview your results + +251 +00:10:51,885 --> 00:10:55,288 +and keep track of what has +been scanned so far. + +252 +00:10:55,288 --> 00:10:57,824 +It can be difficult for people +peering through a screen + +253 +00:10:57,824 --> 00:11:00,827 +to understand the location +of virtual objects in space + +254 +00:11:00,827 --> 00:11:03,196 +unless they're behaving +realistically. + +255 +00:11:03,196 --> 00:11:06,065 +Depth cues that help people see +how far away things are + +256 +00:11:06,065 --> 00:11:07,967 +can help with this. + +257 +00:11:07,967 --> 00:11:10,537 +The size of objects, +perspective effects + +258 +00:11:10,537 --> 00:11:12,639 +like diminishing +towards the horizon, + +259 +00:11:12,639 --> 00:11:14,541 +realistic shadows and lighting, + +260 +00:11:14,541 --> 00:11:16,776 +the proper amount +of detail in your textures, + +261 +00:11:16,776 --> 00:11:19,212 +and overlapping objects +create a feeling of depth + +262 +00:11:19,212 --> 00:11:22,749 +and help people perceive +spatial relationships. + +263 +00:11:22,749 --> 00:11:25,485 +I want to highlight one depth +cue that's tricky to work with + +264 +00:11:25,485 --> 00:11:27,320 +but can really +help people understand + +265 +00:11:27,320 --> 00:11:30,056 +where things are located +relative to each other: + +266 +00:11:30,056 --> 00:11:33,459 +overlapping, +also known as occlusion. + +267 +00:11:33,459 --> 00:11:37,096 +Here's an example of overlapping +you can see in AR Quick Look. + +268 +00:11:37,096 --> 00:11:39,265 +The virtual airplane +seems like it's positioned + +269 +00:11:39,265 --> 00:11:41,367 +behind the wooden blocks +on the desk + +270 +00:11:41,367 --> 00:11:44,003 +because the lower part of it +is hidden. + +271 +00:11:44,003 --> 00:11:46,539 +Finally, craft experiences +that last + +272 +00:11:46,539 --> 00:11:48,575 +no more than a minute or two. + +273 +00:11:48,575 --> 00:11:51,110 +This is for the ergonomic +reasons I mentioned earlier, + +274 +00:11:51,110 --> 00:11:55,048 +but also because AR is +a resource-intensive superpower + +275 +00:11:55,048 --> 00:11:59,352 +that has a big impact +on battery and thermals. + +276 +00:11:59,352 --> 00:12:02,055 +If you do end up creating +a longer experience, + +277 +00:12:02,055 --> 00:12:04,357 +make sure that you +build in breaks. + +278 +00:12:04,357 --> 00:12:07,160 +For All Mankind: Time Capsule +is an AR experience + +279 +00:12:07,160 --> 00:12:10,697 +that allows you to explore +the For All Mankind universe. + +280 +00:12:10,697 --> 00:12:12,498 +The app complements +the TV show, + +281 +00:12:12,498 --> 00:12:15,935 +telling stories through +interactive objects. + +282 +00:12:15,935 --> 00:12:17,937 +Time Capsule is +presented in chapters + +283 +00:12:17,937 --> 00:12:19,572 +that provide moments of rest + +284 +00:12:19,572 --> 00:12:23,376 +and a place to pause +then return to the experience. + +285 +00:12:23,376 --> 00:12:26,312 +Today I shared tips to help +overcome some of the challenges + +286 +00:12:26,312 --> 00:12:28,848 +with working on AR features. + +287 +00:12:28,848 --> 00:12:31,250 +You've learned to guide people +to the right environment, + +288 +00:12:31,250 --> 00:12:33,219 +to take advantage +of screen space, + +289 +00:12:33,219 --> 00:12:35,021 +to design for constant +movement, + +290 +00:12:35,021 --> 00:12:38,157 +to think about ergonomics +and a limited field of view, + +291 +00:12:38,157 --> 00:12:41,327 +to use depth cues, and to limit +the duration of the experience + +292 +00:12:41,327 --> 00:12:43,830 +to keep people +from getting fatigued. + +293 +00:12:43,830 --> 00:12:46,699 +Augmented Reality feels +like magic when it's useful, + +294 +00:12:46,699 --> 00:12:49,636 +delightful, and relates +to the physical world. + +295 +00:12:49,636 --> 00:12:51,771 +AR allows you +to see virtual objects + +296 +00:12:51,771 --> 00:12:54,140 +or even to transform +your environment. + +297 +00:12:54,140 --> 00:12:57,176 +You can attach digital +capabilities to physical things + +298 +00:12:57,176 --> 00:13:00,480 +to create a layer of useful +information and actions. + +299 +00:13:00,480 --> 00:13:02,582 +The AR features and apps +that you create + +300 +00:13:02,582 --> 00:13:05,818 +will transform how people work, +learn, play, shop, + +301 +00:13:05,818 --> 00:13:07,687 +and connect with the world. + +302 +00:13:07,687 --> 00:13:09,222 +I can't wait to see +what you'll build next + +303 +00:13:09,222 --> 00:13:10,690 +in augmented reality. + +304 +00:13:10,690 --> 00:13:12,158 +Thank you! + +305 +00:13:12,158 --> 00:13:16,162 +♪ + diff --git a/eng/2022 Session 10132 Discover PhotoKit change history en.srt b/eng/2022 Session 10132 Discover PhotoKit change history en.srt new file mode 100644 index 0000000..916628d --- /dev/null +++ b/eng/2022 Session 10132 Discover PhotoKit change history en.srt @@ -0,0 +1,931 @@ +1 +00:00:00,000 --> 00:00:03,070 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,070 --> 00:00:09,843 +♪ + +3 +00:00:09,843 --> 00:00:11,411 +Hi, my name is Mindy + +4 +00:00:11,411 --> 00:00:14,248 +and I'm an engineer +on the Photos team. + +5 +00:00:14,248 --> 00:00:16,850 +Today, I'll be going over +how to access + +6 +00:00:16,850 --> 00:00:20,754 +Photos change history +in your apps. + +7 +00:00:20,754 --> 00:00:23,457 +PhotoKit provides +a rich set of APIs + +8 +00:00:23,457 --> 00:00:27,828 +for accessing and updating +photos, videos, and albums + +9 +00:00:27,828 --> 00:00:29,930 +stored in the photo library. + +10 +00:00:29,930 --> 00:00:33,133 +PhotoKit is designed for apps +that require a deep level + +11 +00:00:33,133 --> 00:00:36,570 +of Photos access +and integration + +12 +00:00:36,570 --> 00:00:39,907 +for managing or editing photos, +custom cameras, + +13 +00:00:39,907 --> 00:00:42,976 +or apps that give people a way +to browse their photo library + +14 +00:00:42,976 --> 00:00:45,445 +in a unique way. + +15 +00:00:45,445 --> 00:00:48,315 +These types of applications +may want to monitor + +16 +00:00:48,315 --> 00:00:50,717 +how the photo library +changes over time + +17 +00:00:50,717 --> 00:00:54,354 +to closely mirror +the Photos experience. + +18 +00:00:54,354 --> 00:00:57,524 +Let's say I've created +a social hiking app + +19 +00:00:57,524 --> 00:01:00,260 +that allows people +to share and edit photos + +20 +00:01:00,260 --> 00:01:02,796 +of hiking trips with friends. + +21 +00:01:02,796 --> 00:01:04,431 +When someone launches the app, + +22 +00:01:04,431 --> 00:01:07,868 +the app gathers photos +from the start and end timestamp + +23 +00:01:07,868 --> 00:01:11,004 +of their latest hiking workout +to generate a collage + +24 +00:01:11,004 --> 00:01:13,340 +of their experience +on the mountain. + +25 +00:01:13,340 --> 00:01:16,209 +The collages stay in sync +with the selected photos + +26 +00:01:16,209 --> 00:01:18,111 +from the photo library. + +27 +00:01:18,111 --> 00:01:19,813 +If someone receives +hiking photos + +28 +00:01:19,813 --> 00:01:21,581 +from a friend, +for instance, + +29 +00:01:21,581 --> 00:01:25,886 +the app will generate new +collages using these updates. + +30 +00:01:25,886 --> 00:01:28,255 +Up until now, +in order for the app + +31 +00:01:28,255 --> 00:01:30,857 +to discover +newly inserted assets + +32 +00:01:30,857 --> 00:01:33,360 +and changes to previous +hiking collages, + +33 +00:01:33,360 --> 00:01:37,097 +the app would need to perform +a series of fetches. + +34 +00:01:37,097 --> 00:01:40,100 +To determine which assets +were inserted, + +35 +00:01:40,100 --> 00:01:41,635 +the app can fetch assets + +36 +00:01:41,635 --> 00:01:46,573 +with a date created later than +the last app launch date. + +37 +00:01:46,573 --> 00:01:50,510 +Determining asset updates +and deletions is trickier. + +38 +00:01:50,510 --> 00:01:54,281 +The app would need to refetch +every asset in every collage + +39 +00:01:54,281 --> 00:01:56,350 +and check +the modification date + +40 +00:01:56,350 --> 00:01:58,385 +to determine asset updates, + +41 +00:01:58,385 --> 00:02:00,320 +but this could bring up +false positives, + +42 +00:02:00,320 --> 00:02:02,255 +as the asset modification date + +43 +00:02:02,255 --> 00:02:06,426 +can be set by internal +Photos processing activities. + +44 +00:02:06,426 --> 00:02:09,696 +Deletions in the photo library +are more difficult to track, + +45 +00:02:09,696 --> 00:02:13,100 +as all tracked assets +need to be fetched and diffed + +46 +00:02:13,100 --> 00:02:16,169 +for assets that were not +returned with the fetch. + +47 +00:02:16,169 --> 00:02:19,306 +In total, this means that +there are three separate checks + +48 +00:02:19,306 --> 00:02:22,576 +that need to be done +each time the app is launched, + +49 +00:02:22,576 --> 00:02:24,578 +which can be especially costly + +50 +00:02:24,578 --> 00:02:28,515 +if the app is displaying +large quantities of assets. + +51 +00:02:28,515 --> 00:02:31,118 +Instead of performing different +fetches and checks + +52 +00:02:31,118 --> 00:02:32,986 +for uncertain results, + +53 +00:02:32,986 --> 00:02:35,856 +what if there was a way +to know exactly what changed + +54 +00:02:35,856 --> 00:02:38,525 +in one unified API call? + +55 +00:02:38,525 --> 00:02:42,529 +Well, I'm excited to say +that we've done just that! + +56 +00:02:42,529 --> 00:02:46,099 +The new change history API +allows for an easier way + +57 +00:02:46,099 --> 00:02:50,437 +to track offline updates +to the photo library. + +58 +00:02:50,437 --> 00:02:53,573 +Change history consists +of a timeline of changes + +59 +00:02:53,573 --> 00:02:55,976 +such as insertions, updates, + +60 +00:02:55,976 --> 00:02:58,678 +and deletions +to the photo library. + +61 +00:02:58,678 --> 00:03:00,380 +In this example timeline, + +62 +00:03:00,380 --> 00:03:04,184 +there are a variety of asset, +album, and folder changes + +63 +00:03:04,184 --> 00:03:07,921 +in the change history +from the past three days. + +64 +00:03:07,921 --> 00:03:10,657 +Using this timeline, +how can you determine + +65 +00:03:10,657 --> 00:03:14,027 +which changes have happened +in the last two days, + +66 +00:03:14,027 --> 00:03:17,497 +or the last time +you launched your app? + +67 +00:03:17,497 --> 00:03:20,534 +You can now use +a persistent change token + +68 +00:03:20,534 --> 00:03:23,103 +that represents +the state of the photo library + +69 +00:03:23,103 --> 00:03:25,672 +at a given point in time. + +70 +00:03:25,672 --> 00:03:29,476 +This token can be persisted +across app launches, + +71 +00:03:29,476 --> 00:03:32,712 +and it can be used to fetch +changes to the photo library + +72 +00:03:32,712 --> 00:03:34,815 +that have occurred +since that token, + +73 +00:03:34,815 --> 00:03:37,551 +including third-party app +changes. + +74 +00:03:37,551 --> 00:03:40,487 +Note that if your app +is in limited library mode, + +75 +00:03:40,487 --> 00:03:43,457 +only changes for user-selected +PhotoKit objects + +76 +00:03:43,457 --> 00:03:45,459 +will be returned. + +77 +00:03:45,459 --> 00:03:48,695 +This change token +is local to the device + +78 +00:03:48,695 --> 00:03:51,164 +and is cheap to access +from a persistent change + +79 +00:03:51,164 --> 00:03:55,435 +or photo library instance +at any time. + +80 +00:03:55,435 --> 00:03:58,238 +This new API is available +on every platform + +81 +00:03:58,238 --> 00:04:06,313 +that supports PhotoKit: +macOS, iOS, iPadOS, and tvOS. + +82 +00:04:06,313 --> 00:04:10,250 +As your app is running and +working with the photo library, + +83 +00:04:10,250 --> 00:04:14,855 +you can store a persistent +change token within the app. + +84 +00:04:14,855 --> 00:04:19,192 +Later, you can use the token to +fetch the photo library changes + +85 +00:04:19,192 --> 00:04:21,328 +that have occurred since. + +86 +00:04:21,328 --> 00:04:23,263 +For each persistent change, + +87 +00:04:23,263 --> 00:04:25,298 +you can grab +the change details + +88 +00:04:25,298 --> 00:04:28,368 +for three types +of Photos objects: + +89 +00:04:28,368 --> 00:04:33,273 +asset, asset collection, +and collection list. + +90 +00:04:33,273 --> 00:04:36,543 +So what does this look like +in code? + +91 +00:04:36,543 --> 00:04:39,446 +You first use the last stored +change token + +92 +00:04:39,446 --> 00:04:42,249 +to fetch the persistent changes. + +93 +00:04:42,249 --> 00:04:45,585 +Next, you enumerate +through the persistent changes + +94 +00:04:45,585 --> 00:04:50,157 +and grab the change details -- +in this case, of type "asset" -- + +95 +00:04:50,157 --> 00:04:53,160 +for each persistent change +object. + +96 +00:04:53,160 --> 00:04:55,529 +These change details +provide information + +97 +00:04:55,529 --> 00:04:59,633 +on which local identifiers +were updated, deleted, + +98 +00:04:59,633 --> 00:05:01,701 +and inserted +into the photo library + +99 +00:05:01,701 --> 00:05:04,404 +since the change token. + +100 +00:05:04,404 --> 00:05:06,473 +After processing these changes, + +101 +00:05:06,473 --> 00:05:10,777 +you can store the last +change token for future use. + +102 +00:05:10,777 --> 00:05:14,681 +Let's compare and contrast +the new persistent history API + +103 +00:05:14,681 --> 00:05:18,418 +with the existing +change observer APIs. + +104 +00:05:18,418 --> 00:05:22,255 +PHChanges deal with active, +in-memory fetch results + +105 +00:05:22,255 --> 00:05:25,692 +and are used to record live +changes to the photo library + +106 +00:05:25,692 --> 00:05:28,328 +while your app is running. + +107 +00:05:28,328 --> 00:05:30,430 +Persistent history, +on the other hand, + +108 +00:05:30,430 --> 00:05:33,567 +records long-running changes +to the photo library, + +109 +00:05:33,567 --> 00:05:35,468 +and can be used +to report changes + +110 +00:05:35,468 --> 00:05:38,572 +from when your app +is not active. + +111 +00:05:38,572 --> 00:05:41,575 +You can use both +or either of these APIs + +112 +00:05:41,575 --> 00:05:45,212 +depending on the requirements +of your app. + +113 +00:05:45,212 --> 00:05:47,380 +Going back to the hiking app +example, + +114 +00:05:47,380 --> 00:05:50,383 +I'd like to now use +the persistent history API + +115 +00:05:50,383 --> 00:05:53,119 +to track asset changes +in order to create + +116 +00:05:53,119 --> 00:05:55,722 +and update hiking collages. + +117 +00:05:55,722 --> 00:05:59,459 +First, I'll use the last +stored change token + +118 +00:05:59,459 --> 00:06:02,062 +and fetch +the persistent changes. + +119 +00:06:02,062 --> 00:06:05,565 +Next, I'll iterate through +the persistent changes, + +120 +00:06:05,565 --> 00:06:08,368 +grab the relevant +asset change details, + +121 +00:06:08,368 --> 00:06:11,137 +and process the inserted, +updated, + +122 +00:06:11,137 --> 00:06:13,807 +and deleted identifiers. + +123 +00:06:13,807 --> 00:06:16,910 +Now I need to identify +library changes + +124 +00:06:16,910 --> 00:06:19,713 +that affect the app +from the change history, + +125 +00:06:19,713 --> 00:06:22,182 +as the app doesn't need +all of the information + +126 +00:06:22,182 --> 00:06:25,518 +returned from fetching changes. + +127 +00:06:25,518 --> 00:06:28,188 +It's important for the app +to know which assets + +128 +00:06:28,188 --> 00:06:32,826 +were added to the photo library +for new hiking workouts, + +129 +00:06:32,826 --> 00:06:34,995 +and updated and deleted assets + +130 +00:06:34,995 --> 00:06:38,598 +referenced in previous +hiking collages. + +131 +00:06:38,598 --> 00:06:40,734 +I've already identified +the three sets + +132 +00:06:40,734 --> 00:06:44,871 +of inserted, updated, and +deleted asset local identifiers + +133 +00:06:44,871 --> 00:06:47,707 +from enumerating through +the persistent changes. + +134 +00:06:47,707 --> 00:06:51,344 +How do I now update the app +to reflect this? + +135 +00:06:51,344 --> 00:06:53,980 +Using the insertedIdentifiers +set, + +136 +00:06:53,980 --> 00:06:56,416 +I can determine which assets +were added + +137 +00:06:56,416 --> 00:07:00,620 +between hiking timestamps +by fetching the inserted assets + +138 +00:07:00,620 --> 00:07:02,656 +and checking +their creation dates + +139 +00:07:02,656 --> 00:07:08,128 +against each hike +start and end date. + +140 +00:07:08,128 --> 00:07:11,631 +The updated assets may now +have adjustments applied, + +141 +00:07:11,631 --> 00:07:15,001 +so I can use the new +hasAdjustments API + +142 +00:07:15,001 --> 00:07:20,907 +to check if I need to redraw +the asset in the UI. + +143 +00:07:20,907 --> 00:07:23,843 +I can use the deleted asset +local identifiers + +144 +00:07:23,843 --> 00:07:27,747 +to determine which collages +need to be regenerated. + +145 +00:07:27,747 --> 00:07:31,785 +Now I've handled all of the +offline photo library changes + +146 +00:07:31,785 --> 00:07:36,056 +and my app is up to date. + +147 +00:07:36,056 --> 00:07:38,525 +Here are a few things +you should keep in mind + +148 +00:07:38,525 --> 00:07:42,495 +when using the new +change history API. + +149 +00:07:42,495 --> 00:07:45,498 +First, determine what changes +are important to you + +150 +00:07:45,498 --> 00:07:49,269 +and your app, and only check +for those changes. + +151 +00:07:49,269 --> 00:07:51,905 +Consider performing +one large fetch request + +152 +00:07:51,905 --> 00:07:54,474 +of updated and inserted assets + +153 +00:07:54,474 --> 00:07:59,579 +instead of multiple smaller +requests to improve performance. + +154 +00:07:59,579 --> 00:08:02,549 +Photo libraries can change a lot +due to processing + +155 +00:08:02,549 --> 00:08:05,218 +and sync activity +under the hood, + +156 +00:08:05,218 --> 00:08:07,020 +so you may end up +enumerating through + +157 +00:08:07,020 --> 00:08:08,788 +a large amount of changes, + +158 +00:08:08,788 --> 00:08:12,158 +especially if your app +is not frequently launched. + +159 +00:08:12,158 --> 00:08:14,394 +Because of this, +we recommend + +160 +00:08:14,394 --> 00:08:17,731 +that you ask for change history +on a background thread + +161 +00:08:17,731 --> 00:08:20,734 +to not block the UI. + +162 +00:08:20,734 --> 00:08:22,802 +There are two types of errors +that can occur + +163 +00:08:22,802 --> 00:08:25,572 +when fetching +persistent history. + +164 +00:08:25,572 --> 00:08:26,606 +If the change token + +165 +00:08:26,606 --> 00:08:29,542 +is older than the available +history of changes, + +166 +00:08:29,542 --> 00:08:33,513 +the expired change token error +will be returned. + +167 +00:08:33,513 --> 00:08:35,715 +In some cases, +the persistent change + +168 +00:08:35,715 --> 00:08:38,685 +cannot be relied upon +to completely reconstruct + +169 +00:08:38,685 --> 00:08:40,420 +the changes that occurred, + +170 +00:08:40,420 --> 00:08:44,958 +and will return an error that +change details are unavailable. + +171 +00:08:44,958 --> 00:08:47,761 +In these instances, +we recommend + +172 +00:08:47,761 --> 00:08:51,030 +refetching tracked objects +in the photo library + +173 +00:08:51,030 --> 00:08:54,234 +to ensure your app +is up to date. + +174 +00:08:54,234 --> 00:08:56,669 +Before we wrap up, +there are a few more + +175 +00:08:56,669 --> 00:09:01,040 +new PhotoKit APIs +I'd like to share with you. + +176 +00:09:01,040 --> 00:09:04,411 +PhotoKit now supports +accessing cinematic videos + +177 +00:09:04,411 --> 00:09:08,181 +by media subtype +and smart album. + +178 +00:09:08,181 --> 00:09:11,584 +There are also +two new error codes. + +179 +00:09:11,584 --> 00:09:13,019 +If the photo library bundle + +180 +00:09:13,019 --> 00:09:17,123 +is in a File Provider +sync root directory on macOS, + +181 +00:09:17,123 --> 00:09:19,092 +the library can become +corrupted, + +182 +00:09:19,092 --> 00:09:23,563 +and an error will be returned +when trying to perform changes. + +183 +00:09:23,563 --> 00:09:25,732 +If an asset resource +cannot be found + +184 +00:09:25,732 --> 00:09:27,434 +due to network issues, + +185 +00:09:27,434 --> 00:09:31,171 +the resource request +will now return a network error. + +186 +00:09:31,171 --> 00:09:33,106 +Please check +the developer documentation + +187 +00:09:33,106 --> 00:09:35,408 +for all of the latest updates. + +188 +00:09:35,408 --> 00:09:38,545 +Lastly, be sure to check out +this year's session + +189 +00:09:38,545 --> 00:09:39,813 +on the Photos picker, + +190 +00:09:39,813 --> 00:09:44,584 +as it is the easiest way +to work with and access Photos. + +191 +00:09:44,584 --> 00:09:48,321 +We're so excited for you to use +the new change history API + +192 +00:09:48,321 --> 00:09:51,291 +and all of the great new +features in PhotoKit. + +193 +00:09:51,291 --> 00:09:52,992 +Thank you! + +194 +00:09:52,992 --> 00:09:57,397 +♪ + diff --git a/eng/2022 Session 10133 Build a productivity app for Apple Watch en.srt b/eng/2022 Session 10133 Build a productivity app for Apple Watch en.srt new file mode 100644 index 0000000..ea812e2 --- /dev/null +++ b/eng/2022 Session 10133 Build a productivity app for Apple Watch en.srt @@ -0,0 +1,2080 @@ +1 +00:00:01,268 --> 00:00:07,274 +[spacey music] + +2 +00:00:09,977 --> 00:00:12,079 +Anne: Hello, and welcome! + +3 +00:00:12,112 --> 00:00:13,247 +I'm Anne Hitchcock, + +4 +00:00:13,280 --> 00:00:15,616 +and I'm a watchOS software engineer. + +5 +00:00:15,649 --> 00:00:17,017 +Today, I want to show you + +6 +00:00:17,050 --> 00:00:18,585 +how to create a productivity app + +7 +00:00:18,619 --> 00:00:20,521 +on watchOS. + +8 +00:00:20,554 --> 00:00:22,422 +Since the introduction of SwiftUI + +9 +00:00:22,456 --> 00:00:25,292 +and Independent Watch apps in watchOS 6, + +10 +00:00:25,325 --> 00:00:27,961 +you've been able to do more +in your Watch apps. + +11 +00:00:28,529 --> 00:00:33,166 +Each year, SwiftUI on watchOS gets +more capabilities. + +12 +00:00:33,200 --> 00:00:34,501 +At the same time, + +13 +00:00:34,535 --> 00:00:37,771 +watchOS has gotten new features, +like the keyboard, + +14 +00:00:37,804 --> 00:00:41,208 +that let you build whole new kinds of apps +for Watch. + +15 +00:00:41,875 --> 00:00:45,045 +I'd like to show you how to knit together +some of those features + +16 +00:00:45,078 --> 00:00:48,182 +to build an app to track +a list of things to get done. + +17 +00:00:49,016 --> 00:00:52,319 +We're going to create a new Watch app, + +18 +00:00:52,352 --> 00:00:55,889 +add a simple list of items to display, + +19 +00:00:55,923 --> 00:00:58,225 +Let people add items to the list, + +20 +00:00:58,258 --> 00:00:59,927 +and then edit the items. + +21 +00:01:00,894 --> 00:01:02,596 +As we add these features, + +22 +00:01:02,629 --> 00:01:06,700 +we'll talk about common app +navigation strategies in Watch apps + +23 +00:01:06,733 --> 00:01:08,735 +and how to pick the correct one. + +24 +00:01:10,103 --> 00:01:13,106 +We'll share items with a friend +to share the load. + +25 +00:01:14,241 --> 00:01:16,243 +Then we'll add a chart to our app + +26 +00:01:16,276 --> 00:01:20,080 +to help us spot productivity trends +and keep us motivated. + +27 +00:01:21,281 --> 00:01:23,150 +And we'll use the Digital Crown to make + +28 +00:01:23,183 --> 00:01:24,751 +our chart scrollable + +29 +00:01:24,785 --> 00:01:27,054 +to show a larger data range. + +30 +00:01:28,188 --> 00:01:31,158 +Let's get started by creating +a new app. + +31 +00:01:32,492 --> 00:01:34,361 +Create a new project in Xcode. + +32 +00:01:35,262 --> 00:01:39,032 +In the watchOS tab, choose App +and click Next. + +33 +00:01:40,400 --> 00:01:44,605 +After choosing a product name, +you have a couple of choices. + +34 +00:01:44,638 --> 00:01:46,073 +The most important one + +35 +00:01:46,106 --> 00:01:48,775 +is whether to create a Watch-only App + +36 +00:01:48,809 --> 00:01:52,246 +or to create a Watch app +with a companion iOS app. + +37 +00:01:52,846 --> 00:01:55,849 +Let's talk about what makes +a great Watch app + +38 +00:01:55,883 --> 00:01:58,919 +and when you want a companion iOS app. + +39 +00:01:59,953 --> 00:02:03,156 +Great Watch apps enable quick interaction, + +40 +00:02:03,190 --> 00:02:04,992 +like the interface in Workout + +41 +00:02:05,025 --> 00:02:08,595 +that allows you to start +your favorite workouts quickly. + +42 +00:02:08,629 --> 00:02:11,532 +No one wants to stand around, +holding up their arm, + +43 +00:02:11,565 --> 00:02:13,267 +tapping through to try to find something. + +44 +00:02:14,101 --> 00:02:18,038 +Great Watch apps make it easy +to access important information + +45 +00:02:18,071 --> 00:02:19,206 +and features. + +46 +00:02:20,707 --> 00:02:24,011 +Great Watch apps focus +on the essential purpose of the app. + +47 +00:02:24,912 --> 00:02:29,283 +For example, the Weather app displays +the forecast for today, + +48 +00:02:29,316 --> 00:02:33,353 +relevant current conditions, +and a simple 10-day forecast. + +49 +00:02:34,488 --> 00:02:36,690 +Focus on the essentials in your app + +50 +00:02:36,723 --> 00:02:39,159 +so people can easily find the information + +51 +00:02:39,193 --> 00:02:40,694 +and actions they need. + +52 +00:02:42,229 --> 00:02:44,698 +A great Watch app is designed to be used + +53 +00:02:44,731 --> 00:02:47,034 +independently of the companion iPhone. + +54 +00:02:47,901 --> 00:02:52,172 +The Contacts app, for example, +synchronizes with your phone, + +55 +00:02:52,206 --> 00:02:54,608 +but doesn't require your iPhone +to be nearby + +56 +00:02:54,641 --> 00:02:56,376 +to access contact information + +57 +00:02:56,410 --> 00:02:57,878 +on your Apple Watch. + +58 +00:02:59,479 --> 00:03:02,049 +There are many reasons you might also want + +59 +00:03:02,082 --> 00:03:05,118 +a companion iOS app for your Watch app, + +60 +00:03:05,152 --> 00:03:10,090 +including providing a historical record +of data captured by Apple Watch + +61 +00:03:10,123 --> 00:03:13,794 +or detailed analysis of trends, +as in the Fitness app. + +62 +00:03:15,729 --> 00:03:18,432 +Since our app has a focused feature set, + +63 +00:03:18,465 --> 00:03:20,901 +quick interaction, and limited data, + +64 +00:03:20,934 --> 00:03:23,003 +we're going to create a Watch-only app. + +65 +00:03:24,705 --> 00:03:27,508 +At this point, +I want to spend a few minutes + +66 +00:03:27,541 --> 00:03:29,810 +talking about the targets that +are created. + +67 +00:03:31,612 --> 00:03:33,714 +If you've built a Watch app in the past, + +68 +00:03:33,747 --> 00:03:36,550 +your project has two targets for Watch: + +69 +00:03:36,583 --> 00:03:39,086 +a WatchKit App target with a storyboard, + +70 +00:03:39,119 --> 00:03:42,990 +assets, and perhaps +some localization-related files, + +71 +00:03:43,023 --> 00:03:47,160 +and a WatchKit Extension target +with all of your app code. + +72 +00:03:47,194 --> 00:03:49,897 +These dual targets are a holdover + +73 +00:03:49,930 --> 00:03:52,199 +from the early days of watchOS, + +74 +00:03:52,232 --> 00:03:56,737 +and there really isn't a good reason +for multiple Watch targets anymore. + +75 +00:03:57,671 --> 00:04:03,510 +Starting in Xcode 14, new Watch apps +have a single Watch app target. + +76 +00:04:03,544 --> 00:04:06,547 +All of the code, assets, localizations, + +77 +00:04:06,580 --> 00:04:08,916 +and Siri Intent and Widget extensions + +78 +00:04:08,949 --> 00:04:10,551 +associated with your Watch app + +79 +00:04:10,584 --> 00:04:12,085 +belong in this target. + +80 +00:04:13,353 --> 00:04:16,623 +The great news is +that single-target Watch apps + +81 +00:04:16,657 --> 00:04:19,526 +are supported back to watchOS 7! + +82 +00:04:19,560 --> 00:04:24,097 +You can simplify your project structure +and reduce confusion and duplication + +83 +00:04:24,131 --> 00:04:26,800 +while still supporting customers +who aren't running + +84 +00:04:26,834 --> 00:04:28,168 +the latest watchOS. + +85 +00:04:29,636 --> 00:04:31,338 +If you have an existing app + +86 +00:04:31,371 --> 00:04:33,307 +with a WatchKit Extension target, + +87 +00:04:33,340 --> 00:04:34,942 +it will continue to work, + +88 +00:04:34,975 --> 00:04:38,245 +and you can continue +to update your app using Xcode + +89 +00:04:38,278 --> 00:04:40,848 +and publish your app +through the App Store. + +90 +00:04:42,282 --> 00:04:44,284 +If you already have a Watch app that uses + +91 +00:04:44,318 --> 00:04:46,286 +the SwiftUI lifecycle, + +92 +00:04:46,320 --> 00:04:52,626 +transitioning to a single target is easy +using the migration tool in Xcode 14. + +93 +00:04:52,659 --> 00:04:55,562 +Select your target and choose +Validate Settings + +94 +00:04:55,596 --> 00:04:57,598 +from the Editor menu. + +95 +00:04:57,631 --> 00:04:59,967 +The target collapsing option +will be offered + +96 +00:05:00,000 --> 00:05:02,936 +if your deployment target +is watchOS 7 or later. + +97 +00:05:04,705 --> 00:05:06,673 +If you haven't already made the leap, + +98 +00:05:06,707 --> 00:05:08,876 +now is a good time to start the process + +99 +00:05:08,909 --> 00:05:12,179 +of converting your app to use +the SwiftUI life cycle + +100 +00:05:12,212 --> 00:05:15,749 +to enjoy the simplicity +of a single-target Watch app + +101 +00:05:15,782 --> 00:05:18,585 +and all of the features of SwiftUI. + +102 +00:05:20,087 --> 00:05:24,391 +The targets aren't the only thing +we simplified in Xcode 14! + +103 +00:05:24,424 --> 00:05:26,426 +We've also made it a lot easier + +104 +00:05:26,460 --> 00:05:28,295 +to add an icon for your app + +105 +00:05:28,328 --> 00:05:33,000 +by only requiring +a single 1024x1024 pixel image. + +106 +00:05:34,067 --> 00:05:35,903 +The app icon image will be scaled + +107 +00:05:35,936 --> 00:05:38,172 +for display on all Watch devices. + +108 +00:05:39,473 --> 00:05:41,074 +Be sure to test with your app icon + +109 +00:05:41,108 --> 00:05:43,310 +on devices on the home screen, + +110 +00:05:43,343 --> 00:05:45,479 +in notifications, + +111 +00:05:45,512 --> 00:05:47,381 +and in the settings for your app + +112 +00:05:47,414 --> 00:05:49,216 +in the Watch app on the iPhone. + +113 +00:05:50,584 --> 00:05:52,286 +You can add custom images + +114 +00:05:52,319 --> 00:05:55,756 +for specific smaller sizes if necessary. + +115 +00:05:55,789 --> 00:05:56,823 +For example, + +116 +00:05:56,857 --> 00:05:59,359 +if your app icon has details in the image + +117 +00:05:59,393 --> 00:06:01,795 +that get lost at smaller sizes, + +118 +00:06:01,828 --> 00:06:04,932 +you can add specific icon images +for those sizes + +119 +00:06:04,965 --> 00:06:07,234 +with the image details removed. + +120 +00:06:07,267 --> 00:06:09,803 +Now let's add some functionality +to our app + +121 +00:06:09,837 --> 00:06:12,072 +by adding a list of task items. + +122 +00:06:12,806 --> 00:06:14,775 +We'll start by creating a data model + +123 +00:06:14,808 --> 00:06:16,944 +for our list of tasks. + +124 +00:06:16,977 --> 00:06:20,647 +The ListItem struct +will be Identifiable and Hashable, + +125 +00:06:20,681 --> 00:06:22,816 +and we'll give it a description +to display. + +126 +00:06:24,284 --> 00:06:27,387 +Then, create a simple model to store +our data + +127 +00:06:27,421 --> 00:06:29,489 +and publish the array of list items. + +128 +00:06:30,624 --> 00:06:33,927 +And finally, add the model +as an environment object + +129 +00:06:33,961 --> 00:06:35,929 +so our views can access our model. + +130 +00:06:37,397 --> 00:06:41,502 +Now let's create a List in SwiftUI +with our data model. + +131 +00:06:41,535 --> 00:06:44,037 +Since there are no tasks yet, + +132 +00:06:44,071 --> 00:06:46,607 +when we preview this, +we get an empty list. + +133 +00:06:47,641 --> 00:06:50,010 +We need to do something about that. + +134 +00:06:50,043 --> 00:06:53,447 +We should give people a way to add +some tasks to their list. + +135 +00:06:54,448 --> 00:06:57,017 +We'd like to add a Button +that people can tap + +136 +00:06:57,050 --> 00:06:59,052 +to add a new item to the list. + +137 +00:06:59,887 --> 00:07:03,023 +Text field link, new in watchOS 9, + +138 +00:07:03,056 --> 00:07:06,159 +lets you invoke text input options +from a button, + +139 +00:07:06,193 --> 00:07:08,028 +and offers several styling options + +140 +00:07:08,061 --> 00:07:10,697 +to make it feel right at home in your app. + +141 +00:07:12,266 --> 00:07:16,436 +You can create a TextFieldLink +with a simple string + +142 +00:07:16,470 --> 00:07:19,039 +or use a Label for a more custom button. + +143 +00:07:20,374 --> 00:07:23,477 +Modify the button's appearance +with view modifiers, + +144 +00:07:23,510 --> 00:07:25,746 +including foregroundColor, + +145 +00:07:25,779 --> 00:07:27,814 +foregroundStyle, + +146 +00:07:27,848 --> 00:07:29,249 +and buttonStyle. + +147 +00:07:30,784 --> 00:07:33,086 +We'll create an AddItemLink view + +148 +00:07:33,120 --> 00:07:35,689 +to encapsulate the styling and behavior + +149 +00:07:35,722 --> 00:07:38,525 +of the TextFieldLink +we're using in our app. + +150 +00:07:39,593 --> 00:07:42,462 +We'll use a custom label for the button, + +151 +00:07:42,496 --> 00:07:44,231 +and when someone enters text, + +152 +00:07:44,264 --> 00:07:46,366 +we'll add the new item to our list. + +153 +00:07:47,734 --> 00:07:49,870 +Now that we've decided +to use TextFieldLink + +154 +00:07:49,903 --> 00:07:52,806 +to add a button to add a new list item, + +155 +00:07:52,840 --> 00:07:55,876 +we need to think about where to put +the TextFieldLink. + +156 +00:07:57,644 --> 00:08:00,280 +When adding actions to lists +in Watch apps, + +157 +00:08:00,314 --> 00:08:02,783 +we have a couple of options. + +158 +00:08:02,816 --> 00:08:06,320 +Use a button, navigation link, +or TextFieldLink + +159 +00:08:06,353 --> 00:08:11,859 +at the end of the list +for primary actions in short lists. + +160 +00:08:11,892 --> 00:08:15,262 +Adding an action as an item +at the end of a list + +161 +00:08:15,295 --> 00:08:17,531 +is a good choice for a primary action + +162 +00:08:17,564 --> 00:08:18,966 +in a short list of items + +163 +00:08:18,999 --> 00:08:21,068 +like the list of cities in World Clock. + +164 +00:08:21,802 --> 00:08:23,036 +However, + +165 +00:08:23,070 --> 00:08:25,439 +if you anticipate a long list of items, + +166 +00:08:25,472 --> 00:08:27,975 +people will have to keep scrolling +to the end of the list + +167 +00:08:28,008 --> 00:08:31,578 +each time they want to do the action. + +168 +00:08:31,612 --> 00:08:33,046 +For commonly used actions + +169 +00:08:33,080 --> 00:08:36,083 +with longer lists, use a toolbar item. + +170 +00:08:37,451 --> 00:08:41,755 +To add a toolbar item, +add the toolbar modifier to the list, + +171 +00:08:41,788 --> 00:08:44,791 +and use your action view as the content. + +172 +00:08:44,825 --> 00:08:47,594 +This will add a single toolbar item +to the list + +173 +00:08:47,628 --> 00:08:50,097 +with automatic toolbar item placement. + +174 +00:08:50,130 --> 00:08:52,466 +While I'd like to think I'll always keep + +175 +00:08:52,499 --> 00:08:53,800 +my to-do list short, + +176 +00:08:53,834 --> 00:08:55,836 +I'm fairly sure that I won't. + +177 +00:08:55,869 --> 00:08:58,705 +So I'm going to put the text field links +in a toolbar item + +178 +00:08:58,739 --> 00:09:00,707 +to make it easy to access. + +179 +00:09:02,242 --> 00:09:05,479 +Let's take a moment to review +what we've accomplished. + +180 +00:09:05,512 --> 00:09:08,315 +We've created a model for our list items, + +181 +00:09:08,348 --> 00:09:11,485 +stored it as an environment object, + +182 +00:09:11,518 --> 00:09:13,554 +created a list to display the items, + +183 +00:09:13,587 --> 00:09:16,723 +and added a text field link +to add new items. + +184 +00:09:23,330 --> 00:09:25,432 +Creating an item with only a description + +185 +00:09:25,465 --> 00:09:28,535 +is simple, but it isn't very useful. + +186 +00:09:28,569 --> 00:09:31,638 +We're going to need +to mark the item as complete, + +187 +00:09:31,672 --> 00:09:34,041 +and we might want a way to set a priority + +188 +00:09:34,074 --> 00:09:36,143 +or add an estimate of the amount of work + +189 +00:09:36,176 --> 00:09:37,778 +for a task. + +190 +00:09:37,811 --> 00:09:40,781 +To do this, we'll add a detail view. + +191 +00:09:40,814 --> 00:09:42,783 +Before we do this, I want to review + +192 +00:09:42,816 --> 00:09:45,052 +the options for app navigation structure + +193 +00:09:45,085 --> 00:09:47,387 +in SwiftUI on Watch. + +194 +00:09:48,222 --> 00:09:50,657 +Hierarchical navigation is used for views + +195 +00:09:50,691 --> 00:09:53,594 +with a list-detail relationship. + +196 +00:09:53,627 --> 00:09:57,397 +Starting in watchOS 9, +use the SwiftUI NavigationStack + +197 +00:09:57,431 --> 00:10:00,834 +to create interfaces +with this type of navigation structure. + +198 +00:10:02,135 --> 00:10:04,571 +Page-based navigation is used for views + +199 +00:10:04,605 --> 00:10:06,173 +with a flat structure, + +200 +00:10:06,206 --> 00:10:08,242 +where all of the views are peers. + +201 +00:10:09,510 --> 00:10:12,212 +A great example of page-based navigation + +202 +00:10:12,246 --> 00:10:15,015 +is the in-workout view of the Workout app, + +203 +00:10:15,048 --> 00:10:16,650 +where people can easily swipe + +204 +00:10:16,683 --> 00:10:20,587 +between the workout controls, metrics, +and playback controls + +205 +00:10:20,621 --> 00:10:21,788 +during a workout. + +206 +00:10:23,223 --> 00:10:25,459 +A full-screen app has a single view + +207 +00:10:25,492 --> 00:10:27,861 +that uses the entire display. + +208 +00:10:27,895 --> 00:10:30,464 +This is generally used for apps like games + +209 +00:10:30,497 --> 00:10:32,966 +and other apps +that have a single main view. + +210 +00:10:34,234 --> 00:10:38,205 +For a full-screen view, +use the ignoresSafeArea modifier + +211 +00:10:38,238 --> 00:10:41,642 +to extend your content +to the edges of the display, + +212 +00:10:41,675 --> 00:10:45,245 +and the toolbar modifier +with a visibility value of hidden + +213 +00:10:45,279 --> 00:10:46,914 +to hide the navigation bar. + +214 +00:10:48,415 --> 00:10:50,450 +A modal sheet is a full-screen view + +215 +00:10:50,484 --> 00:10:52,653 +that slides over the current view. + +216 +00:10:52,686 --> 00:10:54,755 +It should be used for important tasks + +217 +00:10:54,788 --> 00:10:57,491 +that should be completed +as part of the current workflow. + +218 +00:10:58,892 --> 00:11:00,727 +It's important to differentiate + +219 +00:11:00,761 --> 00:11:03,197 +when to use a hierarchical flow + +220 +00:11:03,230 --> 00:11:05,532 +versus when to use a modal sheet. + +221 +00:11:07,000 --> 00:11:11,839 +Mail uses a hierarchical style +to display the list of messages + +222 +00:11:11,872 --> 00:11:15,976 +and show each message or thread +as a detail view. + +223 +00:11:16,009 --> 00:11:18,979 +There are actions you can do +from the message detail, + +224 +00:11:19,012 --> 00:11:21,014 +but there is nothing you must do + +225 +00:11:21,048 --> 00:11:22,816 +before returning to the list. + +226 +00:11:23,917 --> 00:11:27,387 +If you go back to the list, +and tap New Message, + +227 +00:11:27,421 --> 00:11:31,225 +Mail uses a modal sheet to show +the New Message view. + +228 +00:11:32,125 --> 00:11:34,294 +A modal sheet is the right choice + +229 +00:11:34,328 --> 00:11:37,598 +because you need to fill in the details +of the new message, or cancel, + +230 +00:11:37,631 --> 00:11:39,233 +before continuing. + +231 +00:11:40,267 --> 00:11:41,935 +To display a modal sheet, + +232 +00:11:41,969 --> 00:11:46,173 +create a property to control +the sheet presentation state. + +233 +00:11:46,206 --> 00:11:50,344 +Set the property based on an action in +the user interface, + +234 +00:11:50,377 --> 00:11:52,145 +and use the sheet modifier + +235 +00:11:52,179 --> 00:11:54,681 +to display the custom modal sheet content + +236 +00:11:54,715 --> 00:11:57,317 +when the presentation state property +is true. + +237 +00:11:58,986 --> 00:12:01,889 +To add custom toolbar items +to the modal sheet, + +238 +00:12:01,922 --> 00:12:04,558 +add a toolbar with your items. + +239 +00:12:04,591 --> 00:12:07,661 +Note that your toolbar items should use +modal placements + +240 +00:12:07,694 --> 00:12:09,530 +like confirmationAction, + +241 +00:12:09,563 --> 00:12:11,031 +cancellationAction, + +242 +00:12:11,064 --> 00:12:12,432 +and destructiveAction. + +243 +00:12:14,201 --> 00:12:17,070 +We're going to use the modal sheet +for our detail view + +244 +00:12:17,104 --> 00:12:18,972 +because we're editing an item + +245 +00:12:19,006 --> 00:12:21,241 +and we want to focus on this single task + +246 +00:12:21,275 --> 00:12:23,544 +until we've finished and tapped Done. + +247 +00:12:24,745 --> 00:12:27,514 +To learn more about navigation in SwiftUI, + +248 +00:12:27,548 --> 00:12:30,083 +including more details +about NavigationStack + +249 +00:12:30,117 --> 00:12:31,885 +and programmatic navigation, + +250 +00:12:31,919 --> 00:12:35,189 +check out "The SwiftUI cookbook +for navigation." + +251 +00:12:36,757 --> 00:12:40,060 +Now that we've decided how to navigate +to our detail view, + +252 +00:12:40,093 --> 00:12:42,396 +we'll update our list item struct. + +253 +00:12:42,429 --> 00:12:45,132 +We have new properties to store +estimated work, + +254 +00:12:45,165 --> 00:12:47,568 +creation date, and completion date. + +255 +00:12:49,102 --> 00:12:52,206 +Let's give people a way to view and edit +these details. + +256 +00:12:53,240 --> 00:12:57,444 +We'll create a detail view +with a TextField to edit the description + +257 +00:12:57,477 --> 00:13:01,648 +and a toggle to mark the task as complete +or not. + +258 +00:13:01,682 --> 00:13:05,285 +But what should we do +with the estimated work? + +259 +00:13:05,319 --> 00:13:07,721 +We know the values will all be numbers, + +260 +00:13:07,754 --> 00:13:10,657 +and we can specify a range +of valid values. + +261 +00:13:11,758 --> 00:13:14,228 +Beginning in watchOS 9, + +262 +00:13:14,261 --> 00:13:16,697 +we can use the Stepper. + +263 +00:13:16,730 --> 00:13:18,699 +The Stepper is a great option + +264 +00:13:18,732 --> 00:13:22,469 +when you want to provide granular control +to edit sequential values. + +265 +00:13:23,470 --> 00:13:27,774 +You can specify a range +of values and optionally provide a step. + +266 +00:13:29,543 --> 00:13:33,113 +You can also use the Stepper to edit +logically sequential, + +267 +00:13:33,146 --> 00:13:35,749 +but not necessarily numeric values. + +268 +00:13:36,683 --> 00:13:39,286 +For example, maybe we want to note + +269 +00:13:39,319 --> 00:13:41,588 +the estimated stress level for an item. + +270 +00:13:42,623 --> 00:13:44,791 +We could create an array of emoji + +271 +00:13:44,825 --> 00:13:47,294 +to indicate the stress level, + +272 +00:13:47,327 --> 00:13:49,730 +then create a Stepper, + +273 +00:13:49,763 --> 00:13:54,601 +binding the value to the selected index +in the stress level emoji array + +274 +00:13:54,635 --> 00:13:58,672 +and setting the range to the range +of emoji indices. + +275 +00:13:58,705 --> 00:14:00,073 +Stepping through the values + +276 +00:14:00,107 --> 00:14:01,675 +increases or decreases + +277 +00:14:01,708 --> 00:14:04,178 +the stress level we're estimating +for the item. + +278 +00:14:05,312 --> 00:14:08,582 +Preparing a WWDC session is fun, + +279 +00:14:08,615 --> 00:14:12,019 +but sharing great Watch app development +with all of you is a party. + +280 +00:14:12,853 --> 00:14:15,589 +When I have stressful items on my list, + +281 +00:14:15,622 --> 00:14:19,459 +or just a lot of items on my list +that are making me feel stressed, + +282 +00:14:19,493 --> 00:14:21,962 +I'd like to share an item from my list +with a friend + +283 +00:14:21,995 --> 00:14:23,230 +to ask for help. + +284 +00:14:24,498 --> 00:14:27,301 +We're going to add a button +to our detail view + +285 +00:14:27,334 --> 00:14:29,036 +to allow people to share an item + +286 +00:14:29,069 --> 00:14:31,171 +using the share sheet. + +287 +00:14:31,205 --> 00:14:33,674 +I want to be able to tap a button +on my detail view + +288 +00:14:33,707 --> 00:14:35,375 +to share the item, + +289 +00:14:35,409 --> 00:14:38,178 +pick from a list of friends +to ask for help, + +290 +00:14:38,212 --> 00:14:40,080 +edit my message, and send it. + +291 +00:14:41,348 --> 00:14:43,817 +To do this, we're going to use a new tool + +292 +00:14:43,851 --> 00:14:47,588 +available to us in SwiftUI on watchOS 9: + +293 +00:14:47,621 --> 00:14:49,723 +ShareLink. + +294 +00:14:49,756 --> 00:14:53,827 +We can share our list item by creating +a ShareLink with our item. + +295 +00:14:53,861 --> 00:14:55,495 +We can optionally customize + +296 +00:14:55,529 --> 00:14:57,130 +the initial text of the message + +297 +00:14:57,164 --> 00:15:00,033 +with a subject and message. + +298 +00:15:00,067 --> 00:15:02,936 +And provide a Preview +to display in the Share Sheet + +299 +00:15:02,970 --> 00:15:05,539 +when someone shares the item. + +300 +00:15:05,572 --> 00:15:08,876 +You can use ShareLink to share +from your SwiftUI app + +301 +00:15:08,909 --> 00:15:12,513 +in iOS, macOS, and watchOS. + +302 +00:15:13,847 --> 00:15:15,983 +Be sure to check out "Meet Transferable" + +303 +00:15:16,016 --> 00:15:18,051 +to learn more details and options + +304 +00:15:18,085 --> 00:15:19,786 +for ShareLink. + +305 +00:15:19,820 --> 00:15:22,890 +Now that I can track when I completed +items + +306 +00:15:22,923 --> 00:15:25,492 +and call for help to get things done, + +307 +00:15:25,526 --> 00:15:27,294 +I'd also like to add a chart + +308 +00:15:27,327 --> 00:15:29,096 +to see my productivity. + +309 +00:15:29,863 --> 00:15:32,199 +I've chosen to use a bar chart + +310 +00:15:32,232 --> 00:15:34,468 +because I have a single data series + +311 +00:15:34,501 --> 00:15:36,136 +and distinct data values. + +312 +00:15:37,037 --> 00:15:38,705 +A bar chart will clearly show + +313 +00:15:38,739 --> 00:15:40,774 +this data on a Watch display + +314 +00:15:40,807 --> 00:15:45,779 +as long as I limit the amount of data +I display at one time. + +315 +00:15:45,812 --> 00:15:48,015 +We're going to start by adding +the chart view + +316 +00:15:48,048 --> 00:15:50,717 +to our app's navigation structure. + +317 +00:15:50,751 --> 00:15:53,654 +I've chosen +a page-based navigation strategy + +318 +00:15:53,687 --> 00:15:55,989 +because there is +no list-detail relationship + +319 +00:15:56,023 --> 00:15:57,991 +between the item list and the chart. + +320 +00:15:58,759 --> 00:16:00,427 +Someone can swipe between the list + +321 +00:16:00,460 --> 00:16:02,029 +and the chart at any time. + +322 +00:16:03,664 --> 00:16:05,432 +To add the page-based navigation + +323 +00:16:05,465 --> 00:16:07,234 +for our list and chart, + +324 +00:16:07,267 --> 00:16:11,405 +let's start by creating an ItemList struct +to encapsulate the list view. + +325 +00:16:12,940 --> 00:16:15,642 +I moved the entire content +of the content view + +326 +00:16:15,676 --> 00:16:18,045 +to this new item list. + +327 +00:16:18,078 --> 00:16:20,948 +Encapsulating the item list here +will allow us + +328 +00:16:20,981 --> 00:16:23,584 +to have simple, easy-to-read tab view code + +329 +00:16:23,617 --> 00:16:24,852 +in the content view. + +330 +00:16:26,553 --> 00:16:29,623 +We also need to create a struct +for our chart view. + +331 +00:16:31,225 --> 00:16:33,527 +I'll temporarily put in a placeholder + +332 +00:16:33,560 --> 00:16:36,096 +so we can focus +on our navigation structure + +333 +00:16:36,129 --> 00:16:37,898 +before we build our chart. + +334 +00:16:39,466 --> 00:16:42,069 +Now we'll set up a content view + +335 +00:16:42,102 --> 00:16:44,972 +with a page-style tab view with 2 tabs: + +336 +00:16:45,005 --> 00:16:47,274 +the item list and the chart. + +337 +00:16:49,309 --> 00:16:51,845 +Since we've set up +our navigation structure, + +338 +00:16:51,879 --> 00:16:53,914 +let's talk about how to build this chart. + +339 +00:16:54,615 --> 00:16:57,117 +I know I could use a SwiftUI Canvas + +340 +00:16:57,150 --> 00:16:58,952 +and draw a chart, + +341 +00:16:58,986 --> 00:17:00,687 +but starting in watchOS 9, + +342 +00:17:00,721 --> 00:17:04,091 +we have an easier answer: Swift Charts. + +343 +00:17:04,825 --> 00:17:09,563 +Swift Charts are also available on iOS, +macOS, and tvOS, + +344 +00:17:09,596 --> 00:17:13,267 +so you can reuse your charts anywhere +you're using SwiftUI. + +345 +00:17:14,401 --> 00:17:16,803 +We'll aggregate the data we want to chart + +346 +00:17:16,837 --> 00:17:19,406 +and then let Swift Charts display it +for us. + +347 +00:17:21,041 --> 00:17:25,712 +For our chart, we want to show the number +of items completed by date. + +348 +00:17:25,746 --> 00:17:28,815 +We'll create a struct +to store the aggregate data for the chart. + +349 +00:17:30,050 --> 00:17:31,685 +Then we'll write a small method + +350 +00:17:31,718 --> 00:17:33,453 +to aggregate our list item data + +351 +00:17:33,487 --> 00:17:35,155 +into chart data elements. + +352 +00:17:36,690 --> 00:17:41,128 +Display a simple chart by specifying +the data to display + +353 +00:17:41,161 --> 00:17:43,997 +and defining the series from the data. + +354 +00:17:44,031 --> 00:17:46,633 +We're using the date as the x-value + +355 +00:17:46,667 --> 00:17:49,603 +and the number of items completed +as the y-value. + +356 +00:17:51,338 --> 00:17:54,541 +To achieve the appearance I want +on my Watch display, + +357 +00:17:54,575 --> 00:17:56,844 +I'm customizing the x-axis + +358 +00:17:56,877 --> 00:18:00,013 +using the Chart's chartXAxis modifier. + +359 +00:18:00,614 --> 00:18:04,818 +I'm specifying a format style +for the axis value labels. + +360 +00:18:04,852 --> 00:18:07,254 +I also don't want vertical gridlines, + +361 +00:18:07,287 --> 00:18:10,791 +so I omitted an AxisGridLine mark. + +362 +00:18:10,824 --> 00:18:16,029 +I'm also customizing the y-axis using +the chartYAxis modifier. + +363 +00:18:16,063 --> 00:18:20,767 +I specify a gridline style that looks +good with my chart on Watch. + +364 +00:18:20,801 --> 00:18:23,937 +I'm formatting the axis value labels +as integers + +365 +00:18:23,971 --> 00:18:25,873 +and omitting the top label + +366 +00:18:25,906 --> 00:18:29,142 +to prevent it from being clipped +at the top of the chart. + +367 +00:18:29,176 --> 00:18:33,747 +To learn more about the amazing things +you can achieve with Swift Charts, + +368 +00:18:33,780 --> 00:18:36,283 +check out "Hello Swift Charts" + +369 +00:18:36,316 --> 00:18:39,319 +and "Swift Charts: Raise the bar." + +370 +00:18:40,854 --> 00:18:42,689 +Our chart looks pretty good, + +371 +00:18:42,723 --> 00:18:44,925 +but I'd like to show a little more data + +372 +00:18:44,958 --> 00:18:47,828 +but still keep a great Watch experience, + +373 +00:18:47,861 --> 00:18:50,297 +so I'm going to make it scrollable. + +374 +00:18:50,330 --> 00:18:52,900 +To accomplish this, we're going to use + +375 +00:18:52,933 --> 00:18:55,636 +a new digitalCrownRotation modifier + +376 +00:18:55,669 --> 00:18:57,838 +that allows us to set a callback + +377 +00:18:57,871 --> 00:18:59,773 +for digital crown events, + +378 +00:18:59,806 --> 00:19:01,041 +and we're going to implement + +379 +00:19:01,074 --> 00:19:03,610 +a custom scrolling behavior for our chart. + +380 +00:19:05,412 --> 00:19:08,549 +Let's get ready to add +the digitalCrownRotation modifier + +381 +00:19:08,582 --> 00:19:10,984 +by adding some properties to store +the state + +382 +00:19:11,018 --> 00:19:12,920 +as someone scrolls across the chart. + +383 +00:19:13,854 --> 00:19:17,824 +The highlightedDateIndex +is the index of the data point + +384 +00:19:17,858 --> 00:19:20,027 +for the current scroll position. + +385 +00:19:21,061 --> 00:19:22,729 +We'll store the crown offset + +386 +00:19:22,763 --> 00:19:25,232 +so we can display +the current crown position + +387 +00:19:25,265 --> 00:19:27,634 +as the person is scrolling +across the chart. + +388 +00:19:28,368 --> 00:19:32,239 +This is an intermediate value, +on or between data points, + +389 +00:19:32,272 --> 00:19:34,208 +while the crown is moving. + +390 +00:19:35,409 --> 00:19:38,478 +To keep track of whether +someone is actively scrolling, + +391 +00:19:38,512 --> 00:19:40,848 +we'll store the the idle state. + +392 +00:19:40,881 --> 00:19:42,316 +We'll use this information + +393 +00:19:42,349 --> 00:19:44,017 +to add a little animation + +394 +00:19:44,051 --> 00:19:46,854 +as crown scrolling stops and starts. + +395 +00:19:48,856 --> 00:19:52,059 +Now that we have the properties +to store values, + +396 +00:19:52,092 --> 00:19:54,828 +we can add +the digitalCrownRotation modifier. + +397 +00:19:56,129 --> 00:20:00,033 +We'll bind the detent value +to the highlightedDateIndex property. + +398 +00:20:01,134 --> 00:20:04,771 +In mechanical terms, +a detent is a mechanism + +399 +00:20:04,805 --> 00:20:06,507 +that holds something in a position + +400 +00:20:06,540 --> 00:20:09,309 +until enough force is applied to move it. + +401 +00:20:09,343 --> 00:20:12,446 +For instance, when I open my car door, + +402 +00:20:12,479 --> 00:20:15,516 +there is a "stop" position +where the door will settle. + +403 +00:20:15,549 --> 00:20:17,017 +I can push a little harder + +404 +00:20:17,050 --> 00:20:19,953 +and open the door wider to another "stop." + +405 +00:20:19,987 --> 00:20:22,656 +To close it, I need to pull hard enough + +406 +00:20:22,689 --> 00:20:26,527 +to overcome the resistance +to pull it out of the "stop." + +407 +00:20:26,560 --> 00:20:30,631 +Otherwise, it will spring back +into that resting position. + +408 +00:20:30,664 --> 00:20:32,599 +This is a detent. + +409 +00:20:33,367 --> 00:20:34,735 +The stop for the car door + +410 +00:20:34,768 --> 00:20:37,504 +helps us understand detent in this API. + +411 +00:20:38,338 --> 00:20:40,741 +The detent is the resting notch position + +412 +00:20:40,774 --> 00:20:42,376 +of the crown on your view. + +413 +00:20:44,211 --> 00:20:46,980 +In the handler for the onChange callback, + +414 +00:20:47,014 --> 00:20:49,983 +we'll set the value for isCrownIdle +to false, + +415 +00:20:50,017 --> 00:20:52,686 +since we know that the crown is scrolling, + +416 +00:20:52,719 --> 00:20:55,789 +and we'll set the crownOffset value +to the current value + +417 +00:20:55,822 --> 00:20:59,560 +to let us show the current position +on the chart during scrolling. + +418 +00:21:00,827 --> 00:21:03,564 +In the handler for the onIdle callback, + +419 +00:21:03,597 --> 00:21:06,466 +we'll set the value for isCrownIdle +to true. + +420 +00:21:08,068 --> 00:21:10,270 +Now we can display the position +of the crown + +421 +00:21:10,304 --> 00:21:11,872 +as we scroll on the chart. + +422 +00:21:12,773 --> 00:21:16,777 +To do this, we can use +the RuleMark from Swift Charts. + +423 +00:21:17,644 --> 00:21:20,881 +A RuleMark is +a straight line on your chart. + +424 +00:21:20,914 --> 00:21:24,351 +You can use it to display +a horizontal or vertical line, + +425 +00:21:24,384 --> 00:21:26,787 +to display a threshold, for example, + +426 +00:21:26,820 --> 00:21:28,555 +or to display a sloped line. + +427 +00:21:29,656 --> 00:21:31,592 +We're going to create a RuleMark + +428 +00:21:31,625 --> 00:21:33,393 +with the crown offset date value + +429 +00:21:33,427 --> 00:21:36,363 +to display the current location +of crown scrolling. + +430 +00:21:38,031 --> 00:21:40,033 +Just to make this look a little better, + +431 +00:21:40,067 --> 00:21:42,703 +I'd like to have +the crown position line fade + +432 +00:21:42,736 --> 00:21:44,238 +when the crown stops moving. + +433 +00:21:45,072 --> 00:21:46,840 +It's simple to animate this + +434 +00:21:46,874 --> 00:21:49,643 +using the isCrownIdle property we added. + +435 +00:21:50,777 --> 00:21:54,147 +We'll add a property to store the opacity +for the color we're using + +436 +00:21:54,181 --> 00:21:56,650 +in the foregroundStyle for the RuleMark. + +437 +00:21:57,985 --> 00:22:01,054 +And add an onChange modifier to the chart + +438 +00:22:01,088 --> 00:22:04,391 +to animate +the crownPositionOpacity value change + +439 +00:22:04,424 --> 00:22:07,160 +when the isCrownIdle value changes. + +440 +00:22:08,262 --> 00:22:12,766 +Then update the foregroundStyle +for the RuleMark to use the opacity. + +441 +00:22:14,568 --> 00:22:18,705 +To display the value next to the bar +on the chart as we scroll, + +442 +00:22:18,739 --> 00:22:21,208 +we can add an annotation to the BarMark. + +443 +00:22:21,975 --> 00:22:26,246 +We'll position the annotation +on the top leading side of the bar + +444 +00:22:26,280 --> 00:22:28,348 +when it's the last bar. + +445 +00:22:28,382 --> 00:22:29,917 +Otherwise, we'll position it + +446 +00:22:29,950 --> 00:22:31,785 +on the top trailing side. + +447 +00:22:33,387 --> 00:22:35,722 +Let's take a moment to see +what we've accomplished + +448 +00:22:35,756 --> 00:22:38,559 +with just +the digitalCrownRotation modifier, + +449 +00:22:38,592 --> 00:22:40,494 +the RuleMark in Swift Charts, + +450 +00:22:40,527 --> 00:22:42,829 +and a simple SwiftUI animation. + +451 +00:22:44,731 --> 00:22:48,202 +The final step to creating +our custom scrollable chart + +452 +00:22:48,235 --> 00:22:52,606 +is adjusting the data range for the chart +as someone scrolls. + +453 +00:22:52,639 --> 00:22:54,975 +Create a property to store +the visible range. + +454 +00:22:56,009 --> 00:23:00,414 +Create the chartData variable to provide +the data in the range to the chart. + +455 +00:23:01,281 --> 00:23:04,284 +When the highlightedDateIndex changes, + +456 +00:23:04,318 --> 00:23:07,020 +call a method to check the chartDataRange + +457 +00:23:07,054 --> 00:23:09,056 +and update it if necessary. + +458 +00:23:10,157 --> 00:23:14,161 +As someone scrolls across the chart using +the Digital Crown, + +459 +00:23:14,194 --> 00:23:16,897 +the chart will scroll to display +the available data. + +460 +00:23:17,865 --> 00:23:21,602 +Now we've finished implementing +all the features we had planned. + +461 +00:23:23,303 --> 00:23:26,139 +To learn more +about the new SwiftUI features + +462 +00:23:26,173 --> 00:23:28,008 +available in watchOS 9, + +463 +00:23:28,041 --> 00:23:30,911 +check out "What's New in SwiftUI." + +464 +00:23:31,712 --> 00:23:33,614 +As you're planning your Watch app, + +465 +00:23:33,647 --> 00:23:35,716 +or your new Watch app features, + +466 +00:23:35,749 --> 00:23:36,783 +think about what makes + +467 +00:23:36,817 --> 00:23:38,785 +a great Watch app experience. + +468 +00:23:39,686 --> 00:23:41,421 +While you're designing your app, + +469 +00:23:41,455 --> 00:23:43,891 +consider your app navigation strategy + +470 +00:23:43,924 --> 00:23:46,960 +to ensure that your app is easy +and intuitive. + +471 +00:23:47,828 --> 00:23:52,165 +And use SwiftUI for simpler +and richer development options. + +472 +00:23:52,199 --> 00:23:54,868 +Keep building great Watch apps. + +473 +00:23:54,902 --> 00:23:56,203 +And remember, + +474 +00:23:56,236 --> 00:23:58,438 +because of you, there's an app for that! + +475 +00:23:59,006 --> 00:24:01,074 +[spacey music] + diff --git a/eng/2022 Session 10135 Get timely alerts from Bluetooth devices on watchOS en.srt b/eng/2022 Session 10135 Get timely alerts from Bluetooth devices on watchOS en.srt new file mode 100644 index 0000000..a58e3ee --- /dev/null +++ b/eng/2022 Session 10135 Get timely alerts from Bluetooth devices on watchOS en.srt @@ -0,0 +1,1055 @@ +1 +00:00:01,468 --> 00:00:07,474 +[spacey music] + +2 +00:00:09,977 --> 00:00:11,578 +Yann Ly-Gagnon: Hi. +my name is Yann. + +3 +00:00:11,612 --> 00:00:13,146 +I'm a core Bluetooth engineer. + +4 +00:00:13,180 --> 00:00:15,349 +Today, I want to talk to you +about timely alerts + +5 +00:00:15,382 --> 00:00:17,484 +for your Bluetooth devices on Apple Watch. + +6 +00:00:18,752 --> 00:00:22,022 +First, we will review how to update +a complication + +7 +00:00:22,055 --> 00:00:24,124 +while your watchOS App +is in the background. + +8 +00:00:26,026 --> 00:00:30,664 +Then, we will dive into how to listen +for timely alerts on your watchOS App. + +9 +00:00:32,833 --> 00:00:36,203 +We will also see new ways +you can discover peripherals + +10 +00:00:36,236 --> 00:00:37,504 +on watchOS 9. + +11 +00:00:39,606 --> 00:00:43,076 +Finally, we will provide best practices +and recommendations + +12 +00:00:43,110 --> 00:00:45,312 +to help you design +your Bluetooth accessory. + +13 +00:00:47,748 --> 00:00:49,950 +Let's jump into our first topic: + +14 +00:00:49,983 --> 00:00:53,520 +how to update a complication +in the background for your watchOS App. + +15 +00:00:54,288 --> 00:00:57,758 +Last year, in watchOS 8, +we introduced a way + +16 +00:00:57,791 --> 00:01:00,060 +to update complications +with your Bluetooth accessory + +17 +00:01:00,093 --> 00:01:01,595 +during Background app refresh. + +18 +00:01:03,163 --> 00:01:06,266 +This is great for data +that can be updated periodically, + +19 +00:01:06,300 --> 00:01:09,036 +like in this example showing me +the current air temperature. + +20 +00:01:12,506 --> 00:01:17,411 +As a quick refresher, last year watchOS +allows you to update your complication + +21 +00:01:17,444 --> 00:01:21,548 +and use Background app refresh, +which runs periodically in the background. + +22 +00:01:21,582 --> 00:01:24,985 +Whenever Background app refresh happens, +it allows your app to reconnect + +23 +00:01:25,018 --> 00:01:26,320 +to your Bluetooth peripheral, + +24 +00:01:26,353 --> 00:01:29,156 +retrieves data, +and then disconnects from your peripheral. + +25 +00:01:30,090 --> 00:01:32,993 +For more details about this, +watch the video called + +26 +00:01:33,026 --> 00:01:35,429 +"Connect Bluetooth devices +to Apple Watch." + +27 +00:01:37,564 --> 00:01:40,834 +But what if a time-sensitive event +happens on your Bluetooth peripheral + +28 +00:01:40,868 --> 00:01:42,336 +that the user wants to know about? + +29 +00:01:43,637 --> 00:01:47,407 +In watchOS 9, +we are introducing a way to listen + +30 +00:01:47,441 --> 00:01:50,177 +for alerts from your Bluetooth +accessories in the background. + +31 +00:01:52,312 --> 00:01:54,147 +Here's how it works. + +32 +00:01:54,181 --> 00:01:56,884 +You will connect your device +when your app is running, + +33 +00:01:56,917 --> 00:01:58,652 +and start monitoring a characteristic. + +34 +00:02:00,621 --> 00:02:03,624 +When your app stops running, +Core Bluetooth maintains the connection + +35 +00:02:03,657 --> 00:02:06,126 +to your device on your behalf, +and continues + +36 +00:02:06,159 --> 00:02:08,161 +listening +for changes to your characteristic. + +37 +00:02:09,630 --> 00:02:12,666 +When your device changes +the value of that characteristic, + +38 +00:02:12,699 --> 00:02:15,769 +your app will get runtime +to process that event. + +39 +00:02:15,802 --> 00:02:20,340 +You could post a local notification +or send a network request, for example. + +40 +00:02:20,374 --> 00:02:23,544 +This is intended to provide users +with time-sensitive information + +41 +00:02:23,577 --> 00:02:24,811 +they care about. + +42 +00:02:26,613 --> 00:02:29,216 +Let's say I have a food thermometer. + +43 +00:02:29,249 --> 00:02:32,486 +I can set a desired cook temperature +to get alerted + +44 +00:02:32,519 --> 00:02:34,588 +when I should remove my food +from the oven. + +45 +00:02:35,656 --> 00:02:38,559 +As the temperature approaches +the desired temperature, + +46 +00:02:38,592 --> 00:02:41,361 +the thermometer changes +a characteristic's value, + +47 +00:02:41,395 --> 00:02:44,698 +and the app posts a local notification +that the food is almost ready. + +48 +00:02:46,200 --> 00:02:49,269 +When the food is done, +I receive the desired notification. + +49 +00:02:51,205 --> 00:02:55,275 +And if the temperature keeps rising, +I get one final notification. + +50 +00:02:58,445 --> 00:03:02,049 +First, let's review how +to configure the Background modes. + +51 +00:03:03,350 --> 00:03:08,689 +Add Bluetooth-central to UIBackgroundModes +in your Watch app's Info.plist. + +52 +00:03:10,324 --> 00:03:14,528 +In Xcode it's called +"Required background modes," + +53 +00:03:14,561 --> 00:03:17,931 +and you should add +"App communicates using CoreBluetooth." + +54 +00:03:19,099 --> 00:03:25,472 +Note that those Info.plist entries +are the same as your app has for iOS + +55 +00:03:25,506 --> 00:03:27,841 +if you want to use +background execution as a central. + +56 +00:03:29,910 --> 00:03:33,480 +You will need to edit +your watchApp info.plist manually + +57 +00:03:33,514 --> 00:03:37,150 +and not rely on the iOS +"Signing capabilities." + +58 +00:03:38,619 --> 00:03:40,320 +Let's look at the code. + +59 +00:03:40,354 --> 00:03:43,590 +Assuming you're already connected, +you found a GATT service + +60 +00:03:43,624 --> 00:03:46,593 +and just discovered a GATT characteristic. + +61 +00:03:46,627 --> 00:03:50,664 +You will get +the didDiscoverCharacteristicFor callback. + +62 +00:03:53,200 --> 00:03:54,535 +Inside the callback, + +63 +00:03:54,568 --> 00:03:57,871 +you can decide to get notifications +every time the value changes. + +64 +00:03:58,972 --> 00:04:02,009 +This is the same API as in watchOS 8, + +65 +00:04:02,042 --> 00:04:05,979 +with the difference that it will also work +while your app is in the background. + +66 +00:04:08,282 --> 00:04:11,485 +Then implement the delegate method +to handle changes + +67 +00:04:11,518 --> 00:04:15,322 +to the characteristic's value +with didUpdateValueFor. + +68 +00:04:17,691 --> 00:04:22,563 +Once the characteristic changes, +you can post a local notification here, + +69 +00:04:22,596 --> 00:04:26,433 +send a network request, +or whatever makes sense for your app. + +70 +00:04:27,267 --> 00:04:31,438 +This method will be called both +in the foreground and in the background, + +71 +00:04:31,471 --> 00:04:35,309 +so make sure you perform +the correct action in both cases. + +72 +00:04:37,044 --> 00:04:40,848 +Now, let's talk about some situations +you need to consider. + +73 +00:04:42,216 --> 00:04:45,285 +First, on the topic +of Bluetooth reconnections. + +74 +00:04:45,319 --> 00:04:48,689 +If your device goes out of range, +the Bluetooth connection + +75 +00:04:48,722 --> 00:04:50,557 +will disconnect after a timeout. + +76 +00:04:51,725 --> 00:04:55,696 +If this happens, your app +will briefly get background runtime + +77 +00:04:55,729 --> 00:05:00,367 +to call "connectPeripheral" +in order to attempt reconnection. + +78 +00:05:00,400 --> 00:05:03,871 +This is the same as what happens on iOS. + +79 +00:05:03,904 --> 00:05:08,075 +As soon as the device is in range again, +Core Bluetooth will reconnect to it. + +80 +00:05:09,710 --> 00:05:12,246 +Now, let's talk about some limits. + +81 +00:05:12,279 --> 00:05:15,449 +These limits are important +to maintain optimal battery life + +82 +00:05:15,482 --> 00:05:17,017 +for Apple Watch users. + +83 +00:05:19,987 --> 00:05:23,423 +If your device is on the edge +of Bluetooth range + +84 +00:05:23,457 --> 00:05:27,461 +and repeatedly disconnects +while in Background BLE connection, + +85 +00:05:27,494 --> 00:05:30,097 +the reconnection range will be reduced. + +86 +00:05:30,130 --> 00:05:34,201 +This means only devices close +to the Apple Watch will reconnect. + +87 +00:05:35,869 --> 00:05:39,373 +Those limits are counted +on a rolling window of 24 hours + +88 +00:05:39,406 --> 00:05:42,476 +and are reset whenever +the user interacts on your App. + +89 +00:05:43,544 --> 00:05:47,381 +Another limitation is regarding the number +of background runtime opportunities + +90 +00:05:47,414 --> 00:05:48,682 +for timely alerts. + +91 +00:05:50,250 --> 00:05:53,053 +Only monitor characteristics +that will change + +92 +00:05:53,086 --> 00:05:56,123 +when something critical to the user +happens. + +93 +00:05:56,156 --> 00:05:59,526 +If you need to gather +periodical data from your device, + +94 +00:05:59,560 --> 00:06:01,295 +this should be done +with Background app refresh. + +95 +00:06:03,564 --> 00:06:07,501 +When your app is about to exceed +the limit, the notification + +96 +00:06:07,534 --> 00:06:12,806 +LeGattNearBackgroundNotificationLimit +will be posted. + +97 +00:06:12,840 --> 00:06:16,109 +It is a good practice for your app +to monitor that error + +98 +00:06:16,143 --> 00:06:18,912 +and realize that the user +isn't interacting with the watchOS app. + +99 +00:06:20,147 --> 00:06:23,650 +If this alert is important, +it might be the right time + +100 +00:06:23,684 --> 00:06:25,986 +to find another way to communicate +with your user, + +101 +00:06:26,019 --> 00:06:28,856 +such as through a network request + +102 +00:06:28,889 --> 00:06:31,425 +or UI changes +on your Bluetooth peripheral. + +103 +00:06:34,728 --> 00:06:38,966 +After the limit is exceeded, +the notification called + +104 +00:06:38,999 --> 00:06:43,837 +LeGattExceededBackgroundNotificationLimit +will be posted. + +105 +00:06:45,005 --> 00:06:49,510 +After this point, your app +will no longer receive background runtime + +106 +00:06:49,543 --> 00:06:52,546 +and will revert back to watchOS 8 behavior, + +107 +00:06:52,579 --> 00:06:54,815 +where there will be +no background connection + +108 +00:06:54,848 --> 00:06:56,617 +and only background app refresh. + +109 +00:06:57,951 --> 00:07:00,053 +You can retrieve those two notifications + +110 +00:07:00,087 --> 00:07:03,757 +in the error field +of the GATT Notification Update. + +111 +00:07:03,790 --> 00:07:06,026 +For background BLE connection, + +112 +00:07:06,059 --> 00:07:07,761 +we recommend using the error + +113 +00:07:07,794 --> 00:07:09,229 +to know when the limit is reached + +114 +00:07:09,263 --> 00:07:10,597 +instead of counting down. + +115 +00:07:11,632 --> 00:07:16,737 +For watchOS 9, +the background runtime limit is set to 5. + +116 +00:07:16,770 --> 00:07:21,175 +Both of these limits are reset +whenever the user interacts with your app. + +117 +00:07:21,208 --> 00:07:24,778 +They also reset 24 hours +after the limit was reached + +118 +00:07:24,811 --> 00:07:28,715 +in case there has been no user interaction +with your app. + +119 +00:07:28,749 --> 00:07:33,820 +Note: These limits only apply +to Bluetooth Background LE connections. + +120 +00:07:33,854 --> 00:07:37,090 +Background app refresh will continue +to happen if your complication + +121 +00:07:37,124 --> 00:07:40,327 +is on the active watch face, +regardless of these limits. + +122 +00:07:41,595 --> 00:07:45,699 +The amount of time you get +to process each event is very short. + +123 +00:07:45,732 --> 00:07:49,269 +There may not be enough time +to do extremely complex processing, + +124 +00:07:49,303 --> 00:07:51,939 +but enough to alert the user +something important is happening. + +125 +00:07:52,873 --> 00:07:55,843 +Finally, listening for timely alerts +in the background + +126 +00:07:55,876 --> 00:07:59,079 +requires Apple Watch Series 6 or later. + +127 +00:07:59,112 --> 00:08:03,250 +Listening for alerts isn't the only thing +you can do in the background. + +128 +00:08:03,283 --> 00:08:07,487 +In watchOS 9, you can discover peripherals +while your app is in the background. + +129 +00:08:09,823 --> 00:08:13,660 +Let's say +I have a Bluetooth medical device + +130 +00:08:13,694 --> 00:08:16,930 +and a watchOS app that detects +any timely alerts from it. + +131 +00:08:17,698 --> 00:08:21,268 +To conserve power, +the peripheral doesn't advertise + +132 +00:08:21,301 --> 00:08:23,103 +until it detects a serious condition. + +133 +00:08:24,705 --> 00:08:29,243 +Therefore, there's no connection +between the device and Apple Watch yet. + +134 +00:08:30,077 --> 00:08:34,948 +Here, the watchOS App will scan +for a unique Service UUID + +135 +00:08:34,982 --> 00:08:36,183 +from the medical device. + +136 +00:08:37,451 --> 00:08:41,388 +Now, when the medical device detects +something is serious, + +137 +00:08:41,421 --> 00:08:43,457 +it starts advertising. + +138 +00:08:43,490 --> 00:08:47,828 +The Apple Watch discovers this peripheral +and launches the app in the background. + +139 +00:08:49,363 --> 00:08:52,065 +The app can then alert +the user of the condition detected. + +140 +00:08:54,701 --> 00:08:56,003 +Here's how it works: + +141 +00:08:56,937 --> 00:09:00,374 +The Watch app will initiate +a scan for peripherals, + +142 +00:09:00,407 --> 00:09:03,043 +and Core Bluetooth will continue +scanning in the background. + +143 +00:09:05,579 --> 00:09:08,515 +Once the peripheral's +advertisement is detected, + +144 +00:09:08,549 --> 00:09:12,319 +the app is given background runtime +and can initiate a connection. + +145 +00:09:15,088 --> 00:09:18,692 +Let's dive into the code +to make this happen. + +146 +00:09:18,725 --> 00:09:21,662 +The API hasn't changed from watchOS 8, + +147 +00:09:21,695 --> 00:09:25,299 +but the scan will be honored +even the app is in the background. + +148 +00:09:26,533 --> 00:09:28,969 +Call "scanForPeripherals" + +149 +00:09:29,002 --> 00:09:32,306 +with the service UUID +you want to watch for. + +150 +00:09:32,339 --> 00:09:36,009 +You can do this while your app +is in foreground, and it will continue + +151 +00:09:36,043 --> 00:09:38,779 +while the app is in the background. + +152 +00:09:38,812 --> 00:09:42,649 +Note that if you ask for the option +"allowDuplicatesKey," + +153 +00:09:42,683 --> 00:09:45,419 +it will only be available +when the app is in foreground. + +154 +00:09:46,253 --> 00:09:47,621 +Now, let's talk about some limits. + +155 +00:09:48,622 --> 00:09:51,358 +There is a limit on the number +of times your app will be given + +156 +00:09:51,391 --> 00:09:54,261 +background runtime between app launches. + +157 +00:09:54,294 --> 00:09:55,495 +This limit is combined + +158 +00:09:55,529 --> 00:09:57,598 +with the background runtime +we saw previously + +159 +00:09:57,631 --> 00:10:00,367 +when a GATT characteristic changes. + +160 +00:10:00,400 --> 00:10:02,970 +Also, scanning for peripherals +in the background + +161 +00:10:03,003 --> 00:10:05,372 +requires Apple Watch Series 6 or later. + +162 +00:10:06,974 --> 00:10:10,177 +In summary, we can now scan +for a limited number + +163 +00:10:10,210 --> 00:10:14,715 +of Bluetooth service UUID while +the watch is scanning in the background. + +164 +00:10:15,716 --> 00:10:19,052 +Now let's talk about how to design +your accessory + +165 +00:10:19,086 --> 00:10:20,821 +to make the most of these features. + +166 +00:10:23,557 --> 00:10:25,259 +There is a power tradeoff +you need to consider + +167 +00:10:25,292 --> 00:10:27,060 +when designing +your Bluetooth accessory. + +168 +00:10:30,230 --> 00:10:33,700 +If power consumption is a concern, +you should opt for a topology + +169 +00:10:33,734 --> 00:10:35,969 +where your device can go into deep sleep + +170 +00:10:36,003 --> 00:10:38,138 +and only advertise relevant information + +171 +00:10:38,172 --> 00:10:39,873 +when an alert happens. + +172 +00:10:39,907 --> 00:10:44,611 +The tradeoff is, you will have extra +latency with the Bluetooth discovery time + +173 +00:10:44,645 --> 00:10:48,215 +at every timely alerts, +but you will be able to save more power. + +174 +00:10:49,316 --> 00:10:53,020 +This is the topology provided +in the example with the medical device. + +175 +00:10:54,855 --> 00:10:56,256 +On the other hand, + +176 +00:10:56,290 --> 00:10:58,025 +if you need low latency + +177 +00:10:58,058 --> 00:10:59,259 +for your timely alerts, + +178 +00:10:59,293 --> 00:11:01,295 +but the power is not so much a concern, + +179 +00:11:01,328 --> 00:11:03,096 +you can consider using + +180 +00:11:03,130 --> 00:11:04,965 +Background LE connection + +181 +00:11:04,998 --> 00:11:05,999 +and send your alerts + +182 +00:11:06,033 --> 00:11:07,301 +with GATT indications. + +183 +00:11:07,868 --> 00:11:11,305 +Note that there is a limit +of two Bluetooth connections for each app. + +184 +00:11:12,806 --> 00:11:16,243 +This is the topology we saw in the example +for temperature sensor. + +185 +00:11:17,611 --> 00:11:22,816 +In order for your users to have +the best experience with timely alerts, + +186 +00:11:22,850 --> 00:11:27,287 +consider adding as much processing +and intelligence on your peripheral + +187 +00:11:27,321 --> 00:11:30,791 +to filter the data that is time critical +versus non-time critical. + +188 +00:11:32,159 --> 00:11:36,930 +Coming back to our temperature example, +instead of transmitting every temperature, + +189 +00:11:36,964 --> 00:11:40,667 +you can send only the relevant events +or when the temperature changes. + +190 +00:11:41,568 --> 00:11:44,204 +The benefit of this approach is, +if you properly separate + +191 +00:11:44,238 --> 00:11:47,140 +the time-critical events +from periodic data, + +192 +00:11:47,174 --> 00:11:49,943 +both your peripheral +and the Apple Watch user + +193 +00:11:49,977 --> 00:11:53,847 +will save on power, +thus an overall better experience. + +194 +00:11:56,016 --> 00:11:59,152 +When your device disconnects, +we recommend advertising + +195 +00:11:59,186 --> 00:12:00,954 +to re-establish the connection. + +196 +00:12:01,822 --> 00:12:04,057 +The advertisement interval depends +on the requirement + +197 +00:12:04,091 --> 00:12:06,693 +of your Bluetooth peripheral, + +198 +00:12:06,727 --> 00:12:12,332 +such as how fast it needs to reconnect, +battery life, et cetera. + +199 +00:12:12,366 --> 00:12:16,837 +In the accessory guidelines, we offer +a few different values you can use. + +200 +00:12:17,738 --> 00:12:20,974 +For example, +if your device is battery constrained, + +201 +00:12:21,008 --> 00:12:25,846 +you can use a value +of 1022.5 milliseconds. + +202 +00:12:27,114 --> 00:12:30,717 +Another example: if you advertise +at a rate of 20 milliseconds, + +203 +00:12:30,751 --> 00:12:34,788 +it should allow for a detection +within a second in ideal conditions. + +204 +00:12:36,156 --> 00:12:40,127 +You could design such that +this high advertisement rate can be used + +205 +00:12:40,160 --> 00:12:42,996 +only while a critical event happens. + +206 +00:12:44,965 --> 00:12:48,235 +Now let's talk about connection interval. + +207 +00:12:48,268 --> 00:12:52,973 +If you opt for a topology where your device +remains connected in the background, + +208 +00:12:53,006 --> 00:12:55,409 +we highly recommend using +a long connection interval, + +209 +00:12:55,442 --> 00:12:58,645 +such as at least 150 milliseconds. + +210 +00:12:58,679 --> 00:13:00,981 +This will allow to save battery +on your peripheral + +211 +00:13:01,014 --> 00:13:03,717 +and provides best user experience +on Apple Watch. + +212 +00:13:04,751 --> 00:13:09,656 +Bluetooth 5.3 is coming to Apple Watch, +along with connection sub-rating. + +213 +00:13:09,690 --> 00:13:12,259 +This would allow to increase +the connection interval + +214 +00:13:12,292 --> 00:13:14,428 +while the Bluetooth peripheral is idle + +215 +00:13:14,461 --> 00:13:17,064 +and quickly change +to smaller connection interval + +216 +00:13:17,097 --> 00:13:18,532 +when you need lower latency. + +217 +00:13:19,900 --> 00:13:24,071 +Here is a table showing +the differences between platforms. + +218 +00:13:24,104 --> 00:13:28,876 +These are the currently supported +configurations for Bluetooth Low Energy. + +219 +00:13:28,909 --> 00:13:32,946 +Last year we introduced +Background app refresh for watchOS + +220 +00:13:32,980 --> 00:13:35,449 +as a new background execution mode. + +221 +00:13:35,482 --> 00:13:38,685 +This year, +if you own a Series 6 and above, + +222 +00:13:38,719 --> 00:13:42,422 +we improved the background execution +with timely alerts as we described today. + +223 +00:13:44,057 --> 00:13:45,359 +Thanks for watching! + +224 +00:13:46,693 --> 00:13:48,662 +[spacey music] + diff --git a/eng/2022 Session 10136 Hello Swift Charts en.srt b/eng/2022 Session 10136 Hello Swift Charts en.srt new file mode 100644 index 0000000..fe8c643 --- /dev/null +++ b/eng/2022 Session 10136 Hello Swift Charts en.srt @@ -0,0 +1,1341 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,776 +♪ + +3 +00:00:09,776 --> 00:00:11,545 +Hello, +I am Dominik + +4 +00:00:11,545 --> 00:00:15,015 +and I am thrilled to +introduce you to Swift Charts, + +5 +00:00:15,015 --> 00:00:18,185 +Apple's new framework +for making informative + +6 +00:00:18,185 --> 00:00:21,154 +and delightful charts +in SwiftUI. + +7 +00:00:21,154 --> 00:00:24,791 +Data surrounds us and provides +an unprecedented resource + +8 +00:00:24,791 --> 00:00:28,996 +for understanding the world +and making better decisions. + +9 +00:00:28,996 --> 00:00:32,032 +Yet, data alone +is of little use. + +10 +00:00:32,032 --> 00:00:35,435 +To make data useful, +we must make sense of it. + +11 +00:00:35,435 --> 00:00:38,805 +A well-designed and accessible +data visualization + +12 +00:00:38,805 --> 00:00:41,441 +can clearly communicate +complex data + +13 +00:00:41,441 --> 00:00:45,312 +and turn data into understanding +and knowledge. + +14 +00:00:45,312 --> 00:00:49,016 +At Apple, we spent years +studying the best practices + +15 +00:00:49,016 --> 00:00:51,351 +for visualizations. + +16 +00:00:51,351 --> 00:00:53,787 +We learned that charts work best + +17 +00:00:53,787 --> 00:00:57,691 +when they show additional +useful context around data + +18 +00:00:57,691 --> 00:01:00,694 +like the trends and fluctuations +of stock prices + +19 +00:01:00,694 --> 00:01:03,397 +over some time range, + +20 +00:01:03,397 --> 00:01:07,567 +your heart rate +during the last workout, + +21 +00:01:07,567 --> 00:01:12,039 +and when the day +will cool off in the evening. + +22 +00:01:12,039 --> 00:01:14,241 +And these are just some +of the many examples + +23 +00:01:14,241 --> 00:01:18,045 +across all our platforms +where we use charts. + +24 +00:01:18,045 --> 00:01:21,982 +Today, I'm excited to introduce +you to a new framework + +25 +00:01:21,982 --> 00:01:27,454 +so you can make informative and +delightful charts in your apps. + +26 +00:01:27,454 --> 00:01:31,758 +Say hello to Swift Charts. + +27 +00:01:31,758 --> 00:01:34,728 +Swift Charts is +a flexible framework + +28 +00:01:34,728 --> 00:01:37,764 +for making +Apple-designed charts. + +29 +00:01:37,764 --> 00:01:41,001 +It uses the same +declarative syntax as SwiftUI, + +30 +00:01:41,001 --> 00:01:45,806 +so you already know +the language of Swift Charts. + +31 +00:01:45,806 --> 00:01:49,943 +So today, let's make +some informative, accessible, + +32 +00:01:49,943 --> 00:01:53,847 +and delightful visualizations +with Swift Charts. + +33 +00:01:53,847 --> 00:01:55,382 +In the team, +we've been helping + +34 +00:01:55,382 --> 00:02:00,320 +a pop-up pancake food truck +track its sales with an app. + +35 +00:02:00,320 --> 00:02:02,456 +The truck serves +an international variety + +36 +00:02:02,456 --> 00:02:09,363 +of sweet and savory pancakes +like cachapa, injera, crêpe, + +37 +00:02:09,363 --> 00:02:16,069 +jian bing, dosa, +or American pancakes. + +38 +00:02:16,069 --> 00:02:19,539 +The food truck served +more than 4500 pancakes + +39 +00:02:19,539 --> 00:02:23,944 +across these styles +in the last 30 days. + +40 +00:02:23,944 --> 00:02:27,914 +Cachapa were the most popular +and the app already shows + +41 +00:02:27,914 --> 00:02:32,419 +the most important +information in the title. + +42 +00:02:32,419 --> 00:02:35,088 +Let's add a chart to show +a detailed breakdown + +43 +00:02:35,088 --> 00:02:38,492 +for the six pancakes. + +44 +00:02:38,492 --> 00:02:41,161 +To make this visualization +in Swift Charts, + +45 +00:02:41,161 --> 00:02:45,866 +we can use the same +declarative syntax as SwiftUI. + +46 +00:02:45,866 --> 00:02:50,170 +In Swift Charts, +you build charts by composition. + +47 +00:02:50,170 --> 00:02:55,108 +The main components +of a bar chart are the bars, + +48 +00:02:55,108 --> 00:03:00,547 +which are visual elements +for each item in your data. + +49 +00:03:00,547 --> 00:03:05,585 +Swift Charts calls +these visual elements "marks." + +50 +00:03:05,585 --> 00:03:08,688 +Let's jump into Xcode +to make this chart. + +51 +00:03:08,688 --> 00:03:11,558 +We start with adding a chart. + +52 +00:03:11,558 --> 00:03:16,696 +To make a bar, I add a BarMark +inside the Chart. + +53 +00:03:16,696 --> 00:03:19,132 +To show a bar +for the number of cachapas, + +54 +00:03:19,132 --> 00:03:21,968 +we have to set +the name and the sales. + +55 +00:03:23,837 --> 00:03:27,407 +We set the x-position of the bar +to be derived from the value + +56 +00:03:27,407 --> 00:03:33,313 +of the name of pancake -- +in this case "Cachapa." + +57 +00:03:33,313 --> 00:03:36,450 +The first argument +to the .value factory method + +58 +00:03:36,450 --> 00:03:38,919 +is the description of the value, + +59 +00:03:38,919 --> 00:03:42,355 +and the second +is the value itself. + +60 +00:03:42,355 --> 00:03:45,692 +And now we get a single bar +in the preview. + +61 +00:03:45,692 --> 00:03:49,329 +The height of each bar +described by the y attribute + +62 +00:03:49,329 --> 00:03:56,236 +should be set by the number of +cachapas sold, which were 916. + +63 +00:03:56,236 --> 00:03:58,305 +To indicate that we're not +setting the position + +64 +00:03:58,305 --> 00:04:03,610 +or height of the bar directly, +we use .value. + +65 +00:04:03,610 --> 00:04:07,547 +Swift Charts automatically +creates not only the bar + +66 +00:04:07,547 --> 00:04:11,718 +but also a label +for the bars on the x-axis + +67 +00:04:11,718 --> 00:04:17,557 +and a y-axis that shows +what the length of a bar means. + +68 +00:04:17,557 --> 00:04:19,693 +Let's add a second bar +for injera, + +69 +00:04:19,693 --> 00:04:22,529 +of which the truck sold 850. + +70 +00:04:24,564 --> 00:04:27,767 +Now, it's cool to build +individual marks + +71 +00:04:27,767 --> 00:04:30,070 +and see them appear in the app. + +72 +00:04:30,070 --> 00:04:32,172 +However, we usually want +to create a chart + +73 +00:04:32,172 --> 00:04:35,308 +driven by a collection +such as an array of structs. + +74 +00:04:35,308 --> 00:04:38,311 +I'll start by adding a struct +for my pancake sales. + +75 +00:04:46,286 --> 00:04:49,756 +It has a name -- +which is a string -- + +76 +00:04:49,756 --> 00:04:52,292 +and how many pancakes +the truck sold -- + +77 +00:04:52,292 --> 00:04:55,829 +which is an int. + +78 +00:04:55,829 --> 00:04:58,198 +Because we want +to use it to repeat, + +79 +00:04:58,198 --> 00:05:00,233 +we make it identifiable... + +80 +00:05:04,538 --> 00:05:12,412 +...and define an ID-computed +property that returns the name. + +81 +00:05:12,412 --> 00:05:17,551 +Now we can create our data set +as an array of pancakes. + +82 +00:05:17,551 --> 00:05:20,320 +We could load this +from an external data source + +83 +00:05:20,320 --> 00:05:22,556 +but here we'll define it +in the code. + +84 +00:05:22,556 --> 00:05:27,227 +Besides cachapa and injera, +we also add crêpe. + +85 +00:05:27,227 --> 00:05:30,931 +We can make the bar chart +data driven with ForEach. + +86 +00:05:30,931 --> 00:05:33,500 +First, remove the second bar. + +87 +00:05:37,370 --> 00:05:39,139 +And all we need to do now + +88 +00:05:39,139 --> 00:05:42,042 +is to repeat the BarMark +with a ForEach. + +89 +00:05:45,979 --> 00:05:49,115 +I pick "element" as the name +of the variable in the loop. + +90 +00:05:54,621 --> 00:05:57,724 +Then we can use +element.name for x... + +91 +00:06:02,028 --> 00:06:05,031 +...and element.sales for y. + +92 +00:06:07,400 --> 00:06:10,637 +If ForEach is the only +content in the chart, + +93 +00:06:10,637 --> 00:06:12,172 +we can also put +the data directly + +94 +00:06:12,172 --> 00:06:14,474 +into the chart initializer. + +95 +00:06:19,746 --> 00:06:21,848 +We can now add the +remaining three entries + +96 +00:06:21,848 --> 00:06:25,218 +for the jian bing, dosa, +and american pancakes. + +97 +00:06:28,622 --> 00:06:30,724 +As we add more entries +to the array, + +98 +00:06:30,724 --> 00:06:33,860 +we add new bar marks +to the chart. + +99 +00:06:33,860 --> 00:06:35,996 +Lastly, we see that the labels + +100 +00:06:35,996 --> 00:06:38,198 +are becoming close +to each other. + +101 +00:06:38,198 --> 00:06:42,168 +By swapping x and y, +we transpose the chart + +102 +00:06:42,168 --> 00:06:45,872 +and give the labels +for each bar more space. + +103 +00:06:45,872 --> 00:06:48,708 +The Swift Charts framework +automatically chooses + +104 +00:06:48,708 --> 00:06:52,512 +an appropriate axis style +to make your chart beautiful. + +105 +00:06:52,512 --> 00:06:55,415 +And with that, we made +our first data visualization + +106 +00:06:55,415 --> 00:06:56,883 +in Swift Charts. + +107 +00:06:56,883 --> 00:06:59,319 +And using the new +variant feature in Xcode, + +108 +00:06:59,319 --> 00:07:02,622 +we can see that this chart +looks beautiful in Dark Mode, + +109 +00:07:02,622 --> 00:07:04,891 +adapts to different +font sizes, + +110 +00:07:08,695 --> 00:07:12,265 +device sizes and orientations, + +111 +00:07:12,265 --> 00:07:14,434 +and supports accessibility. + +112 +00:07:18,772 --> 00:07:21,875 +To access the data +in a visual representation, + +113 +00:07:21,875 --> 00:07:24,477 +you need to be able to see. + +114 +00:07:24,477 --> 00:07:28,448 +Swift Charts exposes the data +in a visualization to VoiceOver + +115 +00:07:28,448 --> 00:07:33,186 +so that everyone can explore the +details of the popular pancakes. + +116 +00:07:33,186 --> 00:07:35,221 +When I navigate the chart +in VoiceOver, + +117 +00:07:35,221 --> 00:07:39,526 +it speaks the name and +number of pancakes sold. + +118 +00:07:39,526 --> 00:07:43,930 +VoiceOver: Cachapa, 916. +Injera, 850. Crêpes, 802. + +119 +00:07:43,930 --> 00:07:51,438 +Jian Ping, 753. Dosa, 654. +American, 618. + +120 +00:07:51,438 --> 00:07:53,006 +Dominik: And of course, +the chart supports + +121 +00:07:53,006 --> 00:07:56,476 +the Audio Graphs feature +Apple presented in 2021, + +122 +00:07:56,476 --> 00:07:58,745 +including the sonifications. + +123 +00:07:58,745 --> 00:08:01,448 +VoiceOver: Describe chart. +Chart Details. + +124 +00:08:01,448 --> 00:08:03,049 +Play Audio Graph. + +125 +00:08:03,049 --> 00:08:08,722 +[DESCENDING BEEPS] + +126 +00:08:08,722 --> 00:08:11,224 +Complete. + +127 +00:08:11,224 --> 00:08:13,026 +Dominik: We just used +Swift Charts to add + +128 +00:08:13,026 --> 00:08:17,731 +an informative and accessible +chart to the food truck app. + +129 +00:08:17,731 --> 00:08:19,399 +The chart shows +how many pancakes + +130 +00:08:19,399 --> 00:08:23,570 +the truck sold of each style. + +131 +00:08:23,570 --> 00:08:26,539 +Besides the summaries +for each style of pancake, + +132 +00:08:26,539 --> 00:08:31,277 +the food truck also +has per-day sales data. + +133 +00:08:31,277 --> 00:08:36,049 +The truck can park in Cupertino +and San Francisco. + +134 +00:08:36,049 --> 00:08:38,451 +We want to help the food truck +know where to park + +135 +00:08:38,451 --> 00:08:40,920 +on different days of the week. + +136 +00:08:40,920 --> 00:08:43,890 +To answer this question, +let's visualize the data + +137 +00:08:43,890 --> 00:08:48,361 +as a time series +for our two cities. + +138 +00:08:48,361 --> 00:08:51,531 +To see how easy it is +to explore different designs, + +139 +00:08:51,531 --> 00:08:55,335 +we will make three iterations +of the chart. + +140 +00:08:55,335 --> 00:09:00,707 +We will start by making +a bar graph for Cupertino. + +141 +00:09:00,707 --> 00:09:06,346 +Then, we will add the data for +San Francisco and add a picker. + +142 +00:09:06,346 --> 00:09:08,448 +And finally, +we will combine the data + +143 +00:09:08,448 --> 00:09:11,851 +into one multiseries line chart. + +144 +00:09:11,851 --> 00:09:14,554 +Let's start with the bar chart +showing the average number + +145 +00:09:14,554 --> 00:09:18,858 +of pancakes sold +per day of the week. + +146 +00:09:18,858 --> 00:09:22,796 +The sales data has the weekday +stored as a date + +147 +00:09:22,796 --> 00:09:27,267 +and how many pancakes +the truck sold as an integer. + +148 +00:09:27,267 --> 00:09:33,106 +The data for Cupertino is +in a variable, cupertinoData. + +149 +00:09:33,106 --> 00:09:38,411 +As before, we start making +a chart with a BarMark. + +150 +00:09:38,411 --> 00:09:40,313 +We set the x-position of the bar + +151 +00:09:40,313 --> 00:09:42,816 +to be derived from +the day of the date... + +152 +00:09:45,485 --> 00:09:48,288 +...and the height to be derived +from the sales. + +153 +00:09:52,292 --> 00:09:54,961 +And this gives us +a first iteration of a chart + +154 +00:09:54,961 --> 00:09:59,866 +that shows the sales data per +day of the week for Cupertino. + +155 +00:09:59,866 --> 00:10:04,938 +For the second iteration, let's +add the data for San Francisco. + +156 +00:10:04,938 --> 00:10:07,807 +Using this chart, we want +to help the pancake truck + +157 +00:10:07,807 --> 00:10:11,010 +decide where to park +during the week. + +158 +00:10:11,010 --> 00:10:15,748 +The sales data for San Francisco +is in the sfData variable. + +159 +00:10:15,748 --> 00:10:18,218 +We want to toggle +between the two cities + +160 +00:10:18,218 --> 00:10:20,987 +and see the bar chart +for each city. + +161 +00:10:20,987 --> 00:10:26,092 +We start with adding +a state variable, city. + +162 +00:10:26,092 --> 00:10:30,730 +And then we add a SwiftUI +picker for the city to the View. + +163 +00:10:35,568 --> 00:10:38,705 +To toggle between the sales +summaries for the two cities + +164 +00:10:38,705 --> 00:10:40,740 +via the city variable, + +165 +00:10:40,740 --> 00:10:43,576 +we add a switch statement +for the data variable. + +166 +00:10:48,648 --> 00:10:52,752 +And all we have to do now is to +replace the data for Cupertino + +167 +00:10:52,752 --> 00:10:54,888 +with the one that toggles +between the two -- + +168 +00:10:54,888 --> 00:10:59,359 +Cupertino and San Francisco. + +169 +00:10:59,359 --> 00:11:01,628 +If I switch the toggle, +the charts toggles + +170 +00:11:01,628 --> 00:11:03,696 +between the two cities. + +171 +00:11:03,696 --> 00:11:07,166 +Swift Charts works +with SwiftUI animations, + +172 +00:11:07,166 --> 00:11:10,169 +so if we specify that the +transition should be animated + +173 +00:11:10,169 --> 00:11:11,938 +with easeInOut, + +174 +00:11:11,938 --> 00:11:16,376 +the bars animate as I toggle +between the two cities + +175 +00:11:16,376 --> 00:11:19,145 +and also only shows +one location at a time. + +176 +00:11:21,481 --> 00:11:25,652 +This gives our app +the right look and feel. + +177 +00:11:25,652 --> 00:11:28,955 +As our final iteration, +we will show both series + +178 +00:11:28,955 --> 00:11:31,257 +in a line chart. + +179 +00:11:31,257 --> 00:11:34,160 +To make this line chart, +we start with the bar chart + +180 +00:11:34,160 --> 00:11:36,529 +from Cupertino from before. + +181 +00:11:36,529 --> 00:11:39,065 +The data for Cupertino +and San Francisco + +182 +00:11:39,065 --> 00:11:41,768 +is in an array of series. + +183 +00:11:41,768 --> 00:11:46,739 +Series structs have +the city and sales data. + +184 +00:11:46,739 --> 00:11:48,274 +Before we show both series, + +185 +00:11:48,274 --> 00:11:53,079 +let's focus on +the Cupertino data. + +186 +00:11:53,079 --> 00:11:57,617 +In the chart, we can loop +over the series data. + +187 +00:11:57,617 --> 00:11:59,419 +Remember, +the chart initializer + +188 +00:11:59,419 --> 00:12:01,354 +acts just like a ForEach. + +189 +00:12:03,590 --> 00:12:07,293 +Then we can replace +the specific data for Cupertino + +190 +00:12:07,293 --> 00:12:09,896 +with the sales data +from the series. + +191 +00:12:12,031 --> 00:12:15,034 +To distinguish the data +for the two cities, + +192 +00:12:15,034 --> 00:12:19,572 +I want the color of the bars +to be derived from the city. + +193 +00:12:19,572 --> 00:12:21,574 +For this, we set +the foregroundStyle + +194 +00:12:21,574 --> 00:12:24,577 +to be derived from +the city in the series. + +195 +00:12:27,013 --> 00:12:29,482 +To show what city +a color represents, + +196 +00:12:29,482 --> 00:12:33,086 +Swift Charts creates +a legend below the chart. + +197 +00:12:33,086 --> 00:12:36,389 +Now, I add the data +for the second location. + +198 +00:12:37,790 --> 00:12:39,826 +As you can see in the preview, +Swift Charts + +199 +00:12:39,826 --> 00:12:42,996 +automatically chooses +a color for San Francisco, + +200 +00:12:42,996 --> 00:12:47,200 +and it shows the bars +for both cities in the chart. + +201 +00:12:47,200 --> 00:12:50,069 +Charts show data +for a particular context + +202 +00:12:50,069 --> 00:12:51,804 +and a visualization +may need to change + +203 +00:12:51,804 --> 00:12:55,942 +as our data +or questions change. + +204 +00:12:55,942 --> 00:12:59,812 +Swift Charts makes it easy +to quickly change your chart + +205 +00:12:59,812 --> 00:13:02,115 +to explore different designs. + +206 +00:13:02,115 --> 00:13:03,483 +The stacked bar chart is great + +207 +00:13:03,483 --> 00:13:06,753 +for showing the total +average sales per day; + +208 +00:13:06,753 --> 00:13:08,388 +but what if I wanted +to do a comparison + +209 +00:13:08,388 --> 00:13:10,423 +between the two cities? + +210 +00:13:10,423 --> 00:13:14,060 +Maybe a point or line chart +would be better. + +211 +00:13:14,060 --> 00:13:18,464 +We change the mark type +from a BarMark to a PointMark + +212 +00:13:18,464 --> 00:13:21,300 +to display the pancakes +sold as points, + +213 +00:13:21,300 --> 00:13:25,972 +or to a LineMark to show +the data as a line chart. + +214 +00:13:25,972 --> 00:13:28,341 +A line chart is a good fit +for the sales data + +215 +00:13:28,341 --> 00:13:33,579 +since it lets us compare the two +cities in each day of the week. + +216 +00:13:33,579 --> 00:13:35,648 +Charts can combine +multiple marks. + +217 +00:13:35,648 --> 00:13:38,017 +For example, +I can add a PointMark. + +218 +00:13:44,090 --> 00:13:46,959 +To make the series +differentiable without color, + +219 +00:13:46,959 --> 00:13:50,430 +we set the symbol +to be derived from the city. + +220 +00:13:56,269 --> 00:14:01,207 +Now each point indicates the +city by its color and symbol. + +221 +00:14:01,207 --> 00:14:03,643 +Because it is common +to show points on a line, + +222 +00:14:03,643 --> 00:14:05,712 +Swift Charts has +a shorthand for this + +223 +00:14:05,712 --> 00:14:09,682 +where we apply the symbol +modifier to the LineMark. + +224 +00:14:09,682 --> 00:14:13,686 +The style of the points +adapts to the line. + +225 +00:14:13,686 --> 00:14:15,221 +This chart is great. + +226 +00:14:15,221 --> 00:14:19,058 +We can easily compare the sales +trends throughout the week. + +227 +00:14:19,058 --> 00:14:21,427 +We observe that the sales +are especially high + +228 +00:14:21,427 --> 00:14:24,630 +in San Francisco on Sundays. + +229 +00:14:24,630 --> 00:14:26,332 +Swift Charts made it +very easy for us + +230 +00:14:26,332 --> 00:14:31,571 +to iterate through many designs +in just a few minutes. + +231 +00:14:31,571 --> 00:14:35,308 +So to wrap up, let's look at +how Swift Charts makes it easy + +232 +00:14:35,308 --> 00:14:39,178 +to iterate quickly and at the +same time be flexible enough + +233 +00:14:39,178 --> 00:14:43,249 +to seamlessly integrate charts +into your app's unique style. + +234 +00:14:43,249 --> 00:14:45,184 +In Swift Charts, +you build charts + +235 +00:14:45,184 --> 00:14:50,256 +by composition of marks +with mark properties. + +236 +00:14:50,256 --> 00:14:52,859 +In the Pancake app, +we composed charts + +237 +00:14:52,859 --> 00:14:57,363 +with three different marks +and four mark properties. + +238 +00:14:57,363 --> 00:15:00,399 +For example, +we made a simple bar chart + +239 +00:15:00,399 --> 00:15:05,371 +as a bar mark +with x and y properties. + +240 +00:15:05,371 --> 00:15:08,174 +We also changed the mark +to quickly explore designs, + +241 +00:15:08,174 --> 00:15:11,144 +like charts with points, + +242 +00:15:11,144 --> 00:15:13,813 +or line charts where +we used the line mark + +243 +00:15:13,813 --> 00:15:16,048 +with x and y properties. + +244 +00:15:18,885 --> 00:15:21,487 +We also saw +that we can add properties, + +245 +00:15:21,487 --> 00:15:27,193 +like Foreground Style, to show +multiple series in a line chart. + +246 +00:15:27,193 --> 00:15:30,263 +And a chart doesn't have +to have just one mark. + +247 +00:15:30,263 --> 00:15:35,134 +We combined points and lines, + +248 +00:15:35,134 --> 00:15:40,773 +and showed the same value +with two mark properties. + +249 +00:15:40,773 --> 00:15:44,177 +Swift Charts supports even more +marks and mark properties + +250 +00:15:44,177 --> 00:15:46,345 +than we used today. + +251 +00:15:46,345 --> 00:15:51,384 +It's also extensible +and you can add custom marks. + +252 +00:15:51,384 --> 00:15:54,620 +Marks and mark properties +is what allows Swift Charts + +253 +00:15:54,620 --> 00:15:57,456 +to express a wide range +of chart designs + +254 +00:15:57,456 --> 00:16:02,061 +with a small number +of declarative building blocks. + +255 +00:16:02,061 --> 00:16:03,996 +There are many ways +in which you can combine + +256 +00:16:03,996 --> 00:16:06,999 +these building block to create +great data visualizations + +257 +00:16:06,999 --> 00:16:09,202 +for your apps. + +258 +00:16:09,202 --> 00:16:12,171 +Together with what you can +already do in SwiftUI, + +259 +00:16:12,171 --> 00:16:16,475 +the possibilities +are truly endless. + +260 +00:16:16,475 --> 00:16:19,846 +And as I've showed you today, +you get support for Dark Mode, + +261 +00:16:19,846 --> 00:16:23,850 +different device screen sizes, +Dynamic Type, VoiceOver, + +262 +00:16:23,850 --> 00:16:26,953 +and Audio Graphs for free. + +263 +00:16:26,953 --> 00:16:31,524 +In addition, Swift Charts +supports High-Contrast mode. + +264 +00:16:31,524 --> 00:16:34,894 +And finally, Swift Charts +works across locales + +265 +00:16:34,894 --> 00:16:37,230 +and is multiplatform. + +266 +00:16:37,230 --> 00:16:38,831 +The chart with the same code + +267 +00:16:38,831 --> 00:16:41,500 +works across all +Apple platforms. + +268 +00:16:41,500 --> 00:16:43,970 +And the same customizations +work everywhere + +269 +00:16:43,970 --> 00:16:47,773 +so you can tailor the chart +to each platform. + +270 +00:16:47,773 --> 00:16:50,309 +Today, I showed you +how Swift Charts + +271 +00:16:50,309 --> 00:16:53,346 +uses SwiftUI's powerful +compositional syntax + +272 +00:16:53,346 --> 00:16:57,049 +so you can make more charts +with less code. + +273 +00:16:57,049 --> 00:16:58,284 +Swift Charts also provides + +274 +00:16:58,284 --> 00:17:00,887 +a rich set of +customization options, + +275 +00:17:00,887 --> 00:17:05,057 +so you can style the chart +to match your application. + +276 +00:17:05,057 --> 00:17:07,360 +And now that you know +how to chart new territory + +277 +00:17:07,360 --> 00:17:10,963 +and make a chart, you can learn +how to customize it in the docs + +278 +00:17:10,963 --> 00:17:13,833 +and in our follow-up talk +where you'll raise the bar. + +279 +00:17:13,833 --> 00:17:18,537 +♪ + diff --git a/eng/2022 Session 10137 Swift Charts - Raise the bar en.srt b/eng/2022 Session 10137 Swift Charts - Raise the bar en.srt new file mode 100644 index 0000000..7471b40 --- /dev/null +++ b/eng/2022 Session 10137 Swift Charts - Raise the bar en.srt @@ -0,0 +1,2260 @@ +1 +00:00:00,033 --> 00:00:03,036 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,036 --> 00:00:10,377 +♪ + +3 +00:00:10,377 --> 00:00:11,645 +Hello, I'm Donghao. + +4 +00:00:11,645 --> 00:00:14,348 +In this session, we are going +to learn more about how to build + +5 +00:00:14,348 --> 00:00:16,950 +great data visualizations +with Swift Charts. + +6 +00:00:16,950 --> 00:00:18,886 +In other words, +we will raise the bar + +7 +00:00:18,886 --> 00:00:21,855 +of what you can do +with charts in your apps. + +8 +00:00:21,855 --> 00:00:25,225 +A great data visualization +makes your app more informative + +9 +00:00:25,225 --> 00:00:26,927 +and more engaging. + +10 +00:00:26,927 --> 00:00:29,129 +In order to build +a great chart in our apps, + +11 +00:00:29,129 --> 00:00:31,431 +there are many things +to consider. + +12 +00:00:31,431 --> 00:00:33,734 +Of course, we would want +our charts to faithfully + +13 +00:00:33,734 --> 00:00:38,372 +communicate the underlying data +and be accessible for everyone. + +14 +00:00:38,372 --> 00:00:41,241 +But charts in an app +don't live in a vacuum. + +15 +00:00:41,241 --> 00:00:43,911 +They are part of the app's +user interface. + +16 +00:00:43,911 --> 00:00:47,748 +We have to support localization +and OS features like Dark Mode. + +17 +00:00:47,748 --> 00:00:50,517 +A chart has to have a proper +layout that fits seamlessly + +18 +00:00:50,517 --> 00:00:52,519 +into the rest of the UI; + +19 +00:00:52,519 --> 00:00:57,224 +it should support Dynamic Type +and device screen sizes. + +20 +00:00:57,224 --> 00:01:00,160 +We'll also want charts +to work in all platforms, + +21 +00:01:00,160 --> 00:01:04,164 +and have great animation +for the best look and feel. + +22 +00:01:04,164 --> 00:01:06,633 +Swift Charts takes care of +a lot of these basic features + +23 +00:01:06,633 --> 00:01:09,603 +for you automatically, +so you can focus more + +24 +00:01:09,603 --> 00:01:12,806 +on building the best charts to +communicate your unique data + +25 +00:01:12,806 --> 00:01:15,575 +and make it accessible +for everyone. + +26 +00:01:15,575 --> 00:01:17,811 +Swift Charts achieves +this by providing you + +27 +00:01:17,811 --> 00:01:21,882 +with a declarative syntax +that feels just like SwiftUI. + +28 +00:01:21,882 --> 00:01:23,750 +You specify what you want +in a chart + +29 +00:01:23,750 --> 00:01:26,653 +with a small amount of code, +and Swift Charts will + +30 +00:01:26,653 --> 00:01:30,057 +automatically produce +a great chart out of the box. + +31 +00:01:30,057 --> 00:01:31,158 +Swift Charts also provides + +32 +00:01:31,158 --> 00:01:33,794 +a rich set +of customization options, + +33 +00:01:33,794 --> 00:01:37,364 +so you can style the chart to +match your unique application. + +34 +00:01:37,364 --> 00:01:38,665 +This session is about giving you + +35 +00:01:38,665 --> 00:01:41,802 +a deeper understanding +of Swift Charts. + +36 +00:01:41,802 --> 00:01:43,737 +We will start by covering +the basic building blocks + +37 +00:01:43,737 --> 00:01:45,472 +of the declarative syntax: + +38 +00:01:45,472 --> 00:01:48,642 +marks and the composition +of marks. + +39 +00:01:48,642 --> 00:01:51,945 +Here are some examples where +we use charts in Apple products. + +40 +00:01:51,945 --> 00:01:54,514 +As you can see, +there is a wide variety of data, + +41 +00:01:54,514 --> 00:01:56,984 +chart types, and styles. + +42 +00:01:56,984 --> 00:01:58,986 +Instead of providing +a prebuilt component + +43 +00:01:58,986 --> 00:02:01,888 +for each type of chart, +Swift Charts is built + +44 +00:02:01,888 --> 00:02:05,559 +on the idea of creating charts +by composition. + +45 +00:02:05,559 --> 00:02:08,829 +It provides a small number +of basic building blocks + +46 +00:02:08,829 --> 00:02:10,931 +by combining them +in different ways, + +47 +00:02:10,931 --> 00:02:13,333 +you can create +a wide range of charts. + +48 +00:02:13,333 --> 00:02:14,968 +Let me show you how it works. + +49 +00:02:14,968 --> 00:02:17,371 +To do that, +I'll need an example. + +50 +00:02:17,371 --> 00:02:19,439 +All of my teammates +love pancakes, + +51 +00:02:19,439 --> 00:02:22,142 +so we are having an app +to track orders for a food truck + +52 +00:02:22,142 --> 00:02:24,711 +selling various +kinds of pancakes. + +53 +00:02:24,711 --> 00:02:27,080 +Here is a chart showing +the number of pancakes delivered + +54 +00:02:27,080 --> 00:02:30,717 +over the last 30 days, +broken down by style. + +55 +00:02:30,717 --> 00:02:33,887 +This is widely known +as a bar chart. + +56 +00:02:33,887 --> 00:02:37,357 +In Swift Charts, we consider +each of the six blue rectangles + +57 +00:02:37,357 --> 00:02:38,992 +a mark. + +58 +00:02:38,992 --> 00:02:43,697 +A mark is a graphical element +that represents data. + +59 +00:02:43,697 --> 00:02:45,966 +This is a bar mark +showing the number of cachapas + +60 +00:02:45,966 --> 00:02:48,935 +sold over the last 30 days. + +61 +00:02:48,935 --> 00:02:51,438 +In this chart +we have six bar marks, + +62 +00:02:51,438 --> 00:02:53,673 +each showing a pancake style + +63 +00:02:53,673 --> 00:02:56,943 +and the corresponding +sales number. + +64 +00:02:56,943 --> 00:02:59,746 +Let's see how +this is written in code. + +65 +00:02:59,746 --> 00:03:02,682 +Here we have a SwiftUI view that +includes a descriptive title + +66 +00:03:02,682 --> 00:03:06,787 +like "Most Sold Style, Cachapa," +and an empty chart. + +67 +00:03:06,787 --> 00:03:08,989 +The chart type here +is the top-level view + +68 +00:03:08,989 --> 00:03:11,324 +that defines a single chart. + +69 +00:03:11,324 --> 00:03:13,727 +You can add a chart +in your SwiftUI application + +70 +00:03:13,727 --> 00:03:16,363 +just like adding any other view. + +71 +00:03:16,363 --> 00:03:19,633 +For the rest of the talk, +we'll focus on the chart part. + +72 +00:03:19,633 --> 00:03:22,302 +You can add marks to a chart. + +73 +00:03:22,302 --> 00:03:24,404 +Here we have a single +BarMark showing cachapa + +74 +00:03:24,404 --> 00:03:26,440 +and its sales number. + +75 +00:03:26,440 --> 00:03:29,142 +This makes a chart +with a single bar mark. + +76 +00:03:29,142 --> 00:03:31,778 +As shown in the screenshot, +the chart fits nicely + +77 +00:03:31,778 --> 00:03:33,880 +into the rest +of the user interface + +78 +00:03:33,880 --> 00:03:36,249 +and has a great default style. + +79 +00:03:36,249 --> 00:03:40,353 +For example, nicely rounded +numbers in the X-axis. + +80 +00:03:40,353 --> 00:03:43,457 +If you add another bar mark +with different name and sales, + +81 +00:03:43,457 --> 00:03:45,358 +you'll get a second bar. + +82 +00:03:45,358 --> 00:03:48,161 +You can repeat this +to add more bars. + +83 +00:03:48,161 --> 00:03:49,162 +In a real app, + +84 +00:03:49,162 --> 00:03:51,965 +we'll probably generate +these marks programmatically. + +85 +00:03:51,965 --> 00:03:54,601 +You can do so by providing +an array of structs or tuples + +86 +00:03:54,601 --> 00:03:57,370 +to the chart, +and use a ForEach + +87 +00:03:57,370 --> 00:04:00,707 +to create the bar marks with +the values from each element. + +88 +00:04:00,707 --> 00:04:04,244 +If ForEach is the only content +in the chart, like in this case, + +89 +00:04:04,244 --> 00:04:07,814 +you can also put data +in the chart directly. + +90 +00:04:07,814 --> 00:04:10,717 +Many SwiftUI modifiers +are available for marks. + +91 +00:04:10,717 --> 00:04:13,153 +For example, you could set +the color of the bars + +92 +00:04:13,153 --> 00:04:15,856 +with the .foregroundStyle +modifier. + +93 +00:04:15,856 --> 00:04:17,724 +Here we are setting it +to a named color, + +94 +00:04:17,724 --> 00:04:21,862 +which can be created +in Xcode as a named asset. + +95 +00:04:21,862 --> 00:04:23,964 +It's very important +that the chart is accessible + +96 +00:04:23,964 --> 00:04:25,599 +for everyone. + +97 +00:04:25,599 --> 00:04:28,869 +By default, the chart will be +exposed to VoiceOver users + +98 +00:04:28,869 --> 00:04:32,672 +with automatically generated +accessibility elements. + +99 +00:04:32,672 --> 00:04:35,142 +You can customize these +with the .accessibilityLabel + +100 +00:04:35,142 --> 00:04:37,944 +and .accessibilityValue +modifiers. + +101 +00:04:37,944 --> 00:04:40,480 +Here for example, +we set the label to the name + +102 +00:04:40,480 --> 00:04:43,383 +of the pancake, +and the value to the sales + +103 +00:04:43,383 --> 00:04:45,385 +with the suffix "sold." + +104 +00:04:45,385 --> 00:04:50,123 +As a result, we get a custom +experience for VoiceOver users. + +105 +00:04:50,123 --> 00:04:55,562 +VoiceOver: Cachapa, 916 sold. +Injera, 850 sold. + +106 +00:04:55,562 --> 00:04:59,499 +Crêpe, 802 sold. + +107 +00:04:59,499 --> 00:05:01,868 +Donghao: Our app also tracks +pancake deliveries + +108 +00:05:01,868 --> 00:05:03,503 +over the days. + +109 +00:05:03,503 --> 00:05:06,239 +Here, we have a view showing +the number of pancakes sold + +110 +00:05:06,239 --> 00:05:08,141 +in the last 30 days. + +111 +00:05:08,141 --> 00:05:12,512 +The chart gives a detailed view +of each individual day. + +112 +00:05:12,512 --> 00:05:14,514 +Let's see how +to make this chart. + +113 +00:05:14,514 --> 00:05:17,384 +Here, we have a data array +with day and sales. + +114 +00:05:17,384 --> 00:05:19,386 +The day is represented +by a Date value + +115 +00:05:19,386 --> 00:05:21,388 +at the start of the day. + +116 +00:05:21,388 --> 00:05:23,723 +We visualize the data +with bar marks, + +117 +00:05:23,723 --> 00:05:26,493 +where x shows day, +and the unit parameter here + +118 +00:05:26,493 --> 00:05:28,595 +means that the Date values +represent the duration + +119 +00:05:28,595 --> 00:05:30,697 +of a calendar day, + +120 +00:05:30,697 --> 00:05:33,466 +and y shows the sales +of the day. + +121 +00:05:33,466 --> 00:05:35,001 +As you see on the right, + +122 +00:05:35,001 --> 00:05:38,838 +we get a bar chart +showing sales over the days. + +123 +00:05:38,838 --> 00:05:41,875 +Bar chart is not the only way +to visualize this data. + +124 +00:05:41,875 --> 00:05:43,677 +Let's try a line chart. + +125 +00:05:43,677 --> 00:05:45,011 +The only change we have to make + +126 +00:05:45,011 --> 00:05:48,048 +is to replace BarMark +with LineMark. + +127 +00:05:48,048 --> 00:05:50,817 +With a declarative syntax, +it is very easy to switch + +128 +00:05:50,817 --> 00:05:54,187 +between chart types +in Swift Charts. + +129 +00:05:54,187 --> 00:05:57,557 +The previous chart shows +total sales over time. + +130 +00:05:57,557 --> 00:06:00,827 +But the food truck service has +been operating in two cities, + +131 +00:06:00,827 --> 00:06:03,530 +and we would love to compare +the sales between the two cities + +132 +00:06:03,530 --> 00:06:07,801 +on each weekday, +to help us decide where to go. + +133 +00:06:07,801 --> 00:06:11,705 +The lines in this chart show +the sales from Monday to Sunday. + +134 +00:06:11,705 --> 00:06:13,974 +Each line represents a city. + +135 +00:06:13,974 --> 00:06:16,776 +Let's see how this is built. + +136 +00:06:16,776 --> 00:06:18,812 +Now suppose we have +two cities' data + +137 +00:06:18,812 --> 00:06:21,748 +defined here +with an array of tuples, + +138 +00:06:21,748 --> 00:06:25,819 +each contains city name +and the sales of the weekdays. + +139 +00:06:25,819 --> 00:06:28,555 +Then, we can wrap +the line chart we had before + +140 +00:06:28,555 --> 00:06:32,425 +with a ForEach that loops +over the series data. + +141 +00:06:32,425 --> 00:06:34,794 +In order to differentiate +the two cities, + +142 +00:06:34,794 --> 00:06:36,696 +we use the +.foregroundStyle(by:) modifier + +143 +00:06:36,696 --> 00:06:39,799 +to style the two lines +by the city name. + +144 +00:06:39,799 --> 00:06:42,769 +As you can see, Swift Charts +automatically picks two colors + +145 +00:06:42,769 --> 00:06:45,205 +for the two cities, +color the lines, + +146 +00:06:45,205 --> 00:06:48,742 +and then add a legend to +indicate what each color means. + +147 +00:06:48,742 --> 00:06:51,278 +The default colors are chosen +to be system colors + +148 +00:06:51,278 --> 00:06:54,147 +that are easy to differentiate. + +149 +00:06:54,147 --> 00:06:55,715 +To make the chart +easier to read + +150 +00:06:55,715 --> 00:06:59,319 +for people with color blindness, +we can add symbols to the lines + +151 +00:06:59,319 --> 00:07:01,588 +to further differentiate them. + +152 +00:07:01,588 --> 00:07:03,890 +This can be done by adding +the .symbol(by:) modifier + +153 +00:07:03,890 --> 00:07:06,026 +with city as the data. + +154 +00:07:06,026 --> 00:07:08,995 +Finally, to make the lines +look smoother, + +155 +00:07:08,995 --> 00:07:11,197 +we can use a curve +as the interpolation method + +156 +00:07:11,197 --> 00:07:13,199 +for the lines. + +157 +00:07:13,199 --> 00:07:15,135 +Even with the two series +line chart, + +158 +00:07:15,135 --> 00:07:19,005 +we can still go back to bar mark +and see how it looks like. + +159 +00:07:19,005 --> 00:07:21,074 +Changing the mark +type to BarMark, + +160 +00:07:21,074 --> 00:07:24,744 +and remove the modifiers +that aren't relevant to bars, + +161 +00:07:24,744 --> 00:07:27,047 +we get a stacked bar chart. + +162 +00:07:27,047 --> 00:07:28,648 +The bars are +automatically stacked + +163 +00:07:28,648 --> 00:07:31,618 +because we now have +two bars for each month. + +164 +00:07:31,618 --> 00:07:33,486 +While the stacked bar chart +is great for showing + +165 +00:07:33,486 --> 00:07:35,889 +the total sales value +for the two cities, + +166 +00:07:35,889 --> 00:07:39,526 +it's not very good for comparing +between the two cities. + +167 +00:07:39,526 --> 00:07:42,028 +To make it easier to compare, +we can turn this + +168 +00:07:42,028 --> 00:07:46,633 +into a grouped bar chart with +the .position(by:) modifier. + +169 +00:07:46,633 --> 00:07:49,736 +So far, we've seen +bar marks and line marks. + +170 +00:07:49,736 --> 00:07:52,472 +Swift Charts supports a couple +of other mark types + +171 +00:07:52,472 --> 00:07:57,777 +including point mark, area mark, +rule mark, and rectangle mark. + +172 +00:07:57,777 --> 00:08:00,914 +You can combine these marks +to build more complex charts. + +173 +00:08:00,914 --> 00:08:03,616 +Let's see an example. + +174 +00:08:03,616 --> 00:08:06,019 +Let's start with a line chart +showing the average daily sales + +175 +00:08:06,019 --> 00:08:07,620 +for each month. + +176 +00:08:07,620 --> 00:08:09,255 +While the average is useful, + +177 +00:08:09,255 --> 00:08:11,825 +we also want to see the minimum +and maximum daily sales + +178 +00:08:11,825 --> 00:08:15,095 +to get an idea +of the more extreme values. + +179 +00:08:15,095 --> 00:08:18,164 +Let's start by adding +these values to the data. + +180 +00:08:18,164 --> 00:08:21,034 +We introduce daily min +and daily max to each element + +181 +00:08:21,034 --> 00:08:23,236 +in the data array. + +182 +00:08:23,236 --> 00:08:26,573 +We can then visualize the min +and the max with an area mark, + +183 +00:08:26,573 --> 00:08:30,043 +where x is showing month, +and y starts at the daily min, + +184 +00:08:30,043 --> 00:08:32,011 +and ends at the daily max. + +185 +00:08:32,011 --> 00:08:34,581 +This way, we have a chart +showing daily average values + +186 +00:08:34,581 --> 00:08:36,783 +with a line, +and daily min and max values + +187 +00:08:36,783 --> 00:08:39,419 +with the area around the line. + +188 +00:08:39,419 --> 00:08:41,321 +A line plus area +isn't the only way + +189 +00:08:41,321 --> 00:08:43,390 +to visualize this kind of data. + +190 +00:08:43,390 --> 00:08:44,858 +You can easily switch +to other mark types + +191 +00:08:44,858 --> 00:08:47,227 +to explore more design options. + +192 +00:08:47,227 --> 00:08:49,996 +For example, +here we are using a BarMark. + +193 +00:08:49,996 --> 00:08:54,200 +However, the line doesn't seem +to fit very well with the bars. + +194 +00:08:54,200 --> 00:08:56,669 +Let's change the line mark +to a RectangleMark, + +195 +00:08:56,669 --> 00:08:58,905 +with a height of two points. + +196 +00:08:58,905 --> 00:09:02,008 +This way, the rectangle mark +creates horizontal lines + +197 +00:09:02,008 --> 00:09:05,145 +inside the bars +showing the average value. + +198 +00:09:05,145 --> 00:09:08,114 +You can also adjust +the width of these marks. + +199 +00:09:08,114 --> 00:09:09,949 +Here, for example, +we are setting width + +200 +00:09:09,949 --> 00:09:12,051 +to be with a ratio of .6 + +201 +00:09:12,051 --> 00:09:14,320 +This means the width +of the bars and rectangles + +202 +00:09:14,320 --> 00:09:17,757 +will be 60 percent +of the width of a whole month, + +203 +00:09:17,757 --> 00:09:20,393 +as you can see +in the screenshot. + +204 +00:09:20,393 --> 00:09:22,095 +Finally, we'd like +to have an option + +205 +00:09:22,095 --> 00:09:25,765 +to show the average daily sales +among all the months. + +206 +00:09:25,765 --> 00:09:27,700 +To do so, we first set +the foreground style + +207 +00:09:27,700 --> 00:09:31,137 +to a fade gray color +to deemphasize them. + +208 +00:09:31,137 --> 00:09:33,506 +Then, we add a rule mark +outside the ForEach, + +209 +00:09:33,506 --> 00:09:35,742 +with y showing +the average value. + +210 +00:09:35,742 --> 00:09:38,144 +This adds a horizontal rule. + +211 +00:09:38,144 --> 00:09:41,414 +To make it clear that the rule +is showing the yearly average, + +212 +00:09:41,414 --> 00:09:43,383 +we can add +an annotation to the rule + +213 +00:09:43,383 --> 00:09:45,685 +using the .annotation modifier. + +214 +00:09:45,685 --> 00:09:48,521 +This adds a text label +at the top of the rule mark, + +215 +00:09:48,521 --> 00:09:50,790 +with leading alignment. + +216 +00:09:50,790 --> 00:09:52,492 +We've discussed +a couple of examples + +217 +00:09:52,492 --> 00:09:54,727 +of building charts +by composition. + +218 +00:09:54,727 --> 00:09:56,996 +There are many different ways +to use and combine + +219 +00:09:56,996 --> 00:09:58,631 +these basic marks. + +220 +00:09:58,631 --> 00:10:03,736 +Like a box plot, multi-series +line chart, population pyramid, + +221 +00:10:03,736 --> 00:10:08,441 +range plot, stream graph, +multi-series scatter plot, + +222 +00:10:08,441 --> 00:10:12,245 +heat map, or even a plot +of a vector field. + +223 +00:10:12,245 --> 00:10:14,781 +These are just some examples +of the wide variety of charts + +224 +00:10:14,781 --> 00:10:17,250 +you can build with Swift Charts. + +225 +00:10:17,250 --> 00:10:18,885 +Let's dive into the next topic. + +226 +00:10:18,885 --> 00:10:21,588 +Plotting data +with mark properties. + +227 +00:10:21,588 --> 00:10:24,791 +Swift Charts support +three major types or data: + +228 +00:10:24,791 --> 00:10:28,328 +quantitative, +nominal, and temporal. + +229 +00:10:28,328 --> 00:10:30,897 +Quantitative data +is a numerical value, + +230 +00:10:30,897 --> 00:10:33,199 +such as the number +of products sold, + +231 +00:10:33,199 --> 00:10:36,936 +the temperature of a room, +or the price of a stock. + +232 +00:10:36,936 --> 00:10:39,839 +Swift Charts treats +Swift numerical types such as + +233 +00:10:39,839 --> 00:10:44,344 +Int, Float, and Double +as quantitative data. + +234 +00:10:44,344 --> 00:10:46,613 +Nominal data, +or categorical data, + +235 +00:10:46,613 --> 00:10:49,749 +represent discrete +categories or groups. + +236 +00:10:49,749 --> 00:10:51,718 +For example, +the name of a person, + +237 +00:10:51,718 --> 00:10:55,555 +a continent, +or the type of a product. + +238 +00:10:55,555 --> 00:10:58,091 +You can use a string +or a custom string-valued enum + +239 +00:10:58,091 --> 00:10:59,993 +as nominal data. + +240 +00:10:59,993 --> 00:11:03,530 +Temporal data represents +a point or interval in time. + +241 +00:11:03,530 --> 00:11:06,699 +For example, the duration +of a particular day, + +242 +00:11:06,699 --> 00:11:09,469 +or the exact time +of a transaction. + +243 +00:11:09,469 --> 00:11:12,772 +Swift Charts treats +"Date" as temporal data. + +244 +00:11:12,772 --> 00:11:15,141 +A chart works +by transforming abstract data, + +245 +00:11:15,141 --> 00:11:18,678 +like sales value, +into the properties of marks. + +246 +00:11:18,678 --> 00:11:22,882 +Let's take a look at BarMark, +which can plot data with X, Y, + +247 +00:11:22,882 --> 00:11:25,318 +and Foreground Style properties. + +248 +00:11:25,318 --> 00:11:29,289 +In this example, we plot sales +value, which is quantitative, + +249 +00:11:29,289 --> 00:11:33,059 +with the x property, +and name, which is nominal, + +250 +00:11:33,059 --> 00:11:35,328 +with the y property. + +251 +00:11:35,328 --> 00:11:37,864 +The resulting chart +consists of horizontal bars, + +252 +00:11:37,864 --> 00:11:41,901 +each showing Sales on X, +and Name on Y. + +253 +00:11:41,901 --> 00:11:45,772 +If we swap Name and Sales, +such that Name is on X + +254 +00:11:45,772 --> 00:11:50,209 +and Sales is on Y, we get +a vertical bar chart instead. + +255 +00:11:50,209 --> 00:11:52,312 +As you can see, +the BarMark's behavior + +256 +00:11:52,312 --> 00:11:56,616 +depends on the data type plotted +with its X and Y properties. + +257 +00:11:56,616 --> 00:11:58,084 +The orientation of the bar + +258 +00:11:58,084 --> 00:12:01,454 +depends on where +the quantitative property is. + +259 +00:12:01,454 --> 00:12:03,990 +Now, let's look at another chart +with all three properties + +260 +00:12:03,990 --> 00:12:05,725 +used to plot data. + +261 +00:12:05,725 --> 00:12:09,262 +Here we plot Weekday, +which is temporal, with X, + +262 +00:12:09,262 --> 00:12:12,932 +Sales with Y, +and City with Foreground Style. + +263 +00:12:12,932 --> 00:12:15,301 +The resulting chart +is a stacked bar chart, + +264 +00:12:15,301 --> 00:12:18,938 +where X-axis shows Weekday, +Y-axis shows Sales, + +265 +00:12:18,938 --> 00:12:21,240 +and the bars +are colored by city. + +266 +00:12:21,240 --> 00:12:23,276 +Swift Charts has six mark types, + +267 +00:12:23,276 --> 00:12:26,846 +and six mark properties +that you can plot data with. + +268 +00:12:26,846 --> 00:12:29,649 +Remember that data +can be of the three kinds, + +269 +00:12:29,649 --> 00:12:33,186 +so there is a vast array +of possible combinations. + +270 +00:12:33,186 --> 00:12:35,588 +This is what allows Swift Charts +to support a wide range + +271 +00:12:35,588 --> 00:12:40,493 +of chart designs with a small +number of basic building blocks. + +272 +00:12:40,493 --> 00:12:42,161 +When you plot data +with a mark property, + +273 +00:12:42,161 --> 00:12:45,698 +for example, sales with Y, +Swift Charts creates a mapping + +274 +00:12:45,698 --> 00:12:47,567 +that transforms +the abstract data + +275 +00:12:47,567 --> 00:12:50,503 +into a proper value +of the property. + +276 +00:12:50,503 --> 00:12:52,639 +In this case, +it will transform Sales value + +277 +00:12:52,639 --> 00:12:55,608 +to Y coordinates +in screen space. + +278 +00:12:55,608 --> 00:12:57,810 +We use the term "scale" +to refer to the mapping + +279 +00:12:57,810 --> 00:13:00,780 +from abstract data, like sales, +to mark property, + +280 +00:13:00,780 --> 00:13:02,482 +like Y position. + +281 +00:13:02,482 --> 00:13:04,283 +You can think of Scale +as a function + +282 +00:13:04,283 --> 00:13:07,920 +that takes data value +and returns a property value. + +283 +00:13:07,920 --> 00:13:11,824 +For example, here is a yScale +function that takes sales + +284 +00:13:11,824 --> 00:13:14,694 +and returns the Y position +of the bar. + +285 +00:13:14,694 --> 00:13:16,696 +The name "scale" +comes from the fact + +286 +00:13:16,696 --> 00:13:20,233 +that for position properties, +we often scale the input value + +287 +00:13:20,233 --> 00:13:24,537 +by some factor to convert it to +a reasonable screen coordinate. + +288 +00:13:24,537 --> 00:13:26,706 +When you plot data +with mark properties, + +289 +00:13:26,706 --> 00:13:28,741 +a scale is created +to transform the data + +290 +00:13:28,741 --> 00:13:31,210 +into the corresponding +mark property. + +291 +00:13:31,210 --> 00:13:34,147 +For example, in this chart +we have three scales, + +292 +00:13:34,147 --> 00:13:37,684 +each transforming +Weekday to X, Sales to Y, + +293 +00:13:37,684 --> 00:13:41,087 +and City to Foreground Style, +respectively. + +294 +00:13:41,087 --> 00:13:44,490 +By default, Swift Charts infers +the scales automatically + +295 +00:13:44,490 --> 00:13:48,661 +from the data, so you get +a nice chart out of the box. + +296 +00:13:48,661 --> 00:13:49,929 +You can use the scale modifiers + +297 +00:13:49,929 --> 00:13:52,398 +to configure the scales +in a chart. + +298 +00:13:52,398 --> 00:13:54,801 +Let's take a look +at a few examples. + +299 +00:13:54,801 --> 00:13:57,270 +In this example, +the Y scale is automatically + +300 +00:13:57,270 --> 00:14:00,339 +inferred to be zero to 150. + +301 +00:14:00,339 --> 00:14:02,742 +However, +we'd like to fix the Y scale + +302 +00:14:02,742 --> 00:14:05,411 +so that no matter what +the current sales looks like, + +303 +00:14:05,411 --> 00:14:08,281 +we always have +a consistent Y scale. + +304 +00:14:08,281 --> 00:14:10,083 +Let's change it +such that the Y scale + +305 +00:14:10,083 --> 00:14:13,619 +always starts at zero +and ends at 200. + +306 +00:14:13,619 --> 00:14:16,989 +To do so, we can use +the .chartYScale modifier, + +307 +00:14:16,989 --> 00:14:21,127 +and set the domain +of the scale to be zero to 200. + +308 +00:14:21,127 --> 00:14:26,065 +Now, as you can see, +the axis goes from zero to 200. + +309 +00:14:26,065 --> 00:14:28,167 +Similarly, we can change +how the two cities + +310 +00:14:28,167 --> 00:14:30,069 +map to foreground style using + +311 +00:14:30,069 --> 00:14:33,673 +the .chartForegroundStyleScale +modifier. + +312 +00:14:33,673 --> 00:14:36,776 +Now we have new colors +for the two cities. + +313 +00:14:36,776 --> 00:14:38,678 +Now we know +how to compose marks + +314 +00:14:38,678 --> 00:14:40,680 +and plot data +with mark properties. + +315 +00:14:40,680 --> 00:14:42,448 +Let's dive into more +customization options + +316 +00:14:42,448 --> 00:14:44,951 +that Swift Charts provides. + +317 +00:14:44,951 --> 00:14:47,920 +A chart consists of axes, +possibly a legend, + +318 +00:14:47,920 --> 00:14:50,089 +and a plot area. + +319 +00:14:50,089 --> 00:14:53,192 +Axes and legends +help us interpret the chart. + +320 +00:14:53,192 --> 00:14:56,629 +The plot area is the area +between the two axes. + +321 +00:14:56,629 --> 00:14:59,866 +This is where we +plot data with marks. + +322 +00:14:59,866 --> 00:15:02,869 +All of these elements are +customizable in Swift Charts. + +323 +00:15:02,869 --> 00:15:04,303 +Let's first see a few examples + +324 +00:15:04,303 --> 00:15:07,306 +on how to customize +axes and legends. + +325 +00:15:07,306 --> 00:15:10,276 +Here is a chart showing +total monthly sales. + +326 +00:15:10,276 --> 00:15:12,612 +Without customization, +Swift Charts generates + +327 +00:15:12,612 --> 00:15:16,215 +a default axis that features +nicely rounded values. + +328 +00:15:16,215 --> 00:15:20,520 +Right now, the X-axis shows +a label for every quarter. + +329 +00:15:20,520 --> 00:15:22,522 +Let's change it to show +a label every month, + +330 +00:15:22,522 --> 00:15:24,857 +and use a single-letter +month label. + +331 +00:15:24,857 --> 00:15:27,593 +We'll start by adding +a .chartXAxis modifier + +332 +00:15:27,593 --> 00:15:32,665 +to customize the X-axis, +with AxisMarks as the content. + +333 +00:15:32,665 --> 00:15:37,370 +AxisMarks with no parameter +recreates the default axis. + +334 +00:15:37,370 --> 00:15:40,072 +Let's start by changing +the axis values. + +335 +00:15:40,072 --> 00:15:42,742 +Since we want to have +regular calendar intervals, + +336 +00:15:42,742 --> 00:15:45,311 +we can use stride(by:), +similar to the stride function + +337 +00:15:45,311 --> 00:15:48,080 +from the standard library. + +338 +00:15:48,080 --> 00:15:50,082 +Now we have a label +for each month. + +339 +00:15:50,082 --> 00:15:52,485 +However, the default labels +feel too crowded, + +340 +00:15:52,485 --> 00:15:54,053 +as you see in the screenshot. + +341 +00:15:54,053 --> 00:15:55,621 +Some labels get truncated + +342 +00:15:55,621 --> 00:15:58,658 +because we don't have +enough space for them. + +343 +00:15:58,658 --> 00:16:02,495 +Let's change the labels +to use a single-letter format. + +344 +00:16:02,495 --> 00:16:04,630 +To do so, let's build up +the axis marks + +345 +00:16:04,630 --> 00:16:06,032 +from the individual components, + +346 +00:16:06,032 --> 00:16:10,903 +including AxisGridLine, +AxisTick, and AxisValueLabel. + +347 +00:16:10,903 --> 00:16:14,774 +Let's set the format of the +label to use narrow month names. + +348 +00:16:14,774 --> 00:16:17,810 +Now we have one letter +for each month. + +349 +00:16:17,810 --> 00:16:20,012 +The value parameter passed +into the result builder + +350 +00:16:20,012 --> 00:16:23,349 +provides information +about the current axis value. + +351 +00:16:23,349 --> 00:16:25,184 +You can use this +to conditionally determine + +352 +00:16:25,184 --> 00:16:28,221 +the existence and style +of axis marks. + +353 +00:16:28,221 --> 00:16:30,890 +For example, here we have +a condition that tests + +354 +00:16:30,890 --> 00:16:35,027 +whether the value as a date +is the first quarter of a month. + +355 +00:16:35,027 --> 00:16:37,797 +If yes, we can highlight +the first month of each quarter + +356 +00:16:37,797 --> 00:16:40,199 +with a different +foreground style. + +357 +00:16:40,199 --> 00:16:42,735 +If not, we only display +a grid line, + +358 +00:16:42,735 --> 00:16:44,670 +without tick and label. + +359 +00:16:44,670 --> 00:16:46,639 +Since we are now +showing quarters, + +360 +00:16:46,639 --> 00:16:49,842 +we can change the format +to quarter style. + +361 +00:16:49,842 --> 00:16:51,978 +With the customizations +we just did, + +362 +00:16:51,978 --> 00:16:55,615 +we get a more unique +X-axis showing quarter data, + +363 +00:16:55,615 --> 00:16:59,485 +with subgrid lines +showing every month. + +364 +00:16:59,485 --> 00:17:00,586 +In addition to values, + +365 +00:17:00,586 --> 00:17:02,688 +axis marks have other properties +that allows you + +366 +00:17:02,688 --> 00:17:05,725 +to configure the general +appearance and style. + +367 +00:17:05,725 --> 00:17:07,927 +Suppose we'd like +to have the Y-axis appear + +368 +00:17:07,927 --> 00:17:09,228 +at the leading edge of the chart + +369 +00:17:09,228 --> 00:17:12,031 +instead of the default +trailing edge, + +370 +00:17:12,031 --> 00:17:14,033 +we can set the position +parameter to leading + +371 +00:17:14,033 --> 00:17:17,169 +to move them +to the leading edge. + +372 +00:17:17,169 --> 00:17:20,273 +Swift Charts provides a default +preset for the axis marks + +373 +00:17:20,273 --> 00:17:24,176 +based on the axis and the type +of data being visualized. + +374 +00:17:24,176 --> 00:17:27,146 +You can override the default +using the preset parameter. + +375 +00:17:27,146 --> 00:17:29,749 +For example, here we are using +the .extended preset + +376 +00:17:29,749 --> 00:17:32,685 +for the Y-axis to help +with visual alignment + +377 +00:17:32,685 --> 00:17:35,721 +with the rest +of the user interface. + +378 +00:17:35,721 --> 00:17:39,125 +In some charts, you may not +want the axis to be visible. + +379 +00:17:39,125 --> 00:17:41,994 +For example, the purpose +of the top sales chart here + +380 +00:17:41,994 --> 00:17:44,196 +is to give people +a brief overview, + +381 +00:17:44,196 --> 00:17:47,133 +so the axes aren't +really necessary. + +382 +00:17:47,133 --> 00:17:49,902 +You can pass in .hidden +to the chart axis modifiers + +383 +00:17:49,902 --> 00:17:51,704 +to hide an axis. + +384 +00:17:51,704 --> 00:17:54,473 +Legend configuration +is similar to axis. + +385 +00:17:54,473 --> 00:17:55,608 +For example, + +386 +00:17:55,608 --> 00:17:58,778 +in this chart that highlights +the best day and location, + +387 +00:17:58,778 --> 00:18:01,681 +we already use opacity +to highlight the best city, + +388 +00:18:01,681 --> 00:18:04,750 +so we can hide the +automatically generated legend. + +389 +00:18:04,750 --> 00:18:07,420 +To do so, we can add +the .chartLegend modifier + +390 +00:18:07,420 --> 00:18:09,922 +with .hidden as parameter. + +391 +00:18:09,922 --> 00:18:12,758 +Now let's talk +about the plot area. + +392 +00:18:12,758 --> 00:18:15,094 +You can use +the .chartPlotStyle modifier + +393 +00:18:15,094 --> 00:18:17,964 +to configure +the plot area of a chart. + +394 +00:18:17,964 --> 00:18:20,066 +In the trailing closure, +we will write a function + +395 +00:18:20,066 --> 00:18:21,734 +that takes original plot area, + +396 +00:18:21,734 --> 00:18:24,303 +and returns +a modified plot area. + +397 +00:18:24,303 --> 00:18:27,039 +Let's see a few examples. + +398 +00:18:27,039 --> 00:18:29,041 +In some cases, +we might want the plot area + +399 +00:18:29,041 --> 00:18:32,078 +to have an exact size +or aspect ratio. + +400 +00:18:32,078 --> 00:18:35,081 +For example, in this case, +we want the plot area's height + +401 +00:18:35,081 --> 00:18:38,384 +to be driven by the number +of categories in the chart. + +402 +00:18:38,384 --> 00:18:41,187 +To achieve this, we can apply +a .frame modifier + +403 +00:18:41,187 --> 00:18:45,591 +to the plot area, and return +the modified plot area. + +404 +00:18:45,591 --> 00:18:48,728 +This will set the height +of the plot area. + +405 +00:18:48,728 --> 00:18:52,631 +We can also use modifiers to +achieve a special visual effect. + +406 +00:18:52,631 --> 00:18:54,934 +For example, +in this Dark Mode chart, + +407 +00:18:54,934 --> 00:18:57,436 +we use the .background modifier +to add a pink background + +408 +00:18:57,436 --> 00:19:02,008 +with opacity of .2 to make the +chart stand out a little bit. + +409 +00:19:02,008 --> 00:19:05,644 +And then add a 1-pt border +with the same pink color. + +410 +00:19:05,644 --> 00:19:09,315 +This creates a unique +visual effect for the chart. + +411 +00:19:09,315 --> 00:19:11,317 +Earlier in the session +we mentioned scales, + +412 +00:19:11,317 --> 00:19:13,219 +which are functions +that map data values + +413 +00:19:13,219 --> 00:19:17,223 +to mark properties +such as X and Y. + +414 +00:19:17,223 --> 00:19:19,825 +Swift Charts provides +a ChartProxy that allows you + +415 +00:19:19,825 --> 00:19:23,396 +to access the X and Y scales +in a chart. + +416 +00:19:23,396 --> 00:19:26,399 +You can use the ChartProxy's +position(for:) method + +417 +00:19:26,399 --> 00:19:29,468 +to get the position +for a given data value, + +418 +00:19:29,468 --> 00:19:31,404 +or use the value(at:) method + +419 +00:19:31,404 --> 00:19:34,840 +to get the data value +at a given position. + +420 +00:19:34,840 --> 00:19:38,577 +This allows you to coordinate +other views with the chart. + +421 +00:19:38,577 --> 00:19:41,013 +Let's look at an example. + +422 +00:19:41,013 --> 00:19:43,382 +We are going to build +this interactive brushing view. + +423 +00:19:43,382 --> 00:19:45,751 +Here you can select +an interval from the chart + +424 +00:19:45,751 --> 00:19:48,354 +with a drag gesture, +and that interval + +425 +00:19:48,354 --> 00:19:52,058 +will then be used to filter +the rows in the details view. + +426 +00:19:52,058 --> 00:19:54,393 +We can get a chart proxy +object from the .chartOverlay + +427 +00:19:54,393 --> 00:19:56,595 +or .chartBackground modifiers. + +428 +00:19:56,595 --> 00:19:59,165 +These two modifiers +are similar to SwiftUI's overlay + +429 +00:19:59,165 --> 00:20:00,966 +and background modifiers, + +430 +00:20:00,966 --> 00:20:04,103 +but they provide you +with a chart proxy. + +431 +00:20:04,103 --> 00:20:05,237 +To build this example, + +432 +00:20:05,237 --> 00:20:09,108 +we'll start by defining +the base chart just like before. + +433 +00:20:09,108 --> 00:20:10,943 +We then add +a .chartOverlay modifier + +434 +00:20:10,943 --> 00:20:13,412 +that provides us +with the chart proxy. + +435 +00:20:13,412 --> 00:20:16,348 +Inside, we have a geometry +reader that gives us access + +436 +00:20:16,348 --> 00:20:19,051 +to the geometry +of the overlay view. + +437 +00:20:19,051 --> 00:20:22,354 +Then, we have a Rectangle view +that is configured to respond + +438 +00:20:22,354 --> 00:20:25,191 +to SwiftUI's DragGesture. + +439 +00:20:25,191 --> 00:20:28,694 +When the drag gesture happens, +we first find the x coordinates + +440 +00:20:28,694 --> 00:20:30,896 +of the start +and the current locations + +441 +00:20:30,896 --> 00:20:32,998 +inside the chart's plot area. + +442 +00:20:32,998 --> 00:20:35,434 +This is done by subtracting +the plot area's origin + +443 +00:20:35,434 --> 00:20:39,138 +from the locations +provided by the gestures. + +444 +00:20:39,138 --> 00:20:42,475 +Once we have these coordinates, +we can then use the chart proxy + +445 +00:20:42,475 --> 00:20:45,344 +to find the corresponding +Date values, + +446 +00:20:45,344 --> 00:20:47,746 +and finally set it +to a SwiftUI state + +447 +00:20:47,746 --> 00:20:51,317 +that keeps track of +the current date interval. + +448 +00:20:51,317 --> 00:20:54,220 +With the range state, we can +then define a rectangle mark + +449 +00:20:54,220 --> 00:20:58,090 +in the chart to visualize the +currently selected date range. + +450 +00:20:58,090 --> 00:21:00,192 +This state can also be used +to control other parts + +451 +00:21:00,192 --> 00:21:01,994 +of your application + +452 +00:21:01,994 --> 00:21:03,629 +for example, +to filter the contents + +453 +00:21:03,629 --> 00:21:05,898 +of the details view +below the chart. + +454 +00:21:05,898 --> 00:21:06,999 +This is a simple example + +455 +00:21:06,999 --> 00:21:09,802 +to illustrate how +chart proxy works. + +456 +00:21:09,802 --> 00:21:11,737 +You can use it to build +many interesting features, + +457 +00:21:11,737 --> 00:21:13,973 +for example, +this interactive chart + +458 +00:21:13,973 --> 00:21:16,475 +that shows the selected day +and sales value + +459 +00:21:16,475 --> 00:21:19,545 +with an overlay +that looks like a lollipop. + +460 +00:21:19,545 --> 00:21:20,980 +In this session, +we have discussed + +461 +00:21:20,980 --> 00:21:23,482 +how to create charts +by composing marks; + +462 +00:21:23,482 --> 00:21:25,417 +how to plot data +with mark properties, + +463 +00:21:25,417 --> 00:21:27,887 +and how to customize a chart. + +464 +00:21:27,887 --> 00:21:30,022 +You can go to the design +sessions to learn more + +465 +00:21:30,022 --> 00:21:32,858 +about how to design great +app experiences with charts + +466 +00:21:32,858 --> 00:21:35,761 +and how to design +an effective chart. + +467 +00:21:35,761 --> 00:21:37,596 +We think you will love +building data visualizations + +468 +00:21:37,596 --> 00:21:38,831 +with Swift Charts. + +469 +00:21:38,831 --> 00:21:39,932 +Thanks for watching. + +470 +00:21:39,932 --> 00:21:43,869 +♪ + diff --git a/eng/2022 Session 10139 Make a great SharePlay experience en.srt b/eng/2022 Session 10139 Make a great SharePlay experience en.srt new file mode 100644 index 0000000..92dcf15 --- /dev/null +++ b/eng/2022 Session 10139 Make a great SharePlay experience en.srt @@ -0,0 +1,1982 @@ +1 +00:00:00,133 --> 00:00:02,669 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:02,669 --> 00:00:09,676 +♪ + +3 +00:00:09,676 --> 00:00:12,980 +♪ Suspenseful +classical music ♪ + +4 +00:00:12,980 --> 00:00:13,847 +[FACETIME RINGING] + +5 +00:00:13,847 --> 00:00:16,717 +Ryan Williams: One call +can change everything. + +6 +00:00:16,717 --> 00:00:18,518 +Priya Shah: OK, +are you sure you have + +7 +00:00:18,518 --> 00:00:20,153 +everything you need +for this demo? + +8 +00:00:20,153 --> 00:00:23,090 +Because after I drop this +call, you're on your own. + +9 +00:00:23,090 --> 00:00:26,426 +Ryan: I’ve got this! +It's going to be a breeze. + +10 +00:00:26,426 --> 00:00:28,362 +It should have been a breeze. + +11 +00:00:28,362 --> 00:00:31,498 +I just had to choose one great +SharePlay experience + +12 +00:00:31,498 --> 00:00:34,001 +to kick off our talk +while my partner + +13 +00:00:34,001 --> 00:00:36,169 +got some well-deserved +shut-eye. + +14 +00:00:36,169 --> 00:00:39,806 +I narrowed the list of suspects +down but I couldn't choose. + +15 +00:00:39,806 --> 00:00:41,608 +I'd have to call in +my whole team + +16 +00:00:41,608 --> 00:00:45,045 +to help me make a break +in this switch case. + +17 +00:00:45,045 --> 00:00:46,346 +First up, +[SHAREPLAY NOTIFICATION CHIME] + +18 +00:00:46,346 --> 00:00:48,682 +a test run +with Adam in the gym. + +19 +00:00:48,682 --> 00:00:49,650 +[CLATTERING] +Adam: I'm so excited! + +20 +00:00:49,650 --> 00:00:51,551 +We're always so down +between the lines. + +21 +00:00:51,551 --> 00:00:53,120 +Siri: Burpee. + +22 +00:00:53,120 --> 00:00:55,489 +Adam: Calling functions, +[HEAVY BREATHING] + +23 +00:00:55,489 --> 00:00:56,657 +refactoring code. +[CLATTERING] + +24 +00:00:56,657 --> 00:00:58,592 +Siri: Butt kicks. + +25 +00:00:58,592 --> 00:01:00,027 +Adam: It's nice to hit Run + +26 +00:01:00,027 --> 00:01:01,628 +and check out +what people are building. + +27 +00:01:01,628 --> 00:01:03,196 +Siri: Rest. +[HEAVY BREATHING] + +28 +00:01:03,196 --> 00:01:06,400 +Ryan: Adam left me +inspired and perspired, + +29 +00:01:06,400 --> 00:01:08,936 +but I still had a few more +if checks to evaluate. + +30 +00:01:08,936 --> 00:01:11,371 +So I called Agnes +and Gav to shop around. + +31 +00:01:11,371 --> 00:01:12,739 +[SHAREPLAY NOTIFICATION CHIME] + +32 +00:01:12,739 --> 00:01:15,409 +But much as I like browsing +the toy market, + +33 +00:01:15,409 --> 00:01:18,178 +I wasn't ready +to make a bid just yet. + +34 +00:01:18,178 --> 00:01:19,813 +Maybe a piano break with Justin + +35 +00:01:19,813 --> 00:01:22,716 +would be the subroutine +I needed. + +36 +00:01:22,716 --> 00:01:23,951 +[SHAREPLAY NOTIFICATION CHIME] + +37 +00:01:23,951 --> 00:01:29,556 +♪ Somber piano music ♪ + +38 +00:01:29,556 --> 00:01:34,027 +My thoughts were starting +to compile. + +39 +00:01:34,027 --> 00:01:37,965 +But first, I had one last +spy game to play. + +40 +00:01:37,965 --> 00:01:39,666 +Gav: Wow, here he is. +Olivia: We thought... + +41 +00:01:39,666 --> 00:01:40,801 +Adam: ...you thought... +Olivia: ...you might be + +42 +00:01:40,801 --> 00:01:42,002 +too overloaded. +[SHAREPLAY NOTIFICATION CHIME] + +43 +00:01:42,002 --> 00:01:44,538 +Ryan: My mother didn't +instantiate a quitter. + +44 +00:01:44,538 --> 00:01:45,872 +Let's go. + +45 +00:01:45,872 --> 00:01:51,878 +As we played, somewhere -- +somewhere deep, a synapse fired. + +46 +00:01:51,878 --> 00:01:54,481 +The case was +a simple one after all. + +47 +00:01:54,481 --> 00:01:58,318 +I'd been looking for one +great app, but the truth is, + +48 +00:01:58,318 --> 00:02:00,954 +I learned something +from them all. + +49 +00:02:00,954 --> 00:02:03,457 +Not our usual demo, I know, +but forgive a man + +50 +00:02:03,457 --> 00:02:06,927 +a little virtualization +now and again. + +51 +00:02:06,927 --> 00:02:10,497 +Now if you'll excuse me, +I'm off to my next case. + +52 +00:02:10,497 --> 00:02:13,934 +All the great experiences +still waiting to be found. + +53 +00:02:13,934 --> 00:02:16,169 +♪ Suspenseful +classical music ♪ + +54 +00:02:18,205 --> 00:02:23,110 +Hi. I'm Ryan, an engineer +on the SharePlay team. + +55 +00:02:23,110 --> 00:02:25,946 +Our team has had +a lot of fun exploring + +56 +00:02:25,946 --> 00:02:29,249 +all the incredible SharePlay +experiences you've made. + +57 +00:02:29,249 --> 00:02:33,954 +Your work has brought people +together like never before, + +58 +00:02:33,954 --> 00:02:37,024 +and we've also learned +a lot over the past year + +59 +00:02:37,024 --> 00:02:38,592 +about how you can give people + +60 +00:02:38,592 --> 00:02:41,261 +the best SharePlay experience +possible. + +61 +00:02:41,261 --> 00:02:44,231 +So today, we're going to take +you through how to design + +62 +00:02:44,231 --> 00:02:48,602 +and build a great SharePlay +experience in your app. + +63 +00:02:48,602 --> 00:02:53,173 +We'll talk about using SharePlay +as a new way to connect people. + +64 +00:02:53,173 --> 00:02:56,143 +Then we'll go over how to foster +a sense of presence, + +65 +00:02:56,143 --> 00:02:59,212 +breaking down the digital +barriers between people + +66 +00:02:59,212 --> 00:03:01,048 +and making them feel +like they're together + +67 +00:03:01,048 --> 00:03:03,283 +in the same space. + +68 +00:03:03,283 --> 00:03:05,886 +We'll go over the different +types of SharePlay experiences + +69 +00:03:05,886 --> 00:03:07,821 +you can design in your app, + +70 +00:03:07,821 --> 00:03:10,323 +and then finally, we'll tell you +some tips and tricks + +71 +00:03:10,323 --> 00:03:11,792 +we've learned along the way + +72 +00:03:11,792 --> 00:03:13,894 +for how to make it +a seamless experience + +73 +00:03:13,894 --> 00:03:16,229 +for people using your app. + +74 +00:03:16,229 --> 00:03:19,566 +More people than ever +are relying on FaceTime + +75 +00:03:19,566 --> 00:03:22,569 +and iMessage in order +to stay connected. + +76 +00:03:22,569 --> 00:03:27,774 +With iMessage, we connect by +sharing text or sharing images, + +77 +00:03:27,774 --> 00:03:31,011 +and with FaceTime, we connect +by sharing audio and video. + +78 +00:03:33,780 --> 00:03:35,882 +However, some of the most +meaningful moments + +79 +00:03:35,882 --> 00:03:37,951 +people have together +are about more + +80 +00:03:37,951 --> 00:03:40,387 +than just sharing +a conversation. + +81 +00:03:40,387 --> 00:03:45,292 +When we're together, we connect +most by sharing experiences. + +82 +00:03:45,292 --> 00:03:47,561 +So to foster that sense +of closeness, + +83 +00:03:47,561 --> 00:03:49,663 +we built SharePlay, + +84 +00:03:49,663 --> 00:03:52,632 +and by adopting the +Group Activities framework, + +85 +00:03:52,632 --> 00:03:55,602 +you can connect people +in this entirely new way + +86 +00:03:55,602 --> 00:03:58,505 +by allowing them to participate +in SharePlay activities + +87 +00:03:58,505 --> 00:04:00,874 +in your app. + +88 +00:04:00,874 --> 00:04:04,478 +It all comes down +to this concept of activities. + +89 +00:04:04,478 --> 00:04:07,414 +When someone in a FaceTime call +starts an activity, + +90 +00:04:07,414 --> 00:04:10,484 +SharePlay brings the +group directly into your app, + +91 +00:04:10,484 --> 00:04:14,788 +where you can create +almost any type of experience. + +92 +00:04:14,788 --> 00:04:17,491 +And an activity can +really be anything; + +93 +00:04:17,491 --> 00:04:20,827 +from cooking together, +to playing piano together. + +94 +00:04:20,827 --> 00:04:23,563 +We've seen a lot of +creative examples of activities + +95 +00:04:23,563 --> 00:04:25,232 +from you already. + +96 +00:04:25,232 --> 00:04:27,801 +And this year, +we focused on elevating + +97 +00:04:27,801 --> 00:04:31,738 +the core SharePlay experience, +making it even easier + +98 +00:04:31,738 --> 00:04:35,275 +for people to share experiences +in your app. + +99 +00:04:35,275 --> 00:04:39,346 +Now, people can start SharePlay +directly from your app. + +100 +00:04:39,346 --> 00:04:42,115 +When someone finds that perfect +piece of content in your app + +101 +00:04:42,115 --> 00:04:44,284 +that they want to share +with their friends, + +102 +00:04:44,284 --> 00:04:47,487 +they can choose who they want +to experience that content with + +103 +00:04:47,487 --> 00:04:51,291 +and invite them to SharePlay +directly from your app. + +104 +00:04:51,291 --> 00:04:54,227 +This way, people can jump +into a shared experience + +105 +00:04:54,227 --> 00:04:57,731 +with their friends whenever +it feels most natural for them, + +106 +00:04:57,731 --> 00:05:01,301 +without having +to plan in advance. + +107 +00:05:01,301 --> 00:05:05,038 +They'll even get the option to +start SharePlay from Messages, + +108 +00:05:05,038 --> 00:05:07,374 +letting them share an experience +with their friends + +109 +00:05:07,374 --> 00:05:09,809 +without needing to be +on a FaceTime call. + +110 +00:05:09,809 --> 00:05:12,679 +We think people are going +to love all the new ways + +111 +00:05:12,679 --> 00:05:15,148 +they can start SharePlay +from your app. + +112 +00:05:15,148 --> 00:05:17,517 +We'll go over how to adopt +all of these new features + +113 +00:05:17,517 --> 00:05:20,487 +in the session +"What's new in SharePlay." + +114 +00:05:20,487 --> 00:05:22,122 +And that just scratches +the surface + +115 +00:05:22,122 --> 00:05:24,090 +of what SharePlay can do + +116 +00:05:24,090 --> 00:05:27,060 +but we think the real potential +of SharePlay is unlocked + +117 +00:05:27,060 --> 00:05:30,630 +when you unleash your creativity +to build experiences + +118 +00:05:30,630 --> 00:05:33,533 +that bring people +closer together. + +119 +00:05:33,533 --> 00:05:37,370 +And a big part of that is +fostering a sense of presence. + +120 +00:05:37,370 --> 00:05:40,540 +So before we go into how +to build these experiences, + +121 +00:05:40,540 --> 00:05:42,676 +we first have to understand +what it means + +122 +00:05:42,676 --> 00:05:45,645 +to be present with others +and how to take aspects + +123 +00:05:45,645 --> 00:05:47,514 +of a great in-person +experience + +124 +00:05:47,514 --> 00:05:50,283 +and bring them into your apps. + +125 +00:05:50,283 --> 00:05:53,887 +Typically, digital communication +tools are designed to bridge + +126 +00:05:53,887 --> 00:05:57,157 +the gaps in presence, +but in order for SharePlay + +127 +00:05:57,157 --> 00:05:59,893 +to create meaningful moments +between people, + +128 +00:05:59,893 --> 00:06:02,796 +we need to find ways to actually +make people feel present + +129 +00:06:02,796 --> 00:06:06,800 +with each other, as if they were +in the same physical space. + +130 +00:06:06,800 --> 00:06:09,202 +Now, knowing how to foster +this sense of presence + +131 +00:06:09,202 --> 00:06:12,105 +can be different +for every experience + +132 +00:06:12,105 --> 00:06:14,507 +but one trick we can do +is to think about your app + +133 +00:06:14,507 --> 00:06:17,143 +being used +in the physical world. + +134 +00:06:17,143 --> 00:06:20,180 +So, here's an exercise +we recommend doing: + +135 +00:06:20,180 --> 00:06:23,583 +think of SharePlay like +a portal transporting people + +136 +00:06:23,583 --> 00:06:26,519 +through their phones +and into the same space. + +137 +00:06:26,519 --> 00:06:27,821 +What would they do with your app + +138 +00:06:27,821 --> 00:06:30,624 +if they were actually present +with each other? + +139 +00:06:30,624 --> 00:06:33,293 +If that sounds impossible, +don't worry. + +140 +00:06:33,293 --> 00:06:35,362 +We'll go over some ways +that you can foster + +141 +00:06:35,362 --> 00:06:38,298 +this sense of presence +in your own apps. + +142 +00:06:38,298 --> 00:06:40,533 +Let's do this exercise together. + +143 +00:06:40,533 --> 00:06:42,702 +Imagine you're transported +to a room + +144 +00:06:42,702 --> 00:06:44,971 +with a physical record player. + +145 +00:06:44,971 --> 00:06:48,174 +Everyone can go up to it +and interact with it. + +146 +00:06:48,174 --> 00:06:52,145 +They can put on +their favorite song + +147 +00:06:52,145 --> 00:06:54,914 +and once it's playing, +anyone else in the room + +148 +00:06:54,914 --> 00:06:58,351 +can also go up to the record +player and interact with it. + +149 +00:06:58,351 --> 00:07:00,253 +They can skip forward +in the song + +150 +00:07:00,253 --> 00:07:03,590 +or they can skip +the song entirely. + +151 +00:07:03,590 --> 00:07:05,358 +From this, we found +that we needed + +152 +00:07:05,358 --> 00:07:09,562 +to offer shared playback +controls in our app. + +153 +00:07:09,562 --> 00:07:13,133 +And in fact, it's critical that +we don't put any limitations + +154 +00:07:13,133 --> 00:07:15,735 +on who can interact +with these controls. + +155 +00:07:15,735 --> 00:07:17,704 +We want to create +an engaging experience + +156 +00:07:17,704 --> 00:07:20,006 +where everyone feels like +they're able to walk up + +157 +00:07:20,006 --> 00:07:22,942 +and control +that same music player. + +158 +00:07:22,942 --> 00:07:26,279 +OK, now someone has started +playing some music. + +159 +00:07:26,279 --> 00:07:28,615 +The next thing they might +do is look around + +160 +00:07:28,615 --> 00:07:30,917 +and see who's there +listening with them. + +161 +00:07:30,917 --> 00:07:34,688 +Now this may seem obvious, +but that person may be playing + +162 +00:07:34,688 --> 00:07:37,357 +their favorite song for someone +and probably wants + +163 +00:07:37,357 --> 00:07:40,994 +to be reminded of their friend +there listening with them. + +164 +00:07:40,994 --> 00:07:42,729 +When people are doing +these experiences + +165 +00:07:42,729 --> 00:07:44,998 +through their phones, +we need to find ways + +166 +00:07:44,998 --> 00:07:47,300 +to offer reminders +that there are others there + +167 +00:07:47,300 --> 00:07:49,035 +doing that experience +with them. + +168 +00:07:51,671 --> 00:07:54,140 +This exercise is a great way +to start thinking + +169 +00:07:54,140 --> 00:07:56,676 +about the design considerations +that will help + +170 +00:07:56,676 --> 00:08:00,447 +to foster presence in almost +any kind of experience. + +171 +00:08:00,447 --> 00:08:03,116 +Now, I'm going to hand it over +to Priya to talk through + +172 +00:08:03,116 --> 00:08:07,287 +some things to keep in mind +when designing your experience. + +173 +00:08:07,287 --> 00:08:09,389 +Priya: Thank you, Ryan. + +174 +00:08:09,389 --> 00:08:11,891 +When designing +an experience for groups, + +175 +00:08:11,891 --> 00:08:14,828 +the first thing to think about +is what shared activities + +176 +00:08:14,828 --> 00:08:16,596 +your app will support. + +177 +00:08:16,596 --> 00:08:19,366 +It might be challenging at first +since many apps + +178 +00:08:19,366 --> 00:08:22,902 +have been designed +with a single person in mind. + +179 +00:08:22,902 --> 00:08:25,772 +Apps have been personalized +to build engaging experiences + +180 +00:08:25,772 --> 00:08:27,407 +for individual people + +181 +00:08:27,407 --> 00:08:29,342 +but a great personalized +experience + +182 +00:08:29,342 --> 00:08:33,446 +might not always translate +to a great group experience. + +183 +00:08:33,446 --> 00:08:36,249 +Here is an opportunity for you +to think of your app + +184 +00:08:36,249 --> 00:08:39,085 +from a different perspective -- +to think of an experience + +185 +00:08:39,085 --> 00:08:41,454 +designed specifically +for groups. + +186 +00:08:41,454 --> 00:08:43,022 +When designing your activity, + +187 +00:08:43,022 --> 00:08:45,959 +think about what people like +to do when they're together. + +188 +00:08:45,959 --> 00:08:47,927 +From cooking together +with your friends, + +189 +00:08:47,927 --> 00:08:49,929 +to breathing +through a yoga flow, + +190 +00:08:49,929 --> 00:08:53,099 +watching your favorite team +win the finals + +191 +00:08:53,099 --> 00:08:56,469 +or gasping at every +"Severance" cliffhanger. + +192 +00:08:56,469 --> 00:08:58,938 +All of these are good candidates +for SharePlay + +193 +00:08:58,938 --> 00:09:01,741 +because they're activities that +people love to do together -- + +194 +00:09:01,741 --> 00:09:03,209 +in person! + +195 +00:09:03,209 --> 00:09:05,311 +SharePlay provides +the ability to create + +196 +00:09:05,311 --> 00:09:08,848 +rich coordinated experiences +for groups where everyone + +197 +00:09:08,848 --> 00:09:12,318 +has control and shares +the same experience in sync, + +198 +00:09:12,318 --> 00:09:14,220 +to help foster +a sense of presence + +199 +00:09:14,220 --> 00:09:15,922 +when people are apart. + +200 +00:09:15,922 --> 00:09:19,559 +For example, when using +SharePlay with Apple TV+, + +201 +00:09:19,559 --> 00:09:21,961 +pressing play on a video +enables everyone + +202 +00:09:21,961 --> 00:09:23,363 +to watch together. + +203 +00:09:23,363 --> 00:09:27,233 +Everyone's playhead is in sync, +and pause, play, seek commands + +204 +00:09:27,233 --> 00:09:28,868 +are all coordinated. + +205 +00:09:28,868 --> 00:09:31,871 +So when Ryan pauses +or Adam resumes, + +206 +00:09:31,871 --> 00:09:36,075 +the video pauses and plays +for everyone at the same time. + +207 +00:09:36,075 --> 00:09:38,511 +Apple TV+ is a great example + +208 +00:09:38,511 --> 00:09:41,214 +of a single-view +coordinated experience, + +209 +00:09:41,214 --> 00:09:44,851 +where everyone can watch +and listen to the same thing. + +210 +00:09:44,851 --> 00:09:47,020 +If you're building +a coordinated media experience + +211 +00:09:47,020 --> 00:09:50,757 +like this for your app, you can +add shared playback controls + +212 +00:09:50,757 --> 00:09:53,426 +by adopting +AVPlaybackCoordinator + +213 +00:09:53,426 --> 00:09:56,563 +and using the +coordinateWithSession method. + +214 +00:09:56,563 --> 00:09:59,098 +The system will keep everyone +in perfect sync + +215 +00:09:59,098 --> 00:10:03,169 +as if they were all in the same +room sharing one remote. + +216 +00:10:03,169 --> 00:10:05,138 +But sometimes, +we share activities + +217 +00:10:05,138 --> 00:10:07,640 +that participants +experience differently. + +218 +00:10:07,640 --> 00:10:10,477 +Some participants +might have different views + +219 +00:10:10,477 --> 00:10:13,213 +of the same coordinated +experience. + +220 +00:10:13,213 --> 00:10:15,982 +For example, when playing +a game of Heads Up!, + +221 +00:10:15,982 --> 00:10:18,818 +one person has to guess +the words on the screen + +222 +00:10:18,818 --> 00:10:22,155 +while their friends shout out +or act out clues. + +223 +00:10:22,155 --> 00:10:23,823 +Now, it wouldn't be +a very difficult game + +224 +00:10:23,823 --> 00:10:26,226 +if the guessers could see +the answers… + +225 +00:10:26,226 --> 00:10:28,528 +so there are two views +in this game, + +226 +00:10:28,528 --> 00:10:31,931 +and not all participants +see the same view. + +227 +00:10:31,931 --> 00:10:35,068 +If you're building a multiview +coordinated experience + +228 +00:10:35,068 --> 00:10:37,971 +like this, you can use +the send method + +229 +00:10:37,971 --> 00:10:41,641 +of the GroupSessionMessenger +class and specify the subsets + +230 +00:10:41,641 --> 00:10:45,178 +of the participants that should +receive the message. + +231 +00:10:45,178 --> 00:10:47,780 +So once you've decided on +the activity you are designing + +232 +00:10:47,780 --> 00:10:49,883 +and the views needed +to support it, + +233 +00:10:49,883 --> 00:10:53,152 +the next step is to build a +great experience for the people + +234 +00:10:53,152 --> 00:10:55,154 +interacting with your app. + +235 +00:10:55,154 --> 00:10:56,789 +Let's discuss some +of the factors + +236 +00:10:56,789 --> 00:10:58,791 +that can make your app's +SharePlay experience + +237 +00:10:58,791 --> 00:11:02,128 +intuitive and easy to use. + +238 +00:11:02,128 --> 00:11:04,898 +In order to build a great +SharePlay experience, + +239 +00:11:04,898 --> 00:11:07,534 +it is important to think +through the different stages + +240 +00:11:07,534 --> 00:11:12,005 +of a shared activity -- +starting, during, and ending. + +241 +00:11:12,005 --> 00:11:14,874 +We've added the ability +to start a SharePlay session + +242 +00:11:14,874 --> 00:11:17,510 +without the need +for an active FaceTime call. + +243 +00:11:17,510 --> 00:11:20,713 +So people can start a SharePlay +session directly from your app + +244 +00:11:20,713 --> 00:11:23,583 +when they come +across content or an activity + +245 +00:11:23,583 --> 00:11:25,919 +that they want to share +with their friends. + +246 +00:11:25,919 --> 00:11:28,421 +This helps bring everyone +directly to the experience + +247 +00:11:28,421 --> 00:11:31,691 +you've created in your app. + +248 +00:11:31,691 --> 00:11:33,960 +When people browse your app +and come across something + +249 +00:11:33,960 --> 00:11:35,695 +they want to share +with their friends, + +250 +00:11:35,695 --> 00:11:38,798 +they might tap the Share button +to present the Share Sheet. + +251 +00:11:38,798 --> 00:11:41,701 +In order to surface the ability +to SharePlay here, + +252 +00:11:41,701 --> 00:11:44,971 +you can register your app's +group activity by using + +253 +00:11:44,971 --> 00:11:47,440 +the registerGroupActivity(_:) +method. + +254 +00:11:47,440 --> 00:11:49,742 +People will see the option +to SharePlay the activity + +255 +00:11:49,742 --> 00:11:53,680 +from your app directly +from the Share Sheet. + +256 +00:11:53,680 --> 00:11:56,049 +In order to make +group activities in your app + +257 +00:11:56,049 --> 00:11:58,918 +even more discoverable, +you can support the ability + +258 +00:11:58,918 --> 00:12:01,521 +to SharePlay directly +in your app's UI + +259 +00:12:01,521 --> 00:12:03,289 +with the SharePlay button. + +260 +00:12:03,289 --> 00:12:07,727 +To do this, you can adopt the +GroupActivitySharingContoller. + +261 +00:12:07,727 --> 00:12:10,730 +This allows people to start +a SharePlay session, + +262 +00:12:10,730 --> 00:12:13,032 +select the friends they want +to SharePlay with, + +263 +00:12:13,032 --> 00:12:15,101 +and whether they want +to SharePlay over messages + +264 +00:12:15,101 --> 00:12:16,536 +or FaceTime. + +265 +00:12:16,536 --> 00:12:18,504 +I'm going to go ahead +and SharePlay some music + +266 +00:12:18,504 --> 00:12:21,007 +with Adam and Ryan. + +267 +00:12:21,007 --> 00:12:22,709 +The system will +display information + +268 +00:12:22,709 --> 00:12:25,979 +about the group activity +to them, including the title, + +269 +00:12:25,979 --> 00:12:28,982 +an optional subtitle and image. + +270 +00:12:28,982 --> 00:12:31,451 +It's important that this +information is descriptive + +271 +00:12:31,451 --> 00:12:34,053 +so that Ryan and Adam +know what to expect + +272 +00:12:34,053 --> 00:12:36,756 +when they join +the SharePlay session. + +273 +00:12:36,756 --> 00:12:39,025 +So be sure to implement +the metadata property + +274 +00:12:39,025 --> 00:12:41,828 +for your group activity +with meaningful information + +275 +00:12:41,828 --> 00:12:45,798 +and return a +GroupActivityMetadata instance. + +276 +00:12:45,798 --> 00:12:48,668 +Also consider including +a web-based URL + +277 +00:12:48,668 --> 00:12:51,270 +for participants who might not +have your app installed + +278 +00:12:51,270 --> 00:12:53,206 +on their device already. + +279 +00:12:53,206 --> 00:12:55,141 +This will make it quick +and easy for them + +280 +00:12:55,141 --> 00:12:59,812 +to download your app and join +their friends in the activity. + +281 +00:12:59,812 --> 00:13:01,314 +It's important to remember + +282 +00:13:01,314 --> 00:13:03,349 +that just because someone's +on a Messages group + +283 +00:13:03,349 --> 00:13:04,550 +or FaceTime call + +284 +00:13:04,550 --> 00:13:08,021 +doesn't mean that they have +joined the group activity. + +285 +00:13:08,021 --> 00:13:09,756 +To find out who else +is participating + +286 +00:13:09,756 --> 00:13:11,090 +in the SharePlay session, + +287 +00:13:11,090 --> 00:13:13,726 +people can navigate +to the details view. + +288 +00:13:13,726 --> 00:13:15,395 +Looks like Ryan's +listening with me, + +289 +00:13:15,395 --> 00:13:17,530 +but Adam, who's on +the FaceTime call, + +290 +00:13:17,530 --> 00:13:20,733 +hasn't joined +the SharePlay activity yet. + +291 +00:13:20,733 --> 00:13:23,503 +When possible, you can also +show similar information + +292 +00:13:23,503 --> 00:13:25,238 +in your app's UI. + +293 +00:13:25,238 --> 00:13:26,639 +This is a great way to know + +294 +00:13:26,639 --> 00:13:29,142 +everyone is on +the same page as you. + +295 +00:13:29,142 --> 00:13:32,779 +It also helps maintain a sense +of presence with the group. + +296 +00:13:32,779 --> 00:13:34,280 +If you were all +in the same room, + +297 +00:13:34,280 --> 00:13:35,515 +you would know +how many people + +298 +00:13:35,515 --> 00:13:39,819 +were enjoying the activity +with you just by looking around. + +299 +00:13:39,819 --> 00:13:42,455 +Now we all have some friends +who are always fashionably late + +300 +00:13:42,455 --> 00:13:45,358 +to a party; so, depending on +your app's experience, + +301 +00:13:45,358 --> 00:13:47,493 +you might want to consider +supporting a lobby + +302 +00:13:47,493 --> 00:13:49,362 +where people can wait +for people to join + +303 +00:13:49,362 --> 00:13:51,431 +before beginning +the experience. + +304 +00:13:51,431 --> 00:13:54,233 +For example with Heads Up!, +once everyone has joined + +305 +00:13:54,233 --> 00:13:57,103 +the SharePlay session, +you can tap Let's Play! + +306 +00:13:57,103 --> 00:13:59,272 +to start the game. + +307 +00:13:59,272 --> 00:14:01,808 +OK, so we've covered +how to make starting + +308 +00:14:01,808 --> 00:14:04,610 +and joining a SharePlay +activity seamless. + +309 +00:14:04,610 --> 00:14:07,747 +Now let's consider how to create +an intuitive experience + +310 +00:14:07,747 --> 00:14:09,749 +during an activity. + +311 +00:14:09,749 --> 00:14:12,985 +As part of the ongoing activity, +everyone will be interacting + +312 +00:14:12,985 --> 00:14:15,488 +with your app +at the same time. + +313 +00:14:15,488 --> 00:14:18,091 +We have built a relationship +with our devices + +314 +00:14:18,091 --> 00:14:19,492 +that changes occur +as a result + +315 +00:14:19,492 --> 00:14:21,728 +of us directly +touching something. + +316 +00:14:21,728 --> 00:14:23,663 +But when you build +a shared experience + +317 +00:14:23,663 --> 00:14:25,698 +across different devices, + +318 +00:14:25,698 --> 00:14:29,168 +it's possible that things would +change on someone's screen + +319 +00:14:29,168 --> 00:14:31,571 +due to someone else's +interaction. + +320 +00:14:31,571 --> 00:14:34,574 +So when things change +during a SharePlay session, + +321 +00:14:34,574 --> 00:14:37,143 +it is important to let +everyone know why. + +322 +00:14:37,143 --> 00:14:39,612 +For example, +Ryan might pause the movie + +323 +00:14:39,612 --> 00:14:42,014 +the group is watching +on his device. + +324 +00:14:42,014 --> 00:14:43,950 +When Ryan pauses the video, + +325 +00:14:43,950 --> 00:14:45,985 +it pauses +for everyone else as well, + +326 +00:14:45,985 --> 00:14:48,988 +without any interaction +with their devices. + +327 +00:14:48,988 --> 00:14:51,891 +The system already handles +communicating changes + +328 +00:14:51,891 --> 00:14:55,461 +like these to the rest +of the group with notices + +329 +00:14:55,461 --> 00:14:58,631 +but, for some activities, +people may benefit + +330 +00:14:58,631 --> 00:15:01,300 +from in-app +contextual information + +331 +00:15:01,300 --> 00:15:04,570 +about what's going on +during the activity as well. + +332 +00:15:04,570 --> 00:15:05,738 +For example, + +333 +00:15:05,738 --> 00:15:08,975 +when using SharePlay +with the Flow app by Moleskin, + +334 +00:15:08,975 --> 00:15:12,345 +multiple people might be +drawing on the same page. + +335 +00:15:12,345 --> 00:15:15,748 +When someone draws something, +the app shows the initials + +336 +00:15:15,748 --> 00:15:17,617 +of who drew it, +and this makes for + +337 +00:15:17,617 --> 00:15:22,221 +a much more intuitive experience +for the entire group. + +338 +00:15:22,221 --> 00:15:24,724 +It also helps reinforce +the feeling of presence + +339 +00:15:24,724 --> 00:15:26,359 +with the rest of the group. + +340 +00:15:26,359 --> 00:15:28,628 +This is even more important +now that you can SharePlay + +341 +00:15:28,628 --> 00:15:31,430 +over Messages, where you may not +have continuous feedback + +342 +00:15:31,430 --> 00:15:34,300 +from your friends +over audio or video. + +343 +00:15:34,300 --> 00:15:36,636 +All right, so we've started +a SharePlay activity + +344 +00:15:36,636 --> 00:15:37,804 +with our friends. + +345 +00:15:37,804 --> 00:15:40,006 +We've made sure +everyone has joined. + +346 +00:15:40,006 --> 00:15:43,309 +Now, the focus is on +the activity itself. + +347 +00:15:43,309 --> 00:15:46,379 +You might have a fully-immersive +experience that supports + +348 +00:15:46,379 --> 00:15:50,449 +full-screen viewing and might be +hiding the status bar. + +349 +00:15:50,449 --> 00:15:54,187 +For example, the Apple TV app +hides the status bar + +350 +00:15:54,187 --> 00:15:55,755 +and other interface elements + +351 +00:15:55,755 --> 00:15:58,324 +when people are +watching a video + +352 +00:15:58,324 --> 00:16:02,528 +but… it is important to remember +that the pill in the status bar + +353 +00:16:02,528 --> 00:16:06,599 +gives people access +to important controls. + +354 +00:16:06,599 --> 00:16:08,768 +So, let people redisplay +a hidden status bar + +355 +00:16:08,768 --> 00:16:12,038 +using a simple, discoverable +gesture to give them access + +356 +00:16:12,038 --> 00:16:15,708 +to these types of controls +during the ongoing activity. + +357 +00:16:15,708 --> 00:16:19,846 +In the Apple TV app, a single +tap shows the status bar again + +358 +00:16:19,846 --> 00:16:24,851 +and reveals important +experience controls in the UI. + +359 +00:16:24,851 --> 00:16:26,519 +It is also important to remember + +360 +00:16:26,519 --> 00:16:28,387 +that the activity +in your app's interface + +361 +00:16:28,387 --> 00:16:31,390 +is only one part +of the whole experience. + +362 +00:16:31,390 --> 00:16:33,159 +Think about how the people +using your app + +363 +00:16:33,159 --> 00:16:34,927 +will engage with +the rest of the group + +364 +00:16:34,927 --> 00:16:37,129 +during the SharePlay +experience you've built. + +365 +00:16:37,129 --> 00:16:40,666 +Whether they are on a FaceTime +call or texting over Messages, + +366 +00:16:40,666 --> 00:16:42,535 +people are multitasking + +367 +00:16:42,535 --> 00:16:45,338 +so there may be instances +when someone navigates away + +368 +00:16:45,338 --> 00:16:48,674 +from your app +while in a group activity. + +369 +00:16:48,674 --> 00:16:51,978 +For cases like this, remember +to support Picture in Picture + +370 +00:16:51,978 --> 00:16:54,347 +for coordinated +media experiences. + +371 +00:16:54,347 --> 00:16:56,649 +This will allow everyone +to continue watching + +372 +00:16:56,649 --> 00:16:57,884 +with their friends, + +373 +00:16:57,884 --> 00:17:01,954 +maintaining a sense of presence +with the rest of the group. + +374 +00:17:01,954 --> 00:17:04,123 +Whether someone is +responding to a message + +375 +00:17:04,123 --> 00:17:06,392 +or connecting with the group +over FaceTime, + +376 +00:17:06,392 --> 00:17:09,428 +the video doesn't have +to pause every single time. + +377 +00:17:09,428 --> 00:17:12,131 +Considerations like these +when building group activities + +378 +00:17:12,131 --> 00:17:13,532 +for your app +will help make + +379 +00:17:13,532 --> 00:17:16,402 +the end-to-end SharePlay +experience seamless, + +380 +00:17:16,402 --> 00:17:18,104 +breaking down +the digital barriers + +381 +00:17:18,104 --> 00:17:20,940 +to create meaningful moments +where people feel present + +382 +00:17:20,940 --> 00:17:22,875 +with each other. + +383 +00:17:22,875 --> 00:17:26,078 +So make sure to register +your group activity + +384 +00:17:26,078 --> 00:17:29,782 +so people can start to SharePlay +directly from your app; + +385 +00:17:29,782 --> 00:17:33,452 +configure descriptive metadata +so people know what to expect + +386 +00:17:33,452 --> 00:17:35,288 +when they join the activity; + +387 +00:17:35,288 --> 00:17:37,623 +have an experience +for late joiners, + +388 +00:17:37,623 --> 00:17:39,558 +and provide contextual +information + +389 +00:17:39,558 --> 00:17:42,395 +about why the UI +might be changing; + +390 +00:17:42,395 --> 00:17:45,064 +make sure controls +are easy to access; + +391 +00:17:45,064 --> 00:17:48,601 +and support Picture in Picture +for media. + +392 +00:17:48,601 --> 00:17:52,638 +We've talked about a lot today, +so let's do a quick recap. + +393 +00:17:52,638 --> 00:17:55,675 +Now, you can start a SharePlay +session directly from your app + +394 +00:17:55,675 --> 00:17:58,311 +without the need +for an ongoing FaceTime call. + +395 +00:17:58,311 --> 00:17:59,812 +This expands the opportunities + +396 +00:17:59,812 --> 00:18:02,581 +for building a variety +of group activities. + +397 +00:18:02,581 --> 00:18:04,684 +So we recommend +that you do the portal exercise + +398 +00:18:04,684 --> 00:18:07,253 +to start thinking about +how to design an experience + +399 +00:18:07,253 --> 00:18:09,221 +for your app +that helps people feel + +400 +00:18:09,221 --> 00:18:11,724 +like they're in +the same space together; + +401 +00:18:11,724 --> 00:18:14,360 +reimagine your app's +content and activities + +402 +00:18:14,360 --> 00:18:17,430 +to create great experiences +for groups; + +403 +00:18:17,430 --> 00:18:20,199 +and use best practices +when building your experience + +404 +00:18:20,199 --> 00:18:23,369 +to prevent digital barriers +from getting in the way. + +405 +00:18:23,369 --> 00:18:25,604 +We cannot wait to see +the creative uses of SharePlay + +406 +00:18:25,604 --> 00:18:26,939 +that you all come up with. + +407 +00:18:26,939 --> 00:18:28,641 +We hope you enjoyed the session. + +408 +00:18:28,641 --> 00:18:30,242 +Adam and Priya: +Thanks for watching! + +409 +00:18:30,242 --> 00:18:34,513 +♪ + diff --git a/eng/2022 Session 10140 What's new in SharePlay en.srt b/eng/2022 Session 10140 What's new in SharePlay en.srt new file mode 100644 index 0000000..aedee88 --- /dev/null +++ b/eng/2022 Session 10140 What's new in SharePlay en.srt @@ -0,0 +1,1236 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,877 --> 00:00:13,747 +Adam: Hi, my name's Adam +and I'm an engineer on the SharePlay team. + +3 +00:00:13,780 --> 00:00:16,316 +I'm excited to talk to you +about what's new in SharePlay + +4 +00:00:16,350 --> 00:00:18,485 +and how you can adopt it in your app. + +5 +00:00:18,519 --> 00:00:24,091 +To start, we'll talk about some new APIs +for starting SharePlay from your app. + +6 +00:00:24,124 --> 00:00:28,595 +Next, we'll get into some exciting +GroupSessionMessenger updates. + +7 +00:00:28,629 --> 00:00:34,201 +Finally, some best practices around +implementing a SharePlay experience. + +8 +00:00:34,234 --> 00:00:36,303 +Starting SharePlay from your app. + +9 +00:00:36,336 --> 00:00:38,805 +We heard your feedback and we delivered, + +10 +00:00:38,839 --> 00:00:44,211 +As of iOS 15.4 you can now leverage new +API to allow your app to start SharePlay + +11 +00:00:44,244 --> 00:00:46,813 +without an existing FaceTime call. + +12 +00:00:46,847 --> 00:00:48,782 +So let's see what that looks like. + +13 +00:00:48,815 --> 00:00:52,386 +So now all we have to do +is find our favorite SharePlay app. + +14 +00:00:52,419 --> 00:00:54,621 +Let's say the Music app. + +15 +00:00:54,655 --> 00:00:56,723 +And we'll find a song that +we want to SharePlay, + +16 +00:00:56,757 --> 00:01:00,227 +like Viral Hits, +and press and hold it. + +17 +00:01:00,260 --> 00:01:01,728 +Now in the contextual menu, + +18 +00:01:01,762 --> 00:01:03,730 +you'll see that we now have +a new SharePlay button. + +19 +00:01:03,764 --> 00:01:05,899 +So I'll go ahead and press that, + +20 +00:01:05,933 --> 00:01:07,634 +and it brings up the people picker. + +21 +00:01:07,668 --> 00:01:11,238 +So we can select Sue +and start a FaceTime call. + +22 +00:01:13,507 --> 00:01:16,076 +And, as you can see, +we now have a pill here + +23 +00:01:16,109 --> 00:01:17,511 +with the staged activity. + +24 +00:01:17,544 --> 00:01:20,480 +So if Sue joins, +we can go ahead and start, + +25 +00:01:20,514 --> 00:01:22,216 +and we'll have group session. + +26 +00:01:24,151 --> 00:01:26,520 +Well, I thought that was all pretty cool, + +27 +00:01:26,553 --> 00:01:29,189 +but let's break that down +into some more detail. + +28 +00:01:30,257 --> 00:01:34,094 +Here we have the ability for users +to start SharePlay from share sheet, + +29 +00:01:34,127 --> 00:01:37,898 +and you may be wondering +what you need to do for this to work. + +30 +00:01:37,931 --> 00:01:41,602 +Well, the answer is that +if your app is entitled for SharePlay + +31 +00:01:41,635 --> 00:01:44,972 +then you get this button for free +with our zero adoption flow, + +32 +00:01:45,005 --> 00:01:47,774 +but this isn't the optimal user experience + +33 +00:01:47,808 --> 00:01:52,312 +since the user won't be able to start +the GroupActivity through system UI + +34 +00:01:52,346 --> 00:01:55,449 +and will, instead, +need to re-interact with your app + +35 +00:01:55,482 --> 00:01:58,352 +to pick the content to SharePlay. + +36 +00:01:58,385 --> 00:02:03,023 +So let's see how you'd adopt +our new APIs for your app. + +37 +00:02:03,056 --> 00:02:06,360 +The answer is, it's as simple as +registering your GroupActivity + +38 +00:02:06,393 --> 00:02:08,061 +on NSItemProvider + +39 +00:02:08,095 --> 00:02:11,698 +and then providing the ItemProvider +to the share sheet. + +40 +00:02:13,500 --> 00:02:15,202 +Want to still offer the SharePlay button + +41 +00:02:15,235 --> 00:02:17,204 +but not display it as prominently? + +42 +00:02:17,237 --> 00:02:18,539 +No problem. + +43 +00:02:18,572 --> 00:02:21,441 +You can tune the behavior +with allowsProminentActivity + +44 +00:02:21,475 --> 00:02:23,911 +on the UIActivityViewController. + +45 +00:02:23,944 --> 00:02:26,747 +Just set allowsProminentActivity +to false. + +46 +00:02:27,981 --> 00:02:31,785 +Or what if you have a piece of content in +your app that doesn't support SharePlay? + +47 +00:02:31,818 --> 00:02:35,022 +Well, while we'd love for everything +to support SharePlay, + +48 +00:02:35,055 --> 00:02:37,791 +you can make SharePlay not show up +in the share sheet + +49 +00:02:37,824 --> 00:02:42,229 +by telling UIActivityViewController +to exclude the SharePlay activity type. + +50 +00:02:44,164 --> 00:02:46,733 +And if you want to place a button +directly within your app, + +51 +00:02:46,767 --> 00:02:50,304 +then you can use our new API +GroupActivitySharingController + +52 +00:02:50,337 --> 00:02:54,208 +to create our UIViewController +and then, just present it! + +53 +00:02:55,909 --> 00:02:58,011 +Once someone +presses your in-app experience + +54 +00:02:58,045 --> 00:03:00,314 +and starts a FaceTime +or SharePlay session, + +55 +00:03:00,347 --> 00:03:05,252 +they'll then be presented with the ability +to activate the staged GroupActivity. + +56 +00:03:05,285 --> 00:03:08,355 +Once activated, +your app will receive the GroupSession. + +57 +00:03:08,388 --> 00:03:10,591 +And don't worry if you're saying +to yourself "Wait, Adam, + +58 +00:03:10,624 --> 00:03:12,726 +did you just say 'Staged GroupActivity'?" + +59 +00:03:12,759 --> 00:03:14,628 +Why, yes. Yes, I did! + +60 +00:03:14,661 --> 00:03:17,898 +But let's hold onto that thought +and dive deeper into that later + +61 +00:03:17,931 --> 00:03:20,367 +when we're talking about best practices. + +62 +00:03:20,400 --> 00:03:24,605 +For now, let's see how we can adopt this +in our DrawTogether app. + +63 +00:03:25,706 --> 00:03:27,274 +This is our DrawTogether app. + +64 +00:03:27,307 --> 00:03:31,211 +It's the same app +from our previous WWDC talk from 2021 + +65 +00:03:31,245 --> 00:03:34,014 +"Build custom experiences +with Group Activities," + +66 +00:03:34,047 --> 00:03:39,219 +so if you haven't seen it already, +I highly recommend checking it out. + +67 +00:03:39,253 --> 00:03:41,321 +Now that you've gone ahead and seen that, + +68 +00:03:41,355 --> 00:03:44,124 +you'll remember there weren't +any share buttons in our app, + +69 +00:03:44,157 --> 00:03:46,159 +but we did have a SharePlay button + +70 +00:03:46,193 --> 00:03:48,762 +when you were eligible for +a GroupSession. + +71 +00:03:48,795 --> 00:03:51,098 +Let's go ahead and modify that behavior + +72 +00:03:51,131 --> 00:03:54,268 +so that even when +isEligibleForGroupSession is false, + +73 +00:03:54,301 --> 00:03:58,539 +we show the button and, now, allow +the user to start a SharePlay session. + +74 +00:04:00,040 --> 00:04:02,876 +And now we can go ahead +and see it in action. + +75 +00:04:02,910 --> 00:04:05,712 +Let's go ahead +and go to our ControlBar code. + +76 +00:04:05,746 --> 00:04:07,848 +Now, as you can see here, +we have an ‘if’ statement + +77 +00:04:07,881 --> 00:04:10,050 +that makes sure +that we don't have a group session + +78 +00:04:10,083 --> 00:04:13,053 +and that we're eligible +for a group session. + +79 +00:04:13,086 --> 00:04:16,990 +So let's go ahead +and remove the latter statement, + +80 +00:04:17,024 --> 00:04:18,859 +and move it down here. + +81 +00:04:24,865 --> 00:04:27,234 +And now what we have to do +is register a new variable + +82 +00:04:27,267 --> 00:04:30,404 +so that we know when to present +our GroupActivity sharing controller. + +83 +00:04:30,437 --> 00:04:34,942 +So we'll have a new variable up here, + +84 +00:04:34,975 --> 00:04:39,513 +and now let's handle when that variable +changes to true. + +85 +00:04:43,150 --> 00:04:45,452 +And we have to have a wrapper now + +86 +00:04:45,485 --> 00:04:49,389 +so we can present the GroupActivity +sharing controller in SwiftUI. + +87 +00:04:55,562 --> 00:04:59,700 +And now, finally, all we have to do +is have an 'else' statement + +88 +00:04:59,733 --> 00:05:02,603 +to set isSharingControllerPresented +to true + +89 +00:05:02,636 --> 00:05:05,005 +if we're not eligible for a GroupSession. + +90 +00:05:08,008 --> 00:05:10,177 +And now we can see our code in action. + +91 +00:05:10,210 --> 00:05:12,546 +So we'll go ahead +and go to the DrawTogether app., + +92 +00:05:12,579 --> 00:05:14,681 +and you can see, +we have our SharePlay button. + +93 +00:05:14,715 --> 00:05:17,751 +So we can now press it, +and we're given the people picker. + +94 +00:05:19,920 --> 00:05:24,157 +And now we've got a great experience +for starting SharePlay from your app. + +95 +00:05:24,191 --> 00:05:26,660 +But that's not the only update we made. + +96 +00:05:26,693 --> 00:05:30,864 +So now let's talk about some of the +GroupSessionMessenger updates we've made. + +97 +00:05:30,898 --> 00:05:34,768 +We've got two exciting new updates +in our GroupSessionMessenger. + +98 +00:05:34,801 --> 00:05:38,839 +For the first update, +you may have run into this magical number. + +99 +00:05:38,872 --> 00:05:43,310 +It's the payload size you're able to send +over the GroupSessionMessenger. + +100 +00:05:43,343 --> 00:05:45,646 +Well, not anymore. + +101 +00:05:45,679 --> 00:05:51,885 +We've now made it so that the payload size +is four times larger at 256KB. + +102 +00:05:51,919 --> 00:05:53,887 +With this change, +your app doesn't need to worry + +103 +00:05:53,921 --> 00:05:57,024 +about breaking up your message +into smaller messages. + +104 +00:05:57,057 --> 00:06:01,795 +You can simply send your message +and focus on building a great experience. + +105 +00:06:01,828 --> 00:06:03,497 +And if that didn't excite you enough, + +106 +00:06:03,530 --> 00:06:06,066 +then I'm sure our next update will. + +107 +00:06:06,099 --> 00:06:08,202 +Unreliable messaging. + +108 +00:06:08,235 --> 00:06:10,003 +As part of the GroupSessionMessenger, + +109 +00:06:10,037 --> 00:06:13,006 +you can now choose +your messages' reliability. + +110 +00:06:13,040 --> 00:06:16,810 +This allows you to choose +between reliable or unreliable messaging + +111 +00:06:16,844 --> 00:06:19,513 +depending on your desired experience. + +112 +00:06:20,948 --> 00:06:23,617 +All we have to do +is leverage the new initializer + +113 +00:06:23,650 --> 00:06:25,085 +on GroupSessionMessenger + +114 +00:06:25,118 --> 00:06:28,488 +that allows us to specify +the MessageReliability. + +115 +00:06:30,524 --> 00:06:32,960 +Now that we understand how to use the API, + +116 +00:06:32,993 --> 00:06:35,429 +what about the experience? + +117 +00:06:35,462 --> 00:06:38,398 +When would we want to use +unreliable messaging? + +118 +00:06:38,432 --> 00:06:40,634 +Well, that's a great question + +119 +00:06:41,802 --> 00:06:46,240 +People are performing real-time actions +on FaceTime and SharePlay. + +120 +00:06:46,273 --> 00:06:48,909 +So let's imagine that we have +three people in a session. + +121 +00:06:48,942 --> 00:06:51,912 +Amy, Brian, and Chris. + +122 +00:06:51,945 --> 00:06:54,348 +They're all joined into a session +and synced + +123 +00:06:54,381 --> 00:06:58,485 +so as time progresses so does our movie. + +124 +00:06:58,519 --> 00:07:00,787 +But what happens +if Amy wants to do something + +125 +00:07:00,821 --> 00:07:04,925 +relevant to the specific time +that they're at in that moment? + +126 +00:07:04,958 --> 00:07:07,094 +Well, if you use reliable messaging, + +127 +00:07:07,127 --> 00:07:11,131 +then we guarantee that messages +will be received on all the devices, + +128 +00:07:11,164 --> 00:07:13,333 +but that doesn't mean +that they'll be received + +129 +00:07:13,367 --> 00:07:16,003 +at the time that they're expecting it. + +130 +00:07:16,036 --> 00:07:18,572 +For example, +Chris received the message, + +131 +00:07:18,605 --> 00:07:21,041 +but Brian has the message dropped +the first time + +132 +00:07:21,074 --> 00:07:23,377 +and receives it properly after that. + +133 +00:07:23,410 --> 00:07:26,947 +But remember, the movie is still playing. + +134 +00:07:26,980 --> 00:07:30,484 +So now we get to where Amy intended +the message to be reflected + +135 +00:07:30,517 --> 00:07:32,085 +and Brian doesn't have it. + +136 +00:07:32,119 --> 00:07:35,756 +He receives it later, +but at that point, it's too late. + +137 +00:07:35,789 --> 00:07:39,493 +Well, this is a perfect case +for unreliable networking. + +138 +00:07:39,526 --> 00:07:42,429 +It allows you, the developer, +to know what information + +139 +00:07:42,462 --> 00:07:44,965 +needs to be reliably received +on the other side + +140 +00:07:44,998 --> 00:07:46,733 +and what information doesn't. + +141 +00:07:46,767 --> 00:07:50,204 +This is an important concept to understand +when designing protocols + +142 +00:07:50,237 --> 00:07:53,707 +that have the user experience +deeply affected by latency. + +143 +00:07:53,740 --> 00:07:56,043 +Unreliable messages are using UDP + +144 +00:07:56,076 --> 00:07:59,713 +and have less latency and overhead +with each message involved + +145 +00:07:59,746 --> 00:08:02,616 +and, as a result of that, +you'll have a more real-time experience + +146 +00:08:02,649 --> 00:08:05,185 +when sending messages through them. + +147 +00:08:05,219 --> 00:08:08,889 +So now let's talk about how we're going +to use this for our DrawTogether app + +148 +00:08:08,922 --> 00:08:12,693 +You may remember this screen +from WWDC '21, + +149 +00:08:12,726 --> 00:08:15,963 +especially with my beautifully drawn +smiley face. + +150 +00:08:15,996 --> 00:08:18,632 +Let's dive a bit into what happens +when you're drawing + +151 +00:08:18,665 --> 00:08:20,534 +your smiley face on the screen. + +152 +00:08:22,769 --> 00:08:26,673 +In our app we have some code +that listens to a GestureRecognizer + +153 +00:08:26,707 --> 00:08:30,777 +and then we sent messages +each time we noticed a change. + +154 +00:08:30,811 --> 00:08:33,647 +This meant that as we were drawing +our smiley face, + +155 +00:08:33,680 --> 00:08:35,682 +we were constantly sending new messages + +156 +00:08:35,716 --> 00:08:38,719 +for each point +our GestureRecognizer gives us. + +157 +00:08:38,752 --> 00:08:40,787 +That's a lot of messages! + +158 +00:08:40,821 --> 00:08:44,491 +Well, we can now change our protocol +to use unreliable messaging + +159 +00:08:44,525 --> 00:08:47,027 +to make a more seamless +drawing experience. + +160 +00:08:48,562 --> 00:08:51,698 +What we'll do here is make it so that +each time we receive an update + +161 +00:08:51,732 --> 00:08:53,133 +from our GestureRecognizer, + +162 +00:08:53,166 --> 00:08:57,504 +we'll send our newly added point +using unreliable messaging. + +163 +00:08:57,538 --> 00:09:00,674 +Once the gesture is complete, +we'll then use reliable messaging + +164 +00:09:00,707 --> 00:09:05,779 +and give all of the points so a client can +catch up with any points that they missed. + +165 +00:09:05,812 --> 00:09:08,482 +This allows us to take advantage +of the lower latency + +166 +00:09:08,515 --> 00:09:13,353 +provided by unreliable messaging to have +a more immediate drawing experience. + +167 +00:09:13,387 --> 00:09:16,089 +So let's see how we would do this +in code. + +168 +00:09:16,123 --> 00:09:19,059 +So first let's go to our messages file. + +169 +00:09:20,427 --> 00:09:24,331 +And we'll go ahead and define +our new message type. + +170 +00:09:25,132 --> 00:09:28,302 +As you can see, this new message type +is pretty much the same as our old one, + +171 +00:09:28,335 --> 00:09:32,573 +but this time, will contain all +of the points for our stroke. + +172 +00:09:32,606 --> 00:09:35,609 +Now we'll go over to our canvas file. + +173 +00:09:40,514 --> 00:09:42,683 +And we need to set up a handler function + +174 +00:09:42,716 --> 00:09:45,385 +to handle the new message +that we're gonna get. + +175 +00:09:50,791 --> 00:09:54,294 +And let's go ahead and create +our unreliable messenger. + +176 +00:09:54,328 --> 00:09:56,897 +First, we'll create a variable. + +177 +00:09:59,833 --> 00:10:02,102 +And now, let's just initialize it. + +178 +00:10:06,006 --> 00:10:08,742 +Now we'll listen +for the finished stroke message. + +179 +00:10:12,846 --> 00:10:16,650 +And mark the previous message +as unreliable messenger as well. + +180 +00:10:19,720 --> 00:10:21,522 +But we need a way to send the message. + +181 +00:10:21,555 --> 00:10:24,291 +So we'll go up to finishedStroke. + +182 +00:10:26,360 --> 00:10:29,296 +And we'll go ahead and send +our new message type. + +183 +00:10:31,365 --> 00:10:34,635 +And let's change our old function +for sending all of the points + +184 +00:10:34,668 --> 00:10:37,104 +to use the unreliable messenger. + +185 +00:10:39,039 --> 00:10:41,575 +And now we can see our code in action. + +186 +00:10:41,608 --> 00:10:43,744 +So we go over to the DrawTogether app + +187 +00:10:43,777 --> 00:10:47,080 +and we can see how seamless it is. + +188 +00:10:47,114 --> 00:10:48,415 +And that's it! + +189 +00:10:49,816 --> 00:10:53,320 +And now, as promised, +let's talk about some best practices + +190 +00:10:53,353 --> 00:10:55,956 +for your SharePlay implementation. + +191 +00:10:55,989 --> 00:10:58,425 +You may remember this term from earlier: + +192 +00:10:58,458 --> 00:11:00,460 +Staged GroupActivity. + +193 +00:11:00,494 --> 00:11:02,930 +What does that term mean for your app? + +194 +00:11:02,963 --> 00:11:05,132 +Well, let's talk through a scenario. + +195 +00:11:07,100 --> 00:11:09,636 +Let's say that the device +on our left, "Adam", + +196 +00:11:09,670 --> 00:11:12,806 +starts SharePlay +with the device on our right, "Brian". + +197 +00:11:12,840 --> 00:11:15,475 +But Adam is trying to resume the show +they were watching. + +198 +00:11:15,509 --> 00:11:18,078 +So when someone activates +the staged GroupActivity, + +199 +00:11:18,111 --> 00:11:21,982 +we want to jump into that resumed show +at a specific time, + +200 +00:11:22,015 --> 00:11:24,117 +rather than starting over. + +201 +00:11:24,151 --> 00:11:27,855 +This poses a problem because +"Adam" knew we had 11 minutes remaining + +202 +00:11:27,888 --> 00:11:31,024 +in the show, +but Brian's device didn't. + +203 +00:11:31,058 --> 00:11:35,295 +This means that if Brian's device +activated the staged GroupActivity, + +204 +00:11:35,329 --> 00:11:37,631 +the show may start over. + +205 +00:11:37,664 --> 00:11:39,366 +So what can we do here? + +206 +00:11:39,399 --> 00:11:42,202 +It really depends on your app +and experience. + +207 +00:11:43,203 --> 00:11:45,305 +So let's walk through some ideas. + +208 +00:11:45,339 --> 00:11:46,640 +For the playback case, + +209 +00:11:46,673 --> 00:11:49,977 +we'll want to have each device +contribute its initial playback state + +210 +00:11:50,010 --> 00:11:52,613 +to the others in catch-up. + +211 +00:11:52,646 --> 00:11:57,317 +This means that since Adam's device +knew the playback state was 23 seconds, + +212 +00:11:57,351 --> 00:12:00,087 +when he joins the session, +he'll tell all the other devices + +213 +00:12:00,120 --> 00:12:02,222 +his intended playback state, + +214 +00:12:02,256 --> 00:12:05,359 +and they'll use that +as the source of truth. + +215 +00:12:05,392 --> 00:12:09,830 +This same principle applies to any +experience you create using SharePlay. + +216 +00:12:09,863 --> 00:12:12,499 +Each person that joins a session +should contribute + +217 +00:12:12,533 --> 00:12:15,602 +their understanding of the session +to the others. + +218 +00:12:15,636 --> 00:12:18,805 +This is because sessions are peer-to-peer +and ownerless. + +219 +00:12:18,839 --> 00:12:21,375 +So let's talk a bit more about that. + +220 +00:12:21,408 --> 00:12:24,711 +Ownerless sessions +are a hard concept to grasp, + +221 +00:12:24,745 --> 00:12:29,583 +but they're important when +designing a proper SharePlay experience. + +222 +00:12:29,616 --> 00:12:31,752 +In this case, Adam, on the left, + +223 +00:12:31,785 --> 00:12:34,721 +wants to hand off his session +to his Apple TV. + +224 +00:12:34,755 --> 00:12:39,860 +This results in his phone dropping off +the GroupSession and his TV joining. + +225 +00:12:39,893 --> 00:12:42,429 +But what happens +if we had ownership implemented? + +226 +00:12:42,462 --> 00:12:46,600 +Well, the owner dropped off, so... + +227 +00:12:46,633 --> 00:12:49,636 +And remember, this isn't just for TVs. + +228 +00:12:50,771 --> 00:12:53,874 +In iOS 16 we now have +FaceTime handoff. + +229 +00:12:53,907 --> 00:12:56,810 +So Adam goes ahead and hands off his iPad, + +230 +00:12:56,844 --> 00:12:59,046 +and well, same thing. + +231 +00:12:59,079 --> 00:13:00,180 +Boom + +232 +00:13:00,214 --> 00:13:01,682 +And that isn't all. + +233 +00:13:01,715 --> 00:13:04,418 +We just talked about some examples +of a user flow + +234 +00:13:04,451 --> 00:13:08,322 +where someone tries to move the session +from one device to another, + +235 +00:13:08,355 --> 00:13:10,891 +but there's other cases to think about. + +236 +00:13:10,924 --> 00:13:14,895 +Okay, don't worry, we'll keep it short +with just one more example. + +237 +00:13:14,928 --> 00:13:17,664 +This screen may seem a little familiar. + +238 +00:13:17,698 --> 00:13:19,366 +It's the FaceTime HUD. + +239 +00:13:19,399 --> 00:13:22,202 +But what happens +if we click the SharePlay button? + +240 +00:13:23,437 --> 00:13:25,973 +We're now presented with a button, +End SharePlay, + +241 +00:13:26,006 --> 00:13:30,410 +that allows you, you guessed it, +end SharePlay. + +242 +00:13:30,444 --> 00:13:32,779 +This allows you +to end SharePlay for everyone, + +243 +00:13:32,813 --> 00:13:35,549 +essentially the system calling .end() +on the GroupSession + +244 +00:13:35,582 --> 00:13:38,018 +on your application's behalf. + +245 +00:13:38,051 --> 00:13:41,288 +This means that no matter how careful +you are about not calling .end() + +246 +00:13:41,321 --> 00:13:44,691 +unless that device is the owner, +the system is still able to call .end() + +247 +00:13:44,725 --> 00:13:47,895 +on the GroupSession on your behalf. + +248 +00:13:47,928 --> 00:13:50,931 +So remember, +while it may be a hard concept to grasp, + +249 +00:13:50,964 --> 00:13:54,301 +making sure that your application +doesn't have a sense of ownership + +250 +00:13:54,334 --> 00:13:58,672 +means that it'll, overall, +result in a much better experience. + +251 +00:13:58,705 --> 00:14:00,507 +Now that you've listened +to the whole session, + +252 +00:14:00,541 --> 00:14:04,444 +go and adopt our new APIs for starting +SharePlay from within your app, + +253 +00:14:04,478 --> 00:14:06,847 +and explore ways for your app +to communicate + +254 +00:14:06,880 --> 00:14:10,717 +in new, low latency, ways +using unreliable messaging. + +255 +00:14:11,952 --> 00:14:14,955 +We love hearing from you all, +so please continue to file feedback + +256 +00:14:14,988 --> 00:14:16,690 +using the Feedback Assistant. + +257 +00:14:16,723 --> 00:14:19,426 +I hope you enjoyed +all the changes that we've made + +258 +00:14:19,459 --> 00:14:22,829 +and we look forward to seeing all +the amazing experiences that you build. + +259 +00:14:22,863 --> 00:14:25,999 +If you haven't already, +check out our other WWDC talk, + +260 +00:14:26,033 --> 00:14:28,635 +"Make a great SharePlay experience". + +261 +00:14:28,669 --> 00:14:31,471 +Or, if you're looking for +some amazing enhancements we've made + +262 +00:14:31,505 --> 00:14:32,873 +around media playback, + +263 +00:14:32,906 --> 00:14:36,777 +check out "Display ads +and other interstitials in SharePlay". + +264 +00:14:36,810 --> 00:14:39,980 +If you have any questions, please find us +at the GroupActivities labs + +265 +00:14:40,013 --> 00:14:41,315 +and challenges. + +266 +00:14:41,348 --> 00:14:44,985 +As always, thank you all for tuning in, +and have a great WWDC. + +267 +00:14:45,018 --> 00:14:46,987 +We can't wait to see what you build. + diff --git a/eng/2022 Session 10141 Explore USD tools and rendering en.srt b/eng/2022 Session 10141 Explore USD tools and rendering en.srt new file mode 100644 index 0000000..aad371c --- /dev/null +++ b/eng/2022 Session 10141 Explore USD tools and rendering en.srt @@ -0,0 +1,1499 @@ +1 +00:00:01,335 --> 00:00:07,341 +♪ instrumental hip hop music ♪ + +2 +00:00:09,910 --> 00:00:13,714 +Hi there. +Welcome to this year's WWDC. + +3 +00:00:13,747 --> 00:00:15,916 +My name is Stella. + +4 +00:00:15,949 --> 00:00:16,984 +And I'm Alex. + +5 +00:00:17,017 --> 00:00:20,420 +Stella and I work together +on the many parts of USD at Apple. + +6 +00:00:20,454 --> 00:00:23,891 +Today we'll explore with you +updates to USD tools and rendering. + +7 +00:00:23,924 --> 00:00:24,992 +Take it away, Stella! + +8 +00:00:27,160 --> 00:00:29,530 +USD is a foundational technology + +9 +00:00:29,563 --> 00:00:34,835 +that, with the growing and deepening +integration into content creation tools, + +10 +00:00:34,868 --> 00:00:40,641 +is enabling more and more ways +of creating assets and content, + +11 +00:00:40,674 --> 00:00:45,045 +rendering for games, AR, film, +and the web, + +12 +00:00:45,078 --> 00:00:47,147 +all with USD at the center. + +13 +00:00:48,015 --> 00:00:53,787 +Today, we will focus on two parts +of the ecosystem: tools and rendering. + +14 +00:00:54,688 --> 00:00:58,325 +Let's start with updates to our USD tools. + +15 +00:00:59,693 --> 00:01:02,930 +We'll then show you how your assets +look even better + +16 +00:01:02,963 --> 00:01:07,134 +with new lighting in AR Quick Look, + +17 +00:01:07,167 --> 00:01:12,072 +take a deep dive into USD rendering, + +18 +00:01:12,105 --> 00:01:16,476 +and last but not least, +show you how to integrate Hydra. + +19 +00:01:19,580 --> 00:01:23,450 +Let's get started with USD Tools +in the Apple ecosystem. + +20 +00:01:24,818 --> 00:01:28,922 +We'll cover USDZ Tools, +Reality Converter, + +21 +00:01:28,956 --> 00:01:33,427 +and then follow up +with additional tools and frameworks + +22 +00:01:33,460 --> 00:01:35,095 +that can help you create USDZ content. + +23 +00:01:37,965 --> 00:01:40,167 +USDZ Tools is a package + +24 +00:01:40,200 --> 00:01:43,971 +that contains essential +command line USD Python tools + +25 +00:01:44,004 --> 00:01:50,477 +to help you generate, +validate, and inspect USDZ files. + +26 +00:01:50,511 --> 00:01:55,182 +The package also includes a converter +called usdzconvert + +27 +00:01:55,215 --> 00:02:00,053 +that creates USDZ files +from other major 3D file formats. + +28 +00:02:01,121 --> 00:02:07,227 +The Python scripts give you powerful tools +for automation and batch processing. + +29 +00:02:07,261 --> 00:02:11,698 +Also, they are a great way +for you to explore USD and learn + +30 +00:02:11,732 --> 00:02:13,333 +how to use the API. + +31 +00:02:14,268 --> 00:02:19,673 +This year, we're bringing you +Python 3 and Apple Silicon support. + +32 +00:02:19,706 --> 00:02:23,977 +We've also upgraded the USD version +to give you simple access + +33 +00:02:24,011 --> 00:02:27,681 +to new USD features +and performance improvements. + +34 +00:02:30,117 --> 00:02:35,055 +On top of that, we added +great new features to usdzconvert. + +35 +00:02:35,088 --> 00:02:39,826 +You can now convert OBJ files +with materials to USDZ + +36 +00:02:39,860 --> 00:02:44,298 +with the useObjMtl flag. + +37 +00:02:44,331 --> 00:02:50,304 +We also added support +for points and lines from GLTF files, + +38 +00:02:50,337 --> 00:02:55,776 +and scene time +for animations from FBX files. + +39 +00:02:55,809 --> 00:03:01,882 +To display all usdzconvert options +and ways to customize your conversion, + +40 +00:03:01,915 --> 00:03:06,320 +just type "usdzconvert --help" +on the terminal. + +41 +00:03:08,388 --> 00:03:13,193 +This will show you all the options +you can use with usdzconvert, + +42 +00:03:13,227 --> 00:03:14,394 +such as adding copyright information + +43 +00:03:16,897 --> 00:03:23,103 +or providing the diffuseColor +or normalMap for your 3D model, + +44 +00:03:23,136 --> 00:03:25,272 +and much more. + +45 +00:03:25,305 --> 00:03:28,475 +Alternatively, if you prefer a UI editor +for your workflow + +46 +00:03:29,910 --> 00:03:32,145 +over using the command line, + +47 +00:03:32,179 --> 00:03:38,652 +we also have Reality Converter, +which is built using the USDZ tools + +48 +00:03:38,685 --> 00:03:43,390 +and provides the same capabilities +as usdzconvert, + +49 +00:03:43,423 --> 00:03:46,293 +but in an editor window, + +50 +00:03:46,326 --> 00:03:52,633 +making it easy to convert, view, +and customize USD content on MacOS. + +51 +00:03:53,634 --> 00:03:56,637 +Simply import common 3D file formats, + +52 +00:03:56,670 --> 00:04:00,841 +such as OBJ, GLTF, and FBX, + +53 +00:04:00,874 --> 00:04:05,245 +to view the converted USDZ result. + +54 +00:04:05,279 --> 00:04:09,983 +This year, we've also improved the UI +to streamline your workflow. + +55 +00:04:10,817 --> 00:04:14,221 +You can select the texture to view +more information... + +56 +00:04:19,760 --> 00:04:23,564 +Customize material properties +with your own textures... + +57 +00:04:31,939 --> 00:04:35,509 +Add copyright or edit file metadata... + +58 +00:04:41,915 --> 00:04:47,454 +And choose classic or new lighting, +which we'll get into more details + +59 +00:04:47,487 --> 00:04:48,856 +later in the session. + +60 +00:04:50,791 --> 00:04:54,127 +You can even preview +your USDZ object + +61 +00:04:54,161 --> 00:04:59,166 +under a variety of lighting conditions +with built-in options + +62 +00:04:59,199 --> 00:05:00,501 +and adjust exposure accordingly + +63 +00:05:02,870 --> 00:05:06,206 +On the asset side, we added a new feature + +64 +00:05:06,240 --> 00:05:12,045 +to let you choose texture quality +while exporting a USDZ file. + +65 +00:05:12,079 --> 00:05:17,818 +By default, the textures are exported +in their original, uncompressed size. + +66 +00:05:17,851 --> 00:05:22,956 +But, if you prefer to reduce the size +of your USDZ files, you now have + +67 +00:05:22,990 --> 00:05:26,927 +the option to compress textures +to the JPEG format. + +68 +00:05:29,096 --> 00:05:31,832 +In this example, we used object capture +to scan this chess piece + +69 +00:05:33,667 --> 00:05:35,702 +in high resolution. + +70 +00:05:35,736 --> 00:05:40,073 +In order to reduce the file size +without losing mesh detail, + +71 +00:05:40,107 --> 00:05:46,180 +we used RealityConverter to export +the model with compressed textures. + +72 +00:05:46,213 --> 00:05:49,416 +The visual difference +is hardly noticeable, + +73 +00:05:49,449 --> 00:05:54,354 +and we got a whopping 80% reduction +in the resulting file size! + +74 +00:05:56,290 --> 00:05:58,458 +Here is more exciting news: + +75 +00:05:58,492 --> 00:06:04,164 +RealityConverter can now fix issues +with your USD assets automatically! + +76 +00:06:04,198 --> 00:06:09,870 +It will correct mismatched attributes +and connection types, + +77 +00:06:09,903 --> 00:06:15,976 +fix a stage with multiple top-level prims +and a missing default prim, + +78 +00:06:16,009 --> 00:06:22,683 +update deprecated attributes, +and add missing stage metadata. + +79 +00:06:22,716 --> 00:06:29,189 +We've also added universal binary support +so now it runs natively on Apple Silicon. + +80 +00:06:30,924 --> 00:06:34,561 +Now, what if you want to create +3D models from scratch? + +81 +00:06:35,963 --> 00:06:41,969 +Last year, we launched Object Capture +as a RealityKit API on macOS, + +82 +00:06:42,002 --> 00:06:47,307 +which provides an innovative way +to create USDZ assets. + +83 +00:06:47,341 --> 00:06:52,713 +You can then use Reality Composer +to compose a scene with multiple assets. + +84 +00:06:52,746 --> 00:06:56,884 +This year, we are bringing you +RoomPlan API, + +85 +00:06:56,917 --> 00:07:00,921 +which lets you create parametric 3D scans +of your room. + +86 +00:07:01,889 --> 00:07:05,826 +I highly recommend you +to check out these 3 sessions. + +87 +00:07:05,859 --> 00:07:10,097 +Together, these technologies make it +easier than ever + +88 +00:07:10,130 --> 00:07:13,867 +for anyone to create +immersive AR experiences. + +89 +00:07:14,601 --> 00:07:16,537 +All these tools we covered today + +90 +00:07:16,570 --> 00:07:19,706 +are available for you to download +right now + +91 +00:07:19,740 --> 00:07:24,511 +on our AR Creation Tools +page on the Apple developer website. + +92 +00:07:24,545 --> 00:07:26,346 +Please check them out! + +93 +00:07:26,914 --> 00:07:31,718 +Next, let's take a look+ +at AR Quick Look's new lighting. + +94 +00:07:32,586 --> 00:07:38,158 +AR Quick Look is the built-in, +system-wide AR viewer on iOS + +95 +00:07:38,192 --> 00:07:43,697 +which enables you to place 3D USDZ objects +in your physical space, + +96 +00:07:43,730 --> 00:07:47,134 +like on a table, a wall, or a floor, + +97 +00:07:47,167 --> 00:07:51,972 +and interact with them +with simple touch gestures. + +98 +00:07:52,005 --> 00:07:56,109 +You can even make interactive scenes +built with Reality Composer + +99 +00:07:56,143 --> 00:07:58,812 +and save them to a USDZ file + +100 +00:07:58,846 --> 00:08:05,052 +which you can share with others +on iMessage, Mail, Notes, and more. + +101 +00:08:05,085 --> 00:08:09,489 +We constantly strive to make AR Quick Look + +102 +00:08:09,523 --> 00:08:13,393 +rendering more realistic, and this year, + +103 +00:08:13,427 --> 00:08:18,065 +we are introducing improved lighting +in AR Quick Look, + +104 +00:08:18,098 --> 00:08:23,770 +which is brighter, with enhanced contrast +and improved shape definition + +105 +00:08:23,804 --> 00:08:26,206 +to make your assets look even better. + +106 +00:08:27,407 --> 00:08:31,044 +Here is an example of AirPods Pro +in object mode + +107 +00:08:31,078 --> 00:08:33,580 +with classic and new lighting. + +108 +00:08:33,614 --> 00:08:37,518 +The AirPods Pro look great +in both lighting conditions, + +109 +00:08:37,551 --> 00:08:40,320 +and you will notice +the new lighting option + +110 +00:08:40,354 --> 00:08:42,422 +offers brighter color, + +111 +00:08:42,456 --> 00:08:45,425 +higher contrast, and more highlights. + +112 +00:08:47,895 --> 00:08:52,599 +Now let's place the AirPods Max +on the desk to view it in AR mode + +113 +00:08:52,633 --> 00:08:53,700 +with new lighting! + +114 +00:08:54,668 --> 00:08:56,937 +Stunning, isn't it? + +115 +00:08:57,938 --> 00:09:02,476 +So how do you apply +the new lighting to your assets? + +116 +00:09:02,509 --> 00:09:04,278 +It's really simple. + +117 +00:09:04,311 --> 00:09:08,949 +You just have to pick the lighting +that works best for your content + +118 +00:09:08,982 --> 00:09:14,521 +by setting the new preferredIblVersion +metadata in your USDZ file. + +119 +00:09:15,956 --> 00:09:19,960 +Here we have set the value +of the preferredIblVersion to 2, + +120 +00:09:19,993 --> 00:09:23,797 +which will indicate AR Quick Look +to use the new lighting system. + +121 +00:09:24,998 --> 00:09:28,936 +Let's understand this in more detail. + +122 +00:09:28,969 --> 00:09:31,405 +The preferredIBLVersion metadata + +123 +00:09:31,438 --> 00:09:34,274 +can hold values of 0,1, and 2. + +124 +00:09:36,143 --> 00:09:38,645 +An asset with preferredIblVersion set to 0 + +125 +00:09:38,679 --> 00:09:42,049 +will use the system default lighting. + +126 +00:09:43,016 --> 00:09:46,787 +If set to 1, it will continue +to use the classic lighting. + +127 +00:09:48,255 --> 00:09:51,058 +A value of 2 +will give you the new lighting. + +128 +00:09:52,059 --> 00:09:56,797 +This means you can easily update +your current assets to the new lighting + +129 +00:09:56,830 --> 00:09:57,664 +as well! + +130 +00:09:58,198 --> 00:10:02,970 +We recommend that you make +an explicit choice and set this metadata + +131 +00:10:03,003 --> 00:10:05,339 +in all of your assets. + +132 +00:10:05,372 --> 00:10:07,608 +To do that, you can either use + +133 +00:10:07,641 --> 00:10:12,546 +usdzconvert +with the flag preferrediblversion. + +134 +00:10:12,579 --> 00:10:15,849 +For example, +here is how to use the flag to convert + +135 +00:10:15,883 --> 00:10:20,087 +the fire hydrant OBJ file +to USDZ with the new lighting. + +136 +00:10:20,921 --> 00:10:24,625 +Or you can use Reality Converter, + +137 +00:10:24,658 --> 00:10:27,194 +which will now use +the new lighting by default. + +138 +00:10:28,161 --> 00:10:31,465 +But if you want +to use the classic lighting, + +139 +00:10:31,498 --> 00:10:34,201 +there is a new option +in the Property Panel. + +140 +00:10:35,836 --> 00:10:40,774 +Here we preview the AirPods Max asset +with both the classic and new lighting. + +141 +00:10:42,242 --> 00:10:45,546 +This provides an easy way +to compare the differences. + +142 +00:10:47,247 --> 00:10:50,250 +By clicking the lighting icon, +the applied lighting + +143 +00:10:50,284 --> 00:10:52,986 +will also be highlighted +for your reference. + +144 +00:10:54,821 --> 00:10:59,159 +Lastly, for assets without this metadata, + +145 +00:10:59,193 --> 00:11:02,729 +AR Quick Look will determine +the lighting version automatically + +146 +00:11:02,763 --> 00:11:05,566 +based on the file's date-time stamp. + +147 +00:11:06,900 --> 00:11:13,507 +If the asset was created after July 1st, +2022, it will use the new lighting. + +148 +00:11:14,808 --> 00:11:18,345 +Older assets will continue +to use the classic lighting + +149 +00:11:18,378 --> 00:11:20,113 +so they don't change how they look. + +150 +00:11:21,148 --> 00:11:24,718 +Now, I'll hand it over to Alex +for USD rendering. + +151 +00:11:24,751 --> 00:11:26,053 +Thanks, Stella. + +152 +00:11:26,086 --> 00:11:29,022 +We've already seen a lot +of rendered USD assets today. + +153 +00:11:29,056 --> 00:11:33,460 +Now we'll explore what makes USD rendering +and Hydra a great choice + +154 +00:11:33,493 --> 00:11:35,495 +for your 3D content creation pipeline + +155 +00:11:35,529 --> 00:11:38,298 +and how you can integrate it +in your own applications. + +156 +00:11:39,399 --> 00:11:43,303 +First, let's take a step back +and talk about rendering in general. + +157 +00:11:44,371 --> 00:11:51,011 +A renderer takes a collection +of 3D models, cameras, and lights as input + +158 +00:11:51,044 --> 00:11:53,847 +and uses them to generate an image. + +159 +00:11:53,881 --> 00:11:56,416 +There are many different renderers, +and each one of them + +160 +00:11:56,450 --> 00:12:00,254 +is built for a specific purpose +and optimized for a different use case. + +161 +00:12:01,321 --> 00:12:04,324 +Some renderers are designed +for real-time applications + +162 +00:12:04,358 --> 00:12:07,794 +like rendering a character +in a game or an AR scene. + +163 +00:12:08,662 --> 00:12:12,966 +Others take much longer but produce +a more photorealistic image, + +164 +00:12:13,000 --> 00:12:16,537 +for example, producing visual effects +for a Hollywood movie. + +165 +00:12:17,804 --> 00:12:21,208 +All renderers make choices +about their features and are unique. + +166 +00:12:22,276 --> 00:12:24,678 +Different USD renderers +for different use cases + +167 +00:12:24,711 --> 00:12:26,713 +also exist on Apple platforms. + +168 +00:12:27,414 --> 00:12:30,117 +We're adding documentation +about these different renderers + +169 +00:12:30,150 --> 00:12:31,985 +to developer.apple.com. + +170 +00:12:32,953 --> 00:12:36,223 +It will help you understand +the differences between them, + +171 +00:12:36,256 --> 00:12:39,126 +explain which USD features +they support, + +172 +00:12:39,159 --> 00:12:43,230 +and provide guidance on how to author USDs +that work best for you. + +173 +00:12:44,364 --> 00:12:48,168 +One of the renderers on Apple platforms +is RealityKit, + +174 +00:12:48,202 --> 00:12:51,171 +which features +a photorealistic rendering system, + +175 +00:12:51,205 --> 00:12:55,008 +optimized for interactive augmented +reality experiences, + +176 +00:12:55,042 --> 00:12:57,110 +and is used in AR Quick Look. + +177 +00:12:57,144 --> 00:12:59,880 +It is the primary renderer of USDZ files. + +178 +00:13:00,814 --> 00:13:07,588 +Another option for USD rendering on macOS +is Pixar's open-source renderer, Storm. + +179 +00:13:07,621 --> 00:13:10,190 +It is optimized +for production-grade assets + +180 +00:13:10,224 --> 00:13:13,627 +and designed for real-time preview +of large-scale scenes. + +181 +00:13:14,661 --> 00:13:18,699 +It's a great technology for you +to visualize and iterate on assets + +182 +00:13:18,732 --> 00:13:21,435 +as they flow +through your content creation pipeline. + +183 +00:13:22,402 --> 00:13:25,005 +Storm uses a technology called Hydra. + +184 +00:13:25,939 --> 00:13:30,978 +Hydra is a core aspect +of the USD open source project. + +185 +00:13:31,011 --> 00:13:34,815 +So next, let's understand Hydra +and its connection to Storm. + +186 +00:13:35,649 --> 00:13:38,519 +We start with this diagram from earlier. + +187 +00:13:38,552 --> 00:13:40,988 +Here, we use Storm as the renderer. + +188 +00:13:42,189 --> 00:13:46,527 +The input is usually called +"the scene graph," + +189 +00:13:46,560 --> 00:13:49,162 +and Storm produces a preview render of it. + +190 +00:13:50,364 --> 00:13:55,068 +But what if you want to generate +a photorealistic image of the same scene + +191 +00:13:55,102 --> 00:13:56,537 +using a different renderer? + +192 +00:13:57,538 --> 00:14:00,307 +That's exactly what Hydra is made for. + +193 +00:14:00,340 --> 00:14:03,944 +Instead of tightly coupling +the scene to the renderer, + +194 +00:14:03,977 --> 00:14:07,714 +Hydra acts as an abstract layer +in between them + +195 +00:14:07,748 --> 00:14:10,551 +to transport data from scenes +to renderers. + +196 +00:14:11,752 --> 00:14:14,488 +This allows you to easily swap out +your renderer + +197 +00:14:14,521 --> 00:14:19,393 +based on your needs at any time +without any changes to your scene graph. + +198 +00:14:19,426 --> 00:14:23,297 +For example, you might use Storm +and Pixar's RenderMan. + +199 +00:14:24,431 --> 00:14:28,235 +In your content creation pipeline, +you can use Storm for fast renders + +200 +00:14:28,268 --> 00:14:29,736 +and quick iteration, + +201 +00:14:29,770 --> 00:14:32,039 +and then switch to RenderMan + +202 +00:14:32,072 --> 00:14:34,208 +to produce +the final image when you're ready. + +203 +00:14:35,142 --> 00:14:38,212 +The same goes for the scene graph. + +204 +00:14:38,245 --> 00:14:42,916 +Your input to Hydra can be +a USD scene graph or a different one. + +205 +00:14:43,951 --> 00:14:48,288 +This allows you to leverage +the same renderer in multiple applications + +206 +00:14:48,322 --> 00:14:53,627 +even if each one of them has their own, +totally different scene graph. + +207 +00:14:53,660 --> 00:14:57,731 +The interfaces which connect +Hydra with scene graphs and renderers + +208 +00:14:57,764 --> 00:14:59,900 +are called delegates. + +209 +00:14:59,933 --> 00:15:04,137 +Scene graphs are connected +to Hydra via Scene Delegates, + +210 +00:15:04,171 --> 00:15:07,875 +and Renderers are connected +to Hydra via Render Delegates. + +211 +00:15:08,542 --> 00:15:10,477 +And that is Hydra for you! + +212 +00:15:11,245 --> 00:15:15,816 +Foundry's Nuke 13 is already +using Hydra to render the viewport, + +213 +00:15:15,849 --> 00:15:19,486 +enabling Nuke artists on macOS +to have better quality + +214 +00:15:19,520 --> 00:15:22,589 +and an interactive experience +in Nuke's 3D system. + +215 +00:15:23,657 --> 00:15:27,060 +This is achieved by using +Hydra with a custom scene delegate + +216 +00:15:27,094 --> 00:15:28,862 +and Metal-accelerated Storm. + +217 +00:15:30,063 --> 00:15:33,767 +We are excited to share that +we've worked with Pixar Animation Studios + +218 +00:15:33,800 --> 00:15:38,972 +to publicly release Metal +accelerated Storm to open source. + +219 +00:15:39,006 --> 00:15:42,442 +It is ready for you to use in USD 22.05. + +220 +00:15:43,243 --> 00:15:45,579 +Now that we've seen the power of Hydra, + +221 +00:15:45,612 --> 00:15:47,781 +let's go through +a Hydra sample application + +222 +00:15:47,814 --> 00:15:51,051 +with the typical use case +of a USD scene graph + +223 +00:15:51,084 --> 00:15:53,487 +and Storm as renderer. + +224 +00:15:53,520 --> 00:15:57,291 +It will get you started with using Hydra +to build content creation tools + +225 +00:15:57,324 --> 00:15:59,560 +and 3D workflow applications. + +226 +00:16:00,427 --> 00:16:05,899 +Our sample application, HydraPlayer, +is very simple but powerful. + +227 +00:16:05,933 --> 00:16:10,771 +It renders a USD file with Storm +and lets us move the camera around it. + +228 +00:16:12,172 --> 00:16:14,241 +And we're excited to make HydraPlayer + +229 +00:16:14,274 --> 00:16:18,378 +a public sample project +to get you started right away! + +230 +00:16:18,412 --> 00:16:21,315 +It is available in the session resources +and comes + +231 +00:16:21,348 --> 00:16:23,183 +with full instructions in the readme. + +232 +00:16:24,384 --> 00:16:27,221 +I encourage you to pause this video now, + +233 +00:16:27,254 --> 00:16:31,058 +download the project, and then follow +along as we walk through it. + +234 +00:16:32,326 --> 00:16:35,896 +In your Xcode project, +you will find 4 classes: + +235 +00:16:35,929 --> 00:16:41,068 +The AppDelegate, Camera, +Renderer, and View Controller. + +236 +00:16:41,101 --> 00:16:44,071 +The AppDelegate is basically +your root object + +237 +00:16:44,104 --> 00:16:46,273 +and manages interactions with the system. + +238 +00:16:47,040 --> 00:16:49,776 +The camera class is +a simple representation + +239 +00:16:49,810 --> 00:16:51,545 +of the USD scene camera + +240 +00:16:51,578 --> 00:16:53,947 +to make it easy +to translate the user input. + +241 +00:16:54,882 --> 00:16:58,886 +The renderer class will handle +all our interactions with USD and Hydra. + +242 +00:16:59,920 --> 00:17:04,124 +Last but not least, +the ViewController handles our user input. + +243 +00:17:05,092 --> 00:17:08,795 +Before we can build and launch +HydraPlayer, there are three things to do: + +244 +00:17:09,897 --> 00:17:14,434 +prepare the environment, use Rosetta +on Apple Silicon Macs, + +245 +00:17:14,468 --> 00:17:16,870 +and download and build USD & Hydra. + +246 +00:17:16,904 --> 00:17:18,372 +So let's get started. + +247 +00:17:20,007 --> 00:17:23,010 +First, we prepare +our development environment. + +248 +00:17:23,043 --> 00:17:27,247 +Make sure you have Xcode, +Python, and CMake installed. + +249 +00:17:29,716 --> 00:17:32,553 +Now let's open up a terminal +for the other two steps. + +250 +00:17:34,087 --> 00:17:38,292 +If you are on an Apple Silicon Mac, +you need to run under Rosetta + +251 +00:17:38,325 --> 00:17:42,496 +while USD is transitioning +to fully support arm64. + +252 +00:17:42,529 --> 00:17:45,065 +To do this, use the arch command. + +253 +00:17:46,600 --> 00:17:49,937 +Once your environment is ready, +we have to download the USD + +254 +00:17:49,970 --> 00:17:52,005 +and Hydra source code. + +255 +00:17:52,039 --> 00:17:57,110 +Both live in the same GitHub repository +at PixarAnimationStudios/USD. + +256 +00:17:57,978 --> 00:18:01,148 +Now that we have the code, +we can build it. + +257 +00:18:01,181 --> 00:18:04,818 +USD comes +with a convenient Python build script. + +258 +00:18:04,852 --> 00:18:09,323 +We add the flags "generator Xcode" +and "no-python" + +259 +00:18:09,356 --> 00:18:12,226 +and specify +where we want to install USD to. + +260 +00:18:13,160 --> 00:18:16,997 +Let's put it next to the source code +at "USDInstall." + +261 +00:18:17,030 --> 00:18:21,201 +Once USD finished building, +we are ready to build HydraPlayer! + +262 +00:18:22,169 --> 00:18:26,507 +Let's go back to this diagram +one more time and use it to identify + +263 +00:18:26,540 --> 00:18:29,443 +key parts of HydraPlayer +to check out in detail. + +264 +00:18:30,344 --> 00:18:36,183 +We'll look at how to load the 3D models, +how to set up the camera, + +265 +00:18:36,216 --> 00:18:38,552 +and set the scene light. + +266 +00:18:38,585 --> 00:18:42,389 +Then we'll learn how to get +our scene graph to Storm + +267 +00:18:42,422 --> 00:18:46,126 +and finally, how to produce +an image for the application window. + +268 +00:18:47,160 --> 00:18:50,964 +So let's get started with loading +our 3D models from USD. + +269 +00:18:50,998 --> 00:18:56,170 +In viewDidAppear in our ViewController, +we create a file picker + +270 +00:18:56,203 --> 00:18:59,806 +with an NSOpenPanel +when the view appears the first time. + +271 +00:19:01,875 --> 00:19:04,645 +Once a file has been selected, +the renderer + +272 +00:19:04,678 --> 00:19:07,981 +can start setting up our scene +and load the USD file. + +273 +00:19:09,049 --> 00:19:12,786 +Loading the file is very simple +with the USD API. + +274 +00:19:12,819 --> 00:19:15,956 +We simply open +the USD stage at the file path. + +275 +00:19:17,824 --> 00:19:19,793 +That's it. +We have our file loaded. + +276 +00:19:20,961 --> 00:19:26,466 +Next, we set up our camera. +In our code, this is straightforward. + +277 +00:19:26,500 --> 00:19:30,237 +In setupCamera, +we first create a new scene camera. + +278 +00:19:31,738 --> 00:19:36,543 +Then we calculate the world size +and center based on our scene. + +279 +00:19:37,377 --> 00:19:43,550 +Next, move our camera at a good distance +and then focus on the center. + +280 +00:19:43,584 --> 00:19:46,787 +This way, our camera +captures the whole scene. + +281 +00:19:47,988 --> 00:19:50,324 +Great, now we have our camera set up. + +282 +00:19:50,357 --> 00:19:54,061 +Next, lighting. + +283 +00:19:54,094 --> 00:19:59,266 +We keep it easy and create +one simple ambient light + +284 +00:19:59,299 --> 00:20:03,971 +and set its position to match +the camera, and that's it. + +285 +00:20:04,004 --> 00:20:09,910 +And with that, we have our full scene +with 3D models, a camera and a light! + +286 +00:20:09,943 --> 00:20:13,947 +We can now pass our scene to Storm. + +287 +00:20:13,981 --> 00:20:17,951 +First, we need to initialize +our render engine. + +288 +00:20:17,985 --> 00:20:24,892 +Here, we create a new UsdImagingGLEngine, +which is the class name of Storm. + +289 +00:20:24,925 --> 00:20:28,762 +The most important parameter here +is the rootPath. + +290 +00:20:28,795 --> 00:20:33,100 +It points the engine to our USD stage +with our 3D models. + +291 +00:20:33,133 --> 00:20:37,171 +To learn more about the other parameters +and UsdImagingGLEngine, + +292 +00:20:37,204 --> 00:20:40,440 +please check out +Pixar's USD API documentation. + +293 +00:20:41,575 --> 00:20:45,779 +Next, we set our camera +in our engine + +294 +00:20:45,812 --> 00:20:48,849 +and set our lighting setup. + +295 +00:20:48,882 --> 00:20:53,053 +And last but not least, +we define how we want Storm to render + +296 +00:20:53,086 --> 00:20:55,155 +by setting render parameters. + +297 +00:20:55,856 --> 00:20:59,126 +For example, +we want to render a transparent background + +298 +00:20:59,159 --> 00:21:03,030 +so that the rendered image +works nicely with our app color scheme. + +299 +00:21:03,730 --> 00:21:05,966 +Important for scenes with animation, + +300 +00:21:05,999 --> 00:21:08,535 +this is also where we specify +the time code. + +301 +00:21:10,337 --> 00:21:12,472 +Now we're ready to render an image! + +302 +00:21:13,740 --> 00:21:16,510 +We've already done all the +necessary setup, + +303 +00:21:16,543 --> 00:21:19,813 +so the render command is +just one line of code. + +304 +00:21:20,614 --> 00:21:26,086 +We get the result and display it +in our window, and there we are! + +305 +00:21:26,119 --> 00:21:28,689 +We're rendering the USD toy plane! + +306 +00:21:28,722 --> 00:21:31,825 +And actually, +we're rendering not just one plane. + +307 +00:21:31,859 --> 00:21:35,929 +HydraPlayer can easily render +thousands of animated assets + +308 +00:21:35,963 --> 00:21:37,998 +with tens of millions of triangles. + +309 +00:21:39,166 --> 00:21:42,636 +If you haven't already, +check out the resources for this session, + +310 +00:21:42,669 --> 00:21:45,806 +download the sample project, +and have fun exploring it further. + +311 +00:21:47,074 --> 00:21:50,511 +Together we explored updates +to Apple's USD tools + +312 +00:21:50,544 --> 00:21:54,581 +that make them more powerful and make +your assets look even better. + +313 +00:21:54,615 --> 00:21:56,717 +We learned about Hydra's features + +314 +00:21:56,750 --> 00:21:59,820 +and went +through the HydraPlayer sample project + +315 +00:21:59,853 --> 00:22:03,223 +to see how you can integrate it +into your own applications. + +316 +00:22:04,525 --> 00:22:08,128 +To find out more +about important concepts of USD, + +317 +00:22:08,161 --> 00:22:11,698 +check out the session +"Understand USD fundamentals. + +318 +00:22:12,866 --> 00:22:14,468 +Thank you. + +319 +00:22:14,501 --> 00:22:16,436 +♪ ♪ + diff --git a/eng/2022 Session 10142 Efficiency awaits - Background tasks in SwiftUI en.srt b/eng/2022 Session 10142 Efficiency awaits - Background tasks in SwiftUI en.srt new file mode 100644 index 0000000..5cdd6d9 --- /dev/null +++ b/eng/2022 Session 10142 Efficiency awaits - Background tasks in SwiftUI en.srt @@ -0,0 +1,1197 @@ +1 +00:00:00,234 --> 00:00:03,470 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,470 --> 00:00:09,409 +♪ + +3 +00:00:09,409 --> 00:00:13,280 +Welcome to "Efficiency awaits: +Background Tasks in SwiftUI." + +4 +00:00:13,280 --> 00:00:17,050 +I'm John Gallagher, an engineer +on the watchOS Frameworks team. + +5 +00:00:17,050 --> 00:00:20,420 +In this talk, we'll learn +about a new SwiftUI API + +6 +00:00:20,420 --> 00:00:22,990 +for handling background tasks +using Swift Concurrency + +7 +00:00:22,990 --> 00:00:26,493 +and in a consistent way +across all of Apple's platforms. + +8 +00:00:26,493 --> 00:00:29,930 +We'll begin by describing +a sample app called Stormy, + +9 +00:00:29,930 --> 00:00:32,599 +an app for taking photos +of the sky on stormy days + +10 +00:00:32,599 --> 00:00:36,236 +which makes use +of background tasks. + +11 +00:00:36,236 --> 00:00:39,940 +Then, we'll dive into how +the app uses background tasks + +12 +00:00:39,940 --> 00:00:44,811 +and how background tasks work +under the hood. + +13 +00:00:44,811 --> 00:00:48,081 +Next, we'll learn how to handle +those background tasks + +14 +00:00:48,081 --> 00:00:52,319 +using a new API in SwiftUI. + +15 +00:00:52,319 --> 00:00:57,124 +And finally, we'll review how +the API uses Swift Concurrency + +16 +00:00:57,124 --> 00:01:03,096 +to make handling background +tasks easier than ever. + +17 +00:01:03,096 --> 00:01:07,567 +The new API is shared +across watchOS, iOS, tvOS, + +18 +00:01:07,567 --> 00:01:09,503 +Mac Catalyst, and Widgets, + +19 +00:01:09,503 --> 00:01:11,972 +including iOS apps +running on the Mac, + +20 +00:01:11,972 --> 00:01:13,874 +which means the concepts +and patterns you learn + +21 +00:01:13,874 --> 00:01:15,976 +handling background tasks +for one platform + +22 +00:01:15,976 --> 00:01:19,046 +can apply to work you do +on others. + +23 +00:01:19,046 --> 00:01:22,683 +Utilizing Swift Concurrency, +the new API reduces the need + +24 +00:01:22,683 --> 00:01:25,319 +for deeply nested completion +handlers and callbacks + +25 +00:01:25,319 --> 00:01:26,987 +as well as much of +the mutable state + +26 +00:01:26,987 --> 00:01:29,923 +which was frequently +a side effect. + +27 +00:01:29,923 --> 00:01:32,492 +Swift Concurrency's +native task cancellation + +28 +00:01:32,492 --> 00:01:35,862 +helps applications gracefully +complete tasks in a timely way + +29 +00:01:35,862 --> 00:01:40,100 +to avoid being quit +in the background by the system. + +30 +00:01:40,100 --> 00:01:42,402 +For people who like to keep +their head in the clouds, + +31 +00:01:42,402 --> 00:01:44,338 +we're going to build +an app called Stormy + +32 +00:01:44,338 --> 00:01:46,506 +that will remind users +to take photos of the sky + +33 +00:01:46,506 --> 00:01:49,076 +when it's stormy outside. + +34 +00:01:49,076 --> 00:01:51,945 +The app will show a notification +at noon on stormy days + +35 +00:01:51,945 --> 00:01:56,883 +requesting that the user +takes a picture of the sky. + +36 +00:01:56,883 --> 00:01:58,719 +When the user +taps the notification, + +37 +00:01:58,719 --> 00:02:01,121 +they'll take a photo of the sky +to upload to their profile + +38 +00:02:01,121 --> 00:02:03,090 +for future admiration. + +39 +00:02:03,090 --> 00:02:07,561 +We're going to upload +this photo in the background. + +40 +00:02:07,561 --> 00:02:09,196 +The app will send +another notification + +41 +00:02:09,196 --> 00:02:11,965 +when the upload has finished. + +42 +00:02:11,965 --> 00:02:15,602 +Let's dive in to how background +tasks can let us do this. + +43 +00:02:15,602 --> 00:02:18,372 +In this diagram, +we'll examine at a high level + +44 +00:02:18,372 --> 00:02:20,273 +how the notification +will only get sent + +45 +00:02:20,273 --> 00:02:23,677 +on stormy days +utilizing background tasks. + +46 +00:02:23,677 --> 00:02:26,279 +We'll represent foreground +application runtime + +47 +00:02:26,279 --> 00:02:28,281 +with the bar on the left, + +48 +00:02:28,281 --> 00:02:32,219 +background application runtime +with the bar in the middle, + +49 +00:02:32,219 --> 00:02:36,857 +and the system is represented +on the right. + +50 +00:02:36,857 --> 00:02:40,027 +When our app is first launched +to the foreground by the user, + +51 +00:02:40,027 --> 00:02:41,728 +we can take +our first opportunity + +52 +00:02:41,728 --> 00:02:46,833 +to schedule a background app +refresh task for noon. + +53 +00:02:46,833 --> 00:02:50,971 +Then, when the user leaves our +app and the app is suspended, + +54 +00:02:50,971 --> 00:02:53,540 +the system will know to wake +our application again + +55 +00:02:53,540 --> 00:02:57,544 +in the background +at the time we scheduled. + +56 +00:02:57,544 --> 00:02:59,913 +We scheduled our task for noon, + +57 +00:02:59,913 --> 00:03:02,416 +so that's when system will +wake the app in the background + +58 +00:03:02,416 --> 00:03:07,421 +and send a background app +refresh task. + +59 +00:03:07,421 --> 00:03:08,922 +With this background runtime, + +60 +00:03:08,922 --> 00:03:11,324 +we need to figure out +whether it is stormy outside, + +61 +00:03:11,324 --> 00:03:16,029 +and if it is, +send a notification to the user. + +62 +00:03:16,029 --> 00:03:19,366 +To start, we'll make a network +request to a weather service + +63 +00:03:19,366 --> 00:03:22,702 +to check the current weather. + +64 +00:03:22,702 --> 00:03:25,272 +With the URLSession +scheduled for background, + +65 +00:03:25,272 --> 00:03:26,973 +the application can suspend + +66 +00:03:26,973 --> 00:03:30,710 +and wait for the network request +to complete. + +67 +00:03:30,710 --> 00:03:32,179 +When the background +network request + +68 +00:03:32,179 --> 00:03:33,780 +for weather data completes, + +69 +00:03:33,780 --> 00:03:36,850 +our application will be given +background runtime again + +70 +00:03:36,850 --> 00:03:41,855 +with a new URLSession +background task. + +71 +00:03:41,855 --> 00:03:44,891 +With the results of the weather +data request in hand, + +72 +00:03:44,891 --> 00:03:47,961 +our application knows +whether it is stormy outside + +73 +00:03:47,961 --> 00:03:50,097 +and can choose +whether to send a notification + +74 +00:03:50,097 --> 00:03:55,602 +prompting the user +to take a photo of the sky. + +75 +00:03:55,602 --> 00:03:58,672 +Now that our work for +that URLSession task is done, + +76 +00:03:58,672 --> 00:04:02,943 +the system can once again +suspend the application. + +77 +00:04:02,943 --> 00:04:06,012 +Let's dive into the details +of a single background task + +78 +00:04:06,012 --> 00:04:07,814 +and see how it works. + +79 +00:04:07,814 --> 00:04:11,351 +To do that, we're going to take +a closer look at the lifecycle + +80 +00:04:11,351 --> 00:04:14,321 +of a single app refresh +background task. + +81 +00:04:14,321 --> 00:04:19,025 +Let's zoom in here a little bit. + +82 +00:04:19,025 --> 00:04:21,962 +First, the system +will wake our application + +83 +00:04:21,962 --> 00:04:25,732 +and send it an app refresh +background task. + +84 +00:04:25,732 --> 00:04:29,236 +Then, still in the background, +we make a network request + +85 +00:04:29,236 --> 00:04:32,839 +to check whether +it is stormy outside. + +86 +00:04:32,839 --> 00:04:35,775 +Ideally, +our network request completes + +87 +00:04:35,775 --> 00:04:38,745 +within the allotted background +runtime our application has + +88 +00:04:38,745 --> 00:04:41,948 +for app refresh. + +89 +00:04:41,948 --> 00:04:43,650 +When we get +the network response, + +90 +00:04:43,650 --> 00:04:47,154 +we'd like to post +the notification immediately. + +91 +00:04:47,154 --> 00:04:48,855 +With the notification posted, + +92 +00:04:48,855 --> 00:04:52,192 +we've completed everything we +needed to do during app refresh + +93 +00:04:52,192 --> 00:04:56,296 +and the system can suspend +the application again. + +94 +00:04:56,296 --> 00:04:59,232 +But what about when our +network request for weather data + +95 +00:04:59,232 --> 00:05:02,068 +doesn't complete in time? + +96 +00:05:02,068 --> 00:05:04,838 +If an app is running low +on background runtime + +97 +00:05:04,838 --> 00:05:06,239 +for the current task, + +98 +00:05:06,239 --> 00:05:08,942 +the system signals the app +that the time is running low, + +99 +00:05:08,942 --> 00:05:13,113 +giving us a chance to handle +this situation gracefully. + +100 +00:05:13,113 --> 00:05:15,515 +If applications do not signal +that they've completed + +101 +00:05:15,515 --> 00:05:18,118 +their background work +before runtime expires, + +102 +00:05:18,118 --> 00:05:20,720 +the application may be quit +by the system and throttled + +103 +00:05:20,720 --> 00:05:24,524 +for future +background task requests. + +104 +00:05:24,524 --> 00:05:27,427 +In this case, we should make +sure that our network request + +105 +00:05:27,427 --> 00:05:29,829 +is a background +network request, + +106 +00:05:29,829 --> 00:05:33,233 +which will allow us to complete +our app refresh task immediately + +107 +00:05:33,233 --> 00:05:34,467 +and get woken again + +108 +00:05:34,467 --> 00:05:36,102 +for additional +background runtime + +109 +00:05:36,102 --> 00:05:39,839 +when the network +request completes. + +110 +00:05:39,839 --> 00:05:42,075 +With our background +URLSession scheduled, + +111 +00:05:42,075 --> 00:05:45,645 +the system can suspend +the application again. + +112 +00:05:45,645 --> 00:05:49,783 +Now, let's dive into how the +BackgroundTask API in SwiftUI + +113 +00:05:49,783 --> 00:05:52,686 +can help us build Stormy. + +114 +00:05:52,686 --> 00:05:56,356 +To begin, +we'll need a basic application. + +115 +00:05:56,356 --> 00:06:00,293 +Then, we'll write a function to +schedule background app refresh + +116 +00:06:00,293 --> 00:06:02,796 +for noon tomorrow. + +117 +00:06:02,796 --> 00:06:07,601 +First, we create a date +representing noon tomorrow. + +118 +00:06:07,601 --> 00:06:10,904 +Then, we create +a background app refresh request + +119 +00:06:10,904 --> 00:06:13,406 +with an earliest begin date +of noon tomorrow + +120 +00:06:13,406 --> 00:06:15,742 +and submit it to the scheduler. + +121 +00:06:15,742 --> 00:06:18,078 +This is what tells the system +to wake our application + +122 +00:06:18,078 --> 00:06:21,014 +tomorrow at noon. + +123 +00:06:21,014 --> 00:06:22,916 +We'll want to call this function + +124 +00:06:22,916 --> 00:06:25,118 +when the user +first opens the application + +125 +00:06:25,118 --> 00:06:29,122 +and requests daily +storm notifications at noon. + +126 +00:06:29,122 --> 00:06:31,224 +We can register +a handler corresponding + +127 +00:06:31,224 --> 00:06:32,859 +to the background +task we scheduled + +128 +00:06:32,859 --> 00:06:38,098 +by using the new background task +scene modifier. + +129 +00:06:38,098 --> 00:06:40,367 +When the app receives +a background task, + +130 +00:06:40,367 --> 00:06:42,636 +any blocks registered +with this modifier + +131 +00:06:42,636 --> 00:06:46,439 +that match the background task +received are run. + +132 +00:06:46,439 --> 00:06:50,176 +In this case, we used +the appRefresh task type + +133 +00:06:50,176 --> 00:06:51,745 +which can be scheduled +in advance + +134 +00:06:51,745 --> 00:06:54,447 +to provide our application +with a limited amount of runtime + +135 +00:06:54,447 --> 00:06:57,584 +in the background +at a desired date. + +136 +00:06:57,584 --> 00:06:59,953 +Using the same identifier +for the request + +137 +00:06:59,953 --> 00:07:02,822 +and the handler +in the background task modifier + +138 +00:07:02,822 --> 00:07:05,592 +lets the system identify +which handler to call + +139 +00:07:05,592 --> 00:07:10,730 +when the corresponding task +is received by your application. + +140 +00:07:10,730 --> 00:07:12,265 +In order for us to be sure that + +141 +00:07:12,265 --> 00:07:14,134 +we are scheduled again +for tomorrow, + +142 +00:07:14,134 --> 00:07:16,369 +we'll start our background task +by calling + +143 +00:07:16,369 --> 00:07:19,339 +the scheduleAppRefresh +function we just wrote + +144 +00:07:19,339 --> 00:07:24,911 +to schedule background runtime +again for tomorrow at noon. + +145 +00:07:24,911 --> 00:07:28,281 +Now that our background runtime +at noon is recurring, + +146 +00:07:28,281 --> 00:07:29,783 +we make our network request + +147 +00:07:29,783 --> 00:07:31,718 +to check whether +it is stormy outside + +148 +00:07:31,718 --> 00:07:36,323 +and wait for the result +using the await Swift keyword. + +149 +00:07:36,323 --> 00:07:38,858 +Then, if our network request +has returned + +150 +00:07:38,858 --> 00:07:40,927 +and it is indeed stormy outside, + +151 +00:07:40,927 --> 00:07:43,697 +we await sending +the notification to the user + +152 +00:07:43,697 --> 00:07:46,833 +prompting them to upload +a photo of the sky. + +153 +00:07:46,833 --> 00:07:48,902 +When the body +of our closure returns, + +154 +00:07:48,902 --> 00:07:50,403 +the underlying background task + +155 +00:07:50,403 --> 00:07:52,305 +assigned to our application +by the system + +156 +00:07:52,305 --> 00:07:54,708 +is implicitly marked +as complete, + +157 +00:07:54,708 --> 00:07:58,511 +and the system can suspend +our application again. + +158 +00:07:58,511 --> 00:08:01,748 +Here, using Swift Concurrency +has let do potentially + +159 +00:08:01,748 --> 00:08:04,250 +long-running operations +in our background task + +160 +00:08:04,250 --> 00:08:06,419 +without the need +for an explicit callback + +161 +00:08:06,419 --> 00:08:08,588 +for when the work +has completed. + +162 +00:08:08,588 --> 00:08:11,024 +Many APIs +across Apple Platforms, + +163 +00:08:11,024 --> 00:08:12,726 +such as adding a notification, + +164 +00:08:12,726 --> 00:08:14,661 +already support +Swift Concurrency + +165 +00:08:14,661 --> 00:08:16,830 +for asynchronous operations. + +166 +00:08:16,830 --> 00:08:19,999 +Here, the notifyForPhoto +async function + +167 +00:08:19,999 --> 00:08:22,135 +can be implemented +in a straightforward way + +168 +00:08:22,135 --> 00:08:24,804 +using the asynchronous +addNotification method + +169 +00:08:24,804 --> 00:08:29,075 +found on +UserNotificationCenter. + +170 +00:08:29,075 --> 00:08:32,512 +Let's dive in to how Swift +Concurrency and async/await + +171 +00:08:32,512 --> 00:08:34,314 +do some heavy lifting for us + +172 +00:08:34,314 --> 00:08:38,451 +and make it easier than ever +to handle background tasks. + +173 +00:08:38,451 --> 00:08:41,121 +Let's write the asynchronous +isStormy function + +174 +00:08:41,121 --> 00:08:43,490 +that we've been referencing. + +175 +00:08:43,490 --> 00:08:46,292 +This async function is going to +need to make a network request + +176 +00:08:46,292 --> 00:08:49,562 +checking the weather outside. + +177 +00:08:49,562 --> 00:08:52,198 +To start, we'll get +the shared URLSession + +178 +00:08:52,198 --> 00:08:56,336 +and instantiate a request +for weather data. + +179 +00:08:56,336 --> 00:08:59,005 +URLSession has adopted +Swift Concurrency + +180 +00:08:59,005 --> 00:09:01,274 +and has a method for downloading +data from the network + +181 +00:09:01,274 --> 00:09:06,479 +that can be awaited +from async contexts. + +182 +00:09:06,479 --> 00:09:08,581 +With the network response +in hand, + +183 +00:09:08,581 --> 00:09:12,786 +we can read the weather data +and return our result. + +184 +00:09:12,786 --> 00:09:14,788 +But what about +when our application + +185 +00:09:14,788 --> 00:09:16,656 +can't complete +the network request + +186 +00:09:16,656 --> 00:09:19,926 +before our runtime expires? + +187 +00:09:19,926 --> 00:09:23,296 +Recall that in this case, +we wanted to make sure that + +188 +00:09:23,296 --> 00:09:27,167 +we had set up our URLSession +as a background session + +189 +00:09:27,167 --> 00:09:30,103 +and to ensure that it will send +launch events to our application + +190 +00:09:30,103 --> 00:09:34,441 +using a URLSession +background task. + +191 +00:09:34,441 --> 00:09:37,010 +Back to our code. + +192 +00:09:37,010 --> 00:09:41,448 +We had used +the shared URLSession. + +193 +00:09:41,448 --> 00:09:44,451 +Instead, we should create +a URLSession + +194 +00:09:44,451 --> 00:09:46,953 +from a background +configuration with + +195 +00:09:46,953 --> 00:09:51,191 +the sessionSendsLaunchEvents +property set to true. + +196 +00:09:51,191 --> 00:09:54,661 +This tells the system that some +network requests should run + +197 +00:09:54,661 --> 00:09:58,398 +even when the app is suspended +and to then wake the app + +198 +00:09:58,398 --> 00:10:02,902 +for a URLSession background task +when that request completes. + +199 +00:10:02,902 --> 00:10:06,372 +Note that this is especially +important on watchOS + +200 +00:10:06,372 --> 00:10:09,409 +as all network requests made by +apps running in the background + +201 +00:10:09,409 --> 00:10:16,049 +on watchOS must be requested +through background URLSessions. + +202 +00:10:16,049 --> 00:10:18,318 +We're not quite done though. + +203 +00:10:18,318 --> 00:10:22,322 +Recall that when our background +task runtime is expiring, + +204 +00:10:22,322 --> 00:10:24,390 +the system will cancel +the async task + +205 +00:10:24,390 --> 00:10:26,025 +that is running +the closure provided + +206 +00:10:26,025 --> 00:10:28,928 +to the background task +modifier. + +207 +00:10:28,928 --> 00:10:32,465 +This means that +the network request made here + +208 +00:10:32,465 --> 00:10:38,771 +will also be cancelled when our +background runtime is expiring. + +209 +00:10:38,771 --> 00:10:41,040 +To respond to and handle +that cancellation, + +210 +00:10:41,040 --> 00:10:43,810 +we can use the +withTaskCancellationHandler + +211 +00:10:43,810 --> 00:10:47,113 +function built +in to Swift Concurrency. + +212 +00:10:47,113 --> 00:10:49,482 +Instead of awaiting +the result directly, + +213 +00:10:49,482 --> 00:10:54,721 +we place our download into a +withTaskCancellationHandler call + +214 +00:10:54,721 --> 00:10:57,056 +and await this as well. + +215 +00:10:57,056 --> 00:11:00,326 +The first block passed to +withTaskCancellationHandler + +216 +00:11:00,326 --> 00:11:03,897 +is the async procedure +we'd like to run and await. + +217 +00:11:03,897 --> 00:11:07,767 +The second onCancel trailing +closure is code that will run + +218 +00:11:07,767 --> 00:11:10,236 +when the task is cancelled. + +219 +00:11:10,236 --> 00:11:13,506 +Here, when the immediate +network request is cancelled + +220 +00:11:13,506 --> 00:11:15,441 +due to our runtime expiring, + +221 +00:11:15,441 --> 00:11:19,112 +we promote the network request +to a background download task, + +222 +00:11:19,112 --> 00:11:20,647 +on which we can call resume, + +223 +00:11:20,647 --> 00:11:23,149 +triggering the background +download that will persist + +224 +00:11:23,149 --> 00:11:26,286 +even when our app is suspended. + +225 +00:11:26,286 --> 00:11:30,023 +This code is not making the +underlying network request twice + +226 +00:11:30,023 --> 00:11:33,226 +as we're using the same +URLSession to back both, + +227 +00:11:33,226 --> 00:11:36,930 +and URLSession will deduplicate +any in-process requests + +228 +00:11:36,930 --> 00:11:41,301 +under the hood. + +229 +00:11:41,301 --> 00:11:44,604 +Finally, we need to ensure +that our application is set up + +230 +00:11:44,604 --> 00:11:48,274 +to handle a launch +from a background URLSession. + +231 +00:11:48,274 --> 00:11:51,077 +We can use the background task +modifier again, + +232 +00:11:51,077 --> 00:11:54,781 +but this time with +the URLSession task type. + +233 +00:11:54,781 --> 00:11:56,482 +By using the same identifier + +234 +00:11:56,482 --> 00:11:59,886 +for the background URLSession +configuration we made earlier, + +235 +00:11:59,886 --> 00:12:02,755 +we can ensure +this block is only called + +236 +00:12:02,755 --> 00:12:08,661 +when that specific URLSession +produces a background task. + +237 +00:12:08,661 --> 00:12:12,131 +We've taken a dive into +the new unified SwiftUI API + +238 +00:12:12,131 --> 00:12:13,766 +for handling background tasks + +239 +00:12:13,766 --> 00:12:16,302 +and discovered how Swift +Concurrency makes it easier + +240 +00:12:16,302 --> 00:12:20,607 +than ever for us to manage task +completion and expiration. + +241 +00:12:20,607 --> 00:12:23,376 +For more information +about Swift Concurrency, + +242 +00:12:23,376 --> 00:12:25,945 +check out the +"Meet Async/await in Swift" talk + +243 +00:12:25,945 --> 00:12:29,048 +from WWDC 2021. + +244 +00:12:29,048 --> 00:12:31,384 +And to learn more about +concurrency in SwiftUI, + +245 +00:12:31,384 --> 00:12:34,053 +we recommend "Discover +Concurrency in SwiftUI," + +246 +00:12:34,053 --> 00:12:37,090 +also from WWDC 2021. + +247 +00:12:37,090 --> 00:12:38,091 +Thanks for watching + +248 +00:12:38,091 --> 00:12:41,794 +"Efficiency awaits: +Background Tasks in SwiftUI." + +249 +00:12:41,794 --> 00:12:46,132 +♪ + diff --git a/eng/2022 Session 10143 Discover Managed Device Attestation en.srt b/eng/2022 Session 10143 Discover Managed Device Attestation en.srt new file mode 100644 index 0000000..758c467 --- /dev/null +++ b/eng/2022 Session 10143 Discover Managed Device Attestation en.srt @@ -0,0 +1,1700 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,178 +Bob: Hi, I'm Bob Whiteman, + +3 +00:00:11,211 --> 00:00:13,747 +Senior iOS Device Management Engineer. + +4 +00:00:13,780 --> 00:00:17,651 +It is my pleasure to share with you +an important new security feature + +5 +00:00:17,684 --> 00:00:22,623 +for managed devices in enterprise +and education environments. + +6 +00:00:22,656 --> 00:00:26,593 +Let's start by reviewing +the device security landscape. + +7 +00:00:26,627 --> 00:00:29,596 +Your users need to access +organization resources, + +8 +00:00:29,630 --> 00:00:34,067 +such as websites, +application servers, and databases. + +9 +00:00:34,101 --> 00:00:39,006 +And there's attackers who also +want to access those resources. + +10 +00:00:39,039 --> 00:00:43,277 +The classic model for securing resources +is perimeter security. + +11 +00:00:43,310 --> 00:00:46,413 +You draw a security boundary +around an intranet, + +12 +00:00:46,446 --> 00:00:49,216 +and stand up a firewall or VPN + +13 +00:00:49,249 --> 00:00:53,020 +that permits legitimate clients +and rejects threats. + +14 +00:00:53,053 --> 00:00:59,259 +But this model hasn't kept up with the way +people interact with modern organizations. + +15 +00:00:59,293 --> 00:01:04,331 +Cloud service providers put resources +outside the perimeter. + +16 +00:01:04,364 --> 00:01:07,501 +Threats can start +from inside the perimeter. + +17 +00:01:08,902 --> 00:01:13,707 +And threats can spoof legitimate clients +to penetrate the perimeter. + +18 +00:01:15,442 --> 00:01:20,080 +A more modern security model +does not trust a network perimeter. + +19 +00:01:20,113 --> 00:01:24,451 +Instead, each resource performs +its own trust evaluation. + +20 +00:01:24,484 --> 00:01:28,188 +This is a core principle +of zero trust architecture. + +21 +00:01:29,623 --> 00:01:33,193 +You can think of trust evaluation +as a function: + +22 +00:01:33,227 --> 00:01:36,864 +the input is posture information +about the client, + +23 +00:01:36,897 --> 00:01:41,502 +and the output is the decision +to grant or deny access. + +24 +00:01:41,535 --> 00:01:45,873 +It's critical to get +trust evaluation right. + +25 +00:01:45,906 --> 00:01:49,643 +A false negative gets in the way +of user activities, + +26 +00:01:49,676 --> 00:01:55,682 +or worse, a false positive allows +an attacker to access your resources. + +27 +00:01:55,716 --> 00:02:00,521 +And that means having accurate +posture information is also critical. + +28 +00:02:00,554 --> 00:02:03,824 +Let's investigate +common components of posture. + +29 +00:02:03,857 --> 00:02:08,161 +You use all available information about +the client and its request: + +30 +00:02:08,195 --> 00:02:09,897 +who is making the request, + +31 +00:02:09,930 --> 00:02:13,667 +what device they're using, +where they are, and so on. + +32 +00:02:13,700 --> 00:02:16,103 +Trust evaluation may use different details + +33 +00:02:16,136 --> 00:02:20,240 +within the posture +for accessing different resources. + +34 +00:02:20,274 --> 00:02:25,245 +Accessing a low-security resource +may only require the user's identity, + +35 +00:02:25,279 --> 00:02:30,684 +but accessing critical infrastructure +may require evaluating all posture items. + +36 +00:02:30,717 --> 00:02:34,555 +It's up to the organization +to decide which details are relevant. + +37 +00:02:36,190 --> 00:02:40,093 +One central element of posture +is the user's identity. + +38 +00:02:40,127 --> 00:02:42,896 +This indicates who is making the request. + +39 +00:02:44,364 --> 00:02:48,435 +Apple devices provide several technologies +to support user identity, + +40 +00:02:48,468 --> 00:02:51,605 +such as the Extensible Single Sign On +feature, + +41 +00:02:51,638 --> 00:02:54,208 +including the built-in Kerberos extension, + +42 +00:02:54,241 --> 00:02:59,146 +to facilitate user authentication +for apps, websites, and accounts. + +43 +00:02:59,179 --> 00:03:02,015 +And the new +Enrollment Single Sign On feature + +44 +00:03:02,049 --> 00:03:05,853 +lets an app facilitate +user authentications during and after + +45 +00:03:05,886 --> 00:03:09,323 +the enrollment process +of User Enrollments. + +46 +00:03:09,356 --> 00:03:12,359 +But this session isn't about +user identity, + +47 +00:03:12,392 --> 00:03:15,262 +it's about device identity. + +48 +00:03:15,295 --> 00:03:20,434 +This element of posture indicates +which device is making the request. + +49 +00:03:22,102 --> 00:03:26,406 +The UDID that the device reports +in each MDM communication + +50 +00:03:26,440 --> 00:03:31,945 +is the primary way that your MDM server +knows which device it's managing. + +51 +00:03:31,979 --> 00:03:35,582 +The DeviceInformation query +also provides to the MDM server + +52 +00:03:35,616 --> 00:03:39,887 +attributes of the device, +including the serial number. + +53 +00:03:39,920 --> 00:03:44,625 +A managed device often communicates +with other systems inside the organization + +54 +00:03:44,658 --> 00:03:47,528 +aside from the MDM server. + +55 +00:03:47,561 --> 00:03:52,766 +So, often the MDM server configures +the device with client certificates + +56 +00:03:52,799 --> 00:03:57,504 +that declare the device's identity +to those other systems. + +57 +00:03:57,538 --> 00:04:01,041 +These methods of identifying a device +have served us well, + +58 +00:04:01,074 --> 00:04:06,980 +but they amount to trusting that the +device is the one that it claims to be. + +59 +00:04:07,014 --> 00:04:08,982 +As the landscape has changed, + +60 +00:04:09,016 --> 00:04:11,818 +with devices more distributed +than ever before, + +61 +00:04:11,852 --> 00:04:14,488 +our security needs have evolved. + +62 +00:04:14,521 --> 00:04:18,225 +To address this, +I'm excited to share a powerful new method + +63 +00:04:18,258 --> 00:04:21,094 +for proving a device's identity, + +64 +00:04:21,128 --> 00:04:24,598 +Managed Device Attestation. + +65 +00:04:24,631 --> 00:04:30,237 +With Managed Device Attestation, a device +provides strong evidence about itself + +66 +00:04:30,270 --> 00:04:32,039 +when making a request. + +67 +00:04:32,072 --> 00:04:34,908 +It improves posture information, + +68 +00:04:34,942 --> 00:04:39,012 +so trust evaluations based on that +are more accurate. + +69 +00:04:39,046 --> 00:04:43,283 +In short, Managed Device Attestation +means legitimate devices + +70 +00:04:43,317 --> 00:04:48,889 +reliably access resources, +and attackers are thwarted. + +71 +00:04:48,922 --> 00:04:53,360 +This release brings +Managed Device Attestation for iOS 16, + +72 +00:04:53,393 --> 00:04:58,031 +iPadOS 16, and tvOS 16. + +73 +00:04:58,065 --> 00:05:02,803 +This session we'll start with an overview +of the new attestation features, + +74 +00:05:02,836 --> 00:05:06,673 +explain the benefits +of using attestations, + +75 +00:05:06,707 --> 00:05:10,644 +and then dive into +the implementation details. + +76 +00:05:10,677 --> 00:05:14,648 +First, what are attestations? + +77 +00:05:14,681 --> 00:05:18,185 +An attestation is a declaration of a fact. + +78 +00:05:18,218 --> 00:05:21,788 +If you trust the entity making the claim, + +79 +00:05:21,822 --> 00:05:25,092 +you accept that the fact is true. + +80 +00:05:25,125 --> 00:05:30,297 +In software, an attestation is a fact +that is cryptographically signed. + +81 +00:05:30,330 --> 00:05:33,400 +This is usually an X.509 certificate. + +82 +00:05:33,433 --> 00:05:38,105 +If you trust the signer, +you accept that the fact is true. + +83 +00:05:38,138 --> 00:05:40,674 +For Managed Device Attestation, + +84 +00:05:40,707 --> 00:05:45,012 +the facts are the identity +and other properties of a device, + +85 +00:05:45,045 --> 00:05:47,648 +and the signer is Apple. + +86 +00:05:47,681 --> 00:05:51,885 +Accepting the accuracy of these +device facts requires trusting Apple. + +87 +00:05:51,919 --> 00:05:57,658 +However, it doesn't require trusting +every line of code ever written by Apple. + +88 +00:05:58,525 --> 00:06:01,795 +It only requires trusting +the Secure Enclave + +89 +00:06:01,828 --> 00:06:03,997 +and Apple's attestation servers, + +90 +00:06:04,031 --> 00:06:08,635 +which access Apple's manufacturing records +and operating system catalog. + +91 +00:06:08,669 --> 00:06:11,939 +And if you keep your data +on Apple devices at all, + +92 +00:06:11,972 --> 00:06:15,309 +you're implicitly trusting those. + +93 +00:06:15,342 --> 00:06:20,747 +Here's how we're bringing the power +of attestations to managed devices. + +94 +00:06:20,781 --> 00:06:26,119 +Managed Device Attestation offers two ways +to use attestation certificates. + +95 +00:06:26,153 --> 00:06:29,623 +We've enhanced +the DeviceInformation MDM command, + +96 +00:06:29,656 --> 00:06:34,661 +which makes the benefits of attestation +available to the MDM server. + +97 +00:06:34,695 --> 00:06:39,366 +And we've added support for the Automatic +Certificate Management Environment, + +98 +00:06:39,399 --> 00:06:44,004 +or ACME, protocol +by adding an ACME profile payload, + +99 +00:06:44,037 --> 00:06:46,306 +which makes the benefits of attestation +available + +100 +00:06:46,340 --> 00:06:49,543 +throughout the organization's +infrastructure. + +101 +00:06:50,711 --> 00:06:53,046 +For DeviceInformation attestation, + +102 +00:06:53,080 --> 00:06:58,919 +the MDM server issues a DeviceInformation +query and specifies some new keys. + +103 +00:06:58,952 --> 00:07:02,489 +The device obtains an attestation +from Apple’s servers + +104 +00:07:02,523 --> 00:07:05,225 +and returns it to the MDM server. + +105 +00:07:05,259 --> 00:07:08,462 +Then the MDM server +evaluates the attestation. + +106 +00:07:10,297 --> 00:07:11,965 +But be careful. + +107 +00:07:11,999 --> 00:07:16,036 +DeviceInformation attestation declares +to the MDM server, + +108 +00:07:16,069 --> 00:07:18,972 +"A device exists with these properties." + +109 +00:07:19,006 --> 00:07:23,510 +It does not prove that the device the MDM +server is currently communicating with + +110 +00:07:23,544 --> 00:07:25,712 +is that same device. + +111 +00:07:25,746 --> 00:07:28,815 +For that, +you need ACME payload attestation. + +112 +00:07:31,151 --> 00:07:34,721 +ACME payload attestation +provides the strongest proof + +113 +00:07:34,755 --> 00:07:37,090 +of the identity of the device. + +114 +00:07:37,124 --> 00:07:40,427 +When you install a profile +containing an ACME payload, + +115 +00:07:40,460 --> 00:07:45,299 +the device requests a certificate +from an organization ACME server. + +116 +00:07:45,332 --> 00:07:49,503 +This is very similar +to installing a SCEP payload. + +117 +00:07:49,536 --> 00:07:53,440 +The device provides an attestation +to the ACME server. + +118 +00:07:53,473 --> 00:07:57,244 +Based upon this strong proof +of the identity of the device, + +119 +00:07:57,277 --> 00:08:00,447 +the ACME server issues +a new client certificate + +120 +00:08:00,480 --> 00:08:04,017 +that the rest of +your organization's servers trust. + +121 +00:08:04,051 --> 00:08:06,820 +These two new features use +attestation certificates + +122 +00:08:06,854 --> 00:08:08,689 +to prove several things: + +123 +00:08:08,722 --> 00:08:11,792 +the device is genuine Apple hardware; + +124 +00:08:11,825 --> 00:08:15,495 +the device is a specific device; + +125 +00:08:15,529 --> 00:08:18,832 +the device has certain properties; + +126 +00:08:18,866 --> 00:08:22,836 +and a private key is bound to the device. + +127 +00:08:22,870 --> 00:08:26,573 +And it proves to different servers +that they are communicating + +128 +00:08:26,607 --> 00:08:28,642 +with the same device. + +129 +00:08:28,675 --> 00:08:32,079 +How do these attestations benefit you? + +130 +00:08:32,112 --> 00:08:34,982 +Attestations are primarily +a security feature, + +131 +00:08:35,015 --> 00:08:39,419 +so I'll describe some of the threats +and how attestations mitigate them. + +132 +00:08:40,420 --> 00:08:45,125 +First, a compromised device +lies about its properties, + +133 +00:08:45,158 --> 00:08:47,895 +so Apple attests to the properties. + +134 +00:08:47,928 --> 00:08:49,596 +Even if the OS is compromised, + +135 +00:08:49,630 --> 00:08:52,766 +that doesn't affect the reliability +of the attestation. + +136 +00:08:52,799 --> 00:08:56,703 +It only requires the Secure Enclave +to be intact. + +137 +00:08:56,737 --> 00:09:01,441 +Or a compromised device provides +an outdated attestation of properties + +138 +00:09:01,475 --> 00:09:03,744 +that have since changed. + +139 +00:09:03,777 --> 00:09:09,116 +A nonce in the attestation ensures +that the facts are up to date. + +140 +00:09:09,149 --> 00:09:12,953 +ACME payload attestation +mitigates other threats. + +141 +00:09:12,986 --> 00:09:16,990 +A compromised device +sends a different device's identifiers + +142 +00:09:17,024 --> 00:09:20,127 +when communicating with the MDM server. + +143 +00:09:20,160 --> 00:09:25,799 +So Apple attests device identifiers in +a way that's tied to the client identity + +144 +00:09:25,832 --> 00:09:29,837 +that the device uses +to authenticate its TLS connection. + +145 +00:09:29,870 --> 00:09:31,905 +This proves to your MDM server + +146 +00:09:31,939 --> 00:09:36,210 +and other organization servers +which device they are communicating with. + +147 +00:09:37,678 --> 00:09:42,149 +Or, an attacker extracts a private key +from a legitimate device + +148 +00:09:42,182 --> 00:09:47,120 +and uses it to make requests, +spoofing the legitimate device. + +149 +00:09:47,154 --> 00:09:51,491 +Apple attests that the private key +is protected by the Secure Enclave, + +150 +00:09:51,525 --> 00:09:53,794 +which has exceptionally strong protections + +151 +00:09:53,827 --> 00:09:57,297 +against exporting +or importing private keys. + +152 +00:09:57,331 --> 00:10:01,335 +Last, an attacker hijacks +a certificate request + +153 +00:10:01,368 --> 00:10:06,940 +to get a certificate authority to issue +a certificate for a different device. + +154 +00:10:06,974 --> 00:10:10,177 +Apple attests the identity +of the requesting device + +155 +00:10:10,210 --> 00:10:13,146 +in a way that ties it to the certificate +request, + +156 +00:10:13,180 --> 00:10:15,949 +so the certificate authority +only issues certificates + +157 +00:10:15,983 --> 00:10:19,052 +to the legitimate device. + +158 +00:10:19,086 --> 00:10:23,423 +Attestations give you security benefits +that mitigate several threats. + +159 +00:10:23,457 --> 00:10:26,727 +So how do you use them +in your environment? + +160 +00:10:26,760 --> 00:10:31,965 +Let's get into the details of how to +implement Managed Device Attestation. + +161 +00:10:31,999 --> 00:10:36,270 +First, there are the enhancements +to the DeviceInformation command. + +162 +00:10:36,303 --> 00:10:40,741 +An MDM server can issue this command +to a managed device. + +163 +00:10:40,774 --> 00:10:45,546 +The request includes a list of properties +that the server wants to know. + +164 +00:10:45,579 --> 00:10:49,883 +We've added a new property, +DevicePropertiesAttestation. + +165 +00:10:49,917 --> 00:10:53,120 +Adding it to the Queries array +means the MDM server + +166 +00:10:53,153 --> 00:10:56,323 +is requesting an attestation. + +167 +00:10:56,356 --> 00:10:59,059 +To ensure that the attestation is fresh, + +168 +00:10:59,092 --> 00:11:04,097 +the MDM server can use +the DeviceAttestationNonce key. + +169 +00:11:04,131 --> 00:11:08,702 +This appears at the same level +in the request as the Queries key. + +170 +00:11:08,735 --> 00:11:10,537 +This key is optional. + +171 +00:11:10,571 --> 00:11:15,809 +Its value is a data object, +with a maximum size of 32 bytes. + +172 +00:11:15,843 --> 00:11:19,213 +Here's an example +that requests attestation. + +173 +00:11:19,246 --> 00:11:23,383 +The Queries array contains +the DevicePropertiesAttestation key, + +174 +00:11:23,417 --> 00:11:26,887 +and there's a 32 byte nonce. + +175 +00:11:26,920 --> 00:11:29,623 +When obtaining the attestation +is successful, + +176 +00:11:29,656 --> 00:11:34,127 +the response contains +a DevicePropertiesAttestation key. + +177 +00:11:34,161 --> 00:11:37,331 +Its value is an array of data objects. + +178 +00:11:37,364 --> 00:11:42,336 +Each element in the array +is a certificate in a certificate chain. + +179 +00:11:42,369 --> 00:11:44,938 +This is an example response. + +180 +00:11:44,972 --> 00:11:48,775 +The leaf certificate +appears first in the array, + +181 +00:11:48,809 --> 00:11:53,780 +and it contains device properties +in custom OIDs. + +182 +00:11:53,814 --> 00:11:59,720 +The first two OIDs are device identifying +properties, serial number and UDID. + +183 +00:11:59,753 --> 00:12:05,392 +They are omitted from the certificate if +the MDM enrollment is a User Enrollment. + +184 +00:12:05,425 --> 00:12:10,797 +The remaining OIDs are anonymous, +and available for all enrollment types. + +185 +00:12:10,831 --> 00:12:14,468 +sepOS version refers to the version +of the operating system + +186 +00:12:14,501 --> 00:12:17,437 +that runs on the Secure Enclave. + +187 +00:12:17,471 --> 00:12:20,841 +And the presence of the correct value +in the nonce OID + +188 +00:12:20,874 --> 00:12:24,811 +proves that the certificate +was just generated. + +189 +00:12:24,845 --> 00:12:27,314 +When the MDM server +receives an attestation + +190 +00:12:27,347 --> 00:12:30,851 +it must carefully validate it, +in the following order. + +191 +00:12:30,884 --> 00:12:34,087 +It verifies that the cert chain is rooted +with the expected + +192 +00:12:34,121 --> 00:12:36,757 +Apple certificate authority. + +193 +00:12:36,790 --> 00:12:38,959 +The Apple certificate authority +is available + +194 +00:12:38,992 --> 00:12:42,963 +from the Apple Private PKI Repository. + +195 +00:12:42,996 --> 00:12:46,834 +It verifies that the nonce +in the leaf certificate matches the nonce + +196 +00:12:46,867 --> 00:12:50,270 +in the DeviceInformation request, +if one was specified. + +197 +00:12:50,304 --> 00:12:55,342 +And then it parses out the remaining +OIDs and evaluates their values. + +198 +00:12:55,375 --> 00:12:59,546 +Generating new attestations uses +significant resources on the device + +199 +00:12:59,580 --> 00:13:01,415 +and Apple's servers, + +200 +00:13:01,448 --> 00:13:06,286 +so there's a rate limit on how often new +attestation certificates can be requested, + +201 +00:13:06,320 --> 00:13:10,824 +currently one new attestation +every seven days. + +202 +00:13:10,858 --> 00:13:16,063 +You request a fresh attestation +by specifying a new nonce. + +203 +00:13:16,096 --> 00:13:19,933 +Omitting a nonce indicates +that freshness is not a concern, + +204 +00:13:19,967 --> 00:13:24,771 +so the device can return +its most recent attestation instead. + +205 +00:13:24,805 --> 00:13:29,076 +And if the nonce is specified +and matches that cached attestation, + +206 +00:13:29,109 --> 00:13:32,613 +the cached attestation is returned. + +207 +00:13:32,646 --> 00:13:36,316 +When the MDM server validates +the nonce in an attestation, + +208 +00:13:36,350 --> 00:13:38,952 +it should detect a mismatched nonce + +209 +00:13:38,986 --> 00:13:44,057 +and determine whether that was expected +due to caching or not. + +210 +00:13:44,091 --> 00:13:48,028 +But don't request a new attestation +every seven days + +211 +00:13:48,061 --> 00:13:50,297 +just because that's the rate limit. + +212 +00:13:50,330 --> 00:13:54,201 +Doing that will only delay how quickly +your MDM server discovers changes + +213 +00:13:54,234 --> 00:13:58,605 +in device properties, +not to mention waste resources. + +214 +00:13:58,639 --> 00:14:03,677 +Instead, monitor for relevant changes +in the other DeviceInformation properties, + +215 +00:14:03,710 --> 00:14:05,646 +such as the OS version. + +216 +00:14:05,679 --> 00:14:10,050 +When one of those changes, +then request a fresh attestation. + +217 +00:14:10,083 --> 00:14:14,788 +This ensures the attestation is updated +as quickly as possible after a change, + +218 +00:14:14,821 --> 00:14:18,392 +rather than waiting +for the rate limit to expire. + +219 +00:14:18,425 --> 00:14:23,830 +And, just in case the device is compromised +and lying about these other properties, + +220 +00:14:23,864 --> 00:14:27,701 +slip in the occasional random request +for a fresh attestation + +221 +00:14:27,734 --> 00:14:29,803 +to keep the device honest. + +222 +00:14:29,837 --> 00:14:33,106 +Requesting an attestation may fail. + +223 +00:14:33,140 --> 00:14:36,243 +When that happens, +the device still responds + +224 +00:14:36,276 --> 00:14:38,779 +but some information is omitted. + +225 +00:14:38,812 --> 00:14:43,450 +Either the DevicePropertiesAttestation +field is omitted from the response, + +226 +00:14:43,483 --> 00:14:47,254 +or an expected OID +or its value is omitted. + +227 +00:14:47,287 --> 00:14:49,990 +There are many potential reasons +for a failure: + +228 +00:14:50,023 --> 00:14:55,429 +the device experiences a network issue +reaching Apple's attestation servers. + +229 +00:14:55,462 --> 00:14:58,098 +No server is up 100% of the time, + +230 +00:14:58,131 --> 00:15:02,202 +so there may be an issue +with Apple's attestation servers. + +231 +00:15:02,236 --> 00:15:05,906 +Or the device hardware or software +may be compromised, + +232 +00:15:05,939 --> 00:15:08,542 +or it's not even genuine Apple hardware. + +233 +00:15:08,575 --> 00:15:12,212 +In these last three cases, +Apple's attestation servers + +234 +00:15:12,246 --> 00:15:17,584 +refuse to issue an attestation +for properties that they cannot verify. + +235 +00:15:17,618 --> 00:15:20,187 +The different causes +of a failed attestation + +236 +00:15:20,220 --> 00:15:24,625 +range from a harmless network glitch +to an active attack. + +237 +00:15:24,658 --> 00:15:27,361 +Unfortunately, +there is no trustworthy way + +238 +00:15:27,394 --> 00:15:30,931 +for the MDM server +to know the exact cause. + +239 +00:15:30,964 --> 00:15:34,101 +This is because the only source +of information about the failure + +240 +00:15:34,134 --> 00:15:39,439 +is the device itself, which may be +a compromised device that's lying. + +241 +00:15:39,473 --> 00:15:43,277 +So how should an MDM server +interpret a failure? + +242 +00:15:43,310 --> 00:15:48,148 +Don't always assume the worst +when attestation fails. + +243 +00:15:48,182 --> 00:15:50,484 +If you have a zero trust architecture, + +244 +00:15:50,517 --> 00:15:52,753 +here's how you would likely handle it. + +245 +00:15:52,786 --> 00:15:56,790 +The organization calculates a trust score +for the device, + +246 +00:15:56,823 --> 00:16:02,896 +with a failed or unexpectedly stale +attestation lowering that score. + +247 +00:16:02,930 --> 00:16:05,966 +A lowered trust score +triggers different actions, + +248 +00:16:05,999 --> 00:16:08,969 +such as denying access to services, + +249 +00:16:09,002 --> 00:16:11,705 +flagging the device +for manual investigation, + +250 +00:16:11,738 --> 00:16:15,642 +or remediating by wiping it +and revoking its certificates. + +251 +00:16:15,676 --> 00:16:20,414 +This ensures an appropriate response +to a failed attestation. + +252 +00:16:20,447 --> 00:16:24,952 +Let's move on to implementing +ACME payload attestation. + +253 +00:16:24,985 --> 00:16:28,522 +Installing an ACME payload +involves several steps. + +254 +00:16:28,555 --> 00:16:33,460 +I'll describe the different participants +in the process, then each of those steps. + +255 +00:16:33,493 --> 00:16:37,764 +We start with an iPhone, iPad or Apple TV. + +256 +00:16:38,999 --> 00:16:43,737 +In most cases this is managed +by an MDM server. + +257 +00:16:43,770 --> 00:16:45,606 +There's an ACME server. + +258 +00:16:45,639 --> 00:16:50,777 +This implements the ACME protocol, +RFC 8555, + +259 +00:16:50,811 --> 00:16:56,783 +so it can issue client certificates from +an organization certificate authority. + +260 +00:16:56,817 --> 00:17:01,555 +And there's Apple's attestation servers +that issue the attestations. + +261 +00:17:03,390 --> 00:17:07,294 +The first step is for the MDM server +to install a profile + +262 +00:17:07,327 --> 00:17:10,397 +containing an ACME payload. + +263 +00:17:10,430 --> 00:17:16,036 +The payload specifies properties +of the key the device will generate, + +264 +00:17:16,069 --> 00:17:22,276 +properties of the certificate +that the device will request, + +265 +00:17:22,309 --> 00:17:27,548 +and how to request the certificate +from the ACME server. + +266 +00:17:27,581 --> 00:17:29,483 +To start installing the profile, + +267 +00:17:29,516 --> 00:17:33,587 +the device generates +the requested type of key. + +268 +00:17:33,620 --> 00:17:37,991 +In order to use attestations, +the key must be hardware-bound. + +269 +00:17:38,025 --> 00:17:42,296 +While the ACME payload supports RSA +and various sizes of keys, + +270 +00:17:42,329 --> 00:17:47,801 +in order to get a hardware-bound key, +you must use ECSECPrimeRandom. + +271 +00:17:47,835 --> 00:17:53,707 +Your best choice is +a ECSECPrimeRandom 384 bit key + +272 +00:17:53,740 --> 00:17:58,345 +because that's the highest security +hardware-bound key. + +273 +00:17:58,378 --> 00:18:03,217 +Once the key is created, the device makes +initial contact with the ACME server. + +274 +00:18:05,052 --> 00:18:08,155 +The device requests the DirectoryURL, + +275 +00:18:08,188 --> 00:18:11,792 +which specifies the URLs to use +for the rest of the process + +276 +00:18:11,825 --> 00:18:14,595 +of communicating with the ACME server. + +277 +00:18:14,628 --> 00:18:18,699 +Then the two systems create an account +and an order. + +278 +00:18:18,732 --> 00:18:24,404 +The server offers +the device-attest-01 validation type. + +279 +00:18:24,438 --> 00:18:27,508 +Then, the ACME server generates a nonce + +280 +00:18:27,541 --> 00:18:32,012 +and sends it to the device +in the token field. + +281 +00:18:32,045 --> 00:18:36,483 +The ACME protocol was initially used +to issue server certificates. + +282 +00:18:36,517 --> 00:18:39,319 +Here however, +the validation type we use + +283 +00:18:39,353 --> 00:18:42,356 +was introduced in an IETF draft + +284 +00:18:42,389 --> 00:18:45,692 +specifying an extension +of the ACME protocol + +285 +00:18:45,726 --> 00:18:49,730 +for receiving attestations +and issuing client certificates. + +286 +00:18:50,931 --> 00:18:53,000 +Attestation is optional. + +287 +00:18:53,033 --> 00:18:55,502 +When the payload specifies attestation, + +288 +00:18:55,536 --> 00:18:59,106 +the device requests an attestation +from Apple. + +289 +00:18:59,139 --> 00:19:03,443 +This is nearly identical +to the DeviceInformation attestation. + +290 +00:19:03,477 --> 00:19:05,679 +It uses the same OIDs, + +291 +00:19:05,712 --> 00:19:10,751 +and device-identifying OIDs are omitted +for User Enrollments. + +292 +00:19:10,784 --> 00:19:13,153 +But there's a few differences. + +293 +00:19:13,187 --> 00:19:20,394 +The nonce is hashed using SHA-256 +before embedding it in the attestation. + +294 +00:19:20,427 --> 00:19:25,165 +The nonce comes from the ACME server, +not the MDM server. + +295 +00:19:25,199 --> 00:19:29,436 +And the private key that matches +the attestation leaf certificate + +296 +00:19:29,469 --> 00:19:33,540 +is the one the device just generated. + +297 +00:19:33,574 --> 00:19:36,310 +The attestation certificate +matches the private key, + +298 +00:19:36,343 --> 00:19:42,816 +however that certificate can't be used +for any purposes besides attestation. + +299 +00:19:42,850 --> 00:19:46,687 +So the device requests from +the ACME server another certificate + +300 +00:19:46,720 --> 00:19:50,824 +matching the private key, +and this cert is good for TLS. + +301 +00:19:53,060 --> 00:19:56,029 +The device provides +a certificate signing request + +302 +00:19:56,063 --> 00:20:00,367 +containing the certificate request +properties from the payload. + +303 +00:20:00,400 --> 00:20:03,670 +It provides the attestation chain. + +304 +00:20:03,704 --> 00:20:08,041 +And it provides the ClientIdentifier +from the ACME payload. + +305 +00:20:08,075 --> 00:20:10,344 +Typically this is used like a ticket + +306 +00:20:10,377 --> 00:20:13,413 +that is good for the issuance +of a single certificate, + +307 +00:20:13,447 --> 00:20:15,883 +to prevent repeated requests. + +308 +00:20:15,916 --> 00:20:19,353 +The ACME server must carefully validate +the request + +309 +00:20:19,386 --> 00:20:23,056 +before issuing a certificate, +in this order. + +310 +00:20:23,090 --> 00:20:28,629 +It must verify that the ClientIdentifier +is valid and unused. + +311 +00:20:28,662 --> 00:20:34,401 +The attestation certificate must chain up +to the correct Apple CA. + +312 +00:20:34,434 --> 00:20:40,541 +The public key in the attestation +leaf certificate must match the CSR. + +313 +00:20:40,574 --> 00:20:47,681 +The nonce must match the SHA-256 hash of +the one that the ACME server sent earlier. + +314 +00:20:47,714 --> 00:20:52,386 +And then the ACME server can evaluate +the remaining OIDs. + +315 +00:20:52,419 --> 00:20:55,389 +And remember that attestations may fail. + +316 +00:20:55,422 --> 00:21:00,093 +The ACME server should carefully consider +a failure when issuing a certificate, + +317 +00:21:00,127 --> 00:21:05,532 +just like we reviewed with a failed +attestation in the DeviceInformation case. + +318 +00:21:05,566 --> 00:21:08,402 +From here, things rapidly finish up. + +319 +00:21:08,435 --> 00:21:11,538 +The ACME server issues +a client certificate + +320 +00:21:11,572 --> 00:21:14,842 +from the organization CA +and returns it to the device. + +321 +00:21:17,077 --> 00:21:22,015 +The ACME server is the final authority +for client certificate issuance. + +322 +00:21:22,049 --> 00:21:25,919 +It can choose to honor or override +the properties in the CSR + +323 +00:21:25,953 --> 00:21:28,789 +such as the SubjectAltName. + +324 +00:21:28,822 --> 00:21:31,859 +The device stores +the certificate in the keychain, + +325 +00:21:31,892 --> 00:21:35,095 +and this completes the installation +of the ACME payload. + +326 +00:21:36,930 --> 00:21:38,999 +Let's tie all this together. + +327 +00:21:39,032 --> 00:21:42,469 +How do servers know +the device communicating with them + +328 +00:21:42,503 --> 00:21:45,339 +is the one it claims to be? + +329 +00:21:45,372 --> 00:21:49,376 +The device uses the same private key +in multiple ways: + +330 +00:21:49,409 --> 00:21:51,879 +when getting an attestation from Apple, + +331 +00:21:51,912 --> 00:21:55,048 +when getting a client certificate +from the ACME server, + +332 +00:21:55,082 --> 00:21:58,785 +and when using TLS +to communicate with other servers. + +333 +00:21:58,819 --> 00:22:01,421 +Because the key is hardware-bound, + +334 +00:22:01,455 --> 00:22:05,659 +we know all these actions were performed +by the same device. + +335 +00:22:05,692 --> 00:22:11,465 +And we have an attestation certificate +which describes that device. + +336 +00:22:11,498 --> 00:22:15,802 +Combining these, +organization servers now have confidence + +337 +00:22:15,836 --> 00:22:19,173 +in the device's identity +when granting access. + +338 +00:22:21,742 --> 00:22:24,745 +Just like with the certificate +and SCEP payloads, + +339 +00:22:24,778 --> 00:22:28,081 +other payloads in the profile +can reference the ACME payload + +340 +00:22:28,115 --> 00:22:30,350 +in order to use the certificate. + +341 +00:22:30,384 --> 00:22:33,820 +Use it for MDM, Wi-Fi, VPN, + +342 +00:22:33,854 --> 00:22:35,989 +Kerberos, Safari. + +343 +00:22:36,023 --> 00:22:39,626 +All these systems benefit +from attestation. + +344 +00:22:42,196 --> 00:22:44,865 +A device can have up to 10 ACME payloads + +345 +00:22:44,898 --> 00:22:48,635 +that use attestation +installed at the same time. + +346 +00:22:48,669 --> 00:22:52,105 +Note that hardware-bound keys +are not preserved + +347 +00:22:52,139 --> 00:22:54,808 +when a managed device's backup +is restored, + +348 +00:22:54,842 --> 00:22:58,011 +even when restoring to the same device. + +349 +00:22:58,045 --> 00:23:02,149 +And if you do nothing else +with Managed Device Attestation, + +350 +00:23:02,182 --> 00:23:05,819 +use an ACME payload +for the MDM client identity + +351 +00:23:05,853 --> 00:23:10,290 +so the MDM server can be sure +which device it's managing. + +352 +00:23:10,324 --> 00:23:12,059 +Let's wrap up. + +353 +00:23:12,092 --> 00:23:17,898 +You use Managed Device Attestation +to remediate multiple classes of threats. + +354 +00:23:17,931 --> 00:23:23,403 +You leverage DeviceInformation attestation +to improve the device identity component + +355 +00:23:23,437 --> 00:23:27,107 +of posture for better trust evaluation. + +356 +00:23:27,140 --> 00:23:33,113 +And, you can now prove a device's identity +as it accesses organization resources + +357 +00:23:33,146 --> 00:23:35,349 +using ACME attestation. + +358 +00:23:35,382 --> 00:23:40,053 +We look forward to your implementation +of Managed Device Attestation. + +359 +00:23:40,087 --> 00:23:44,725 +Together, we'll improve the security +of your device deployments. + +360 +00:23:44,758 --> 00:23:48,395 +Thank you, and have a great WWDC. + diff --git a/eng/2022 Session 10144 Deliver reliable streams with HLS Content Steering en.srt b/eng/2022 Session 10144 Deliver reliable streams with HLS Content Steering en.srt new file mode 100644 index 0000000..95416b1 --- /dev/null +++ b/eng/2022 Session 10144 Deliver reliable streams with HLS Content Steering en.srt @@ -0,0 +1,1644 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,977 --> 00:00:12,846 +Zheng Naiwei: +Hello and welcome to WWDC. + +3 +00:00:12,880 --> 00:00:16,750 +My name is Zheng Naiwei +from Apple's AVFoundation Team. + +4 +00:00:16,783 --> 00:00:20,120 +In this session we are going to talk about +how to improve + +5 +00:00:20,153 --> 00:00:25,492 +streaming delivery reliability with new +features we added to HLS Content Steering. + +6 +00:00:26,660 --> 00:00:29,029 +We will cover three topics today. + +7 +00:00:29,062 --> 00:00:31,798 +If you haven't heard of Content Steering, +don't worry. + +8 +00:00:31,832 --> 00:00:33,967 +It's a piece of HLS technology + +9 +00:00:34,001 --> 00:00:37,104 +that can dynamically steer +streaming traffic, + +10 +00:00:37,137 --> 00:00:39,907 +to improve streaming service quality. + +11 +00:00:39,940 --> 00:00:42,976 +I will give you a short review +to help you get on track. + +12 +00:00:44,878 --> 00:00:48,182 +Then I will present +the new Pathway cloning feature, + +13 +00:00:48,215 --> 00:00:52,219 +which can bring +dynamic steering ability beyond limits, + +14 +00:00:52,252 --> 00:00:55,689 +to further improve the reliability +of your streaming service. + +15 +00:00:56,990 --> 00:01:04,031 +Finally, I will use concrete examples +to guide you through how to influence + +16 +00:01:04,063 --> 00:01:06,533 +server-side logic for steering traffic. +Let's wait no more and get started. + +17 +00:01:07,768 --> 00:01:10,571 +Back in the time we don't have +Content Steering, + +18 +00:01:10,604 --> 00:01:14,074 +variant selection in case of error +fallback wasn't standardized + +19 +00:01:14,107 --> 00:01:16,310 +in the HLS specification. + +20 +00:01:16,343 --> 00:01:19,580 +And different client implementations +can behave differently + +21 +00:01:19,613 --> 00:01:22,349 +in choosing the next fallback variant. + +22 +00:01:22,382 --> 00:01:25,586 +But a typical way to do this +is to follow the variant ordering + +23 +00:01:25,619 --> 00:01:27,354 +in the multivariant playlist. + +24 +00:01:28,255 --> 00:01:32,826 +If a streaming provider wants to specify +a set of fallback CDNs, + +25 +00:01:32,860 --> 00:01:35,863 +they need to list every variant +from every CDN + +26 +00:01:35,896 --> 00:01:38,866 +and properly order them +in the multivariant playlist. + +27 +00:01:38,899 --> 00:01:42,302 +This way, in case the client player +encountered a failure + +28 +00:01:42,336 --> 00:01:46,106 +in the first variant, +it can move on to the next variant + +29 +00:01:46,139 --> 00:01:50,511 +in the playlist with the failed variant +penalized from selection. + +30 +00:01:50,544 --> 00:01:55,249 +In this case, we see that the client +player had trouble with the 6 Mbps variant + +31 +00:01:55,282 --> 00:02:01,088 +from CDN1 and thus moved on +to the next 3 Mbps variant from CDN1, + +32 +00:02:01,121 --> 00:02:04,758 +following the order +in the multivariant playlist. + +33 +00:02:04,791 --> 00:02:10,097 +If unfortunately the 3 Mbps variant +from CDN1 also failed, + +34 +00:02:10,130 --> 00:02:14,201 +the client player would be left +with no variants from CDN1, + +35 +00:02:14,234 --> 00:02:18,672 +and move on to +the 6 Mbps variant from CDN2. + +36 +00:02:18,705 --> 00:02:22,776 +It can do this on and on +until no variant to fallback to. + +37 +00:02:22,809 --> 00:02:26,780 +However, even though the playlist +authoring server can control the ordering + +38 +00:02:26,813 --> 00:02:28,248 +of the fallback variants, + +39 +00:02:28,282 --> 00:02:30,117 +such control only happens + +40 +00:02:30,150 --> 00:02:34,354 +at the point of client +requesting the multivariant playlist, + +41 +00:02:34,388 --> 00:02:36,390 +and once the playlist was handed out, + +42 +00:02:36,423 --> 00:02:39,459 +there's no way +to change the fallback ordering. + +43 +00:02:39,493 --> 00:02:42,863 +This is where Content Steering +comes into the picture. + +44 +00:02:42,896 --> 00:02:45,165 +With Content Steering, +the streaming provider + +45 +00:02:45,199 --> 00:02:49,203 +can now group variants into Pathways +with different CDN hosts. + +46 +00:02:50,904 --> 00:02:55,542 +The error fallback behavior is +now standardized for Content Steering. + +47 +00:02:55,576 --> 00:02:57,377 +Pathways are ordered by preference. + +48 +00:02:57,411 --> 00:03:00,514 +In this example, +the CDN1 Pathway at the top + +49 +00:03:00,547 --> 00:03:05,452 +is most preferred, +following CDN2 and CDN3. + +50 +00:03:05,485 --> 00:03:08,922 +The streaming provider also hosts +a Steering Server, + +51 +00:03:08,956 --> 00:03:13,160 +generating Steering Manifests +for each client player. + +52 +00:03:13,193 --> 00:03:16,964 +A Steering Manifest defines rules +of Pathway priorities, + +53 +00:03:16,997 --> 00:03:18,565 +so the player can follow the rules + +54 +00:03:18,599 --> 00:03:22,169 +in selecting and fallback between +variant streams. + +55 +00:03:23,036 --> 00:03:27,241 +Let's say for example the streaming +provider is trying to offload + +56 +00:03:27,274 --> 00:03:31,378 +some of the traffic from CDN1 to CND2. + +57 +00:03:31,411 --> 00:03:33,547 +It would generate a Steering Manifest + +58 +00:03:33,580 --> 00:03:36,283 +with the new Pathway priority rules, + +59 +00:03:36,316 --> 00:03:39,086 +and when a client player playing from CDN1 + +60 +00:03:39,119 --> 00:03:42,122 +requests for a Steering Manifest update, + +61 +00:03:42,155 --> 00:03:45,526 +the Steering Server can send +the prepared Steering Manifest + +62 +00:03:45,559 --> 00:03:48,395 +with the rule changes to the client. + +63 +00:03:48,428 --> 00:03:52,065 +The client will parse and see +the new Pathway Priority rules, + +64 +00:03:52,099 --> 00:03:55,936 +and apply it to its playback session. + +65 +00:03:55,969 --> 00:03:59,106 +In this case the rule change +switched preference order + +66 +00:03:59,139 --> 00:04:02,209 +between Pathway CDN1 and CDN2 + +67 +00:04:02,242 --> 00:04:07,514 +so that the client player will switch +and play from CDN2 immediately. + +68 +00:04:07,548 --> 00:04:09,850 +Then, in case of failures, + +69 +00:04:09,883 --> 00:04:14,988 +the client will first exhaust fallback +variants within the current Pathway, + +70 +00:04:15,022 --> 00:04:17,424 +and fallback to the most preferred Pathway + +71 +00:04:17,457 --> 00:04:20,627 +according to its current Pathway Priority. + +72 +00:04:20,661 --> 00:04:24,498 +In this case, if all variants +from CDN2 were errored out, + +73 +00:04:24,531 --> 00:04:28,168 +the client player can start to choose +from variants from CDN1, + +74 +00:04:28,202 --> 00:04:30,737 +which is the next preferred Pathway. + +75 +00:04:30,771 --> 00:04:34,041 +When we apply Content Steering +to the global scale, + +76 +00:04:34,074 --> 00:04:37,644 +it can solve bigger regional +load balancing problems. + +77 +00:04:37,678 --> 00:04:41,782 +Let's say our streaming service provider +operates all around the world, + +78 +00:04:41,815 --> 00:04:44,284 +with two main CDN providers. + +79 +00:04:44,318 --> 00:04:47,788 +To assign these CDNs +to client players globally, + +80 +00:04:47,821 --> 00:04:51,158 +the Steering Server prepared two +different Steering Manifests, + +81 +00:04:51,191 --> 00:04:56,230 +one prefers CDN1, +and the other prefers CDN2. + +82 +00:04:56,263 --> 00:04:58,699 +Then it distributes +these Steering Manifests + +83 +00:04:58,732 --> 00:05:02,002 +based on the client player's region + +84 +00:05:02,035 --> 00:05:06,306 +so that the North and South America +utilizes CDN1 + +85 +00:05:06,340 --> 00:05:10,143 +and the rest of the world utilizes CDN2. + +86 +00:05:10,177 --> 00:05:13,981 +At the top of the world map +we show a horizontal stacked bar + +87 +00:05:14,014 --> 00:05:18,719 +representing the distribution of traffic +between CDN1 and CDN2. + +88 +00:05:18,752 --> 00:05:23,123 +As of right now, both CDNs are serving +half of the worldwide traffic. + +89 +00:05:24,024 --> 00:05:27,995 +However, over time the streaming +service provider observed + +90 +00:05:28,028 --> 00:05:31,798 +a significant increase of traffic +towards CDN2 + +91 +00:05:31,832 --> 00:05:34,635 +because of global daylight shift. + +92 +00:05:34,668 --> 00:05:38,372 +At the same time, +the traffics toward CDN1 is decreasing. + +93 +00:05:39,640 --> 00:05:44,711 +So the streaming provider decided +to steer the Europe region to use CDN1. + +94 +00:05:44,745 --> 00:05:48,749 +It does so by preparing +a Steering Manifest that prefers CDN1, + +95 +00:05:48,782 --> 00:05:52,653 +and sends it to clients +from the Europe region. + +96 +00:05:52,686 --> 00:05:56,757 +Now those client players in that region +will steer traffic to CDN1, + +97 +00:05:56,790 --> 00:05:58,792 +offloading from CDN2. + +98 +00:05:58,825 --> 00:06:01,461 +And the global traffic +became more balanced. + +99 +00:06:01,495 --> 00:06:05,632 +Now let's take a look at +an HLS multivariant playlist + +100 +00:06:05,666 --> 00:06:07,801 +with Content Steering support. + +101 +00:06:07,835 --> 00:06:11,805 +First we see +the EXT-X-CONTENT-STEERING tag + +102 +00:06:11,839 --> 00:06:16,610 +that indicates this multivariant playlist +uses Content Steering. + +103 +00:06:16,643 --> 00:06:19,713 +Then we have a SERVER-URI attribute + +104 +00:06:19,746 --> 00:06:24,017 +specifying where the client should request +Steering Manifest updates from. + +105 +00:06:25,252 --> 00:06:29,957 +Then the next PATHWAY-ID attribute +specifies the initial pathway to choose + +106 +00:06:29,990 --> 00:06:32,993 +for playback at startup. + +107 +00:06:33,026 --> 00:06:38,198 +Then we can see that each variant stream +were given an PATHWAY-ID attribute + +108 +00:06:38,232 --> 00:06:40,267 +to group them into Pathways. + +109 +00:06:40,300 --> 00:06:43,470 +Each Pathway should have +the same set of variant streams, + +110 +00:06:43,504 --> 00:06:47,975 +with only difference being their URIs +and media group names. + +111 +00:06:48,008 --> 00:06:51,011 +In this example, we have two Pathways, + +112 +00:06:51,044 --> 00:06:53,847 +namely CDN1 and CDN2. + +113 +00:06:53,881 --> 00:06:58,585 +Both contains two variant streams, +one 6 Mbps high resolution video + +114 +00:06:58,619 --> 00:07:02,956 +and one 3 Mbps lower resolution video. + +115 +00:07:02,990 --> 00:07:06,260 +With the only difference +being their URI hostname. + +116 +00:07:06,293 --> 00:07:09,663 +There are also two distinct audio groups +for each Pathway + +117 +00:07:09,696 --> 00:07:13,100 +where they have different URI hostnames. + +118 +00:07:13,133 --> 00:07:17,404 +Here is an example Steering Manifest, +which is a JSON document. + +119 +00:07:17,437 --> 00:07:22,176 +The PATHWAY-PRIORITY field is a list +of Pathway IDs in the preference order. + +120 +00:07:22,209 --> 00:07:27,347 +So in this case, the receiving +client player would prefer CDN1 over CDN2. + +121 +00:07:27,381 --> 00:07:30,784 +This is the Steering Manifest +the Steering Server would provide + +122 +00:07:30,817 --> 00:07:34,922 +to the European clients, +to let them prefer CDN1. + +123 +00:07:34,955 --> 00:07:38,258 +By altering the PATHWAY-PRIORITY field +in a Steering Manifest, + +124 +00:07:38,292 --> 00:07:42,095 +a Steering Server can control +the steering policy for every client. + +125 +00:07:42,129 --> 00:07:45,699 +That's it for a quick overview +of Content Steering. + +126 +00:07:45,732 --> 00:07:48,402 +If you want a more in-depth explanation, + +127 +00:07:48,435 --> 00:07:51,772 +feel free to check out my WWDC21 talk, + +128 +00:07:51,805 --> 00:07:56,710 +Improve global streaming availability +with HLS Content Steering. + +129 +00:07:56,743 --> 00:07:59,713 +However, our journey for +supporting a more scalable + +130 +00:07:59,746 --> 00:08:03,417 +and more reliable streaming service +doesn't stop here. + +131 +00:08:03,450 --> 00:08:08,188 +Nowadays companies can access +more versatile cloud infrastructures + +132 +00:08:08,222 --> 00:08:12,659 +and tools to achieve things +unimaginable in the past, + +133 +00:08:12,693 --> 00:08:16,463 +and we have to catch up +in the leap in technology. + +134 +00:08:16,496 --> 00:08:20,267 +Let's say our streaming service provider +has grown larger this year, + +135 +00:08:20,300 --> 00:08:24,872 +and they are experimenting with a new way +to satisfy the dynamic traffic demands + +136 +00:08:24,905 --> 00:08:27,040 +of its growing user base. + +137 +00:08:27,074 --> 00:08:31,845 +They are doing it by dynamically spawning +fleets of CDN servers in real-time + +138 +00:08:31,879 --> 00:08:35,215 +to alleviate temporal traffics stresses. + +139 +00:08:35,249 --> 00:08:38,819 +For example, +it can spawn a new fleet of CDN3 + +140 +00:08:38,852 --> 00:08:41,788 +and want to advertise it +to existing clients. + +141 +00:08:41,822 --> 00:08:45,926 +However the challenge here +is that the dynamically spawned CDN info + +142 +00:08:45,959 --> 00:08:48,428 +is not included +in the multivariant playlists + +143 +00:08:48,462 --> 00:08:50,964 +when existing client requested it, + +144 +00:08:50,998 --> 00:08:53,133 +because it just didn't exist. + +145 +00:08:53,166 --> 00:08:58,438 +So what can we do to tell our client +players of the emergence of a new CDN? + +146 +00:08:59,273 --> 00:09:03,043 +This is where our new Pathway Cloning +feature comes in handy. + +147 +00:09:03,076 --> 00:09:06,113 +It's a new feature +with backward compatibility + +148 +00:09:06,146 --> 00:09:11,118 +with Content Steering 1.2 +introduced in WWDC21. + +149 +00:09:11,151 --> 00:09:15,022 +With Pathway Cloning, +the Steering Server can announce new CDNs + +150 +00:09:15,055 --> 00:09:16,790 +to existing clients + +151 +00:09:16,823 --> 00:09:20,160 +using a compact manifest definition. + +152 +00:09:20,194 --> 00:09:22,896 +By assuming Pathways +are structure-identical, + +153 +00:09:22,930 --> 00:09:27,901 +new Pathways can be created by copying +and modifying existing Pathways. + +154 +00:09:27,935 --> 00:09:31,672 +Let's take a look +at the structure of a Pathway. + +155 +00:09:31,705 --> 00:09:35,108 +A Pathway consists of one +or many variant streams. + +156 +00:09:35,142 --> 00:09:39,046 +Each variant stream can only be +in one and only one Pathway. + +157 +00:09:39,079 --> 00:09:41,748 +If not specified +the PATHWAY-ID attribute, + +158 +00:09:41,782 --> 00:09:45,619 +it implicitly belongs +to the default “dot” Pathway. + +159 +00:09:45,652 --> 00:09:50,858 +Each variant stream may refer to zero +or one media group for each media type, + +160 +00:09:50,891 --> 00:09:54,027 +out of audio, subtitle, +and closed-caption. + +161 +00:09:54,061 --> 00:09:57,497 +Each media group may be referenced +by multiple variant streams, + +162 +00:09:57,531 --> 00:09:59,600 +even from different Pathways. + +163 +00:09:59,633 --> 00:10:02,636 +When we clone a new Pathway +out of an existing one, + +164 +00:10:02,669 --> 00:10:05,005 +we should not only clone +its variant streams, + +165 +00:10:05,038 --> 00:10:08,108 +but also the referenced media groups, +if any. + +166 +00:10:10,844 --> 00:10:13,180 +Then, to make it a new Pathway, + +167 +00:10:13,213 --> 00:10:16,783 +we need to modify the URIs +of the variant and rendition streams + +168 +00:10:16,817 --> 00:10:19,720 +of the newly cloned Pathway. + +169 +00:10:19,753 --> 00:10:24,491 +Let's take the 6 Mbps variant stream +from the cloned Pathway for example. + +170 +00:10:25,993 --> 00:10:30,330 +Let's say this particular variant stream +has the URI as shown. + +171 +00:10:30,364 --> 00:10:33,867 +To modify it to become the URI +for the new Pathway, + +172 +00:10:33,901 --> 00:10:38,839 +the most flexible way +is to replace the full URI line in whole. + +173 +00:10:38,872 --> 00:10:42,709 +This means we need to store a full set +of URIs in the Steering Manifest + +174 +00:10:42,743 --> 00:10:44,478 +for each cloned Pathway. + +175 +00:10:44,511 --> 00:10:48,415 +However, in practice +we can usually do better than that. + +176 +00:10:48,448 --> 00:10:51,285 +It is common in the industry +to deploy streaming assets + +177 +00:10:51,318 --> 00:10:56,056 +across multiple CDNs +retaining the same URI path structure. + +178 +00:10:56,089 --> 00:11:00,661 +And assets served from the same +URI share the same URI hostname + +179 +00:11:00,694 --> 00:11:02,829 +and query parameters. + +180 +00:11:03,664 --> 00:11:06,767 +If it's the case, +we only need to store the replacement + +181 +00:11:06,800 --> 00:11:09,970 +of host and query parameters +in the manifest, + +182 +00:11:10,003 --> 00:11:14,308 +and replace the components of all cloned +URIs and we got the new Pathway. + +183 +00:11:15,976 --> 00:11:21,381 +Let's take a look at how we can define +the Pathway Clone in a Manifest object. + +184 +00:11:21,415 --> 00:11:25,953 +We added the PATHWAY-CLONES field +with an array of Pathway Clone objects. + +185 +00:11:25,986 --> 00:11:31,558 +Each Pathway Clone object defines a new +Pathway cloned from an existing Pathway. + +186 +00:11:31,592 --> 00:11:35,062 +In this example, +we have one Pathway Clone object. + +187 +00:11:35,095 --> 00:11:41,335 +The BASE-ID field specifies CDN1 +to be the original Pathway to clone from. + +188 +00:11:41,368 --> 00:11:46,673 +The ID field specifies +the new Pathway ID to be CDN3. + +189 +00:11:46,707 --> 00:11:52,479 +Next, we have the URI-REPLACEMENT field +with an object of URI replacement rules. + +190 +00:11:54,081 --> 00:11:58,785 +In this example, we are using the HOST +and query parameters replacement rules, + +191 +00:11:58,819 --> 00:12:00,587 +which should replace the host part, + +192 +00:12:00,621 --> 00:12:06,527 +and insert or replace query parameters +of the stream URIs respectively. + +193 +00:12:06,560 --> 00:12:09,029 +In this case, +we are replacing the host part + +194 +00:12:09,062 --> 00:12:11,465 +to be cdn3.example.com, + +195 +00:12:11,498 --> 00:12:15,936 +and adding or setting query parameter +“foo” with value xyz, + +196 +00:12:15,969 --> 00:12:19,973 +and query parameter “bar” with value 123. + +197 +00:12:21,241 --> 00:12:24,645 +Let's try to apply the host +and parameter URI replacement + +198 +00:12:24,678 --> 00:12:27,714 +on our previous example URI. + +199 +00:12:27,748 --> 00:12:30,317 +First we have the resolved +variant stream URI + +200 +00:12:30,350 --> 00:12:33,220 +based on the multivariant playlist's URI. + +201 +00:12:34,288 --> 00:12:38,225 +In the Steering Manifest +we used HOST URI replacement rule. + +202 +00:12:38,258 --> 00:12:40,727 +So for the host part of the URI, + +203 +00:12:40,761 --> 00:12:44,498 +we replace it with cdn3.example.com, + +204 +00:12:44,531 --> 00:12:48,035 +and got the new host part for the new URI. + +205 +00:12:51,505 --> 00:12:55,943 +Then we retain the URI path component +from the cloned URI. + +206 +00:12:59,179 --> 00:13:03,784 +Finally, we apply the URI query +parameter replacement rule. + +207 +00:13:03,817 --> 00:13:08,956 +Here we replace the “foo” parameter +because it exists on the original URI. + +208 +00:13:08,989 --> 00:13:13,927 +Then we append the “bar” parameter +because it's a new parameter. + +209 +00:13:13,961 --> 00:13:19,132 +Then we have the replaced +query parameter part of the new URI. + +210 +00:13:19,166 --> 00:13:23,737 +The final result URI would be the URI +for the 6 Mbps variant stream + +211 +00:13:23,770 --> 00:13:26,440 +from the new Pathway CDN3. + +212 +00:13:27,608 --> 00:13:31,512 +We apply the same URI replacement rule +to the rest of the variants + +213 +00:13:31,545 --> 00:13:34,381 +and renditions in the cloned Pathway. + +214 +00:13:34,414 --> 00:13:39,253 +For the 3 Mbps variant stream, +we have the original URI, + +215 +00:13:39,286 --> 00:13:44,758 +and apply the host and parameter +replacement rule to get the new URI. + +216 +00:13:44,791 --> 00:13:48,362 +We do the same for the audio +and subtitle renditions. + +217 +00:13:48,395 --> 00:13:53,400 +After we apply the URI replacement rule +to all cloned variants and renditions, + +218 +00:13:53,433 --> 00:13:57,538 +we have a new Pathway +that serves from the new CDN host. + +219 +00:13:58,505 --> 00:14:02,376 +Let's take another example +and let's say the streaming provider + +220 +00:14:02,409 --> 00:14:06,413 +wants to serve their highest bandwidth +video and audio streams + +221 +00:14:06,446 --> 00:14:09,917 +from a specially tuned, fastest CDN host, + +222 +00:14:09,950 --> 00:14:13,287 +different from the rest of +the lower bitrate streams. + +223 +00:14:13,320 --> 00:14:18,625 +This is where per-stable-ID +URI replacement rule comes in handy. + +224 +00:14:18,659 --> 00:14:23,497 +In HLS, STABLE-VARIANT-ID +and STABLE-RENDITION-ID attributes + +225 +00:14:23,530 --> 00:14:27,501 +were introduced to identify +variant and rendition streams. + +226 +00:14:27,534 --> 00:14:30,237 +By setting them +in the multivariant playlist, + +227 +00:14:30,270 --> 00:14:33,674 +we can later reference each variant +or rendition stream, + +228 +00:14:33,707 --> 00:14:36,310 +by their stable ID +in the Pathway Clone object, + +229 +00:14:36,343 --> 00:14:38,045 +in the Steering Manifest, + +230 +00:14:38,078 --> 00:14:41,081 +and assign per-stream +URI replacement rules. + +231 +00:14:41,915 --> 00:14:45,519 +To define these particular special URI +replacement rules, + +232 +00:14:45,552 --> 00:14:49,156 +we need to assign stable IDs +to all the variant and rendition streams + +233 +00:14:49,189 --> 00:14:51,358 +in the multivariant playlist. + +234 +00:14:51,391 --> 00:14:56,430 +For example we assign STABLE-RENDITION-ID +"audio-en-ac3" + +235 +00:14:56,463 --> 00:14:58,599 +to the AC3 English audio, + +236 +00:14:58,632 --> 00:15:01,902 +and STABLE-VARIANT-ID "video-4k-dv" + +237 +00:15:01,935 --> 00:15:05,572 +to the 25 Mbps 4K variant stream. + +238 +00:15:05,606 --> 00:15:07,508 +Then, in the Steering Manifest, + +239 +00:15:07,541 --> 00:15:10,444 +we can add the two +flexible replacement rules + +240 +00:15:10,477 --> 00:15:13,213 +by referencing their stable IDs. + +241 +00:15:13,247 --> 00:15:17,217 +For variant streams, +we added "PER-VARIANT-URIS" field + +242 +00:15:17,251 --> 00:15:19,453 +to the "URI-REPLACEMENT" object, + +243 +00:15:19,486 --> 00:15:24,324 +with a dictionary +of STABLE-VARIANT-ID to URI records. + +244 +00:15:24,358 --> 00:15:27,895 +Here we specify to replace the URI +of the variant stream + +245 +00:15:27,928 --> 00:15:33,934 +with STABLE-VARIANT-ID +"video-4k-dv" with the following URI. + +246 +00:15:33,967 --> 00:15:37,905 +For the rendition stream, +we added "PER-RENDITION-URIS" field + +247 +00:15:37,938 --> 00:15:40,007 +to the "URI-REPLACEMENT" object, + +248 +00:15:40,040 --> 00:15:44,912 +with a dictionary of +STABLE-RENDITION-ID to URI records. + +249 +00:15:44,945 --> 00:15:48,148 +Here we specify to replace +the URI of rendition stream + +250 +00:15:48,182 --> 00:15:53,887 +with STABLE-RENDITION-ID +"audio-en-ac3" with the following URI. + +251 +00:15:55,622 --> 00:15:58,725 +Here we see that +after applying the URI replacement, + +252 +00:15:58,759 --> 00:16:03,897 +all streams are serving from the new +cdn3.exmaple.com host + +253 +00:16:03,931 --> 00:16:08,869 +except the 4K video variant +and AC3 English audio rendition, + +254 +00:16:08,902 --> 00:16:11,605 +where they have +special URI replacement rules + +255 +00:16:11,638 --> 00:16:15,175 +pointing them +to the faster.example.com host, + +256 +00:16:15,209 --> 00:16:18,745 +and with different URI path +and query components. + +257 +00:16:20,414 --> 00:16:24,284 +With Pathway Cloning, when the +streaming provider dynamically spawn + +258 +00:16:24,318 --> 00:16:28,021 +new CDN fleet, in this example, CDN3, + +259 +00:16:28,055 --> 00:16:30,424 +the steering server can add CDN3 + +260 +00:16:30,457 --> 00:16:35,762 +as a Pathway Clone for existing clients +in their Steering Manifest. + +261 +00:16:35,796 --> 00:16:39,800 +It can also choose a region, +for example Europe, + +262 +00:16:39,833 --> 00:16:44,271 +to prioritize CDN3 as the primary Pathway. + +263 +00:16:44,304 --> 00:16:47,975 +When clients in Europe got +the Steering Manifest update, + +264 +00:16:48,008 --> 00:16:51,745 +they will steer their traffics +toward CDN3. + +265 +00:16:51,778 --> 00:16:54,114 +For the final part of this talk, + +266 +00:16:54,147 --> 00:16:57,818 +let's switch our focus +to the details of Steering Server, + +267 +00:16:57,851 --> 00:17:00,754 +to explain how to implement +the server logic, + +268 +00:17:00,787 --> 00:17:05,959 +with concrete examples, to steer client +player traffics for load balancing. + +269 +00:17:06,660 --> 00:17:09,796 +One way to manage and orchestrate +a swamp of client players + +270 +00:17:09,830 --> 00:17:14,268 +and apply partitioned rules +is to put every client into a bucket, + +271 +00:17:14,301 --> 00:17:16,904 +and apply the rules at the buckets level. + +272 +00:17:16,937 --> 00:17:20,240 +It's simple to implement +the bucketing at the Steering Server + +273 +00:17:20,274 --> 00:17:23,343 +without maintaining +any client session states. + +274 +00:17:23,377 --> 00:17:27,014 +When a client player requests +for the initial Steering Manifest, + +275 +00:17:27,047 --> 00:17:31,585 +it makes an HTTP GET request +at the Steering Server URI. + +276 +00:17:31,618 --> 00:17:37,791 +The server then generates a uniform random +number out of 12 possible buckets. + +277 +00:17:37,824 --> 00:17:41,528 +When returning the Steering Manifest, +the server adds the bucket number, + +278 +00:17:41,562 --> 00:17:45,199 +in this case, 7, +to the RELOAD-URI attribute, + +279 +00:17:45,232 --> 00:17:47,568 +which will be the Steering Manifest URI + +280 +00:17:47,601 --> 00:17:50,270 +for the next request +from the client player. + +281 +00:17:50,304 --> 00:17:54,007 +So that the next time the client player +request for Steering Manifest, + +282 +00:17:54,041 --> 00:17:57,578 +it will carry the bucket number +in its request parameter, + +283 +00:17:57,611 --> 00:18:02,382 +and the server can extract it and apply +steering rules based on the bucket number. + +284 +00:18:03,183 --> 00:18:06,753 +Now let's take a look +at a simple bipartition steering rule. + +285 +00:18:06,787 --> 00:18:09,923 +In this case, +we want to steer 50% traffic + +286 +00:18:09,957 --> 00:18:15,462 +to prefer CDN1 +and the other 50% of the traffic to prefer CDN2. + +287 +00:18:15,495 --> 00:18:18,599 +We can write such rule +in terms of the bucket number. + +288 +00:18:18,632 --> 00:18:22,603 +If the client player's bucket number +falls in the first 6 buckets, + +289 +00:18:22,636 --> 00:18:27,641 +we return Steering Manifest +with PATHWAY-PRIORITY prefers CDN1, + +290 +00:18:27,674 --> 00:18:32,946 +and otherwise return that +with PATHWAY-PRIORITY prefers CDN2. + +291 +00:18:32,980 --> 00:18:36,650 +Since clients are assigned +to buckets uniformly, + +292 +00:18:36,683 --> 00:18:42,256 +dividing the 12 buckets into 6 buckets +can bipartition the traffics evenly. + +293 +00:18:43,590 --> 00:18:49,596 +Now let's say a new Pathway +called CDN3 is dynamically spawned. + +294 +00:18:49,630 --> 00:18:53,300 +The Steering Server can advertise it +using Pathway cloning + +295 +00:18:53,333 --> 00:18:57,204 +to clients that don’t know it from +their multivariant playlists. + +296 +00:18:57,237 --> 00:19:00,541 +One common question when +constructing a Steering Manifest + +297 +00:19:00,574 --> 00:19:04,311 +with Pathway Cloning, +is to determine the set of Pathways + +298 +00:19:04,344 --> 00:19:07,981 +that need to be included +in the PATHWAY-CLONES array. + +299 +00:19:08,015 --> 00:19:12,986 +The rule is to clone Pathways that are +not in the client's multivariant playlist. + +300 +00:19:13,020 --> 00:19:15,622 +However, without maintaining +any server side states + +301 +00:19:15,656 --> 00:19:18,926 +about the client session, +how can the Steering Server + +302 +00:19:18,959 --> 00:19:22,863 +know the set of Pathways +in the client's multivariant playlist? + +303 +00:19:24,164 --> 00:19:27,534 +One way to do it +is to include the set of Pathways + +304 +00:19:27,568 --> 00:19:32,172 +in the initial steering server URI +as a query parameter, + +305 +00:19:32,206 --> 00:19:35,509 +during the generation of +the multivariant playlist. + +306 +00:19:35,542 --> 00:19:39,546 +In this case, the multivariant playlist +contains two pathways, + +307 +00:19:39,580 --> 00:19:42,115 +CDN1 and CDN2. + +308 +00:19:42,149 --> 00:19:45,853 +Therefore, it includes them +in the SERVER-URI attribute + +309 +00:19:45,886 --> 00:19:48,121 +as a query parameter. + +310 +00:19:48,155 --> 00:19:52,492 +Then the client player +would send request to the URI, + +311 +00:19:52,526 --> 00:19:56,063 +carrying the parameters +to the steering server. + +312 +00:19:56,096 --> 00:19:58,832 +Then the steering server +can extract the parameter + +313 +00:19:58,866 --> 00:20:03,270 +as the set of Pathways +in the client's multivariant playlist. + +314 +00:20:03,303 --> 00:20:06,573 +Then it can calculate the set +of Pathways to be cloned, + +315 +00:20:06,607 --> 00:20:09,476 +by subtracting the set +of available Pathways + +316 +00:20:09,510 --> 00:20:13,313 +with the set of Pathways +in the client's multivariant playlist. + +317 +00:20:13,347 --> 00:20:17,851 +In this case, +the available Pathways are CDN1, 2, and 3, + +318 +00:20:17,885 --> 00:20:22,856 +and the Pathways in the client's +multivariant playlist are CDN1 and CDN2, + +319 +00:20:22,890 --> 00:20:25,526 +therefore, +the Pathway that needs to be included + +320 +00:20:25,559 --> 00:20:29,096 +in the PATHWAY-CLONES array is CDN3. + +321 +00:20:31,231 --> 00:20:33,700 +Let's also take a look +how the Steering Server + +322 +00:20:33,734 --> 00:20:38,472 +can change its steering rules +when there are three available Pathways. + +323 +00:20:38,505 --> 00:20:41,241 +In this case, +the server wants to partition + +324 +00:20:41,275 --> 00:20:45,379 +the client traffics evenly +across CDN1, 2, and 3. + +325 +00:20:45,412 --> 00:20:50,817 +We write this rule by returning +PATHWAY-PRIORITY that prefers CDN1 + +326 +00:20:50,851 --> 00:20:57,157 +if the client's bucket number falls in +the first third of the 12 buckets, + +327 +00:20:57,191 --> 00:21:00,794 +returning PATHWAY-PRIORITY +that prefers CDN2 + +328 +00:21:00,827 --> 00:21:05,365 +if the client's bucket number falls +in the second third range, + +329 +00:21:05,399 --> 00:21:10,237 +and otherwise returning +PATHWAY-PRIORITY that prefers CDN3. + +330 +00:21:10,270 --> 00:21:15,142 +This way each Pathway would serve +a third of the client traffics. + +331 +00:21:15,175 --> 00:21:18,745 +With everything we covered, +you are now fully equipped + +332 +00:21:18,779 --> 00:21:23,350 +to build your Steering Server +with your own dynamic steering rules. + +333 +00:21:23,383 --> 00:21:27,020 +Doing so can further improve +the reliability of your streaming service. + +334 +00:21:28,555 --> 00:21:32,259 +That's it for our updates +in Content Steering this year. + +335 +00:21:32,292 --> 00:21:33,627 +If you haven't done it yet, + +336 +00:21:33,660 --> 00:21:38,432 +try adopting Content Steering +as your HLS CDN fallback mechanism + +337 +00:21:38,465 --> 00:21:43,303 +as it's more versatile +and provides dynamic traffic steering. + +338 +00:21:43,337 --> 00:21:47,541 +Please also take advantage of +the new Pathway Cloning feature + +339 +00:21:47,574 --> 00:21:50,410 +to improve your service's reliability. + +340 +00:21:50,444 --> 00:21:57,417 +As usual, check out the latest IETF HLS +specification for more technical details. + +341 +00:21:57,451 --> 00:22:01,421 +And remember to utilize +our HTTP Live Streaming Tools + +342 +00:22:01,455 --> 00:22:04,858 +to validate your playlists +as you make changes. + +343 +00:22:04,892 --> 00:22:07,928 +Finally, if you have more questions +or suggestion, + +344 +00:22:07,961 --> 00:22:13,033 +feel free to reach out +at hls-interest@ietf.org. + +345 +00:22:13,066 --> 00:22:16,436 +Thank you for joining, +and have a great day. + diff --git "a/eng/2022 Session 10145 What\342\200\231s new in HLS Interstitials en.srt" "b/eng/2022 Session 10145 What\342\200\231s new in HLS Interstitials en.srt" new file mode 100644 index 0000000..ce9c9bc --- /dev/null +++ "b/eng/2022 Session 10145 What\342\200\231s new in HLS Interstitials en.srt" @@ -0,0 +1,815 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:14,147 +- Hello all. +I'm Prashant, and welcome to WWDC. + +3 +00:00:14,181 --> 00:00:18,085 +In 2021, we introduced HLS Interstitials. + +4 +00:00:18,118 --> 00:00:21,188 +They offer a really simple way +to schedule ads and other interstitials + +5 +00:00:21,221 --> 00:00:23,423 +in your HLS streams. + +6 +00:00:23,457 --> 00:00:24,992 +This year, we've added features + +7 +00:00:25,025 --> 00:00:27,494 +that let you better optimize +your ad inventory + +8 +00:00:27,528 --> 00:00:29,997 +and also fine tune your presentation. + +9 +00:00:30,030 --> 00:00:33,467 +So come, let's find out +what's new in HLS Interstitials. + +10 +00:00:33,500 --> 00:00:39,306 +In this talk, we'll start with a quick +overview of how HLS Interstitials work. + +11 +00:00:39,339 --> 00:00:44,144 +And we'll follow that by discussing the +new ad cueing options that we've added. + +12 +00:00:44,178 --> 00:00:47,147 +After that, we'll see how you +can fine tune your ad experience, + +13 +00:00:47,181 --> 00:00:51,552 +especially in live scenarios +using the SNAP attributes. + +14 +00:00:51,585 --> 00:00:53,820 +We'll then go over some +of the new query parameters + +15 +00:00:53,854 --> 00:00:57,291 +that we've added to the HLS specification, + +16 +00:00:57,324 --> 00:01:00,761 +and finally we'll discuss the changes +to the AVFoundation APIs + +17 +00:01:00,794 --> 00:01:03,564 +that incorporate these new features. + +18 +00:01:03,597 --> 00:01:05,933 +But before we dive into the new features, + +19 +00:01:05,966 --> 00:01:09,369 +let's quickly refresh ourselves +with HLS Interstitials. + +20 +00:01:09,403 --> 00:01:12,573 +With HLS Interstitials, +ads are treated as separate assets + +21 +00:01:12,606 --> 00:01:15,542 +that can be scheduled +onto a program timeline. + +22 +00:01:15,576 --> 00:01:18,545 +They need not be stitched in with +discontinuity tags anymore. + +23 +00:01:18,579 --> 00:01:22,716 +Instead, they can be directly referenced +via their multivariant playlist. + +24 +00:01:22,749 --> 00:01:26,353 +This makes it really simple +to schedule ads, as you'd only need to + +25 +00:01:26,386 --> 00:01:29,323 +point to their multivariant playlist +from your primary content. + +26 +00:01:30,324 --> 00:01:35,028 +This is your typical media playlist +that shows a primary content timeline. + +27 +00:01:35,062 --> 00:01:38,031 +Say you want to schedule +two ads during playback. + +28 +00:01:38,065 --> 00:01:42,202 +You'd like for the first ad to appear +some 5 seconds into playback. + +29 +00:01:42,236 --> 00:01:46,773 +For that, you'd simply specify the start +time of the ad using a DATERANGE tag. + +30 +00:01:48,275 --> 00:01:51,445 +And you'd like for the second ad +to appear at 15 seconds. + +31 +00:01:51,478 --> 00:01:54,748 +For that, you'd add another +DATERANGE tag to the playlist. + +32 +00:01:54,781 --> 00:01:56,316 +It's as simple as that. + +33 +00:01:57,451 --> 00:02:02,222 +It could also be that your primary content +has some ads already stitched in. + +34 +00:02:02,256 --> 00:02:05,325 +You can even replace these ads +by specifying a resume offset + +35 +00:02:05,359 --> 00:02:07,861 +that equals the duration +of the stitched-in ad. + +36 +00:02:08,996 --> 00:02:12,432 +Apart from this, +HLS interstitials also lets you + +37 +00:02:12,466 --> 00:02:14,668 +specify navigation restrictions +for your ads, + +38 +00:02:14,701 --> 00:02:17,371 +and you can even schedule early return +in live scenarios, + +39 +00:02:17,404 --> 00:02:18,639 +among other things. + +40 +00:02:18,672 --> 00:02:21,141 +For more information, +you can check out + +41 +00:02:21,175 --> 00:02:26,513 +"Dynamically insert midrolls and prerolls +in HLS" from WWDC 2021. + +42 +00:02:26,547 --> 00:02:29,650 +While you can schedule ads +along the content's timeline, + +43 +00:02:29,683 --> 00:02:33,086 +this year we've added attributes +that force the placement of the ad + +44 +00:02:33,120 --> 00:02:35,589 +to the beginning +or at the end of playback. + +45 +00:02:35,622 --> 00:02:39,760 +For that we've added the following +new cueing options that you can specify + +46 +00:02:39,793 --> 00:02:41,895 +using the CUE attribute. + +47 +00:02:41,929 --> 00:02:46,400 +This attribute can take on one or +a combination of the following values. + +48 +00:02:46,433 --> 00:02:48,602 +A value of PRE would make the ad appear + +49 +00:02:48,635 --> 00:02:51,872 +before the presentation of +the primary content begins. + +50 +00:02:51,905 --> 00:02:53,774 +This is especially useful +in live scenarios + +51 +00:02:53,807 --> 00:02:56,243 +where you'd want to schedule +a premium ad spot + +52 +00:02:56,276 --> 00:02:58,712 +before tuning into the program. + +53 +00:02:58,745 --> 00:03:00,948 +A value of POST would indicate +that the ad should appear + +54 +00:03:00,981 --> 00:03:03,917 +after the presentation +of your primary content. + +55 +00:03:03,951 --> 00:03:06,954 +This might be useful for event streams +where you'd like to schedule end credits, + +56 +00:03:06,987 --> 00:03:08,989 +say, at the conclusion of a live event. + +57 +00:03:09,890 --> 00:03:13,193 +And a value of ONCE would +make the ad appear only once. + +58 +00:03:13,227 --> 00:03:15,696 +So if the user were to seek back +to before the ad, + +59 +00:03:15,729 --> 00:03:18,332 +they would no longer see the ad +play again. + +60 +00:03:18,365 --> 00:03:20,834 +A great application of this +would be for rating screens + +61 +00:03:20,868 --> 00:03:24,338 +which are typically shown just +the one time at the start of playback. + +62 +00:03:25,939 --> 00:03:29,042 +This shows a playlist example +where you have ads scheduled + +63 +00:03:29,076 --> 00:03:31,311 +as pre and post rolls. + +64 +00:03:31,345 --> 00:03:34,615 +Note that the preroll +is scheduled to play only once. + +65 +00:03:34,648 --> 00:03:37,351 +And we also have an ad +scheduled to play once + +66 +00:03:37,384 --> 00:03:39,520 +at some 10 seconds into playback. + +67 +00:03:40,654 --> 00:03:45,926 +Now, scheduling ads in live scenarios +comes with its own set of challenges. + +68 +00:03:45,959 --> 00:03:48,562 +For instance, your packager +might be using one clock + +69 +00:03:48,595 --> 00:03:51,465 +to set the program date time tags +in the playlist + +70 +00:03:51,498 --> 00:03:53,700 +while the encoder +that is producing the media + +71 +00:03:53,734 --> 00:03:56,136 +is driven by a different clock. + +72 +00:03:56,170 --> 00:03:59,873 +Now, these two clocks +need not necessarily be in sync. + +73 +00:03:59,907 --> 00:04:03,377 +For instance, in this example, +the difference in the date time tags + +74 +00:04:03,410 --> 00:04:07,581 +associated with segment 0 and +segment 100 is about 800 seconds. + +75 +00:04:09,449 --> 00:04:13,420 +However, the accumulated media +duration would be slightly less than that + +76 +00:04:13,453 --> 00:04:16,190 +as individual segment durations +are under eight seconds. + +77 +00:04:17,558 --> 00:04:19,493 +In such scenarios +where your packager clock + +78 +00:04:19,526 --> 00:04:21,929 +is slightly faster +than the encoder clock, + +79 +00:04:21,962 --> 00:04:27,134 +the actual mediatime where the ad starts +might fall somewhere inside the slate. + +80 +00:04:27,167 --> 00:04:29,436 +And if you're expected to rejoin +the main content + +81 +00:04:29,469 --> 00:04:32,506 +at an offset that equals +the ad duration as shown here, + +82 +00:04:32,539 --> 00:04:36,276 +you'd actually end up missing some of the +primary content that follows the slate. + +83 +00:04:37,544 --> 00:04:40,314 +You can now use the SNAP attribute +with the OUT value + +84 +00:04:40,347 --> 00:04:43,317 +to snap out of the primary content +at a segment boundary + +85 +00:04:43,350 --> 00:04:46,653 +that is closest +to the intended ad start time. + +86 +00:04:46,687 --> 00:04:49,523 +And similarly use the SNAP attribute +with the IN value + +87 +00:04:49,556 --> 00:04:54,294 +to snap back in the primary content +that is closest to the ad return time. + +88 +00:04:54,328 --> 00:04:56,964 +Note that we expect you to +use the SNAP attributes + +89 +00:04:56,997 --> 00:05:00,934 +only for live scenarios, as this notion of +clock drift shouldn't exist + +90 +00:05:00,968 --> 00:05:03,637 +when dealing with pre-packaged +video on demand content. + +91 +00:05:04,638 --> 00:05:09,676 +Here we see a live playlist where the ad +is scheduled to both snap-out and snap-in. + +92 +00:05:10,410 --> 00:05:13,480 +This year, we've also added +some query parameters + +93 +00:05:13,514 --> 00:05:15,782 +that will help you +optimize your ad inventory + +94 +00:05:15,816 --> 00:05:18,218 +as well as with session management. + +95 +00:05:18,252 --> 00:05:21,989 +Now, when you're tuning into a live stream +in the middle of an ad pod, + +96 +00:05:22,022 --> 00:05:25,025 +you'd be interested to know +how far that ad pod has progressed + +97 +00:05:25,058 --> 00:05:28,262 +so that you can show your most valuable +inventory in the time remaining. + +98 +00:05:29,396 --> 00:05:33,367 +For that, we've added the +HLS_start_offset query parameter. + +99 +00:05:33,400 --> 00:05:35,235 +The HLS_start_offset query parameter + +100 +00:05:35,269 --> 00:05:39,773 +will be supplied with the request +for the interstitials' Asset-list URL. + +101 +00:05:39,806 --> 00:05:42,709 +For live content, this would specify +the offset into the Asset-list + +102 +00:05:42,743 --> 00:05:45,946 +where playback would begin +when joining a live stream, + +103 +00:05:45,979 --> 00:05:47,481 +and for video on demand content, + +104 +00:05:47,514 --> 00:05:49,583 +it would be the offset +into the Asset-list + +105 +00:05:49,616 --> 00:05:52,653 +when seeking into a region +replaced by the interstitial. + +106 +00:05:54,121 --> 00:05:57,257 +In this sample, we have a live playlist +with a 15-second ad + +107 +00:05:57,291 --> 00:06:01,161 +scheduled to start at 5 seconds +from the top of the playlist. + +108 +00:06:01,195 --> 00:06:05,399 +The highlighted segments shown here +would be replaced by the interstitial. + +109 +00:06:05,432 --> 00:06:07,768 +Now during live join, +the client would typically be + +110 +00:06:07,801 --> 00:06:10,737 +some 3 target durations +behind the live edge. + +111 +00:06:10,771 --> 00:06:13,640 +At that point, the 15-second ad pod +that is scheduled + +112 +00:06:13,674 --> 00:06:15,709 +would've played out for 10 seconds. + +113 +00:06:15,742 --> 00:06:20,080 +So this is supplied as part of +the HLS_start_offset query parameter. + +114 +00:06:20,113 --> 00:06:22,950 +Now that the client only +has 5 seconds of ad time remaining, + +115 +00:06:22,983 --> 00:06:26,954 +the server can construct the Asset-list +such that it places its most valuable ads + +116 +00:06:26,987 --> 00:06:29,089 +in the last 5 seconds of that list. + +117 +00:06:30,190 --> 00:06:32,559 +At your server, +you might probably need a way + +118 +00:06:32,593 --> 00:06:35,896 +to track the same playback session +across multiple ad requests + +119 +00:06:35,929 --> 00:06:39,967 +so that you can avoid serving someone +the same ad over and over. + +120 +00:06:40,868 --> 00:06:44,037 +So to associate ad requests +with primary playback sessions, + +121 +00:06:44,071 --> 00:06:47,741 +we've added the +HLS_primary_id query parameter. + +122 +00:06:47,774 --> 00:06:51,411 +If a client sets the playback session +id request header for all http requests + +123 +00:06:51,445 --> 00:06:53,914 +of a particular playback session, + +124 +00:06:53,947 --> 00:06:55,716 +they can then supply this session ID + +125 +00:06:55,749 --> 00:06:58,418 +as part of the HLS_primary_id query +parameter + +126 +00:06:58,452 --> 00:07:02,890 +to the X-Asset-URI +and X-Asset-List requests. + +127 +00:07:02,923 --> 00:07:05,459 +Clients that do not set the session id +request header + +128 +00:07:05,492 --> 00:07:08,428 +should create a unique value +for every primary playback session + +129 +00:07:08,462 --> 00:07:11,865 +and use that as the +HLS_primary_id query parameter + +130 +00:07:11,899 --> 00:07:15,469 +for both primary +and interstitial asset requests. + +131 +00:07:15,502 --> 00:07:18,038 +You might recall that AVFoundation + +132 +00:07:18,071 --> 00:07:22,943 +offers the AVPlayerInterstitialController +and the AVPlayerInterstitialEvent objects + +133 +00:07:22,976 --> 00:07:26,079 +that let you schedule ads +on the client side. + +134 +00:07:26,113 --> 00:07:28,782 +Now we've added support for +the cue and SNAP options + +135 +00:07:28,815 --> 00:07:32,219 +to the AVPlayerInterstitialEvent +object as well. + +136 +00:07:32,252 --> 00:07:35,589 +You can specify the cueing options, +whether you want the ad to be scheduled + +137 +00:07:35,622 --> 00:07:38,659 +as a preroll or a post roll, +via the Cue property. + +138 +00:07:38,692 --> 00:07:43,063 +The play once option that is signaled via +the Cue attribute in the DATERANGE tag + +139 +00:07:43,096 --> 00:07:46,366 +is set as a Boolean +via the willPlayOnce property. + +140 +00:07:46,400 --> 00:07:48,836 +The option to snap out is signaled via + +141 +00:07:48,869 --> 00:07:51,839 +alignsStartWithPrimarySegmentBoundary +property + +142 +00:07:51,872 --> 00:07:53,807 +and to snap in, you'd set the + +143 +00:07:53,841 --> 00:07:56,877 +alignsResumptionWithPrimarySegmentBoundary +property. + +144 +00:07:58,645 --> 00:08:01,148 +As AVPlayerInterstitialEvent +continues to grow, + +145 +00:08:01,181 --> 00:08:03,917 +we figured it might be better to separate +the setting of the properties + +146 +00:08:03,951 --> 00:08:05,986 +from the creation of the object. + +147 +00:08:06,019 --> 00:08:10,090 +So we've now made the +AVPlayerInterstitialEvent object mutable. + +148 +00:08:10,123 --> 00:08:12,726 +You can now create the object +with just the primary item + +149 +00:08:12,759 --> 00:08:14,728 +and start time of the event. + +150 +00:08:14,761 --> 00:08:17,030 +And you can then specify +the different configuration options + +151 +00:08:17,064 --> 00:08:19,132 +by setting the relevant properties. + +152 +00:08:19,967 --> 00:08:22,236 +Note that once the event +is set on the controller, + +153 +00:08:22,269 --> 00:08:24,471 +any subsequent changes to the event object + +154 +00:08:24,505 --> 00:08:26,273 +will not be reflected at the controller, + +155 +00:08:26,306 --> 00:08:29,343 +since the controller +makes a copy of these objects. + +156 +00:08:29,376 --> 00:08:30,911 +For the changes to take effect, + +157 +00:08:30,944 --> 00:08:34,515 +you'd have to set the event +once again on the controller. + +158 +00:08:34,548 --> 00:08:38,886 +To wrap up, you can now schedule +your ads as pre or post rolls + +159 +00:08:38,919 --> 00:08:40,654 +using the X-CUE attribute. + +160 +00:08:40,687 --> 00:08:45,692 +You can also force your ads to play +only once by setting the X-CUE to once. + +161 +00:08:45,726 --> 00:08:50,464 +To manage clock drift in live scenarios, +you'd use the X-SNAP attribute. + +162 +00:08:50,497 --> 00:08:53,233 +For constructing your asset lists +during live join, + +163 +00:08:53,267 --> 00:08:56,270 +you can use +the HLS_start_offset query parameter + +164 +00:08:56,303 --> 00:08:58,872 +and you'd use the HLS_primary_id query +parameter + +165 +00:08:58,906 --> 00:09:02,543 +to relate interstitial requests +with primary playback sessions. + +166 +00:09:02,576 --> 00:09:06,780 +Did you know that if you use +HLS interstitials to schedule ads, + +167 +00:09:06,813 --> 00:09:11,018 +AVFoundation automatically +manages them while in Shareplay? + +168 +00:09:11,051 --> 00:09:12,619 +For more information, check out the + +169 +00:09:12,653 --> 00:09:16,023 +"Display ads and other interstitials +in SharePlay" talk. + +170 +00:09:16,056 --> 00:09:18,926 +That's it for me, folks. +Thank you. + diff --git a/eng/2022 Session 10147 Create a great video playback experience en.srt b/eng/2022 Session 10147 Create a great video playback experience en.srt new file mode 100644 index 0000000..c9c1212 --- /dev/null +++ b/eng/2022 Session 10147 Create a great video playback experience en.srt @@ -0,0 +1,1964 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,710 --> 00:00:11,678 +Jake: Hi, my name's Jake. + +3 +00:00:11,712 --> 00:00:13,313 +I'm an engineer on the AVKit team, + +4 +00:00:13,347 --> 00:00:16,583 +and welcome to Creating +a Great Video Playback Experience. + +5 +00:00:16,617 --> 00:00:21,021 +In iOS and iPadOS 16, +we've built a completely new media player + +6 +00:00:21,054 --> 00:00:22,389 +from the ground up + +7 +00:00:22,422 --> 00:00:24,658 +bringing a whole new look and feel, + +8 +00:00:24,691 --> 00:00:27,594 +designed to keep the focus +on the content + +9 +00:00:27,628 --> 00:00:30,364 +and fit within a broader spectrum of apps. + +10 +00:00:30,397 --> 00:00:33,166 +We've also built in +many novel interaction models + +11 +00:00:33,200 --> 00:00:37,905 +that make using this new media player +feel even more intuitive and seamless, + +12 +00:00:37,938 --> 00:00:40,741 +and we think you're are gonna love it. + +13 +00:00:40,774 --> 00:00:45,379 +In this session, we'll take a deep dive +into the new system media player. + +14 +00:00:45,412 --> 00:00:49,449 +We'll learn how to design +amazing playback experiences. + +15 +00:00:49,483 --> 00:00:51,952 +We'll see some exciting new visual +intelligence features + +16 +00:00:51,985 --> 00:00:54,655 +coming to macOS and iOS. + +17 +00:00:54,688 --> 00:00:56,990 +I'll introduce +an all new interstitials experience + +18 +00:00:57,024 --> 00:00:59,092 +coming with the new media player + +19 +00:00:59,126 --> 00:01:02,930 +and go over some new APIs +we're bring over from tvOS. + +20 +00:01:02,963 --> 00:01:06,133 +And lastly, +we'll go over a new feature in AVKit: + +21 +00:01:06,166 --> 00:01:08,869 +selectable playback speeds. + +22 +00:01:08,902 --> 00:01:12,940 +For tvOS 15.0 +we redesigned the system player, + +23 +00:01:12,973 --> 00:01:14,608 +bringing a whole new look and feel, + +24 +00:01:14,641 --> 00:01:17,611 +as well as a host of new features +and usability improvements + +25 +00:01:17,644 --> 00:01:19,513 +to the system player. + +26 +00:01:19,546 --> 00:01:21,648 +Well, we heard your requests. + +27 +00:01:21,682 --> 00:01:25,919 +I'm happy to say, we've revamped +the iOS system player as well. + +28 +00:01:25,953 --> 00:01:28,555 +We've completely redesigned +the native media player, + +29 +00:01:28,589 --> 00:01:31,191 +adopting the look and feel +of the tvOS player, + +30 +00:01:31,225 --> 00:01:35,095 +but reimagined for a touch first device. + +31 +00:01:35,128 --> 00:01:37,631 +We've removed the chrome +across the board, + +32 +00:01:37,664 --> 00:01:41,268 +allowing the interface to feel native +within broader spectrum of apps + +33 +00:01:41,301 --> 00:01:44,304 +and bringing a more modern feel +to the player. + +34 +00:01:44,338 --> 00:01:47,307 +Let's dig deeper into some +of the changes we've made. + +35 +00:01:47,341 --> 00:01:51,712 +First, we've brought the play/pause +and skip controls front and center + +36 +00:01:51,745 --> 00:01:54,681 +to make them even easier to interact with. + +37 +00:01:54,715 --> 00:01:58,952 +We've also adjusted the skip interval +from 15 seconds down to 10 + +38 +00:01:58,986 --> 00:02:03,323 +making it easier to track how far +you've jumped with consecutive skips. + +39 +00:02:03,357 --> 00:02:06,193 +Next, we made some significant +improvements to the usability + +40 +00:02:06,226 --> 00:02:08,128 +of the timeline as well. + +41 +00:02:08,161 --> 00:02:12,566 +We're removing the slider knob +marking the timeline's current position. + +42 +00:02:12,599 --> 00:02:15,269 +Instead, the timeline can now +be interacted with + +43 +00:02:15,302 --> 00:02:17,471 +from anywhere along the slider. + +44 +00:02:17,504 --> 00:02:20,140 +Drags no longer needs to begin +at the current time marker. + +45 +00:02:20,174 --> 00:02:24,344 +This makes it even easier to find +exactly where you want to go. + +46 +00:02:24,378 --> 00:02:26,446 +We've also replaced +the video aspect control + +47 +00:02:26,480 --> 00:02:29,016 +with a more intuitive +pinch to zoom gesture, + +48 +00:02:29,049 --> 00:02:30,717 +which I'll show in a moment. + +49 +00:02:30,751 --> 00:02:35,155 +And of course, the new UI looks great +for portrait content as well. + +50 +00:02:37,257 --> 00:02:40,861 +On iPadOS, the player +integrates seamlessly into the system + +51 +00:02:40,894 --> 00:02:43,797 +with full support for keyboards, +trackpads, + +52 +00:02:43,830 --> 00:02:47,367 +mice, game controllers, and much more! + +53 +00:02:47,401 --> 00:02:50,470 +We also added a number of new ways +to interact with the controls + +54 +00:02:50,504 --> 00:02:53,807 +that make navigating the content +and some common interactions + +55 +00:02:53,841 --> 00:02:56,109 +even easier and more intuitive. + +56 +00:02:56,143 --> 00:02:58,145 +Let's take a look at these. + +57 +00:02:58,178 --> 00:03:01,882 +First, we've added a new way +to change the video's fill aspect. + +58 +00:03:01,915 --> 00:03:05,886 +You can now use a pinch gesture +to move through the available zoom levels. + +59 +00:03:05,919 --> 00:03:09,990 +Pinching in will bring the video within +the safe area of the display. + +60 +00:03:10,023 --> 00:03:13,627 +Pinching out will zoom the video +in to fully fill the display. + +61 +00:03:13,660 --> 00:03:17,631 +Next, we stream lined one of +the most common interactions, + +62 +00:03:17,664 --> 00:03:19,266 +play/pause. + +63 +00:03:19,299 --> 00:03:21,468 +You can now tap the center of the display, + +64 +00:03:21,502 --> 00:03:25,138 +even while the controls are hidden +to play and pause the video. + +65 +00:03:25,172 --> 00:03:29,376 +And lastly, we've added a new way +to navigate through the media timeline. + +66 +00:03:29,409 --> 00:03:32,880 +You can now scroll through the timeline +from anywhere over the video + +67 +00:03:32,913 --> 00:03:37,050 +using the same interactions +we all know and love in scroll views. + +68 +00:03:37,084 --> 00:03:39,253 +As you begin to scroll +through the video frames, + +69 +00:03:39,286 --> 00:03:43,924 +the interface drops away, +leaving only the most relevant UI, + +70 +00:03:43,957 --> 00:03:47,160 +allowing the focus to stay on the content. + +71 +00:03:47,194 --> 00:03:50,931 +We've also brought over some new features +from the tvOS player. + +72 +00:03:50,964 --> 00:03:54,134 +AVPlayerViewController now supports +displaying a content title, + +73 +00:03:54,168 --> 00:03:58,605 +subtitle, and description natively +from within the fullscreen UI. + +74 +00:03:59,406 --> 00:04:02,976 +You can provide strings for each of these +by passing AVMetadataItems + +75 +00:04:03,010 --> 00:04:04,611 +to an existing AVKit API. + +76 +00:04:04,645 --> 00:04:07,314 +Let's see how this is done. + +77 +00:04:07,347 --> 00:04:11,218 +By default, the title, subtitle, +and content description will be pulled + +78 +00:04:11,251 --> 00:04:13,287 +from the media's metadata. + +79 +00:04:13,320 --> 00:04:16,757 +However, the values in the media +can be overridden if needed + +80 +00:04:16,790 --> 00:04:19,426 +through the externalMetadata API +on AVPlayerItem. + +81 +00:04:20,961 --> 00:04:24,131 +A title can be added +by creating an AVMetadataItem + +82 +00:04:24,164 --> 00:04:26,900 +with the identifier commonIdentifierTitle + +83 +00:04:26,934 --> 00:04:30,838 +and adding it to the playerItems +externalMetadata property. + +84 +00:04:30,871 --> 00:04:35,342 +Titles should be short and descriptive +to avoid cluttering the UI. + +85 +00:04:35,375 --> 00:04:39,780 +Similarly, here we've added a subtitle +by creating an AVMetadataItem + +86 +00:04:39,813 --> 00:04:44,484 +with the identifier +.iTunesMetadataTrackSubtitle. + +87 +00:04:44,518 --> 00:04:47,087 +The subtitle will be displayed +above the content title + +88 +00:04:47,120 --> 00:04:49,790 +and should be a few words +describing the content. + +89 +00:04:51,358 --> 00:04:53,126 +Lastly, a description can be added + +90 +00:04:53,160 --> 00:04:56,964 +with the identifier +.commonIdentifierDescription. + +91 +00:04:56,997 --> 00:05:00,901 +This will display a chevron +to the right of the title and subtitle. + +92 +00:05:00,934 --> 00:05:05,973 +Selecting the title will display the info +panel containing the content description. + +93 +00:05:06,006 --> 00:05:11,011 +The description should be a few sentences +with more info about the content. + +94 +00:05:11,044 --> 00:05:13,714 +Tapping anywhere +will dismiss the info panel. + +95 +00:05:15,816 --> 00:05:19,219 +So to wrap up, +we've revamped the iOS system player + +96 +00:05:19,253 --> 00:05:23,924 +with an all new look and feel, with new +streamlined touch first interaction models + +97 +00:05:23,957 --> 00:05:26,827 +as well as a host of other improvements. + +98 +00:05:26,860 --> 00:05:29,429 +Using AVPlayerViewController in your app, + +99 +00:05:29,463 --> 00:05:32,132 +you'll have the full support +of the system player– + +100 +00:05:32,165 --> 00:05:36,403 +support for Picture in Picture, +SharePlay, Visual Analysis, + +101 +00:05:36,436 --> 00:05:40,240 +Native Catalyst Support, +New hardware and feature support, + +102 +00:05:40,274 --> 00:05:42,276 +and much, much more. + +103 +00:05:42,309 --> 00:05:46,780 +And of course, all of this +you get with just a few lines of code. + +104 +00:05:46,813 --> 00:05:52,653 +Now, let's talk about how you can design +amazing playback experiences in your apps. + +105 +00:05:52,686 --> 00:05:55,255 +When we set out to redesign +the system media player, + +106 +00:05:55,289 --> 00:05:57,958 +we took a step back +from what we had built in the past + +107 +00:05:57,991 --> 00:05:59,826 +and what we had built for other platforms + +108 +00:05:59,860 --> 00:06:04,431 +and we asked ourselves, +"What makes for a good user experience?" + +109 +00:06:04,464 --> 00:06:06,767 +We wanted to share this process with you; + +110 +00:06:06,800 --> 00:06:09,503 +how we've gone about designing +the new players, + +111 +00:06:09,536 --> 00:06:11,839 +why we designed them the way we did, + +112 +00:06:11,872 --> 00:06:15,909 +and what we think defines +an amazing media experience. + +113 +00:06:15,943 --> 00:06:20,314 +We think there are three things +that make a media experience great. + +114 +00:06:20,347 --> 00:06:23,016 +The experience should be intuitive. + +115 +00:06:23,050 --> 00:06:26,854 +It should feel easy, familiar, natural, + +116 +00:06:26,887 --> 00:06:29,656 +even if you've never used it. + +117 +00:06:29,690 --> 00:06:31,725 +It should be tightly integrated, + +118 +00:06:31,758 --> 00:06:35,262 +both within your app and with the system. + +119 +00:06:35,295 --> 00:06:38,699 +And lastly, it should be content forward. + +120 +00:06:38,732 --> 00:06:42,302 +At the end of the day, +people are there to experience the media + +121 +00:06:42,336 --> 00:06:46,473 +and your apps and designs +should reflect that. + +122 +00:06:46,507 --> 00:06:48,876 +Now let's dig deeper into these three, + +123 +00:06:48,909 --> 00:06:52,779 +starting with making your experience +intuitive. + +124 +00:06:52,813 --> 00:06:55,916 +Oftentimes, when an app feels intuitive + +125 +00:06:55,949 --> 00:06:59,453 +it can be hard to put your finger on why. + +126 +00:06:59,486 --> 00:07:02,289 +You just know it when you see it. + +127 +00:07:02,322 --> 00:07:04,157 +So what actually makes an interface + +128 +00:07:04,191 --> 00:07:06,927 +or an experience feel intuitive? + +129 +00:07:06,960 --> 00:07:10,063 +And how can you design for it? + +130 +00:07:10,097 --> 00:07:12,866 +We think it starts with familiarity. + +131 +00:07:12,900 --> 00:07:17,704 +When you can draw on your past experiences +to help understand something new, + +132 +00:07:17,738 --> 00:07:19,540 +that's intuitive. + +133 +00:07:19,573 --> 00:07:22,242 +When you don't need an explanation +of how it works + +134 +00:07:22,276 --> 00:07:24,278 +or even need to think about how it works– + +135 +00:07:24,311 --> 00:07:28,115 +it just works exactly as you expect. + +136 +00:07:29,483 --> 00:07:35,255 +Every one of us builds experiences daily +interacting technology and the real world. + +137 +00:07:35,289 --> 00:07:38,759 +Both of these are great sources +of experiential familiarity + +138 +00:07:38,792 --> 00:07:43,297 +and is often where we started when +designing the new system media players. + +139 +00:07:43,330 --> 00:07:46,266 +There are many types of experiences +to draw intuition from, + +140 +00:07:46,300 --> 00:07:48,635 +but I want to focus on two; + +141 +00:07:48,669 --> 00:07:53,507 +the two that we most often relied on +when designing the system media players. + +142 +00:07:53,540 --> 00:07:55,542 +Platform paradigms + +143 +00:07:55,576 --> 00:07:57,444 +and the real world. + +144 +00:07:57,477 --> 00:08:02,583 +The first comes from our experiences +using technology every day. + +145 +00:08:02,616 --> 00:08:05,853 +Years of using TV remotes tells you +that the arrow keys + +146 +00:08:05,886 --> 00:08:08,488 +move focus left and right. + +147 +00:08:08,522 --> 00:08:13,694 +Similarly, tapping a volume button on +a touch first device will mute the audio. + +148 +00:08:13,727 --> 00:08:17,464 +These interactions feel intuitive +because they're familiar. + +149 +00:08:17,497 --> 00:08:20,968 +You can use these types of familiar +interactions in your media experiences + +150 +00:08:21,001 --> 00:08:23,136 +to help make your apps feel +more intuitive, + +151 +00:08:23,170 --> 00:08:26,273 +engaging, and even natural to use. + +152 +00:08:26,306 --> 00:08:30,010 +Conversely, finding unfamiliar +or unexpected interactions + +153 +00:08:30,043 --> 00:08:34,648 +can be confusing +and sometimes even frustrating. + +154 +00:08:34,681 --> 00:08:38,752 +Let's look at some examples where we drew +on this type of platform familiarity + +155 +00:08:38,785 --> 00:08:41,255 +in the system players. + +156 +00:08:41,288 --> 00:08:44,658 +A great example of this +is the presentation and dismissal model + +157 +00:08:44,691 --> 00:08:46,793 +of the iOS system player. + +158 +00:08:46,827 --> 00:08:50,297 +The player animates its presentation +in from the bottom, + +159 +00:08:50,330 --> 00:08:55,335 +giving a subtle hit that the player +can be dismissed by pushing it back down. + +160 +00:08:55,369 --> 00:08:59,273 +We see this model used extensively +across our touch first devices. + +161 +00:08:59,306 --> 00:09:02,142 +For example, the now playing UI in Music + +162 +00:09:02,176 --> 00:09:03,777 +presents from the mini bar at the bottom + +163 +00:09:03,810 --> 00:09:08,048 +and can be dismissed +with an interactive swipe downwards. + +164 +00:09:08,081 --> 00:09:11,084 +In some cases though, +we may draw on experiences + +165 +00:09:11,118 --> 00:09:14,922 +not from our understanding of technology, +but from everyday life. + +166 +00:09:16,323 --> 00:09:20,027 +These types of experiences +come from the real world. + +167 +00:09:20,060 --> 00:09:22,763 +Millions of years of evolution +have helped us develop + +168 +00:09:22,796 --> 00:09:26,600 +a deep instinctual understanding +of natural processes. + +169 +00:09:26,633 --> 00:09:28,235 +You can tap in to this understanding + +170 +00:09:28,268 --> 00:09:32,172 +to help build familiar and intuitive +experiences in your software. + +171 +00:09:32,206 --> 00:09:36,777 +A great example of this is our new +scrolling gesture in the iOS player. + +172 +00:09:36,810 --> 00:09:39,413 +Similar to rolling a toy car +across a table, + +173 +00:09:39,446 --> 00:09:42,216 +each swipe over the video has momentum, + +174 +00:09:42,249 --> 00:09:46,186 +continuing the movement of the timeline +past the direct interaction + +175 +00:09:46,220 --> 00:09:49,990 +until the timeline slowly comes to a stop. + +176 +00:09:50,023 --> 00:09:52,459 +The momentum here alone +builds an association + +177 +00:09:52,492 --> 00:09:54,995 +with real world moving objects. + +178 +00:09:55,028 --> 00:09:59,499 +This association helps you discover +the subtle depths of the interaction. + +179 +00:09:59,533 --> 00:10:02,402 +Just like the toy car, +I can make it go faster + +180 +00:10:02,436 --> 00:10:04,471 +by pushing it harder, + +181 +00:10:04,505 --> 00:10:06,773 +or pushing it several times in a row. + +182 +00:10:06,807 --> 00:10:10,210 +And if I grab it, it stops. + +183 +00:10:10,244 --> 00:10:15,148 +It feels natural because it is natural. + +184 +00:10:15,182 --> 00:10:16,817 +And the best part about this is, + +185 +00:10:16,850 --> 00:10:21,121 +if you use the system players, +your app will feel intuitive. + +186 +00:10:21,154 --> 00:10:24,892 +All the natural interactions +we've built and inherit intuition + +187 +00:10:24,925 --> 00:10:27,995 +and familiarity people have +with the system player, + +188 +00:10:28,028 --> 00:10:30,597 +all of the design paradigms +optimized in a way + +189 +00:10:30,631 --> 00:10:35,669 +that tvOS, iOS, and macOS users +will understand natively– + +190 +00:10:35,702 --> 00:10:39,540 +all of this you get +with just a few lines of code. + +191 +00:10:40,641 --> 00:10:46,313 +Building an intuitive design is one aspect +of making your media experience great, + +192 +00:10:46,346 --> 00:10:50,217 +but without all the features +and integration points people expect, + +193 +00:10:50,250 --> 00:10:55,088 +your app can inadvertently pull focus +away from the content. + +194 +00:10:55,122 --> 00:11:00,027 +This leads us to the second crucial aspect +of an amazing media experience– + +195 +00:11:00,527 --> 00:11:02,329 +tight integration. + +196 +00:11:02,362 --> 00:11:06,600 +When an experience is tightly integrated, +all the functionality, + +197 +00:11:06,633 --> 00:11:11,271 +features, and devices +people expect to work just work. + +198 +00:11:11,305 --> 00:11:16,510 +And importantly, they work in a way +that's consistent with their expectations. + +199 +00:11:16,543 --> 00:11:19,213 +As people use their devices, +they become accustomed + +200 +00:11:19,246 --> 00:11:21,782 +to relying on the features +of the platform. + +201 +00:11:21,815 --> 00:11:24,918 +For example, +pulling down the Control Center + +202 +00:11:24,952 --> 00:11:30,390 +and seeing your content populated +in the Now Playing media controls, + +203 +00:11:30,424 --> 00:11:33,894 +or responding to a notification +while watching a TV show, + +204 +00:11:33,927 --> 00:11:38,432 +and having the video continue smoothly +into Picture in Picture. + +205 +00:11:38,465 --> 00:11:41,268 +Building this tight system integration +into your app + +206 +00:11:41,301 --> 00:11:46,173 +is key to making your experience +feel seamless. + +207 +00:11:46,206 --> 00:11:48,709 +Your app should feel like +a native part of the system + +208 +00:11:48,742 --> 00:11:52,679 +and we work hard to provide you with +the tools needed to make that possible. + +209 +00:11:52,713 --> 00:11:56,350 +This includes things like +CoreSpotlight integration + +210 +00:11:56,383 --> 00:11:58,652 +to make your content searchable, + +211 +00:11:58,685 --> 00:12:03,257 +Now Playing info so your content +can appear in the system media UIs, + +212 +00:12:03,290 --> 00:12:07,160 +MediaRemote commands allowing your app +to respond to things like + +213 +00:12:07,194 --> 00:12:10,864 +the play button being pressed +on a keyboard or TV remote. + +214 +00:12:10,898 --> 00:12:15,636 +We even provide the ability to integrate +your media directly into the TV app, + +215 +00:12:15,669 --> 00:12:19,072 +delivering your content +to an even wider audience. + +216 +00:12:19,106 --> 00:12:21,975 +In addition to making your apps +feel native, + +217 +00:12:22,009 --> 00:12:26,079 +it's important to provide +all the features that people love. + +218 +00:12:26,113 --> 00:12:31,018 +Features like AirPlay, SharePlay, +and Picture in Picture. + +219 +00:12:31,051 --> 00:12:33,353 +We think people will expect these features + +220 +00:12:33,387 --> 00:12:38,292 +and providing them +enhances the experience in using your app. + +221 +00:12:38,325 --> 00:12:41,161 +People will use your app +across many devices + +222 +00:12:41,195 --> 00:12:43,630 +and even more input formats. + +223 +00:12:43,664 --> 00:12:46,266 +Providing support for all these is crucial + +224 +00:12:46,300 --> 00:12:50,237 +in ensuring your experience +is accessible to everyone. + +225 +00:12:50,270 --> 00:12:52,840 +This is particularly important on tvOS, + +226 +00:12:52,873 --> 00:12:55,809 +where supporting all available remotes +is critical + +227 +00:12:55,843 --> 00:12:59,012 +to ensuring everyone can use your app. + +228 +00:12:59,046 --> 00:13:03,917 +This is one reason we always recommend +using the system media player on tvOS. + +229 +00:13:03,951 --> 00:13:08,555 +Your apps should ensure a fluid experience +with all TV remotes, keyboards, + +230 +00:13:08,589 --> 00:13:12,659 +trackpads, game controllers, +and headphone controls. + +231 +00:13:12,693 --> 00:13:15,329 +Additionally, +you should ensure your app's UI elements + +232 +00:13:15,362 --> 00:13:17,297 +are drawn within the screen's safe area + +233 +00:13:17,331 --> 00:13:22,269 +to avoid collision with rounded corners +or notches in the display. + +234 +00:13:22,302 --> 00:13:25,839 +We recognize building support +for all of these integration points, + +235 +00:13:25,873 --> 00:13:30,577 +features, and hardware configurations +can be daunting. + +236 +00:13:30,611 --> 00:13:33,280 +This is why we built +AVPlayerViewController, + +237 +00:13:33,313 --> 00:13:35,415 +so with just a few lines of code, + +238 +00:13:35,449 --> 00:13:39,052 +anyone using your app +can have an amazing media experience. + +239 +00:13:40,621 --> 00:13:42,689 +And all of this leads us, lastly, + +240 +00:13:42,723 --> 00:13:46,660 +to the most important aspect in designing +a media experience; + +241 +00:13:46,693 --> 00:13:49,329 +making it content forward. + +242 +00:13:49,363 --> 00:13:52,132 +This should be the primary goal +in your design, + +243 +00:13:52,165 --> 00:13:56,603 +and is what we considering the defining +aspect of a great media experience. + +244 +00:13:56,637 --> 00:13:58,605 +When your app feels intuitive, + +245 +00:13:58,639 --> 00:14:00,574 +when all of the integration points + +246 +00:14:00,607 --> 00:14:04,845 +and all features people expect +just work, + +247 +00:14:04,878 --> 00:14:07,814 +you bring your content into focus, + +248 +00:14:07,848 --> 00:14:10,817 +and everything else +fades into the background. + +249 +00:14:12,152 --> 00:14:14,454 +There are a few things +you should keep in mind, though, + +250 +00:14:14,488 --> 00:14:17,057 +specific to designing your content. + +251 +00:14:17,090 --> 00:14:20,360 +Make sure you provide +all of the appropriate metadata, + +252 +00:14:20,394 --> 00:14:23,697 +both in your interface and to the system. + +253 +00:14:23,730 --> 00:14:26,667 +Providing this info helps give context +to your media + +254 +00:14:26,700 --> 00:14:29,169 +and allows the system to provide +better experiences + +255 +00:14:29,203 --> 00:14:31,872 +in Control Center and the Lock Screen. + +256 +00:14:31,905 --> 00:14:35,075 +This includes things like +a title and subtitle, + +257 +00:14:35,108 --> 00:14:39,146 +a description, thumbnail, +season and episode information, + +258 +00:14:39,179 --> 00:14:41,582 +or start and end dates for live streams. + +259 +00:14:41,615 --> 00:14:45,719 +Always keep your media +in its original aspect ratio. + +260 +00:14:45,752 --> 00:14:48,722 +This allows the system +to position your videos on screen + +261 +00:14:48,755 --> 00:14:50,891 +in the correct location. + +262 +00:14:50,924 --> 00:14:55,762 +Letter boxing your content +can lead to experiences like this + +263 +00:14:55,796 --> 00:14:57,831 +or this. + +264 +00:14:57,865 --> 00:15:02,269 +Make sure to include support for +the latest media standards where possible. + +265 +00:15:02,302 --> 00:15:06,240 +For example, HDR and Dolby Atmos. + +266 +00:15:06,273 --> 00:15:09,910 +And lastly, make sure to include +audio and subtitle tracks + +267 +00:15:09,943 --> 00:15:11,879 +for multiple languages + +268 +00:15:11,912 --> 00:15:15,315 +so your media is accessible +to as many people as possible. + +269 +00:15:17,384 --> 00:15:20,654 +If there is one thing to take away +from this section of the talk, + +270 +00:15:20,687 --> 00:15:24,291 +it's that you should keep the focus +on your content. + +271 +00:15:24,324 --> 00:15:28,362 +We've built the system media players, +provided through AVPlayerViewController, + +272 +00:15:28,395 --> 00:15:32,699 +to make that goal as easy as possible +for you as a developer. + +273 +00:15:32,733 --> 00:15:36,937 +Now, let's go over some new features +we've added to AVPlayerViewController, + +274 +00:15:36,970 --> 00:15:40,040 +starting with the new +visual intelligence features. + +275 +00:15:40,073 --> 00:15:45,812 +In this example, we have a video paused +on a frame with a code snippet in it. + +276 +00:15:45,846 --> 00:15:48,615 +Long pressing the code snippet +will select it. + +277 +00:15:48,649 --> 00:15:53,687 +You can then copy and paste it +directly into playgrounds to try it out. + +278 +00:15:53,720 --> 00:15:55,956 +This works great for macOS as well. + +279 +00:15:55,989 --> 00:15:59,293 +Hovering your cursor over +the same code will show the I-beam + +280 +00:15:59,326 --> 00:16:01,595 +indicating the text is selectable. + +281 +00:16:01,628 --> 00:16:03,997 +You can then use your cursor +to highlight it + +282 +00:16:04,031 --> 00:16:06,767 +or use CMD+A to Select All. + +283 +00:16:06,800 --> 00:16:10,938 +We're introducing a new API +to go along with this functionality. + +284 +00:16:10,971 --> 00:16:16,009 +Available in AVPlayerViewController +on iOS and AVPlayerView on macOS, + +285 +00:16:16,043 --> 00:16:19,913 +allowsVideoFrameAnalysis +will toggle this feature. + +286 +00:16:19,947 --> 00:16:24,151 +This will be enabled by for all apps +linking against the new SDKs. + +287 +00:16:24,184 --> 00:16:26,987 +While allowsVideoFrameAnalysis +is set to true, + +288 +00:16:27,020 --> 00:16:28,755 +and once the media is paused, + +289 +00:16:28,789 --> 00:16:33,927 +AVKit will begin analyzing the current +video frame after a set period. + +290 +00:16:33,961 --> 00:16:36,597 +Note that we will not analyze frames +while scrolling, + +291 +00:16:36,630 --> 00:16:38,131 +for performance reasons, + +292 +00:16:38,165 --> 00:16:40,767 +or for FairPlay protected content. + +293 +00:16:40,801 --> 00:16:46,039 +In general, we think people will expect +this functionality in most situations. + +294 +00:16:46,073 --> 00:16:48,242 +However, there are some instances + +295 +00:16:48,275 --> 00:16:50,878 +where you may want to disable +visual analysis + +296 +00:16:50,911 --> 00:16:54,414 +as is appropriate in your application. + +297 +00:16:54,448 --> 00:16:57,518 +For example, in performance critical +applications, + +298 +00:16:57,551 --> 00:17:00,120 +such as a collection view of videos, + +299 +00:17:00,153 --> 00:17:03,257 +or in cases where interaction +with the video is not expected, + +300 +00:17:03,290 --> 00:17:05,459 +like splash screens. + +301 +00:17:05,492 --> 00:17:08,495 +For more info on how to integrate the +visual intelligence feature set + +302 +00:17:08,529 --> 00:17:12,866 +into your apps, +see our related talks on VisionKit. + +303 +00:17:12,900 --> 00:17:17,971 +Next, let's take a look some improvements +we made to interstitials. + +304 +00:17:18,005 --> 00:17:20,807 +Up until now, +interstitials were only supported + +305 +00:17:20,841 --> 00:17:23,677 +in AVPlayerViewController on tvOS. + +306 +00:17:23,710 --> 00:17:28,482 +Well, I'm happy to announce we're bringing +the same level of support to iOS as well. + +307 +00:17:28,515 --> 00:17:32,186 +Interstitials, +either in the stream or defined locally + +308 +00:17:32,219 --> 00:17:33,987 +through AVPlayerInterstitialEvents, + +309 +00:17:34,021 --> 00:17:36,757 +will now be marked along the timeline. + +310 +00:17:36,790 --> 00:17:40,527 +When the timeline hits a marker, +we'll begin playing the interstitial. + +311 +00:17:40,561 --> 00:17:44,698 +If your interstitials are already +fully defined within your HLS playlist, + +312 +00:17:44,731 --> 00:17:46,934 +you'll get this behavior automatically– + +313 +00:17:46,967 --> 00:17:49,436 +no adoption required. + +314 +00:17:49,469 --> 00:17:53,373 +If not, or if you app requires +some more custom behavior, + +315 +00:17:53,407 --> 00:17:56,310 +we're introducing some new API as well. + +316 +00:17:56,343 --> 00:18:01,481 +AVInterstitialTimeRange is being brought +over from tvOS to iOS. + +317 +00:18:01,515 --> 00:18:05,085 +These will be populated automatically +into the AVPlayerItem property, + +318 +00:18:05,118 --> 00:18:07,221 +interstitialTimeRanges, + +319 +00:18:07,254 --> 00:18:10,657 +which is also being brought over +from tvOS. + +320 +00:18:10,691 --> 00:18:13,560 +When using an HLS stream, +an AVInterstitialTimeRange + +321 +00:18:13,594 --> 00:18:17,097 +will be synthesized +for each interstitial in the stream. + +322 +00:18:17,130 --> 00:18:20,934 +When creating interstitial events locally +through the AVFoundation API, + +323 +00:18:20,968 --> 00:18:23,103 +an AVInterstitialTimeRange +will be synthesized + +324 +00:18:23,136 --> 00:18:26,740 +for each AVPlayerInterstitialEvent. + +325 +00:18:26,773 --> 00:18:28,842 +Unlike on tvOS however, + +326 +00:18:28,876 --> 00:18:32,446 +interstitialTimeRanges +is a read-only property. + +327 +00:18:32,479 --> 00:18:35,816 +Interstitials will either need to be +defined within the HLS stream + +328 +00:18:35,849 --> 00:18:38,385 +or through AVPlayerInterstitialEvents. + +329 +00:18:38,418 --> 00:18:41,488 +For those migrating +their support from their tvOS apps, + +330 +00:18:41,522 --> 00:18:46,827 +this is in essence equivalent to setting +translatesPlayerInterstitialEvents to yes. + +331 +00:18:47,761 --> 00:18:52,533 +We're also bringing over two +delegate methods from tvOS as well. + +332 +00:18:52,566 --> 00:18:55,068 +These can be used to know +when an interstitial has begun + +333 +00:18:55,102 --> 00:18:57,538 +or ended playback. + +334 +00:18:57,571 --> 00:19:00,440 +Let's see how we can use these to APIs +to add a skip button + +335 +00:19:00,474 --> 00:19:03,043 +for a pre-roll ad on iOS. + +336 +00:19:03,076 --> 00:19:05,979 +First, we'll create an +AVPlayerInterstitialEventController + +337 +00:19:06,013 --> 00:19:08,549 +for the primary media's player. + +338 +00:19:08,582 --> 00:19:12,052 +Next, we'll create an interstitial event. + +339 +00:19:12,085 --> 00:19:14,288 +We'll define some restrictions for it. + +340 +00:19:14,321 --> 00:19:17,357 +These restrictions prevent seeking +within the interstitial + +341 +00:19:17,391 --> 00:19:20,761 +and prevent skipping past +the interstitial. + +342 +00:19:20,794 --> 00:19:24,765 +Then we'll add the interstitial +to the event controller. + +343 +00:19:24,798 --> 00:19:29,670 +And lastly, we can implement the new +willPresentInterstitial delegate callback + +344 +00:19:29,703 --> 00:19:33,307 +to bring up an ad skip button +after a set interval. + +345 +00:19:33,340 --> 00:19:36,944 +And once the button is pressed, +we'll cancel the interstitial. + +346 +00:19:36,977 --> 00:19:38,979 +It's that easy. + +347 +00:19:39,012 --> 00:19:42,950 +Note that when adding any custom +UI elements to an AVPlayerViewController, + +348 +00:19:42,983 --> 00:19:45,185 +such as this ad skip button, + +349 +00:19:45,219 --> 00:19:47,421 +always make sure to add to them +as subviews + +350 +00:19:47,454 --> 00:19:49,423 +of the contentOverlayView. + +351 +00:19:49,456 --> 00:19:51,892 +To learn more about how +you can integrate your interstitials + +352 +00:19:51,925 --> 00:19:54,061 +right into your HLS playlists + +353 +00:19:54,094 --> 00:19:57,231 +or how you can use +the AVFoundation interstitials API, + +354 +00:19:57,264 --> 00:20:02,436 +check out our related talks on exploring +HLS dynamic pre-rolls and mid-rolls. + +355 +00:20:02,469 --> 00:20:06,940 +Now we'll go over a new feature we've +added this year across all our platforms; + +356 +00:20:06,974 --> 00:20:09,676 +native support for playback speed control. + +357 +00:20:09,710 --> 00:20:12,079 +Both AVPlayerView +and AVPlayerViewController + +358 +00:20:12,112 --> 00:20:16,717 +can now optionally show a playback speed +menu using some new API we've added. + +359 +00:20:18,185 --> 00:20:22,523 +We're making this available +on macOS, iOS, and tvOS. + +360 +00:20:22,556 --> 00:20:25,058 +Let's take a look at what this looks like. + +361 +00:20:25,092 --> 00:20:29,563 +On tvOS, you'll see a new control +in the transport bar. + +362 +00:20:29,596 --> 00:20:32,599 +Selecting the control will display +a list of the available playback speeds + +363 +00:20:32,633 --> 00:20:34,968 +to choose from. + +364 +00:20:35,002 --> 00:20:39,873 +On iOS, this menu will appear +in the transport control overflow menu. + +365 +00:20:39,907 --> 00:20:45,212 +And similarly, on macOS, the control +will appear in the overflow menu. + +366 +00:20:45,245 --> 00:20:49,816 +All apps linking against the new iOS, +macOS, and tvOS SDKs + +367 +00:20:49,850 --> 00:20:52,119 +will get this functionality automatically + +368 +00:20:52,152 --> 00:20:55,355 +with no additional changes required. + +369 +00:20:55,389 --> 00:20:57,891 +However, depending on your use case, + +370 +00:20:57,925 --> 00:21:01,495 +some applications may want to modify +the list of speeds, + +371 +00:21:01,528 --> 00:21:05,632 +programmatically select a speed, +or disable the menu entirely. + +372 +00:21:05,666 --> 00:21:08,869 +To accommodate these use cases, +we've added some new APIs + +373 +00:21:08,902 --> 00:21:11,939 +to AVPlayerView +and AVPlayerViewController. + +374 +00:21:11,972 --> 00:21:13,273 +Let's take a look at these. + +375 +00:21:14,875 --> 00:21:20,514 +First, we've added a new class in AVKit– +AVPlaybackSpeed. + +376 +00:21:20,547 --> 00:21:23,817 +AVPlaybackSpeeds represent +user selectable speed options + +377 +00:21:23,851 --> 00:21:27,120 +in a playback UI +and they have three properties. + +378 +00:21:27,154 --> 00:21:30,157 +A rate value, defined on initialized, + +379 +00:21:30,190 --> 00:21:34,127 +which will be set on the player +when the playback speed is selected. + +380 +00:21:34,161 --> 00:21:37,164 +A localized name, +used to represent the playback speed + +381 +00:21:37,197 --> 00:21:39,533 +within the accessibility system + +382 +00:21:39,566 --> 00:21:42,669 +For example, +a speed of 2.5 might use a localizedName + +383 +00:21:42,703 --> 00:21:45,172 +of "Two and a half times speeds." + +384 +00:21:45,205 --> 00:21:47,207 +And a localized numeric name. + +385 +00:21:47,241 --> 00:21:50,010 +This value is synthesized +from the rate property + +386 +00:21:50,043 --> 00:21:53,680 +and will be the String displayed +in the playback speed menu + +387 +00:21:53,714 --> 00:21:56,016 +If your app requires a custom +playback speed menu + +388 +00:21:56,049 --> 00:21:59,653 +external to the player, +use this string to represent the speed. + +389 +00:22:01,021 --> 00:22:05,025 +Lastly, AVPlaybackSpeed defines a list +of system default speeds + +390 +00:22:05,058 --> 00:22:08,362 +that should be used whenever possible. + +391 +00:22:08,395 --> 00:22:12,799 +You can use AVPlaybackSpeed in conjunction +with some new API on AVPlayerView + +392 +00:22:12,833 --> 00:22:17,938 +and AVPlayerViewController to adapt +this feature to fit within your app. + +393 +00:22:17,971 --> 00:22:23,076 +The speeds property allows defining +a custom list of playback speeds. + +394 +00:22:23,110 --> 00:22:26,680 +By default this property will be set +to the AVPlaybackSpeed + +395 +00:22:26,713 --> 00:22:29,082 +systemDefaultSpeeds list. + +396 +00:22:29,116 --> 00:22:32,953 +Setting this to an empty list +will hide the menu. + +397 +00:22:32,986 --> 00:22:37,858 +The selected speed property will return +the speed that's currently active. + +398 +00:22:37,891 --> 00:22:41,762 +And lastly, the selectSpeed function +allows programmatic selection + +399 +00:22:41,795 --> 00:22:44,231 +of the current speed. + +400 +00:22:44,264 --> 00:22:46,133 +Note that you should only +use this function + +401 +00:22:46,166 --> 00:22:50,871 +in response to explicit selection of +the speed outside of the player UI. + +402 +00:22:50,904 --> 00:22:54,808 +Never implicitly override +the chosen playback speed. + +403 +00:22:54,842 --> 00:22:58,145 +Let's take a look at an example. + +404 +00:22:58,178 --> 00:23:00,647 +Here we're creating +a AVPlayerViewController + +405 +00:23:00,681 --> 00:23:02,583 +and presenting it. + +406 +00:23:02,616 --> 00:23:07,287 +By default this will use the system +provided list of playback speeds. + +407 +00:23:07,321 --> 00:23:11,625 +You can add a new speed to the menu +by creating an AVPlaybackSpeed + +408 +00:23:11,658 --> 00:23:15,829 +and appending it to the list of speeds +in AVPlayerViewController. + +409 +00:23:15,863 --> 00:23:20,601 +We can also disable the menu +by setting an empty list of speeds. + +410 +00:23:20,634 --> 00:23:23,270 +It's as easy as that. + +411 +00:23:23,303 --> 00:23:27,708 +Note though, you should always call +AVPlayer play() to begin playback. + +412 +00:23:27,741 --> 00:23:30,744 +Never start playback +by calling setRate:1.0, + +413 +00:23:30,777 --> 00:23:34,348 +as the selected rate might not be 1.0. + +414 +00:23:34,381 --> 00:23:37,985 +And with that, +I'd like to wrap up the session. + +415 +00:23:38,018 --> 00:23:41,688 +We saw the new +redesigned iOS system player. + +416 +00:23:41,722 --> 00:23:46,026 +We heard how you can design +amazing playback experiences of your own. + +417 +00:23:46,059 --> 00:23:49,062 +We saw some cool +new visual intelligence features, + +418 +00:23:49,096 --> 00:23:53,200 +and went over our new interstitials +and playback speed APIs. + +419 +00:23:53,233 --> 00:23:54,868 +I hope you enjoyed the session + +420 +00:23:54,902 --> 00:23:58,338 +and look forward to seeing +these features in your apps. + +421 +00:23:58,372 --> 00:24:00,374 +Enjoy the rest of the conference. + diff --git a/eng/2022 Session 10148 Meet Apple Music API and MusicKit en.srt b/eng/2022 Session 10148 Meet Apple Music API and MusicKit en.srt new file mode 100644 index 0000000..cd772e5 --- /dev/null +++ b/eng/2022 Session 10148 Meet Apple Music API and MusicKit en.srt @@ -0,0 +1,1677 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,810 --> 00:00:12,346 +Justin Rennell: Hello and welcome to WWDC. + +3 +00:00:12,379 --> 00:00:16,350 +My name is Justin, and I'm an engineer +on the Apple Music development team. + +4 +00:00:16,383 --> 00:00:19,553 +Today I’m going to give an introduction +to MusicKit with a focus + +5 +00:00:19,586 --> 00:00:22,322 +on the fundamentals +of using Apple Music API. + +6 +00:00:22,356 --> 00:00:25,759 +In this session, I’ll provide an overview +of MusicKit client frameworks + +7 +00:00:25,792 --> 00:00:29,096 +and how they integrate +with the Apple Music API web service. + +8 +00:00:29,129 --> 00:00:32,566 +I'll explain how to get access +as a developer and specific details + +9 +00:00:32,599 --> 00:00:34,768 +about using Apple Music API, including: + +10 +00:00:34,801 --> 00:00:36,904 +Requesting content and resources, + +11 +00:00:36,937 --> 00:00:40,174 +using limits and pagination to work +with large resource collections, + +12 +00:00:40,207 --> 00:00:43,777 +and how to find content in the catalog +with the search endpoint. + +13 +00:00:43,810 --> 00:00:46,847 +Finally, I'll provide a look +at some personalized features available + +14 +00:00:46,880 --> 00:00:50,851 +for Apple Music subscribers and how +to provide access to them in your apps. + +15 +00:00:51,885 --> 00:00:55,689 +I'll start with a brief introduction +to MusicKit and Apple Music API. + +16 +00:00:55,722 --> 00:01:00,160 +MusicKit was first announced +at WWDC in 2017. + +17 +00:01:00,194 --> 00:01:02,796 +Since then, we have made improvements +that make integrating + +18 +00:01:02,829 --> 00:01:04,865 +with Apple Music easier and continue + +19 +00:01:04,898 --> 00:01:07,868 +to add support +for additional functionality. + +20 +00:01:07,901 --> 00:01:10,437 +MusicKit is a combination +of client frameworks + +21 +00:01:10,470 --> 00:01:12,439 +and the Apple Music API. + +22 +00:01:12,472 --> 00:01:14,541 +Used together, +they make it possible to add + +23 +00:01:14,575 --> 00:01:17,444 +Apple Music functionality to your apps. + +24 +00:01:17,477 --> 00:01:20,147 +You can discover +and fetch content from Apple Music, + +25 +00:01:20,180 --> 00:01:23,917 +including artists, albums, +playlists, and more, + +26 +00:01:23,951 --> 00:01:28,021 +with functionality to search the catalog +and browse popularity charts. + +27 +00:01:28,055 --> 00:01:31,692 +With the user’s consent, +MusicKit and Apple Music API let you + +28 +00:01:31,725 --> 00:01:34,328 +authenticate subscribers +to play available content + +29 +00:01:34,361 --> 00:01:36,263 +and provide access +to personalized features, + +30 +00:01:36,296 --> 00:01:38,799 +including their music library, +recommendations, + +31 +00:01:38,832 --> 00:01:41,435 +and recently played history. + +32 +00:01:41,468 --> 00:01:44,238 +MusicKit client frameworks +provide the APIs needed + +33 +00:01:44,271 --> 00:01:45,772 +to authenticate subscribers + +34 +00:01:45,806 --> 00:01:48,775 +and start and control +playback on a device. + +35 +00:01:48,809 --> 00:01:52,145 +Client frameworks are available +for apps on Apple platforms, + +36 +00:01:52,179 --> 00:01:54,748 +web applications using JavaScript, + +37 +00:01:54,781 --> 00:01:57,918 +and Android applications +using the available SDK. + +38 +00:01:59,253 --> 00:02:01,889 +MusicKit on Apple platforms +has everything you need + +39 +00:02:01,922 --> 00:02:04,291 +to integrate Apple Music into your apps. + +40 +00:02:04,324 --> 00:02:07,227 +Subscribers can authenticate +or sign up within your app + +41 +00:02:07,261 --> 00:02:11,265 +to start and control playback +and access other personalized features. + +42 +00:02:11,298 --> 00:02:15,235 +Available APIs accelerate calls +to discover and access content + +43 +00:02:15,269 --> 00:02:19,806 +from Apple Music with native support +for resources and pagination. + +44 +00:02:19,840 --> 00:02:23,277 +Many of the topics covered later +about accessing and making requests + +45 +00:02:23,310 --> 00:02:26,213 +to Apple Music API directly +are handled by MusicKit + +46 +00:02:26,246 --> 00:02:28,949 +on Apple platforms automatically. + +47 +00:02:28,982 --> 00:02:31,919 +Developers writing apps +for Apple platforms are encouraged + +48 +00:02:31,952 --> 00:02:34,721 +to watch the session +"Meet MusicKit for Swift" + +49 +00:02:34,755 --> 00:02:36,790 +for an introduction to those APIs + +50 +00:02:36,823 --> 00:02:39,459 +and the session +"Explore more content with MusicKit" + +51 +00:02:39,493 --> 00:02:41,495 +for more functionality. + +52 +00:02:42,629 --> 00:02:45,232 +MusicKit on the Web makes it easy +to bring Apple Music + +53 +00:02:45,265 --> 00:02:47,568 +to your applications using JavaScript. + +54 +00:02:47,601 --> 00:02:49,670 +You can discover Apple Music content, + +55 +00:02:49,703 --> 00:02:52,973 +let subscribers sign in +to access personalized features + +56 +00:02:53,006 --> 00:02:55,742 +and play content directly on your website. + +57 +00:02:55,776 --> 00:02:58,378 +Full access to Apple Music API +gives you the power + +58 +00:02:58,412 --> 00:03:01,215 +to create unique music experiences. + +59 +00:03:01,248 --> 00:03:03,851 +MusicKit provides a collection +of built-in web components, + +60 +00:03:03,884 --> 00:03:06,186 +including a full-featured media player. + +61 +00:03:06,220 --> 00:03:08,455 +These components +make it easy to get started + +62 +00:03:08,488 --> 00:03:11,291 +and have the flexibility +to customize for your experience. + +63 +00:03:12,960 --> 00:03:15,262 +MusicKit for Android +lets you integrate Apple Music + +64 +00:03:15,295 --> 00:03:17,130 +into your Android applications. + +65 +00:03:17,164 --> 00:03:19,833 +Support for authenticating +Apple Music subscribers, + +66 +00:03:19,867 --> 00:03:21,401 +controlling content playback, + +67 +00:03:21,435 --> 00:03:25,572 +and full access to Apple Music API allows +you to build a rich music experience. + +68 +00:03:27,541 --> 00:03:31,345 +Apple Music API is the common +JSON web service that provides access + +69 +00:03:31,378 --> 00:03:34,715 +to discovery features +and Apple Music catalog content. + +70 +00:03:34,748 --> 00:03:37,518 +Authenticated subscribers can +access personalized features, + +71 +00:03:37,551 --> 00:03:40,020 +including their Music Library, +Recommendations, + +72 +00:03:40,053 --> 00:03:41,922 +and Recently Played history. + +73 +00:03:42,656 --> 00:03:46,126 +Next, I'll explain how to get access. + +74 +00:03:46,159 --> 00:03:50,264 +To make requests to Apple Music API, +you will need a Developer Token. + +75 +00:03:50,297 --> 00:03:52,332 +For applications on Apple platforms, + +76 +00:03:52,366 --> 00:03:54,801 +you can take advantage +of automatic token management + +77 +00:03:54,835 --> 00:03:58,038 +by enabling the MusicKit service +for your app. + +78 +00:03:58,071 --> 00:04:00,474 +This service can be found +in the App ID section + +79 +00:04:00,507 --> 00:04:03,377 +of the Apple Developer portal shown here. + +80 +00:04:04,711 --> 00:04:06,713 +For applications on other platforms, + +81 +00:04:06,747 --> 00:04:10,150 +you can obtain a Developer Token +by enrolling as a MusicKit developer + +82 +00:04:10,184 --> 00:04:12,119 +on the Apple Developer portal. + +83 +00:04:12,152 --> 00:04:15,556 +From there, you can request +and download a private key. + +84 +00:04:15,589 --> 00:04:18,425 +You will need to generate +a JSON Web Token and sign it + +85 +00:04:18,458 --> 00:04:20,727 +with your downloaded private key. + +86 +00:04:20,761 --> 00:04:23,864 +A JSON Web Token is split in two parts. + +87 +00:04:23,897 --> 00:04:25,432 +The first part is required headers, + +88 +00:04:25,465 --> 00:04:30,137 +including the signing algorithm "alg," +which must be ES256, + +89 +00:04:30,170 --> 00:04:32,272 +and the key identifier "kid," + +90 +00:04:32,306 --> 00:04:35,509 +which is included as part +of your private key download. + +91 +00:04:35,542 --> 00:04:38,078 +The second part of the token is claims. + +92 +00:04:38,111 --> 00:04:40,981 +Apple Music API has three required claims: + +93 +00:04:41,014 --> 00:04:43,116 +issuer ID “iss,” + +94 +00:04:43,150 --> 00:04:45,986 +which is your team ID that can be found +in the memberships section + +95 +00:04:46,019 --> 00:04:47,955 +of the Apple Developer portal, + +96 +00:04:47,988 --> 00:04:50,090 +issued at time "iat," + +97 +00:04:50,123 --> 00:04:53,160 +which is represented by the number +of seconds since the Epoch, + +98 +00:04:53,193 --> 00:04:55,529 +and an expiration time "exp." + +99 +00:04:55,562 --> 00:04:58,599 +This is in the same time format +as the issued time. + +100 +00:04:58,632 --> 00:05:01,702 +Tokens can be generated +to expire a maximum of six months + +101 +00:05:01,735 --> 00:05:04,037 +from when they were issued. + +102 +00:05:04,071 --> 00:05:07,107 +For web applications, +we encourage providing an origin claim, + +103 +00:05:07,140 --> 00:05:10,010 +which will issue a token valid +for your website only. + +104 +00:05:11,879 --> 00:05:14,314 +A valid, +signed token must be supplied + +105 +00:05:14,348 --> 00:05:18,352 +in the authorization header +for all requests to Apple Music API. + +106 +00:05:18,385 --> 00:05:21,955 +With the MusicKit service enabled +for your applications on Apple platforms, + +107 +00:05:21,989 --> 00:05:23,924 +this will be done automatically. + +108 +00:05:23,957 --> 00:05:26,860 +MusicKit on the web must be configured +with a token you generate + +109 +00:05:26,894 --> 00:05:29,563 +to make requests to Apple Music API. + +110 +00:05:29,596 --> 00:05:32,833 +More information about generating +JSON Web Tokens can be found + +111 +00:05:32,866 --> 00:05:36,036 +in the Apple Music API documentation. + +112 +00:05:36,069 --> 00:05:40,974 +Now I will show you how to request +resources using Apple Music API. + +113 +00:05:41,008 --> 00:05:43,010 +Resources model Apple Music content, + +114 +00:05:43,043 --> 00:05:46,747 +such as artists, albums, +songs, playlists and more. + +115 +00:05:46,780 --> 00:05:49,416 +Each has its own corresponding type. + +116 +00:05:49,449 --> 00:05:52,219 +Resources can be fetched +from Apple Music API endpoints + +117 +00:05:52,252 --> 00:05:56,657 +through discovery features like search +or by using their identifiers. + +118 +00:05:56,690 --> 00:05:59,960 +Here is an example request URL +to fetch a playlist resource + +119 +00:05:59,993 --> 00:06:02,763 +from the API using its identifier. + +120 +00:06:02,796 --> 00:06:07,701 +Apple Music API +is hosted at api.music.apple.com. + +121 +00:06:07,734 --> 00:06:10,404 +The path of the URL +uses a RESTful pattern + +122 +00:06:10,437 --> 00:06:13,874 +and represents the location +for a resource or feature. + +123 +00:06:13,907 --> 00:06:16,977 +The first element in the location +is the version of the API, + +124 +00:06:17,010 --> 00:06:18,779 +which is currently version 1. + +125 +00:06:18,812 --> 00:06:22,816 +New versions may become available +when significant changes are introduced. + +126 +00:06:22,850 --> 00:06:25,953 +Versions maintain backwards compatibility +with existing calls, + +127 +00:06:25,986 --> 00:06:30,490 +even as enhancements to resources +and new features are made available. + +128 +00:06:30,524 --> 00:06:33,026 +For requests +within the Apple Music catalog, + +129 +00:06:33,060 --> 00:06:34,661 +the next section in the location + +130 +00:06:34,695 --> 00:06:37,798 +represents the specific catalog +to fetch from. + +131 +00:06:37,831 --> 00:06:40,200 +Apple Music is +a worldwide streaming service, + +132 +00:06:40,234 --> 00:06:44,438 +and content can differ between regions, +which we refer to as storefronts. + +133 +00:06:45,772 --> 00:06:48,709 +For information about storefronts +and content availability, + +134 +00:06:48,742 --> 00:06:53,614 +watch the session titled "Cross reference +content with the Apple Music API." + +135 +00:06:53,647 --> 00:06:56,550 +In this example, I will fetch +a playlist from the catalog + +136 +00:06:56,583 --> 00:06:58,318 +in the United States storefront, + +137 +00:06:58,352 --> 00:07:01,855 +denoted +by the two-letter country code “us.” + +138 +00:07:01,889 --> 00:07:05,125 +The final part of this location +is the resource’s identity, + +139 +00:07:05,158 --> 00:07:08,996 +represented by its type "playlists" +followed by its identifier. + +140 +00:07:10,163 --> 00:07:12,466 +Since Apple Music is available +in many regions, + +141 +00:07:12,499 --> 00:07:14,434 +support for localization is possible + +142 +00:07:14,468 --> 00:07:18,005 +using the optional language +tag query parameter "L." + +143 +00:07:18,038 --> 00:07:22,409 +Here, I am specifying +the language tag "en-US" for English. + +144 +00:07:22,442 --> 00:07:28,081 +To fetch content localized in Spanish, +I could specify the tag “es-MX.” + +145 +00:07:28,115 --> 00:07:29,249 +To keep things simple, + +146 +00:07:29,283 --> 00:07:31,718 +I will not specify a language tag +in these examples, + +147 +00:07:31,752 --> 00:07:34,821 +which results +in the storefront’s default localization. + +148 +00:07:34,855 --> 00:07:36,957 +The storefronts +that Apple Music is available in + +149 +00:07:36,990 --> 00:07:38,592 +and their supported localizations + +150 +00:07:38,625 --> 00:07:41,862 +can be found by accessing +the storefronts reference endpoint. + +151 +00:07:41,895 --> 00:07:45,399 +More detail can be found +in the Apple Music API documentation. + +152 +00:07:46,500 --> 00:07:49,269 +Here's a look at making +a GET request for a playlist. + +153 +00:07:49,303 --> 00:07:51,271 +This returns +a resource collection response + +154 +00:07:51,305 --> 00:07:54,441 +with a "data" array +containing the playlist resource. + +155 +00:07:54,474 --> 00:07:59,146 +Resource objects indicate the identity +of the content with an ID value and type. + +156 +00:07:59,179 --> 00:08:03,851 +The combination of ID and type represent +the unique identity of a resource. + +157 +00:08:03,884 --> 00:08:08,388 +The href value indicates the location in +the API where the resource may be fetched. + +158 +00:08:08,422 --> 00:08:12,960 +In this case, note the location +is the same as the path we requested. + +159 +00:08:12,993 --> 00:08:17,331 +The metadata values for the content are +found in the resource's attributes map. + +160 +00:08:17,364 --> 00:08:21,702 +Direct connections to other resources, +such as this playlist curator and tracks, + +161 +00:08:21,735 --> 00:08:23,804 +are found in the relationships map. + +162 +00:08:24,905 --> 00:08:26,940 +Here is a closer look +at the attributes map, + +163 +00:08:26,974 --> 00:08:29,676 +which contains the metadata values +for the playlist. + +164 +00:08:29,710 --> 00:08:32,312 +I'll walk through an example +of using some of these attributes + +165 +00:08:32,346 --> 00:08:34,314 +in the widget on the right, + +166 +00:08:34,348 --> 00:08:39,887 +which uses the playlist's name, +curator name, and description attributes. + +167 +00:08:39,920 --> 00:08:42,823 +playParams is a common attribute +on many resource types + +168 +00:08:42,856 --> 00:08:46,426 +that indicates when the content is +available to stream for subscribers. + +169 +00:08:46,460 --> 00:08:49,296 +This can be used to determine +if play control should be enabled, + +170 +00:08:49,329 --> 00:08:53,634 +as content without playParams is +not available for playback. + +171 +00:08:53,667 --> 00:08:56,537 +Artwork is another common attribute +for many resource types + +172 +00:08:56,570 --> 00:08:59,806 +that contains values for the maximum width +and height for the image, + +173 +00:08:59,840 --> 00:09:02,776 +alongside a url +where the image can be loaded from. + +174 +00:09:05,078 --> 00:09:07,581 +At Apple, +developer feedback is very important + +175 +00:09:07,614 --> 00:09:10,651 +and helps us improve our APIs +and services for everyone. + +176 +00:09:10,684 --> 00:09:12,786 +This year, +we're happy to announce the addition + +177 +00:09:12,819 --> 00:09:15,389 +of one of our most requested features. + +178 +00:09:15,422 --> 00:09:18,258 +Artist artwork is now available +in Apple Music API, + +179 +00:09:18,292 --> 00:09:20,994 +making plain silhouettes +a thing of the past. + +180 +00:09:21,028 --> 00:09:23,864 +All new and existing apps +can add support for these images + +181 +00:09:23,897 --> 00:09:27,634 +by looking for the artwork attribute +added to artist resources. + +182 +00:09:27,668 --> 00:09:31,905 +Artist artwork can be loaded the same way +as artwork for other content types. + +183 +00:09:31,939 --> 00:09:35,209 +Here is an abbreviated example +of an artwork url for the playlist + +184 +00:09:35,242 --> 00:09:37,010 +we were looking at earlier. + +185 +00:09:37,044 --> 00:09:42,482 +Artwork urls contain "w," width and "h," +height tokens, highlighted in blue. + +186 +00:09:42,516 --> 00:09:45,586 +To load artwork using this URL, +replace these tokens + +187 +00:09:45,619 --> 00:09:48,388 +with the desired resolution +for your display. + +188 +00:09:48,422 --> 00:09:51,825 +Since playlist artwork is square, +I will use the same resolution + +189 +00:09:51,859 --> 00:09:53,994 +for both width and height. + +190 +00:09:54,027 --> 00:09:57,998 +Here is an example of requesting +a resolution of 400 by 400, + +191 +00:09:58,031 --> 00:10:02,469 +at 300 by 300, and at 200 by 200. + +192 +00:10:02,503 --> 00:10:05,072 +The images scale for display +at each resolution, + +193 +00:10:05,105 --> 00:10:07,941 +and the file sizes are reduced +for smaller images. + +194 +00:10:09,109 --> 00:10:11,745 +All resource types have a set +of default attributes + +195 +00:10:11,778 --> 00:10:15,415 +that represent the common +or basic metadata for that resource. + +196 +00:10:15,449 --> 00:10:18,118 +Some resources have additional attributes +that may be fetched, + +197 +00:10:18,151 --> 00:10:20,454 +known as "extended attributes." + +198 +00:10:20,487 --> 00:10:24,658 +These extended attributes can be requested +by using the extend query parameter. + +199 +00:10:24,691 --> 00:10:27,227 +For instance, +tracks on Apple Music playlists + +200 +00:10:27,261 --> 00:10:29,329 +are either songs or music videos. + +201 +00:10:29,363 --> 00:10:32,232 +If you'd like to know the types +of tracks a playlist contains, + +202 +00:10:32,266 --> 00:10:35,702 +you can request +the trackTypes extended attribute. + +203 +00:10:35,736 --> 00:10:38,438 +Extended attributes appear +in the resource’s attributes map + +204 +00:10:38,472 --> 00:10:41,175 +alongside their default attributes. + +205 +00:10:41,208 --> 00:10:44,611 +If a playlist's tracks are music videos +rather than songs, + +206 +00:10:44,645 --> 00:10:48,215 +perhaps you would customize +the play button to inform the viewer. + +207 +00:10:49,683 --> 00:10:52,319 +Taking a look now +at the relationships map shows a couple + +208 +00:10:52,352 --> 00:10:55,489 +of direct connections +from this playlist to other resources. + +209 +00:10:55,522 --> 00:10:57,658 +Many resources have +automatic relationships + +210 +00:10:57,691 --> 00:11:00,761 +that will be returned +when requesting some content directly. + +211 +00:11:00,794 --> 00:11:06,033 +The automatic relationships +for a playlist are its curator and tracks. + +212 +00:11:06,066 --> 00:11:09,102 +Relationships are named +to indicate what the connection is, + +213 +00:11:09,136 --> 00:11:11,538 +such as the curator for this playlist. + +214 +00:11:11,572 --> 00:11:15,209 +They have an href location where +the relationship may be fetched directly + +215 +00:11:15,242 --> 00:11:18,579 +and a data array +for the related resource collection. + +216 +00:11:18,612 --> 00:11:22,216 +For playlists, the curator relationship +is automatically related, + +217 +00:11:22,249 --> 00:11:25,953 +meaning only the identity +of the related resources will be present. + +218 +00:11:25,986 --> 00:11:30,824 +This allows for easily linking to +this resource using its identity or href. + +219 +00:11:30,858 --> 00:11:34,094 +Since a playlist already contains +the curator's name as an attribute, + +220 +00:11:34,127 --> 00:11:36,263 +there may be no need +to include other metadata + +221 +00:11:36,296 --> 00:11:38,198 +for the related curator resource. + +222 +00:11:38,232 --> 00:11:41,768 +The identity of the resource could be used +to make the curator name a link, + +223 +00:11:41,802 --> 00:11:45,772 +letting people navigate to the curator to +discover additional content if they wish. + +224 +00:11:45,806 --> 00:11:49,009 +If you want to show additional information +for the curator with the playlist, + +225 +00:11:49,042 --> 00:11:52,713 +such as its artwork, you will need +to fetch the curator's metadata. + +226 +00:11:52,746 --> 00:11:55,415 +Use the include parameter +to specify the relationships + +227 +00:11:55,449 --> 00:11:58,218 +you want related resource metadata for. + +228 +00:11:58,252 --> 00:12:00,888 +Some resource types have +additional relationships not included + +229 +00:12:00,921 --> 00:12:04,057 +by default that can also be requested +with this parameter. + +230 +00:12:04,091 --> 00:12:07,294 +Note that including relationships +can increase the size of responses + +231 +00:12:07,327 --> 00:12:09,830 +and negatively impact +the speed of your app. + +232 +00:12:09,863 --> 00:12:12,900 +Many useful values that may +be desired from related resources + +233 +00:12:12,933 --> 00:12:16,403 +can be found directly as attributes +on the original resource. + +234 +00:12:16,436 --> 00:12:18,739 +For the best performance, +we recommend including + +235 +00:12:18,772 --> 00:12:22,910 +only the relationships +and metadata needed in the response. + +236 +00:12:22,943 --> 00:12:26,079 +Detailed information for resource types +and their available attributes + +237 +00:12:26,113 --> 00:12:30,150 +and relationships can be found +in the Apple Music API documentation. + +238 +00:12:30,184 --> 00:12:32,819 +For more about extended attributes, +relationships, + +239 +00:12:32,853 --> 00:12:35,189 +and special relationships called views, + +240 +00:12:35,222 --> 00:12:39,626 +watch the session “Explore the catalog +with the Apple Music API.” + +241 +00:12:39,660 --> 00:12:42,529 +Revisiting the relationships +for the example playlist, + +242 +00:12:42,563 --> 00:12:45,566 +the tracks for the playlist +are automatically included, + +243 +00:12:45,599 --> 00:12:48,602 +meaning the attribute metadata +for the related track resources + +244 +00:12:48,635 --> 00:12:50,904 +appear in the response. + +245 +00:12:50,938 --> 00:12:54,274 +The attributes for the related tracks +can be used to display a track list + +246 +00:12:54,308 --> 00:12:57,010 +for the playlist using their artwork +and other metadata + +247 +00:12:57,044 --> 00:12:58,946 +shown in the widget on the right. + +248 +00:12:58,979 --> 00:13:01,215 +In the previous section, +I showed an example + +249 +00:13:01,248 --> 00:13:04,518 +of fetching a playlist resource +from the catalog with its tracks. + +250 +00:13:04,551 --> 00:13:07,621 +In this section, +I'll explain how to handle relationships + +251 +00:13:07,654 --> 00:13:09,590 +with a large number of resources. + +252 +00:13:09,623 --> 00:13:13,594 +The related resources for a relationship +appear in its data array. + +253 +00:13:13,627 --> 00:13:16,129 +When the number +of related resources is small, + +254 +00:13:16,163 --> 00:13:19,233 +all of the resources will appear +in a single response. + +255 +00:13:19,266 --> 00:13:22,436 +Relationships with more related resources +than appear in a single response + +256 +00:13:22,469 --> 00:13:25,305 +must be fetched +in multiple parts called pages. + +257 +00:13:26,907 --> 00:13:30,511 +I'll use the tracks relationship +for a large playlist as an example. + +258 +00:13:30,544 --> 00:13:34,915 +Only the first 100 tracks +of a playlist are included by default. + +259 +00:13:34,948 --> 00:13:37,117 +If a playlist has more than 100 tracks, + +260 +00:13:37,150 --> 00:13:41,355 +then the additional tracks +must be fetched in subsequent pages. + +261 +00:13:41,388 --> 00:13:45,192 +Here is an example response +for a playlist with over 100 tracks. + +262 +00:13:45,225 --> 00:13:48,262 +The href and resource collection data +for the tracks relationship appear + +263 +00:13:48,295 --> 00:13:51,532 +in the response as was seen earlier. + +264 +00:13:51,565 --> 00:13:54,735 +Since this playlist has more tracks +than were included in the response, + +265 +00:13:54,768 --> 00:13:57,871 +a next location appears +as a sibling to the data. + +266 +00:13:57,905 --> 00:14:00,307 +The next location represents +where the remaining resources + +267 +00:14:00,340 --> 00:14:03,043 +in the collection can be fetched. + +268 +00:14:03,076 --> 00:14:05,879 +Fetching the next location +makes a direct relationship fetch + +269 +00:14:05,913 --> 00:14:08,448 +and returns the relationship's +resource collection data + +270 +00:14:08,482 --> 00:14:10,551 +starting from the provided offset. + +271 +00:14:10,584 --> 00:14:14,121 +The default limit for the relationship +will apply to each page. + +272 +00:14:14,154 --> 00:14:17,891 +You may select your own page size +by supplying a limit parameter. + +273 +00:14:17,925 --> 00:14:19,893 +The page size limit +can be between one + +274 +00:14:19,927 --> 00:14:22,329 +and the specific relationship's +maximum page size, + +275 +00:14:22,362 --> 00:14:25,132 +which can be found in the documentation. + +276 +00:14:25,165 --> 00:14:27,067 +If the resource collection +for the relationship + +277 +00:14:27,100 --> 00:14:30,137 +contains additional resources +past the ones returned in the page, + +278 +00:14:30,170 --> 00:14:33,974 +a next location will continue +to appear as a sibling to the data. + +279 +00:14:34,007 --> 00:14:38,512 +When the resource collection is exhausted, +a next location will not be present. + +280 +00:14:38,545 --> 00:14:42,683 +Note that next locations do not reflect +the selected page size for the request. + +281 +00:14:42,716 --> 00:14:45,853 +You will need to supply a limit parameter +on each request if you wish + +282 +00:14:45,886 --> 00:14:48,655 +to use a different page size +than the default. + +283 +00:14:48,689 --> 00:14:50,824 +Always use the next location +from the response + +284 +00:14:50,858 --> 00:14:52,659 +to page over resource collections. + +285 +00:14:52,693 --> 00:14:55,262 +Trying to calculate your own offset +can result in issues + +286 +00:14:55,295 --> 00:14:58,098 +with invalid locations +or duplicate results. + +287 +00:14:58,131 --> 00:15:00,701 +In the next section, +I will show how to search fof content + +288 +00:15:00,734 --> 00:15:03,937 +in the Apple Music catalog +using Apple Music API. + +289 +00:15:03,971 --> 00:15:07,674 +Apple Music API provides functionality +to find content in the catalog + +290 +00:15:07,708 --> 00:15:09,243 +using a search term. + +291 +00:15:09,276 --> 00:15:11,845 +A search request resembles +a catalog resource request + +292 +00:15:11,879 --> 00:15:15,916 +with /search as the location +and the search term as a parameter. + +293 +00:15:15,949 --> 00:15:19,920 +You can specify which content types you're +interested in with the types parameter + +294 +00:15:19,953 --> 00:15:23,991 +and the maximum number of results +to include for each type using a limit. + +295 +00:15:24,024 --> 00:15:26,493 +When more results are found +past the requested limit, + +296 +00:15:26,527 --> 00:15:29,463 +pagination can be used +to continue the search. + +297 +00:15:29,496 --> 00:15:33,667 +Here's the response to a search +for albums and songs with the term “pop." + +298 +00:15:33,700 --> 00:15:37,538 +The search endpoint returns a response +with a results object containing a group + +299 +00:15:37,571 --> 00:15:40,240 +for each requested type +with matching content. + +300 +00:15:40,274 --> 00:15:43,911 +For this request, +matches were found for albums and songs. + +301 +00:15:45,345 --> 00:15:48,849 +The response also includes a meta object +containing a recommended order + +302 +00:15:48,882 --> 00:15:51,285 +for the result groups based on relevancy. + +303 +00:15:51,318 --> 00:15:53,487 +This may be helpful +if you're building a search experience + +304 +00:15:53,520 --> 00:15:56,957 +that handles +multiple content types at once. + +305 +00:15:56,990 --> 00:16:00,294 +Each result group has and href +where the results were fetched from + +306 +00:16:00,327 --> 00:16:03,263 +and a resource collection data array +containing the matching resources + +307 +00:16:03,297 --> 00:16:05,866 +of the relevant type +for the search term. + +308 +00:16:05,899 --> 00:16:08,435 +If more matches for +a result group are available, + +309 +00:16:08,468 --> 00:16:12,873 +the group will contain a next location +where additional matches may be fetched. + +310 +00:16:12,906 --> 00:16:16,276 +Requesting the next location +returns more results for the selected type + +311 +00:16:16,310 --> 00:16:18,378 +starting from the offset. + +312 +00:16:18,412 --> 00:16:20,614 +In the last section, +I’ll discuss accessing + +313 +00:16:20,647 --> 00:16:23,150 +personalized features in Apple Music API. + +314 +00:16:24,351 --> 00:16:26,954 +Apple Music API and MusicKit +provide access + +315 +00:16:26,987 --> 00:16:29,523 +to features personalized +to a specific user. + +316 +00:16:29,556 --> 00:16:32,192 +These features let you add support +for subscribers to view + +317 +00:16:32,226 --> 00:16:34,862 +and search for content +in their Apple Music library, + +318 +00:16:34,895 --> 00:16:39,499 +including the ability to add content +they enjoy and create new playlists. + +319 +00:16:39,533 --> 00:16:41,602 +Personalized Recommendations +can be surfaced + +320 +00:16:41,635 --> 00:16:43,937 +based on individual music tastes. + +321 +00:16:43,971 --> 00:16:47,107 +And you can let people rediscover +music they've been listening to recently + +322 +00:16:47,140 --> 00:16:50,511 +to pick up where they left off +or provide entirely new experiences. + +323 +00:16:52,212 --> 00:16:54,114 +Personalized features are available +for people + +324 +00:16:54,147 --> 00:16:56,650 +with an active subscription +to Apple Music. + +325 +00:16:56,683 --> 00:16:59,586 +To support these features, +you must first authenticate the person + +326 +00:16:59,620 --> 00:17:02,155 +with your app using MusicKit +and request permission + +327 +00:17:02,189 --> 00:17:04,658 +to access their music data. + +328 +00:17:04,691 --> 00:17:09,162 +When permission is granted, +a Music User Token will be available. + +329 +00:17:09,196 --> 00:17:13,867 +This token is added to Apple Music API +requests in the music-user-token header + +330 +00:17:13,901 --> 00:17:17,271 +and is used to authenticate access +to personalized data. + +331 +00:17:18,338 --> 00:17:23,110 +A Music User Token is specific to your app +and the device the user authenticated on. + +332 +00:17:23,143 --> 00:17:25,245 +This token must not be shared +across devices, + +333 +00:17:25,279 --> 00:17:28,782 +as permission is granted +for a single device at a time. + +334 +00:17:28,815 --> 00:17:31,985 +A token may become invalid based +on changes to a person’s subscription + +335 +00:17:32,019 --> 00:17:34,888 +or password, +or if they revoke access to your app. + +336 +00:17:34,922 --> 00:17:37,191 +It may also expire with time. + +337 +00:17:37,224 --> 00:17:40,427 +Authentication can be refreshed +by prompting them to sign back in + +338 +00:17:40,460 --> 00:17:42,796 +and grant permission for your app. + +339 +00:17:42,829 --> 00:17:45,299 +Note that this token is managed +automatically by MusicKit + +340 +00:17:45,332 --> 00:17:47,968 +on Apple platforms +and MusicKit on the Web. + +341 +00:17:48,001 --> 00:17:50,304 +Please see the documentation +for the framework you're using + +342 +00:17:50,337 --> 00:17:51,872 +for more details. + +343 +00:17:51,905 --> 00:17:55,008 +In today's session, +I showed how you can integrate Apple Music + +344 +00:17:55,042 --> 00:17:58,078 +into your apps using MusicKit +and Apple Music API, + +345 +00:17:58,111 --> 00:18:01,315 +a quick look at the platforms with support +for MusicKit client frameworks, + +346 +00:18:01,348 --> 00:18:05,152 +and how to use Apple Music API to access +and find content in the catalog, + +347 +00:18:05,185 --> 00:18:08,555 +and some personalized features +available for subscribers. + +348 +00:18:08,589 --> 00:18:11,558 +For more on this year's updates +to MusicKit on Apple platforms, + +349 +00:18:11,592 --> 00:18:14,761 +check out the session +"Explore more content with MusicKit.” + +350 +00:18:14,795 --> 00:18:15,829 +Thank you for watching, + +351 +00:18:15,863 --> 00:18:18,398 +and we hope you enjoy the rest +of this year's WWDC. + +352 +00:18:18,432 --> 00:18:23,103 +[upbeat music] + diff --git "a/eng/2022 Session 10149 What\342\200\231s new in AVQT en.srt" "b/eng/2022 Session 10149 What\342\200\231s new in AVQT en.srt" new file mode 100644 index 0000000..c6422b9 --- /dev/null +++ "b/eng/2022 Session 10149 What\342\200\231s new in AVQT en.srt" @@ -0,0 +1,741 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,943 --> 00:00:11,111 +Ahmed Badr: Hello and welcome. + +3 +00:00:11,144 --> 00:00:14,348 +My name is Ahmed and I am part +of the Display and Color Technologies team + +4 +00:00:14,381 --> 00:00:15,415 +at Apple. + +5 +00:00:15,449 --> 00:00:19,620 +Today, I would like to talk about some of +the new features and enhancements we made + +6 +00:00:19,653 --> 00:00:23,824 +in the Advanced Video Quality Tool, +or short AVQT. + +7 +00:00:23,857 --> 00:00:26,693 +Let me start with a quick recap. + +8 +00:00:26,727 --> 00:00:28,762 +So what's AVQT? + +9 +00:00:28,795 --> 00:00:34,268 +AVQT is a command line executable +that we first released in WWDC 2021. + +10 +00:00:34,301 --> 00:00:38,105 +It's a full reference video quality metric +that assesses the quality of videos + +11 +00:00:38,138 --> 00:00:41,208 +with potential compression +and scaling artifacts. + +12 +00:00:41,241 --> 00:00:45,212 +It attempts to mimic +how people rate the quality of videos. + +13 +00:00:45,245 --> 00:00:51,218 +AVQT supports all AVFoundation-based +video formats, both SDR and HDR. + +14 +00:00:51,251 --> 00:00:56,023 +This includes HDR10, HLG, +and Dolby Vision. + +15 +00:00:56,056 --> 00:00:59,860 +There are three key attributes +that are unique to AVQT. + +16 +00:00:59,893 --> 00:01:04,531 +First, AVQT achieves high correlation +with how people rate videos. + +17 +00:01:04,565 --> 00:01:06,266 +This applies to all content types + +18 +00:01:06,300 --> 00:01:09,603 +such as animation, natural scenes, +and sports. + +19 +00:01:09,636 --> 00:01:13,340 +Second, AVQT comes with +impressive processing speeds, + +20 +00:01:13,373 --> 00:01:16,743 +thanks to AVFoundation and Metal +that AVQT relies on + +21 +00:01:16,777 --> 00:01:19,146 +for video decoding and processing. + +22 +00:01:19,179 --> 00:01:23,650 +Third, AVQT is designed to accommodate +different viewing setups. + +23 +00:01:23,684 --> 00:01:26,286 +The same video can result +in different experiences + +24 +00:01:26,320 --> 00:01:28,121 +depending on the viewing setup. + +25 +00:01:28,155 --> 00:01:30,657 +This can be configured +in AVQT through factors + +26 +00:01:30,691 --> 00:01:34,461 +such as display resolution, +display size, and viewing distance. + +27 +00:01:35,329 --> 00:01:39,566 +To learn more about AVQT, +please check our 2021 video + +28 +00:01:39,600 --> 00:01:43,637 +titled "Evaluate videos +with Advanced Video Quality Tool." + +29 +00:01:43,670 --> 00:01:47,574 +In the WWDC 2021 video +where AVQT was announced, + +30 +00:01:47,608 --> 00:01:51,311 +we presented the impressive +processing speeds it achieves on M1 Macs. + +31 +00:01:51,345 --> 00:01:56,984 +Since then, Apple processors +got more capable and AVQT got faster. + +32 +00:01:57,017 --> 00:02:01,054 +Here are the processing speeds +on the new family of M1 processors. + +33 +00:02:01,088 --> 00:02:04,691 +On M1 Ultra, AVQT evaluates a 2-hour, + +34 +00:02:04,725 --> 00:02:08,862 +HEVC-compressed 4K movie in 20 minutes. + +35 +00:02:08,896 --> 00:02:11,598 +This is six times faster than real time. + +36 +00:02:11,632 --> 00:02:14,268 +For a full HD movie +with a similar duration, + +37 +00:02:14,301 --> 00:02:17,938 +AVQT processes the video +in less than 11 minutes. + +38 +00:02:17,971 --> 00:02:21,675 +This is more than ten times faster +than real time. + +39 +00:02:21,708 --> 00:02:25,279 +You can now evaluate more videos +in the same amount of time. + +40 +00:02:26,513 --> 00:02:29,650 +Next, let me introduce +the new features and enhancements + +41 +00:02:29,683 --> 00:02:32,986 +that we added to AVQT this year. + +42 +00:02:33,020 --> 00:02:36,590 +You can now visualize +the quality of your video using AVQT + +43 +00:02:36,623 --> 00:02:38,825 +without any extra steps. + +44 +00:02:38,859 --> 00:02:42,329 +The latest version of AVQT produces +HTML-based reports + +45 +00:02:42,362 --> 00:02:44,765 +with interactive plots and diagrams + +46 +00:02:44,798 --> 00:02:47,968 +showing analysis of the quality +of your videos. + +47 +00:02:48,001 --> 00:02:52,072 +These reports can easily be generated +by adding the new ‘visualize’ flag. + +48 +00:02:53,006 --> 00:02:55,576 +You can easily identify +sections in your video + +49 +00:02:55,609 --> 00:02:58,545 +where quality did not meet +the expectation. + +50 +00:02:58,579 --> 00:03:01,882 +You can also share AVQT reports +with other colleagues. + +51 +00:03:01,915 --> 00:03:05,152 +They don't need to have AVQT +or any other tool installed. + +52 +00:03:05,185 --> 00:03:07,321 +They can just open the report in Safari. + +53 +00:03:09,056 --> 00:03:11,792 +So let me show you how to create +an interactive report + +54 +00:03:11,825 --> 00:03:14,061 +and how to use some of its features. + +55 +00:03:15,996 --> 00:03:18,365 +I will start by showing the help menu. + +56 +00:03:18,398 --> 00:03:20,501 +We added a new argument, ‘visualize,’ + +57 +00:03:20,534 --> 00:03:22,970 +which you can use to create reports. + +58 +00:03:23,003 --> 00:03:27,608 +Here's an AVQT command that evaluates +the quality of a test video that I created + +59 +00:03:27,641 --> 00:03:32,045 +by compressing a segment +from last year's WWDC presentation. + +60 +00:03:32,079 --> 00:03:36,984 +You can then add the visualize argument +and it will also create a report. + +61 +00:03:37,017 --> 00:03:39,319 +Once it's done, you will see the location + +62 +00:03:39,353 --> 00:03:42,756 +of the HTML report file +printed to standard out. + +63 +00:03:42,789 --> 00:03:45,259 +This file can simply be opened in Safari. + +64 +00:03:46,126 --> 00:03:50,364 +The table on the left shows information +about the analyzed videos. + +65 +00:03:50,397 --> 00:03:53,133 +The one on the right includes +settings and parameters used + +66 +00:03:53,166 --> 00:03:55,102 +while evaluating the videos. + +67 +00:03:57,905 --> 00:04:02,943 +This figure shows both frame-level +and segment-level AVQT scores over time. + +68 +00:04:05,946 --> 00:04:08,015 +If you're interested +in a specific interval, + +69 +00:04:08,048 --> 00:04:10,784 +you can simply zoom in +to get more details. + +70 +00:04:12,252 --> 00:04:15,756 +Hovering over the plot will give you +more information about that point, + +71 +00:04:15,789 --> 00:04:19,459 +such as time, +AVQT score, and index of the frame + +72 +00:04:19,493 --> 00:04:21,361 +or segment that this score belongs to. + +73 +00:04:23,263 --> 00:04:27,267 +To zoom out to the home view, you can +double-click anywhere in the figure. + +74 +00:04:30,537 --> 00:04:33,540 +A pie chart is also included +that shows the distribution + +75 +00:04:33,574 --> 00:04:37,010 +of the AVQT frame scores +of the analyzed video. + +76 +00:04:37,044 --> 00:04:39,479 +It shows the percentage +and number of frames + +77 +00:04:39,513 --> 00:04:44,184 +in each of the five quality categories, +"Bad," "Poor," "Fair," + +78 +00:04:44,218 --> 00:04:46,286 +"Good," and "Excellent". + +79 +00:04:48,155 --> 00:04:50,824 +The figures on the bottom +are similar to those on the top + +80 +00:04:50,858 --> 00:04:53,293 +except that they show PSNR scores. + +81 +00:04:57,497 --> 00:05:01,034 +We hope this feature will help +summarize the quality of your videos + +82 +00:05:01,068 --> 00:05:03,704 +as well as identify issues, if any. + +83 +00:05:03,737 --> 00:05:06,707 +Here's another feature that I am +excited to tell you about. + +84 +00:05:06,740 --> 00:05:09,376 +You can now specify the time windows +from your reference + +85 +00:05:09,409 --> 00:05:11,945 +and test videos to be evaluated. + +86 +00:05:11,979 --> 00:05:16,416 +This enables focusing on a specific scene +or multiple scenes in your video. + +87 +00:05:16,450 --> 00:05:20,754 +It also allows for comparing videos +that are not temporally aligned. + +88 +00:05:20,787 --> 00:05:24,992 +We added four new command-line +arguments to help you accurately specify + +89 +00:05:25,025 --> 00:05:28,862 +the beginning and end of the video +sections you are interested in analyzing. + +90 +00:05:30,063 --> 00:05:34,001 +Let me show you an example +of how to use the time window feature. + +91 +00:05:35,035 --> 00:05:37,971 +Here's the video +I evaluated in the previous demo. + +92 +00:05:38,005 --> 00:05:42,075 +This time, I am interested +in evaluating a single scene of the video. + +93 +00:05:42,109 --> 00:05:44,745 +Here's how I can accomplish this. + +94 +00:05:44,778 --> 00:05:48,749 +In Quicktime Player, I will start +by seeking to that scene. + +95 +00:05:48,782 --> 00:05:51,318 +It's this one. + +96 +00:05:51,351 --> 00:05:54,221 +Clicking here shows the frame index. + +97 +00:05:54,254 --> 00:05:58,659 +I will use the arrow keys +to get to the first frame in the scene. + +98 +00:05:58,692 --> 00:06:00,394 +It's 270. + +99 +00:06:00,427 --> 00:06:04,531 +I will repeat the same steps to find +the last frame in the scene. + +100 +00:06:04,565 --> 00:06:07,267 +It's frame 486. + +101 +00:06:07,301 --> 00:06:10,971 +Now, let's evaluate this scene in AVQT. + +102 +00:06:11,004 --> 00:06:13,707 +The first part of the command +is the same as before. + +103 +00:06:13,740 --> 00:06:18,011 +I will then add the start and end frame +indices I got from QuickTime Player + +104 +00:06:18,045 --> 00:06:21,415 +using these four newly-added +command line arguments. + +105 +00:06:23,951 --> 00:06:26,887 +I now have the AVQT score for this scene. + +106 +00:06:28,922 --> 00:06:31,892 +Running this command is faster +than processing the whole video + +107 +00:06:31,925 --> 00:06:34,494 +and then only looking +at the scores of that scene. + +108 +00:06:34,528 --> 00:06:37,831 +We believe this adds +to the flexibility of AVQT. + +109 +00:06:37,865 --> 00:06:41,235 +Speaking of flexibility, +we also extended the support + +110 +00:06:41,268 --> 00:06:44,371 +of raw YUV formats in this version. + +111 +00:06:44,404 --> 00:06:47,508 +AVQT supports +all AVFoundation video formats. + +112 +00:06:47,541 --> 00:06:51,578 +Besides that, +it also supports raw YUV videos. + +113 +00:06:51,612 --> 00:06:54,214 +This enables scoring videos +that were never compressed, + +114 +00:06:54,248 --> 00:06:56,183 +such as a raw camera feed. + +115 +00:06:56,216 --> 00:06:59,152 +It's also useful when evaluating +videos that are compressed + +116 +00:06:59,186 --> 00:07:02,456 +and decoded outside the ecosystem. + +117 +00:07:02,489 --> 00:07:06,293 +This year, we extended the support +for raw YUV to include a total + +118 +00:07:06,326 --> 00:07:11,398 +of 20 formats with a wide range +of chroma sub-samplings and bit depths. + +119 +00:07:11,431 --> 00:07:13,567 +It's worth noting that `reference-fourcc` + +120 +00:07:13,600 --> 00:07:16,570 +and `test-fourcc` flags +are now deprecated. + +121 +00:07:16,603 --> 00:07:19,706 +We replaced them +with two separate sets of flags, + +122 +00:07:19,740 --> 00:07:22,709 +one for chroma-subsampling +and another for bit-depth. + +123 +00:07:24,111 --> 00:07:26,880 +This feature will allow you +to evaluate more videos + +124 +00:07:26,914 --> 00:07:28,982 +without the need of pre-processing. + +125 +00:07:31,084 --> 00:07:34,755 +Since AVQT was +first released in WWDC 2021, + +126 +00:07:34,788 --> 00:07:37,724 +there has been +a growing interest in the tool. + +127 +00:07:37,758 --> 00:07:40,160 +We received +numerous comments and requests. + +128 +00:07:40,194 --> 00:07:44,097 +One of the most common requests +is to add support for Linux. + +129 +00:07:44,131 --> 00:07:48,702 +Today, we are glad to announce +that AVQT is now available on Linux. + +130 +00:07:50,571 --> 00:07:54,808 +We understand that this will allow people +to evaluate content stored in the cloud + +131 +00:07:54,842 --> 00:07:59,780 +or compressed in Linux-based servers +without the need to move the videos. + +132 +00:07:59,813 --> 00:08:03,851 +Together with the macOS version, +we are also releasing a beta version + +133 +00:08:03,884 --> 00:08:07,988 +of AVQT for Linux that supports +a wide range of Linux distributions. + +134 +00:08:08,021 --> 00:08:12,492 +It can be easily deployed as it doesn't +require any external dependencies. + +135 +00:08:12,526 --> 00:08:15,229 +It's essentially plug and play. + +136 +00:08:15,262 --> 00:08:18,198 +To learn more about the supported +Linux distributions and versions, + +137 +00:08:18,232 --> 00:08:21,468 +please refer to the README file +in the AVQT package. + +138 +00:08:22,903 --> 00:08:25,806 +The Linux version of AVQT is +designed to have the same look + +139 +00:08:25,839 --> 00:08:28,208 +and feel of the macOS version. + +140 +00:08:28,242 --> 00:08:31,211 +It uses the same naming +of the command line flags + +141 +00:08:31,245 --> 00:08:34,648 +and produces output files +of the same format. + +142 +00:08:34,681 --> 00:08:38,118 +The Linux version supports +all 20 raw formats that are included + +143 +00:08:38,151 --> 00:08:41,221 +in the latest version of the macOS tool. + +144 +00:08:41,255 --> 00:08:44,858 +The viewing conditions parameters are not +available in the Linux beta version + +145 +00:08:44,892 --> 00:08:46,793 +but will be enabled in the future. + +146 +00:08:48,295 --> 00:08:51,331 +To wrap up, +here are a few important takeaways. + +147 +00:08:51,365 --> 00:08:56,570 +AVQT is a perceptual video quality tool +that we released in WWDC 2021. + +148 +00:08:56,603 --> 00:08:59,840 +It's available for download +on the developers’ portal. + +149 +00:08:59,873 --> 00:09:03,177 +If you haven't tried the tool, +go ahead and use it. + +150 +00:09:03,210 --> 00:09:06,613 +This year, +we added several enhancements to AVQT. + +151 +00:09:06,647 --> 00:09:10,684 +You can now visualize AVQT scores +of your videos in an interactive + +152 +00:09:10,717 --> 00:09:14,688 +HTML-based report that you can +share with your colleagues. + +153 +00:09:14,721 --> 00:09:17,324 +You can also use the new +‘Time Window’ feature to focus + +154 +00:09:17,357 --> 00:09:20,160 +on the quality +of specific scenes in your video. + +155 +00:09:20,194 --> 00:09:23,864 +And finally, for those of you +who have videos in the cloud + +156 +00:09:23,897 --> 00:09:28,702 +or use Linux-based video encoders, +AVQT is now available on Linux. + +157 +00:09:28,735 --> 00:09:31,305 +Thank you very much. + diff --git a/eng/2022 Session 10151 Add accessibility to your Unity games en.srt b/eng/2022 Session 10151 Add accessibility to your Unity games en.srt new file mode 100644 index 0000000..3f6d8eb --- /dev/null +++ b/eng/2022 Session 10151 Add accessibility to your Unity games en.srt @@ -0,0 +1,1024 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,309 --> 00:00:12,112 +Hello everyone, +my name is Eric, + +3 +00:00:12,145 --> 00:00:17,050 +and I'm excited to tell you how to add +accessibility to your Unity games. + +4 +00:00:18,085 --> 00:00:22,723 +Accessibility is about +allowing everyone to use our products. + +5 +00:00:22,756 --> 00:00:27,361 +And today marks a huge leap forward +for accessible gaming + +6 +00:00:27,394 --> 00:00:31,398 +with the Apple Accessibility +plug-in for Unity developers. + +7 +00:00:31,431 --> 00:00:36,637 +We will focus on three Apple technologies +for making your games accessible. + +8 +00:00:37,738 --> 00:00:43,210 +VoiceOver is a screen reader which helps +users who are blind or low vision. + +9 +00:00:43,243 --> 00:00:47,581 +It reads items on the screen +and provides custom gestures + +10 +00:00:47,614 --> 00:00:50,617 +for users to interact with controls. + +11 +00:00:51,985 --> 00:00:55,522 +Switch control, which allows someone +with low motor control + +12 +00:00:55,556 --> 00:00:59,593 +to use an external switch +for device interaction. + +13 +00:00:59,626 --> 00:01:03,397 +And dynamic type, +which allows people to set + +14 +00:01:03,430 --> 00:01:06,800 +the text size +according to their reading ability. + +15 +00:01:07,801 --> 00:01:10,904 +To get started, clone the repository + +16 +00:01:10,938 --> 00:01:13,407 +and build all Apple's plug-ins + +17 +00:01:13,440 --> 00:01:17,344 +using the build script +in the root of the repository. + +18 +00:01:17,377 --> 00:01:20,914 +This will produce a Build folder +that is ready for integration + +19 +00:01:20,948 --> 00:01:23,784 +into your Unity projects. + +20 +00:01:23,817 --> 00:01:27,888 +Finally, add the Accessibility plug-in +to your own project + +21 +00:01:27,921 --> 00:01:30,924 +using the Unity Package Manager. + +22 +00:01:30,958 --> 00:01:35,362 +For more details, look at +the documentation in the repository + +23 +00:01:35,395 --> 00:01:39,166 +and watch the video about +all of Apple's Unity plug-ins, + +24 +00:01:39,199 --> 00:01:44,671 +called "Plug-in and play: Add Apple +frameworks to your Unity game projects" + +25 +00:01:45,539 --> 00:01:50,410 +Now that you have the plug-in, +I will guide you through three areas. + +26 +00:01:50,444 --> 00:01:53,313 +First is accessibility elements. + +27 +00:01:53,347 --> 00:01:56,683 +This allows you to add support +to assistive technologies + +28 +00:01:56,717 --> 00:02:00,954 +like VoiceOver or Switch Control +for your games. + +29 +00:02:00,988 --> 00:02:03,857 +Next is Dynamic Type. + +30 +00:02:03,891 --> 00:02:08,095 +We create this easy-to-use utility +that helps you scale text + +31 +00:02:08,128 --> 00:02:10,964 +according to user preferences. + +32 +00:02:10,998 --> 00:02:13,600 +And UI Accommodations. + +33 +00:02:13,634 --> 00:02:18,772 +These are utilities that helps you read +other user preferences. + +34 +00:02:18,805 --> 00:02:21,775 +Let's start with accessibility elements. + +35 +00:02:22,876 --> 00:02:27,548 +I built a simple card game +to help illustrate this concept. + +36 +00:02:27,581 --> 00:02:30,551 +You might see that you should tap +the "flip" button + +37 +00:02:30,584 --> 00:02:33,654 +to draw two random cards. + +38 +00:02:33,687 --> 00:02:36,957 +However, VoiceOver would not read +the text on screen + +39 +00:02:36,990 --> 00:02:40,561 +and an external switch +would not tap the button + +40 +00:02:40,594 --> 00:02:44,097 +since these are just pixels +on a screen right now. + +41 +00:02:44,131 --> 00:02:49,136 +We need to help the system understand +what can be interacted with. + +42 +00:02:49,169 --> 00:02:52,039 +Accessibility elements define the things + +43 +00:02:52,072 --> 00:02:55,542 +that assistive technologies can +interact with. + +44 +00:02:57,244 --> 00:03:00,781 +The text, cards, and the button on screen + +45 +00:03:00,814 --> 00:03:03,750 +should be accessibility elements. + +46 +00:03:03,784 --> 00:03:07,221 +And we can describe +each element with a label. + +47 +00:03:07,254 --> 00:03:12,693 +VoiceOver will read each label so the user +can understand what's on the screen. + +48 +00:03:14,261 --> 00:03:17,064 +And if the game supports +multiple languages, + +49 +00:03:17,097 --> 00:03:19,466 +we should localize these labels as well. + +50 +00:03:21,535 --> 00:03:24,538 +Now VoiceOver can describe +what is on the screen, + +51 +00:03:24,571 --> 00:03:29,576 +but it doesn't recognize that there's +a button that can be tapped. + +52 +00:03:29,610 --> 00:03:32,346 +We can use another property +called "traits" + +53 +00:03:32,379 --> 00:03:36,183 +to inform the system +of the element's type. + +54 +00:03:36,216 --> 00:03:39,186 +We should add the "Button" trait here. + +55 +00:03:39,219 --> 00:03:43,156 +Now, VoiceOver will read "Flip button," + +56 +00:03:43,190 --> 00:03:46,393 +and an external switch +can control this button. + +57 +00:03:48,428 --> 00:03:53,800 +We can also add "Static Text" trait +on our text elements. + +58 +00:03:53,834 --> 00:03:59,173 +The "Static Text" trait is +usually given to labels and text areas + +59 +00:03:59,206 --> 00:04:03,443 +so that VoiceOver can provide +a better text interaction experience. + +60 +00:04:04,878 --> 00:04:07,581 +And there are many more traits +beyond "Button" + +61 +00:04:07,614 --> 00:04:10,317 +and "Static Text" that you can explore. + +62 +00:04:11,919 --> 00:04:15,422 +So what trait should we use for the cards? + +63 +00:04:15,455 --> 00:04:21,094 +Well, we don't need to use traits +on every accessibility element. + +64 +00:04:21,128 --> 00:04:24,464 +Our cards don't need any traits. + +65 +00:04:25,098 --> 00:04:28,168 +However, there's still a part of each card + +66 +00:04:28,202 --> 00:04:31,071 +that VoiceOver is not aware of: + +67 +00:04:31,104 --> 00:04:33,473 +the face value. + +68 +00:04:33,507 --> 00:04:38,078 +There's another property that we can +use for this called "Value." + +69 +00:04:39,680 --> 00:04:44,184 +Once we add a "Value" for each card, +VoiceOver will now read + +70 +00:04:44,218 --> 00:04:47,654 +"Card 1 with value 3 of clubs, + +71 +00:04:47,688 --> 00:04:51,458 +card 2 with value Ace of clubs." + +72 +00:04:51,491 --> 00:04:53,260 +And that's it. + +73 +00:04:54,194 --> 00:04:58,065 +Now that you understand the basics, +let's open Unity + +74 +00:04:58,098 --> 00:05:00,934 +to see how to add them to our project. + +75 +00:05:02,336 --> 00:05:05,906 +Here I am in a Unity Editor for this game. + +76 +00:05:05,939 --> 00:05:08,275 +I have already added +the Apple Accessibility + +77 +00:05:08,308 --> 00:05:09,843 +plugin to this project. + +78 +00:05:11,311 --> 00:05:15,649 +First we have the usual +scene objects like camera, + +79 +00:05:15,682 --> 00:05:19,386 +direct light, and a UI Canvas. + +80 +00:05:19,419 --> 00:05:22,756 +Under the canvas, +we have two text elements + +81 +00:05:22,789 --> 00:05:24,925 +and a button. + +82 +00:05:24,958 --> 00:05:28,428 +After that, +we have two game objects for cards. + +83 +00:05:31,798 --> 00:05:35,235 +Each is composed of two mesh components, + +84 +00:05:35,269 --> 00:05:39,806 +each rendering the front and back texture +for the card on each side. + +85 +00:05:39,840 --> 00:05:44,311 +Let's start by defining +our accessibility elements. + +86 +00:05:44,344 --> 00:05:49,416 +To do this, we need the Accessibility Node +component from this plugin. + +87 +00:05:50,851 --> 00:05:55,055 +Select all the objects in the hierarchy +that should be accessible. + +88 +00:05:57,925 --> 00:06:02,763 +And add the Accessibility Node component +to make them accessibility elements. + +89 +00:06:06,366 --> 00:06:08,502 +Next, we add labels. + +90 +00:06:09,236 --> 00:06:12,072 +Select the card object, + +91 +00:06:12,105 --> 00:06:15,676 +go to "Accessibility Node" component +on the right, + +92 +00:06:15,709 --> 00:06:18,045 +and find the "Label" field. + +93 +00:06:19,213 --> 00:06:24,251 +And make sure the label field checkbox +is checked to provide a custom label. + +94 +00:06:26,186 --> 00:06:28,388 +Then type "Card 1." + +95 +00:06:31,859 --> 00:06:35,329 +And the same for Card 2. + +96 +00:06:40,434 --> 00:06:43,604 +Text and buttons need labels too, + +97 +00:06:43,637 --> 00:06:47,975 +but we don't have to provide +an explicit accessibility labels for them + +98 +00:06:48,008 --> 00:06:52,546 +if we are using the standard controls +from Unity UI. + +99 +00:06:52,579 --> 00:06:56,083 +The plug-in already has +default implementations for those. + +100 +00:06:57,384 --> 00:07:00,354 +Next, we need +to add a trait to our button. + +101 +00:07:02,422 --> 00:07:05,492 +Select the flip button + +102 +00:07:05,526 --> 00:07:09,229 +and change the "Traits" +from "None" to "Button." + +103 +00:07:13,567 --> 00:07:15,802 +Select the two text elements... + +104 +00:07:18,138 --> 00:07:21,241 +And change "Traits" to "static text." + +105 +00:07:23,911 --> 00:07:25,312 +Great. + +106 +00:07:25,345 --> 00:07:29,950 +Lastly, we need to set an accessibility +Value for the card faces. + +107 +00:07:29,983 --> 00:07:33,954 +Since the cards are randomly drawn, +I need to add a script + +108 +00:07:33,987 --> 00:07:36,023 +to set the Value dynamically. + +109 +00:07:37,424 --> 00:07:42,396 +Select the two cards and add +a new script called AccessibleCard. + +110 +00:07:44,831 --> 00:07:48,101 +First, in one of my other C Sharp files, + +111 +00:07:48,135 --> 00:07:53,607 +I already have an enum for all +the card faces called Playing Card. + +112 +00:07:53,640 --> 00:07:57,578 +In my new AccessibleCard mono +behavior script, + +113 +00:07:57,611 --> 00:07:59,847 +we have a variable for the card type + +114 +00:07:59,880 --> 00:08:03,350 +and a boolean +for whether the card is facing up or down. + +115 +00:08:05,552 --> 00:08:10,457 +So now let's add +accessibilityValue to these cards. + +116 +00:08:10,490 --> 00:08:15,762 +First, we get the accessibilityNode +component attached to this gameObject. + +117 +00:08:17,064 --> 00:08:21,401 +Next, set the accessibilityValue +delegate to a function + +118 +00:08:21,435 --> 00:08:23,837 +that returns the card face value +dynamically. + +119 +00:08:25,339 --> 00:08:28,809 +Inside this function, +if the card is covered, + +120 +00:08:28,842 --> 00:08:32,713 +we return the "covered" +for the accessibilityValue. + +121 +00:08:32,746 --> 00:08:36,817 +Or if not covered, +we will enumerate all card faces + +122 +00:08:36,850 --> 00:08:41,822 +and return a description for each, +like "Ace of Spades." + +123 +00:08:41,855 --> 00:08:44,191 +And that's it. + +124 +00:08:44,224 --> 00:08:48,395 +Now let's build our project +and see it in action. + +125 +00:08:48,428 --> 00:08:51,532 +Here's our game. +Let's turn on VoiceOver. + +126 +00:08:55,335 --> 00:08:57,804 +Automated voice: VoiceOver on. +Eric's Game. + +127 +00:08:57,838 --> 00:08:59,740 +Card 2, covered. + +128 +00:08:59,773 --> 00:09:03,076 +Eric: I could swipe right +to move to the next element. + +129 +00:09:03,744 --> 00:09:05,546 +Automated voice: Card 1, covered. + +130 +00:09:07,314 --> 00:09:08,982 +Eric's card game. + +131 +00:09:10,884 --> 00:09:12,386 +Flip the cards. + +132 +00:09:14,087 --> 00:09:16,256 +Flip. Button. + +133 +00:09:16,290 --> 00:09:20,661 +Eric: You see that all five objects +are now accessible through VoiceOver, + +134 +00:09:20,694 --> 00:09:23,163 +which is awesome. + +135 +00:09:23,197 --> 00:09:27,568 +To tap the button when VoiceOver is on, +do a double-tap. + +136 +00:09:28,335 --> 00:09:30,504 +Automated voice: Flip. + +137 +00:09:30,537 --> 00:09:32,339 +Eric: Let's check the cards again. + +138 +00:09:34,441 --> 00:09:36,743 +Automated voice: Card 1, Two of Clubs. + +139 +00:09:38,378 --> 00:09:40,681 +Card 2, Ace of Clubs. + +140 +00:09:40,714 --> 00:09:45,352 +Eric: VoiceOver now reads +the updated card faces correctly. + +141 +00:09:45,385 --> 00:09:46,386 +Cool. + +142 +00:09:46,420 --> 00:09:50,357 +So we just made our game accessible +to millions of VoiceOver users + +143 +00:09:50,390 --> 00:09:52,326 +who can now fall in love with it. + +144 +00:09:52,359 --> 00:09:57,097 +And people who use external switch control +can also play our game. + +145 +00:09:57,130 --> 00:10:00,367 +So that was accessibility elements. + +146 +00:10:00,400 --> 00:10:03,504 +Next, let's talk about Dynamic Type. + +147 +00:10:04,838 --> 00:10:07,975 +Games can be difficult to play +for many people + +148 +00:10:08,008 --> 00:10:10,978 +because text is too small to read. + +149 +00:10:11,011 --> 00:10:14,748 +On iOS and tvOS, everyone can choose + +150 +00:10:14,781 --> 00:10:17,985 +the right text size +for their reading ability in Settings. + +151 +00:10:20,120 --> 00:10:23,423 +With the Accessibility Plugin, +you can read this setting + +152 +00:10:23,457 --> 00:10:28,095 +to make sure the text in your game +is displayed at the expected size. + +153 +00:10:30,264 --> 00:10:35,169 +Let's take a look at our game example +to see how we can use Dynamic Type. + +154 +00:10:36,370 --> 00:10:41,441 +Create a mono behavior script +called DynamicTextSize.cs. + +155 +00:10:41,475 --> 00:10:46,213 +In the start function, first store +the default text size into a variable. + +156 +00:10:47,181 --> 00:10:51,852 +Then inside the OnEnable function, +subscribe to setting changed event + +157 +00:10:51,885 --> 00:10:57,057 +using AccessibilitySettings. +onPreferredTextSizesChanged. + +158 +00:10:57,891 --> 00:11:02,429 +This allows us to update the UI as soon +as the user changes the text setting. + +159 +00:11:03,830 --> 00:11:07,334 +Next, inside the settingsChanged function, + +160 +00:11:07,367 --> 00:11:11,205 +first read the +PreferredContentSizeMultiplier. + +161 +00:11:11,238 --> 00:11:14,541 +Then multiply by your original text size + +162 +00:11:14,575 --> 00:11:17,544 +and assign it back to the text element. + +163 +00:11:18,512 --> 00:11:21,949 +Inside Unity Editor, +select all game objects + +164 +00:11:21,982 --> 00:11:25,052 +that have a Text element. + +165 +00:11:25,085 --> 00:11:28,956 +And add the DynamicTextSize component +that we just created. + +166 +00:11:32,492 --> 00:11:36,730 +Now our game is all set +for Dynamic type support. + +167 +00:11:37,397 --> 00:11:41,168 +Before we see the result in action, +first I am going to show you + +168 +00:11:41,201 --> 00:11:45,205 +a trick to quickly test +Dynamic Type in your games. + +169 +00:11:45,239 --> 00:11:49,376 +Open Settings, and find Control Center. + +170 +00:11:54,214 --> 00:11:58,986 +Scroll down until you see Text Size, +and add it to Control Center. + +171 +00:12:02,422 --> 00:12:06,827 +Now we can adjust text sizes quickly +by opening Control Center + +172 +00:12:06,860 --> 00:12:08,996 +and change the text size options. + +173 +00:12:17,905 --> 00:12:23,744 +Great–as I change the text size, +our game adjusts font sizes in real time. + +174 +00:12:24,811 --> 00:12:27,648 +The text percentage value shown +in Control Center + +175 +00:12:27,681 --> 00:12:30,817 +is exactly what we are reading +from that multiplier. + +176 +00:12:30,851 --> 00:12:35,689 +You can also adopt this setting +on non-text objects. + +177 +00:12:35,722 --> 00:12:39,226 +For example, +I can swap the card face assets + +178 +00:12:39,259 --> 00:12:42,496 +to Large Print when the size is increased. + +179 +00:12:45,265 --> 00:12:50,137 +First I create a script called +DynamicCardFaces. + +180 +00:12:50,170 --> 00:12:54,741 +Then same thing as before, +subscribe to the TextSizeChanged event. + +181 +00:12:55,809 --> 00:13:01,481 +Instead of reading a multiplier, +I read an enum of text size category + +182 +00:13:01,515 --> 00:13:05,619 +that is mapped to the ticks +on the Control Center slider. + +183 +00:13:05,652 --> 00:13:10,624 +I could swap the asset whenever +someone selects a larger text size. + +184 +00:13:11,825 --> 00:13:14,728 +And I simply choose +between a regular material + +185 +00:13:14,761 --> 00:13:16,563 +and a large print material. + +186 +00:13:17,464 --> 00:13:20,734 +Now if we select a really large size... + +187 +00:13:26,273 --> 00:13:30,010 +Users would see a large print version +of the cards, + +188 +00:13:30,043 --> 00:13:35,282 +which are great card faces that are much +easier to read for people with low vision. + +189 +00:13:36,783 --> 00:13:40,754 +Lastly, I want to talk to you +about UI accommodation settings + +190 +00:13:40,787 --> 00:13:43,223 +that you can access with this plug-in. + +191 +00:13:44,224 --> 00:13:48,662 +The first setting is Reduce Transparency. + +192 +00:13:48,695 --> 00:13:52,933 +When this setting is on, +the background is turned opaque, + +193 +00:13:52,966 --> 00:13:56,670 +instead of a blur or transparent effect. + +194 +00:13:56,703 --> 00:14:01,108 +It can help improve legibility when +those effects makes text hard to read. + +195 +00:14:02,042 --> 00:14:04,344 +To check this preference, call + +196 +00:14:04,378 --> 00:14:08,482 +AccessibilitySettings. +IsReduceTransparencyEnabled. + +197 +00:14:10,284 --> 00:14:13,887 +Next, the Increase Contrast setting. + +198 +00:14:13,921 --> 00:14:18,926 +Notice how the switches have +a darker grey that helps them stand out, + +199 +00:14:18,959 --> 00:14:23,230 +making controls easier to recognize +across the entire device. + +200 +00:14:24,464 --> 00:14:28,802 +You can increase contrast +for your own UI when this is enabled + +201 +00:14:28,836 --> 00:14:31,405 +by checking this setting using + +202 +00:14:31,438 --> 00:14:35,442 +AccessibilitySettings. +IsIncreaseContrastEnabled. + +203 +00:14:37,144 --> 00:14:40,614 +Next, the Reduce Motion setting. + +204 +00:14:40,647 --> 00:14:45,586 +Some people are sensitive to motion +like we have in this card flip animation. + +205 +00:14:46,854 --> 00:14:50,791 +We should remove that animation +when Reduce Motion is enabled. + +206 +00:14:51,892 --> 00:14:54,061 +Let's look at the code to do this. + +207 +00:14:55,362 --> 00:15:00,701 +In our CardController script, +we have this Flip function. + +208 +00:15:00,734 --> 00:15:05,539 +First we check if user's +reduce motion setting is on. + +209 +00:15:05,572 --> 00:15:11,211 +If not on, we should flip the card by +invoking an animation through Coroutine. + +210 +00:15:12,145 --> 00:15:16,283 +Otherwise we just set the rotation, +and no animations. + +211 +00:15:16,316 --> 00:15:17,851 +And that's it. + +212 +00:15:17,885 --> 00:15:22,422 +Now people who are sensitive to motion +would enjoy our game. + +213 +00:15:23,357 --> 00:15:27,494 +To recap, get started +with the Apple Accessibility plugin + +214 +00:15:27,528 --> 00:15:31,932 +by cloning the GitHub repository +linked in this session's resources. + +215 +00:15:33,000 --> 00:15:36,603 +Add accessibility elements +so that people can use VoiceOver + +216 +00:15:36,637 --> 00:15:38,805 +and Switch Control with your games. + +217 +00:15:40,007 --> 00:15:42,943 +Adapt your text size with Dynamic Type. + +218 +00:15:44,478 --> 00:15:46,947 +And check for UI accommodations + +219 +00:15:46,980 --> 00:15:50,250 +so everyone can have a great experience +with your game. + +220 +00:15:51,385 --> 00:15:53,620 +Thank you so much for joining me. + +221 +00:15:53,654 --> 00:15:57,624 +We look forward to seeing how you make +games available for everyone + +222 +00:15:57,658 --> 00:16:00,961 +with a great accessibility experience. + diff --git a/eng/2022 Session 10152 Create accessible Single App Mode experiences en.srt b/eng/2022 Session 10152 Create accessible Single App Mode experiences en.srt new file mode 100644 index 0000000..9c9b5bb --- /dev/null +++ b/eng/2022 Session 10152 Create accessible Single App Mode experiences en.srt @@ -0,0 +1,1197 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,745 +Drew Haas: Hi there. +My name is Drew Haas, + +3 +00:00:11,778 --> 00:00:14,681 +and I'm an engineer +on the Accessibility team. + +4 +00:00:14,715 --> 00:00:19,086 +I'm here to talk about how to create +accessible Single App Mode experiences. + +5 +00:00:19,119 --> 00:00:22,523 +Single App Modes let you +as a developer serve your users + +6 +00:00:22,556 --> 00:00:25,993 +in really unique ways +by restricting certain behaviors + +7 +00:00:26,026 --> 00:00:28,829 +on iOS and iPadOS devices. + +8 +00:00:28,862 --> 00:00:33,400 +Namely, these modes let you +lock the device to a single app. + +9 +00:00:33,433 --> 00:00:34,601 +Depending on the mode, + +10 +00:00:34,635 --> 00:00:37,838 +the restrictions are either initiated +by the system + +11 +00:00:37,871 --> 00:00:39,740 +or by your app. + +12 +00:00:39,773 --> 00:00:43,677 +This is great because you get to control +the timing between a restricted state + +13 +00:00:43,710 --> 00:00:46,046 +and an unrestricted state. + +14 +00:00:46,079 --> 00:00:50,150 +You're also able to apply additional +restrictions on top of single app mode, + +15 +00:00:50,184 --> 00:00:52,753 +like whether auto-lock is enabled or not. + +16 +00:00:54,121 --> 00:00:57,357 +Let's remember to consider +accessibility use cases. + +17 +00:00:57,391 --> 00:01:01,028 +You should make sure people +who rely using on accessibility features + +18 +00:01:01,061 --> 00:01:03,063 +have the ability to turn them on + +19 +00:01:03,096 --> 00:01:06,934 +so they can use them, +even if the device is restricted. + +20 +00:01:06,967 --> 00:01:11,071 +This ensures your app is inclusive +to people using assistive technologies. + +21 +00:01:11,104 --> 00:01:15,609 +I want to talk about some examples +of when to use Single App Mode. + +22 +00:01:15,642 --> 00:01:19,546 +Let's say you're developing an app +where customers order food or drinks + +23 +00:01:19,580 --> 00:01:21,181 +at a restaurant or event. + +24 +00:01:21,215 --> 00:01:24,051 +Or an app that will be used +in a medical office, + +25 +00:01:24,084 --> 00:01:27,221 +where patients are handling a device +shared by the medical staff + +26 +00:01:27,254 --> 00:01:29,223 +to fill out their information. + +27 +00:01:29,256 --> 00:01:33,560 +Or even an app that helps facilitate +a test for students in a classroom. + +28 +00:01:33,594 --> 00:01:36,697 +These are all scenarios +where Single App Mode restrictions + +29 +00:01:36,730 --> 00:01:40,200 +help create a focused environment +for the user. + +30 +00:01:40,234 --> 00:01:43,270 +And did you know your app +might be used in Single App Mode + +31 +00:01:43,303 --> 00:01:45,806 +even if you didn't write code for it? + +32 +00:01:45,839 --> 00:01:49,309 +That's right, +Guided Access is an accessibility feature + +33 +00:01:49,343 --> 00:01:53,113 +that lets users put any app +in Single App Mode. + +34 +00:01:53,146 --> 00:01:55,282 +First, I'll talk about Guided Access + +35 +00:01:55,315 --> 00:01:57,784 +and go over some considerations +you should have + +36 +00:01:57,818 --> 00:02:01,054 +for when people use your app +during this mode. + +37 +00:02:01,088 --> 00:02:04,525 +Next, I'll talk about single app modes +that you initiate programmatically + +38 +00:02:04,558 --> 00:02:08,929 +from within your app, +by adopting certain APIs and frameworks. + +39 +00:02:08,962 --> 00:02:11,765 +And finally, we provide accessibility API + +40 +00:02:11,798 --> 00:02:15,202 +designed for use +specifically during Single App Mode. + +41 +00:02:15,235 --> 00:02:19,006 +Use this API to turn on or off +certain accessibility features + +42 +00:02:19,039 --> 00:02:22,509 +to create a seamless +and accessible experience. + +43 +00:02:22,543 --> 00:02:24,978 +Let's dive in first with Guided Access. + +44 +00:02:25,012 --> 00:02:28,081 +To start Guided Access, +first make sure it's turned on + +45 +00:02:28,115 --> 00:02:32,653 +in accessibility settings, +then open the app you want to lock on to. + +46 +00:02:32,686 --> 00:02:37,024 +Triple click the side button +to perform the accessibility shortcut. + +47 +00:02:37,057 --> 00:02:42,262 +This shows the guided access workspace, +where you configure system restrictions. + +48 +00:02:42,296 --> 00:02:43,931 +Then tap Start to begin. + +49 +00:02:45,432 --> 00:02:48,101 +Now the device is in a restricted state. + +50 +00:02:48,135 --> 00:02:49,970 +It's locked to the frontmost app, + +51 +00:02:50,003 --> 00:02:53,841 +and the restrictions I configured +in the options menu are applied. + +52 +00:02:53,874 --> 00:02:59,379 +This helps creates a guided experience +for people with cognitive disabilities. + +53 +00:02:59,413 --> 00:03:01,682 +To exit Guided Access, +all you need to do + +54 +00:03:01,715 --> 00:03:04,484 +is perform the accessibility shortcut +again. + +55 +00:03:04,518 --> 00:03:09,056 +The options menu lets you restrict +touch interaction, display motion, + +56 +00:03:09,089 --> 00:03:12,759 +and the software keyboard, +as well as hardware button events, + +57 +00:03:12,793 --> 00:03:16,697 +like the volume buttons, +and the sleep/wake button. + +58 +00:03:16,730 --> 00:03:20,000 +Having a controlled environment in +this way is especially helpful + +59 +00:03:20,033 --> 00:03:21,935 +for people with cognitive disabilities, + +60 +00:03:21,969 --> 00:03:24,938 +but this feature is also enjoyed +by a range of users, + +61 +00:03:24,972 --> 00:03:28,008 +from accessibility users +to parents of young kids + +62 +00:03:28,041 --> 00:03:32,446 +who could all benefit from having +easy access to these device restrictions. + +63 +00:03:32,479 --> 00:03:33,981 +This is where you come in! + +64 +00:03:34,014 --> 00:03:37,551 +UIAccessibility API lets you +create custom restrictions + +65 +00:03:37,584 --> 00:03:41,722 +to tailor the experience +of your app during Guided Access. + +66 +00:03:41,755 --> 00:03:46,059 +Use this an opportunity +to restrict or adjust areas of your app + +67 +00:03:46,093 --> 00:03:48,495 +where someone with a cognitive disability +might feel lost + +68 +00:03:48,529 --> 00:03:50,264 +or overwhelmed with options. + +69 +00:03:50,297 --> 00:03:54,735 +I want to share some design considerations +for cognitive accessibility. + +70 +00:03:54,768 --> 00:03:57,871 +These are principles you should follow +all the time if possible, + +71 +00:03:57,905 --> 00:04:02,242 +but you should especially follow them +in your app during Guided Access. + +72 +00:04:02,276 --> 00:04:04,878 +First is to be forgiving of errors. + +73 +00:04:04,912 --> 00:04:07,014 +Try to mitigate errors before they happen, + +74 +00:04:07,047 --> 00:04:11,552 +by warning users before they perform +significant or irreversible actions, + +75 +00:04:11,585 --> 00:04:16,156 +like deleting or modifying account +information or payment information. + +76 +00:04:16,190 --> 00:04:18,392 +You should aim to reduce +dependence on timing + +77 +00:04:18,425 --> 00:04:20,561 +to act on certain things in your app, + +78 +00:04:20,594 --> 00:04:24,831 +because not everybody +processes information at the same speed. + +79 +00:04:24,865 --> 00:04:28,101 +Using timed alerts +or automatically performing actions + +80 +00:04:28,135 --> 00:04:31,338 +after a short time-out +makes your app harder to use. + +81 +00:04:31,371 --> 00:04:35,175 +And you should always, always, +give the user a chance to confirm + +82 +00:04:35,209 --> 00:04:36,944 +before making a payment, + +83 +00:04:36,977 --> 00:04:40,781 +this includes apps +that have one-tap purchases. + +84 +00:04:40,814 --> 00:04:44,218 +Designing your app with these +considerations promotes independence + +85 +00:04:44,251 --> 00:04:46,520 +for accessibility users. + +86 +00:04:46,553 --> 00:04:48,689 +I want to show you an example. + +87 +00:04:48,722 --> 00:04:51,191 +I'm working on an app +called Reading Observatory + +88 +00:04:51,225 --> 00:04:54,928 +which helps people keep track of how much +they've read during the week. + +89 +00:04:54,962 --> 00:04:58,599 +The home page of my app shows +reading progress and has two buttons: + +90 +00:04:58,632 --> 00:05:02,035 +account settings, +and a continue reading button. + +91 +00:05:02,069 --> 00:05:06,173 +I've added a custom restriction for my app +that will lock the Account Settings button + +92 +00:05:06,206 --> 00:05:08,876 +while Guided Access is active. + +93 +00:05:08,909 --> 00:05:12,546 +This gives my app a unique experience +under this restriction, + +94 +00:05:12,579 --> 00:05:14,915 +to help prevent users from getting lost +somewhere else, + +95 +00:05:14,948 --> 00:05:19,620 +like buried in account settings, +where they may make unintended changes. + +96 +00:05:19,653 --> 00:05:22,189 +This will also reduce the amount of times +someone may get stuck + +97 +00:05:22,222 --> 00:05:25,692 +and need help getting back on track +with the primary purpose of my app, + +98 +00:05:25,726 --> 00:05:27,294 +which is reading. + +99 +00:05:27,327 --> 00:05:30,397 +To do this, first conform your AppDelegate + +100 +00:05:30,430 --> 00:05:33,634 +to the UIGuidedAccessRestrictionDelegate +protocol. + +101 +00:05:33,667 --> 00:05:36,637 +You need to let Guided Access know +about each restriction + +102 +00:05:36,670 --> 00:05:39,139 +by providing an array of identifiers. + +103 +00:05:39,173 --> 00:05:41,375 +I've got just one. + +104 +00:05:41,408 --> 00:05:44,745 +You're also required to give +a user-facing title for each, + +105 +00:05:44,778 --> 00:05:49,116 +as well as an optional description +if you need to provide additional detail. + +106 +00:05:49,149 --> 00:05:52,085 +These strings will show up +in the options menu. + +107 +00:05:52,119 --> 00:05:53,520 +Finally, implement the + +108 +00:05:53,554 --> 00:05:56,890 +guidedAccessRestriction +(withIdentifier:didChange:) method + +109 +00:05:56,924 --> 00:06:00,961 +to receive a callback +when a restriction is toggled. + +110 +00:06:00,994 --> 00:06:04,431 +This is where your app could post +a notification to perform logic + +111 +00:06:04,464 --> 00:06:06,300 +which would accommodate +the restriction, + +112 +00:06:06,333 --> 00:06:09,069 +like turning off user interaction +for certain buttons + +113 +00:06:09,102 --> 00:06:12,706 +or introducing some of those +design principles from earlier. + +114 +00:06:13,440 --> 00:06:16,844 +Check if a custom restriction +is enabled at any time by calling + +115 +00:06:16,877 --> 00:06:21,381 +guidedAccessRestrictionState +(forIdentifier:) on UIAccessibility. + +116 +00:06:21,415 --> 00:06:25,652 +All of the system provided restrictions +and your custom app-based restrictions + +117 +00:06:25,686 --> 00:06:28,455 +make Guided Access extremely configurable, + +118 +00:06:28,488 --> 00:06:32,359 +which is why it is a fantastic +accessibility feature. + +119 +00:06:32,392 --> 00:06:35,996 +Guided Access can be found +in Accessibility Settings. + +120 +00:06:36,029 --> 00:06:39,566 +Other Single App Modes are available +for you to programmatically enter + +121 +00:06:39,600 --> 00:06:41,802 +a restricted session on device. + +122 +00:06:41,835 --> 00:06:44,838 +They share similarities with +Guided Access, but the key difference + +123 +00:06:44,872 --> 00:06:48,809 +is just that: you're programmatically +entering this mode. + +124 +00:06:48,842 --> 00:06:51,378 +This means there's plenty to talk about +for setting up, + +125 +00:06:51,411 --> 00:06:54,748 +starting, and customizing +your Single App Mode session. + +126 +00:06:54,781 --> 00:06:57,417 +Remember each of those scenarios +from earlier? + +127 +00:06:57,451 --> 00:07:00,220 +An iPad being used as a restaurant kiosk, + +128 +00:07:00,254 --> 00:07:03,357 +at a medical office, in a testing +environment– + +129 +00:07:03,390 --> 00:07:07,594 +single App Modes are the right solution +for all of these situations. + +130 +00:07:07,628 --> 00:07:10,330 +Even though each may have +slightly different needs, + +131 +00:07:10,364 --> 00:07:14,101 +they all benefit from locking the device +to a single app. + +132 +00:07:14,134 --> 00:07:16,737 +This prevents people from swiping +to go home, + +133 +00:07:16,770 --> 00:07:21,842 +modifying things in Settings, +or even looking something up in Safari. + +134 +00:07:21,875 --> 00:07:25,212 +Let's go through each of these +and I'll recommend a Single App Mode + +135 +00:07:25,245 --> 00:07:27,214 +based on the restrictions we'll need. + +136 +00:07:28,448 --> 00:07:32,252 +The first mode is the most basic, +and it's the easiest to deploy. + +137 +00:07:32,286 --> 00:07:35,055 +It's simply called Single App Mode. + +138 +00:07:35,088 --> 00:07:37,391 +This is the right solution +for when you have a device + +139 +00:07:37,424 --> 00:07:41,995 +that you intend to stay in a single app +pretty much in perpetuity. + +140 +00:07:42,029 --> 00:07:44,798 +It remains locked in-app after reboot, + +141 +00:07:44,831 --> 00:07:48,202 +and there is no manual intervention +needed for upkeep. + +142 +00:07:48,235 --> 00:07:51,004 +Great for a low-involvement approach. + +143 +00:07:51,038 --> 00:07:52,773 +A few considerations, though! + +144 +00:07:52,806 --> 00:07:54,975 +The device must be supervised. + +145 +00:07:55,008 --> 00:07:58,078 +Supervision tools +let you lock a high volume of devices + +146 +00:07:58,111 --> 00:08:00,047 +into Single App Mode at once, + +147 +00:08:00,080 --> 00:08:04,484 +so this is a very scalable solution +if you're working with a lot of devices. + +148 +00:08:04,518 --> 00:08:08,355 +Apple Configurator lets you manage +supervised devices in many ways, + +149 +00:08:08,388 --> 00:08:11,825 +including putting them in Single App Mode. + +150 +00:08:11,859 --> 00:08:17,231 +Select a supervised device, then under +Advanced, select Start Single App Mode. + +151 +00:08:19,466 --> 00:08:22,736 +The last thing you need to do +is select an app. + +152 +00:08:22,769 --> 00:08:25,005 +Once you do, it will launch automatically, + +153 +00:08:25,038 --> 00:08:26,707 +and lock into the foreground. + +154 +00:08:28,675 --> 00:08:30,844 +There is no other way to exit this mode. + +155 +00:08:30,878 --> 00:08:34,181 +As I mentioned, it will stay locked +to this app even upon reboot + +156 +00:08:34,214 --> 00:08:37,618 +unless you end Single App Mode +through your management software. + +157 +00:08:38,452 --> 00:08:41,588 +Autonomous Single App Mode +is better suited for situations + +158 +00:08:41,622 --> 00:08:45,058 +where a restricted state +is entered and exited often. + +159 +00:08:45,092 --> 00:08:48,395 +For example, at a medical office +where a new patient fills in + +160 +00:08:48,428 --> 00:08:52,299 +their information on an iPad, +and then hands it back to the staff. + +161 +00:08:52,332 --> 00:08:55,702 +This means there is manual intervention +between restrictions. + +162 +00:08:55,736 --> 00:08:57,905 +Your app gets to do this by itself, + +163 +00:08:57,938 --> 00:09:01,708 +by using a single API method call +to get in and out. + +164 +00:09:01,742 --> 00:09:05,279 +Again, considerations– +the device must be supervised, + +165 +00:09:05,312 --> 00:09:08,916 +and the app that wants to use this API +must be allow listed + +166 +00:09:08,949 --> 00:09:11,185 +on the device's configuration profile, + +167 +00:09:11,218 --> 00:09:14,588 +or the request to lock will fail +because it's not privileged. + +168 +00:09:14,621 --> 00:09:16,557 +The all-powerful method is + +169 +00:09:16,590 --> 00:09:21,828 +requestGuidedAccessSession(enabled:complet +ionHandler:) on UIAccessibility. + +170 +00:09:21,862 --> 00:09:23,931 +Hey, that looks familiar! Right? + +171 +00:09:23,964 --> 00:09:27,134 +This API taps right into the roots +of Guided Access. + +172 +00:09:27,167 --> 00:09:30,904 +You're essentially programmatically +entering and exiting this feature + +173 +00:09:30,938 --> 00:09:32,005 +with this code. + +174 +00:09:32,039 --> 00:09:33,674 +Isn't that cool? + +175 +00:09:33,707 --> 00:09:36,710 +That's because Guided Access +serves as the foundation + +176 +00:09:36,743 --> 00:09:38,679 +for other single app modes to exist. + +177 +00:09:38,712 --> 00:09:40,581 +Bet you didn't know that! + +178 +00:09:40,614 --> 00:09:43,617 +Let's see how this code might work +in action. + +179 +00:09:43,650 --> 00:09:46,453 +I have a method each for entering +and exiting this mode + +180 +00:09:46,486 --> 00:09:48,055 +that I'll call when I'm ready. + +181 +00:09:48,088 --> 00:09:51,525 +They're performing different actions +like starting a new patient sign-up sheet + +182 +00:09:51,558 --> 00:09:53,660 +when we enter a restricted state + +183 +00:09:53,694 --> 00:09:57,164 +and then bookkeep their information +upon exiting. + +184 +00:09:57,197 --> 00:10:00,467 +Of course, you'll want to properly address +the result of the completion handler + +185 +00:10:00,501 --> 00:10:02,035 +to know if something goes wrong. + +186 +00:10:02,069 --> 00:10:04,705 +If so, you may want to give +an alert to the user + +187 +00:10:04,738 --> 00:10:08,375 +and hold off on continuing the experience +forward, so they can try again. + +188 +00:10:08,408 --> 00:10:11,678 +Check if Autonomous Single App Mode +is enabled at any time + +189 +00:10:11,712 --> 00:10:16,250 +by calling isGuidedAccessEnabled +on UIAccessibility. + +190 +00:10:16,283 --> 00:10:19,753 +Also observe the +guidedAccessStatusDidChangeNotification + +191 +00:10:19,786 --> 00:10:23,524 +to trigger any additional code +when entering or exiting this mode. + +192 +00:10:23,557 --> 00:10:26,560 +And remember that the app which wants +to use this API must + +193 +00:10:26,593 --> 00:10:29,229 +have the proper supervision and +management, + +194 +00:10:29,263 --> 00:10:33,433 +including allowlisting the app's bundle ID +as an allowed application + +195 +00:10:33,467 --> 00:10:36,270 +for Autonomous Single App Mode. + +196 +00:10:36,303 --> 00:10:38,906 +And finally, assessment mode. + +197 +00:10:38,939 --> 00:10:42,576 +This is the right mode when your app aims +to prevent unfair advantages, + +198 +00:10:42,609 --> 00:10:45,846 +by restricting certain features +during testing. + +199 +00:10:45,879 --> 00:10:48,048 +Turn off autocorrect, spellcheck, + +200 +00:10:48,081 --> 00:10:50,217 +and single app mode locks to the app + +201 +00:10:50,250 --> 00:10:54,454 +so the test taker can't access +outside notes or resources. + +202 +00:10:54,488 --> 00:10:58,525 +Recently these frameworks +have been unified for iOS and macOS, + +203 +00:10:58,559 --> 00:11:02,129 +when we introduced the Automatic +Assessment Configuration framework! + +204 +00:11:02,162 --> 00:11:06,166 +To use this, the device does not need +to be supervised like the previous modes, + +205 +00:11:06,200 --> 00:11:10,337 +but you do need to apply to Apple for +an assessment entitlement for your app. + +206 +00:11:10,370 --> 00:11:13,240 +More information about applying +for the entitlement can be found + +207 +00:11:13,273 --> 00:11:15,042 +in the developer documentation. + +208 +00:11:15,075 --> 00:11:18,278 +My colleague Josh has +a fantastic WWDC session + +209 +00:11:18,312 --> 00:11:22,149 +that explains how to use the Automatic +Assessment Configuration framework, + +210 +00:11:22,182 --> 00:11:25,752 +including tons of code examples +and documentation resources. + +211 +00:11:25,786 --> 00:11:27,487 +Please go check it out. + +212 +00:11:27,521 --> 00:11:30,657 +Once you know which single app +mode solution is right for you, + +213 +00:11:30,691 --> 00:11:33,327 +or if you already have a solution working, + +214 +00:11:33,360 --> 00:11:37,931 +remember to keep in mind that people using +assistive technologies do use your apps. + +215 +00:11:37,965 --> 00:11:41,935 +There may be extra steps you need to take +to make this experience accessible. + +216 +00:11:42,736 --> 00:11:46,940 +In our classic example of an iPad +being used as a restaurant kiosk, + +217 +00:11:46,974 --> 00:11:51,144 +Let's consider that a blind customer +comes by to order. + +218 +00:11:51,178 --> 00:11:55,949 +If the device is plainly locked into +Single App Mode, then we have a problem. + +219 +00:11:55,983 --> 00:11:58,719 +You wouldn't be able to open Settings, +head to Accessibility, + +220 +00:11:58,752 --> 00:12:00,854 +and turn on VoiceOver for this customer. + +221 +00:12:00,888 --> 00:12:02,489 +So what can you do? + +222 +00:12:03,657 --> 00:12:06,493 +Apple Configurator and other +device management software + +223 +00:12:06,527 --> 00:12:10,464 +help you configure options +for Single App Mode. + +224 +00:12:10,497 --> 00:12:15,769 +A handful of accessibility features are +available for you to have always enabled, + +225 +00:12:15,802 --> 00:12:19,840 +and the Accessibility Shortcut, +also known as "triple-click" menu, + +226 +00:12:19,873 --> 00:12:23,610 +is configurable to let users +quickly toggle accessibility features + +227 +00:12:23,644 --> 00:12:25,546 +like VoiceOver, Zoom, + +228 +00:12:25,579 --> 00:12:29,283 +Invert Colors, +AssistiveTouch, and Voice Control. + +229 +00:12:29,316 --> 00:12:31,185 +When these features are always on, + +230 +00:12:31,218 --> 00:12:34,087 +or when they're assigned +to the accessibility shortcut, + +231 +00:12:34,121 --> 00:12:36,523 +you enable people who rely on these +technologies + +232 +00:12:36,557 --> 00:12:39,159 +to use them while they navigate your app. + +233 +00:12:39,193 --> 00:12:43,063 +It's pretty easy to set this up, +so don't miss your chance to do so. + +234 +00:12:44,965 --> 00:12:48,735 +But those options have to be configured +before entering Single App Mode. + +235 +00:12:48,769 --> 00:12:51,572 +So what about during Single App Mode? + +236 +00:12:51,605 --> 00:12:55,843 +Use API on UIAccessibility +to toggle certain accessibility features + +237 +00:12:55,876 --> 00:12:57,511 +directly with code. + +238 +00:12:57,544 --> 00:13:01,682 +It's a great alternative to toggling +a feature with the accessibility shortcut, + +239 +00:13:01,715 --> 00:13:05,152 +which is helpful when device is encased +in a kiosk enclosure + +240 +00:13:05,185 --> 00:13:07,588 +that blocks off hardware buttons. + +241 +00:13:07,621 --> 00:13:11,925 +We made this API so it's easy for you +to turn off accessibility features, + +242 +00:13:11,959 --> 00:13:16,230 +so you can do some housekeeping +and reset state between users. + +243 +00:13:16,263 --> 00:13:20,501 +To do this, call configureForGuidedAccess +on UIAccessibility. + +244 +00:13:20,534 --> 00:13:23,270 +You must pass in +a compatible accessibility feature, + +245 +00:13:23,303 --> 00:13:25,539 +and whether you want it enabled or not. + +246 +00:13:25,572 --> 00:13:29,510 +This works for Zoom, +VoiceOver, Invert Colors, + +247 +00:13:29,543 --> 00:13:32,713 +AssistiveTouch, and Grayscale. + +248 +00:13:32,746 --> 00:13:36,283 +So whether your app has custom +restrictions for Guided Access + +249 +00:13:36,316 --> 00:13:39,953 +or you build your entire experience +around Single App Modes, + +250 +00:13:39,987 --> 00:13:43,457 +it's important to know how to make sure +it's accessible. + +251 +00:13:43,490 --> 00:13:47,394 +The wrong kind of restrictions +make something less accessible to use, + +252 +00:13:47,427 --> 00:13:52,232 +so always consider the experience of +someone with a disability using your app. + +253 +00:13:52,266 --> 00:13:57,337 +I've shown you the APIs you need to create +an accessible Single App Mode experience. + +254 +00:13:57,371 --> 00:14:01,308 +Now go make something great, +and accessible! + diff --git a/eng/2022 Session 10153 What's new in web accessibility en.srt b/eng/2022 Session 10153 What's new in web accessibility en.srt new file mode 100644 index 0000000..86530a7 --- /dev/null +++ b/eng/2022 Session 10153 What's new in web accessibility en.srt @@ -0,0 +1,1368 @@ +1 +00:00:01,301 --> 00:00:07,307 +[spacey music] + +2 +00:00:09,309 --> 00:00:10,978 +Tyler: Hi, my name is Tyler, + +3 +00:00:11,011 --> 00:00:14,214 +and I'm an engineer on the WebKit +accessibility team. + +4 +00:00:14,248 --> 00:00:16,250 +In today's session, +we're going to take a tour + +5 +00:00:16,283 --> 00:00:19,520 +of modern web accessibility, +beginning with a brief overview + +6 +00:00:19,553 --> 00:00:21,922 +of assistive technologies +like screenreaders. + +7 +00:00:23,090 --> 00:00:26,827 +Then, we'll talk about ways you can build +rich, accessible web apps + +8 +00:00:26,860 --> 00:00:31,999 +with tools like custom controls, +speech synthesis markup language, or SSML, + +9 +00:00:32,032 --> 00:00:35,135 +in the Web Speech API, +and the dialog element. + +10 +00:00:36,503 --> 00:00:39,406 +So let's begin by talking +about assistive technologies. + +11 +00:00:40,374 --> 00:00:43,710 +Approximately one in seven people +worldwide have a disability that affects + +12 +00:00:43,744 --> 00:00:47,447 +the way they interact with the world, +their devices, and the web. + +13 +00:00:48,015 --> 00:00:50,484 +People can experience +disabilities at any age, + +14 +00:00:50,517 --> 00:00:53,654 +for any duration, +and at varying levels of severity. + +15 +00:00:53,687 --> 00:00:57,157 +Apple has built a variety of tools +to enable users to interact + +16 +00:00:57,191 --> 00:01:00,027 +with their devices in a way +that works best for them. + +17 +00:01:00,060 --> 00:01:04,765 +These tools include VoiceOver, +Switch Control, Voice Control, + +18 +00:01:04,798 --> 00:01:09,736 +and Full Keyboard Access, all of which +provide alternate means of device usage. + +19 +00:01:09,770 --> 00:01:12,005 +To learn more about these tools +and some others, + +20 +00:01:12,039 --> 00:01:13,674 +check out last year's session, titled: + +21 +00:01:13,707 --> 00:01:16,143 +"Support Full Keyboard Access +in your iOS app." + +22 +00:01:17,211 --> 00:01:19,813 +To get a feel for what this +is like on a real webpage, + +23 +00:01:19,847 --> 00:01:23,383 +let's use VoiceOver to navigate +a sample quiz assessment website. + +24 +00:01:23,417 --> 00:01:27,354 +On my iPad, I'll triple-press +the top button to activate VoiceOver. + +25 +00:01:28,222 --> 00:01:31,358 +Siri: VoiceOver on. +Safari, Show Sidebar, Button. + +26 +00:01:32,426 --> 00:01:36,797 +Tyler: And now, with VoiceOver active, +I'll tap the page heading to focus it... + +27 +00:01:36,830 --> 00:01:38,966 +Siri: Pop Quiz, Heading level 1. + +28 +00:01:38,999 --> 00:01:41,935 +Tyler: And swipe right to move +through the elements on this page. + +29 +00:01:41,969 --> 00:01:46,940 +Siri: One of six: How many slices are in +1/4 of a pizza with eight total slices? + +30 +00:01:46,974 --> 00:01:50,444 +Two slices, radio button, unchecked, +one of four, + +31 +00:01:50,477 --> 00:01:51,478 +Three slices-- + +32 +00:01:51,512 --> 00:01:52,412 +Four slices-- + +33 +00:01:52,446 --> 00:01:53,514 +Six slices-- + +34 +00:01:53,547 --> 00:01:54,948 +Next question. Button. + +35 +00:01:55,816 --> 00:01:58,552 +Tyler: As a web developer, +you have many tools at your disposal + +36 +00:01:58,585 --> 00:02:02,656 +for making your pages accessible +to users of technologies like VoiceOver. + +37 +00:02:02,689 --> 00:02:05,559 +For example, +Safari has built-in accessibility support + +38 +00:02:05,592 --> 00:02:09,930 +for semantic HTML elements like button, +h1 through h6, + +39 +00:02:09,963 --> 00:02:13,867 +table elements, +list elements, and many more. + +40 +00:02:13,901 --> 00:02:16,803 +Using these semantic HTML elements +should be your default, + +41 +00:02:16,837 --> 00:02:20,174 +as this will guarantee a consistent, +accessible experience for your users + +42 +00:02:20,207 --> 00:02:22,776 +across all browsers. + +43 +00:02:22,809 --> 00:02:27,047 +However, sometimes you may have a need +not completely fulfilled by semantic HTML, + +44 +00:02:27,080 --> 00:02:30,417 +and need to create +custom components with JavaScript. + +45 +00:02:30,450 --> 00:02:32,419 +If this is the case, +you'll likely also need + +46 +00:02:32,452 --> 00:02:34,688 +to supplement your components +with ARIA attributes + +47 +00:02:34,721 --> 00:02:38,425 +so that their semantics are properly +conveyed to assistive technologies. + +48 +00:02:38,458 --> 00:02:42,596 +This brings us to our second topic +of the day, custom controls. + +49 +00:02:42,629 --> 00:02:45,933 +Let's say we wanted to make +the pizza quiz question more engaging. + +50 +00:02:45,966 --> 00:02:49,469 +One thing we could do is replace +the radio buttons with a custom control + +51 +00:02:49,503 --> 00:02:53,473 +that allows users to add and remove slices +from a pizza tray with a tap. + +52 +00:02:54,274 --> 00:02:57,477 +Our markup for this custom control +might start with a div and an ID . + +53 +00:02:58,912 --> 00:03:02,716 +In order to make this component operable +for users interacting with a tap or click, + +54 +00:03:02,749 --> 00:03:05,052 +we'll need to add a click event listener. + +55 +00:03:05,085 --> 00:03:07,921 +Let's create a new PizzaControl class +with a constructor + +56 +00:03:07,955 --> 00:03:09,556 +that accepts the ID of an element. + +57 +00:03:10,691 --> 00:03:13,694 +We'll get that element by ID, and then +add a click event listener for it. + +58 +00:03:15,195 --> 00:03:18,632 +This listener will compute the new number +of slices based on the tapped position + +59 +00:03:18,665 --> 00:03:22,402 +and then pass that value to a function +called update to re-render the control. + +60 +00:03:22,436 --> 00:03:25,005 +This will work great for some users, +but not all. + +61 +00:03:26,139 --> 00:03:28,775 +For example, what about our users +with visual disabilities, + +62 +00:03:28,809 --> 00:03:31,345 +who won't know where to click or tap? + +63 +00:03:31,378 --> 00:03:34,314 +When building custom components, +we must always consider how users + +64 +00:03:34,348 --> 00:03:37,918 +of assistive technologies of all types +will interact with our component. + +65 +00:03:37,951 --> 00:03:40,621 +With that in mind, +the first step to making our component + +66 +00:03:40,654 --> 00:03:44,391 +accessible is to give it a role attribute +with a value of "slider". + +67 +00:03:44,424 --> 00:03:47,861 +Our control maps quite nicely +to the model of a slider. + +68 +00:03:47,895 --> 00:03:52,566 +There is a minimum value, zero slices, +a maximum value, eight slices, + +69 +00:03:52,599 --> 00:03:54,868 +and a current value, four slices. + +70 +00:03:55,602 --> 00:03:59,373 +We'll also need to add a tabindex of zero +to ensure our component is focusable + +71 +00:03:59,406 --> 00:04:02,142 +for users of keyboards +and other non-touch interfaces. + +72 +00:04:03,443 --> 00:04:05,512 +We'll also need +to add a few ARIA attributes. + +73 +00:04:06,547 --> 00:04:10,217 +Aria-valuemin and aria-valuemax inform +assistive technologies + +74 +00:04:10,250 --> 00:04:12,853 +of the minimum +and maximum values for this slider. + +75 +00:04:14,054 --> 00:04:16,490 +These attributes are similar +to the min and max attributes + +76 +00:04:16,523 --> 00:04:19,026 +that you can use +on native range type inputs. + +77 +00:04:20,527 --> 00:04:24,498 +Next, let's add aria-valuenow to convey +the current value of the control. + +78 +00:04:25,899 --> 00:04:29,269 +We'll also use aria-valuetext +to provide a more useful description + +79 +00:04:29,303 --> 00:04:31,505 +of the current value, +which is four slices. + +80 +00:04:33,040 --> 00:04:35,876 +Now that we've established +our control as a focusable slider, + +81 +00:04:35,909 --> 00:04:37,911 +we need to handle updates +to the control's value + +82 +00:04:37,945 --> 00:04:40,013 +from assistive technologies. + +83 +00:04:40,047 --> 00:04:43,150 +On iOS, VoiceOver facilitates +the adjustment of sliders + +84 +00:04:43,183 --> 00:04:45,986 +with a single finger swipe up +to increment the slider, + +85 +00:04:46,019 --> 00:04:49,122 +and a swipe down to decrement the slider. + +86 +00:04:49,156 --> 00:04:52,326 +Safari provides an easy way +to handle these gestures. + +87 +00:04:52,359 --> 00:04:55,429 +When a VoiceOver user swipes up +with a slider in focus, + +88 +00:04:55,462 --> 00:04:59,166 +Safari automatically simulates +an arrow-key right event. + +89 +00:04:59,199 --> 00:05:01,668 +And similarly, +if a VoiceOver user swipes down + +90 +00:05:01,702 --> 00:05:05,806 +with a slider in focus, +an arrow key left event will be simulated. + +91 +00:05:05,839 --> 00:05:09,109 +These simulated events behave +the same as real keypresses, + +92 +00:05:09,142 --> 00:05:11,912 +meaning they can be handled +by key event listeners. + +93 +00:05:11,945 --> 00:05:13,814 +With this new knowledge in our tool belt, + +94 +00:05:13,847 --> 00:05:16,950 +let's add a keydown listener +to our pizza control. + +95 +00:05:16,984 --> 00:05:19,753 +If the activated key is +a right arrow or up arrow, + +96 +00:05:19,786 --> 00:05:23,390 +we'll update our control with +the current amount of slices plus one. + +97 +00:05:23,423 --> 00:05:26,960 +And similarly, if the activated key +is a left arrow or down arrow, + +98 +00:05:26,994 --> 00:05:30,531 +we'll update our control with +the current amount of slices minus one. + +99 +00:05:30,564 --> 00:05:34,401 +Adding this key event listener +not only benefits VoiceOver users, + +100 +00:05:34,434 --> 00:05:36,603 +but also users of Full Keyboard Access, + +101 +00:05:36,637 --> 00:05:38,872 +who may heavily or entirely rely +on your web app + +102 +00:05:38,906 --> 00:05:40,541 +being keyboard accessible. + +103 +00:05:40,574 --> 00:05:42,709 +With both +of our event listeners established, + +104 +00:05:42,743 --> 00:05:46,280 +we probably now also want +to define our update function. + +105 +00:05:46,313 --> 00:05:49,349 +First, we'll clamp the value we're given +to ensure it's within bounds, + +106 +00:05:49,383 --> 00:05:51,185 +between zero and eight, + +107 +00:05:51,218 --> 00:05:55,122 +and update our stored slice count state +to this value. + +108 +00:05:55,155 --> 00:05:58,959 +Next, we need to ensure we update +both the visual and ARIA representations + +109 +00:05:58,992 --> 00:06:00,694 +of our control. + +110 +00:06:00,727 --> 00:06:03,830 +When building custom components, +a good rule is that if you're updating + +111 +00:06:03,864 --> 00:06:06,200 +the visual representation of your +component, + +112 +00:06:06,233 --> 00:06:08,135 +you also need to think +about how you'll be updating + +113 +00:06:08,168 --> 00:06:09,503 +the ARIA representation. + +114 +00:06:10,804 --> 00:06:13,540 +In this case, we need to update +both the aria-valuenow + +115 +00:06:13,574 --> 00:06:15,209 +and aria-valuetext attributes + +116 +00:06:15,242 --> 00:06:18,512 +to inform users of assistive technologies +of the new control state. + +117 +00:06:19,980 --> 00:06:23,317 +We'll begin by setting aria-valuenow +to be the current slice count. + +118 +00:06:24,351 --> 00:06:27,921 +Next, we'll set aria-valuetext +to be the more human-friendly description + +119 +00:06:27,955 --> 00:06:31,225 +of the slice count, +plus either the word "slice" or "slices". + +120 +00:06:32,192 --> 00:06:34,161 +Okay, now that all that's in place, + +121 +00:06:34,194 --> 00:06:36,029 +let's go back +to our quiz assessment web app + +122 +00:06:36,063 --> 00:06:38,031 +to see what the experience is like with +VoiceOver. + +123 +00:06:38,065 --> 00:06:40,701 +I'll begin by tapping +the pizza control to focus it. + +124 +00:06:41,468 --> 00:06:43,704 +Siri: Four slices, adjustable. + +125 +00:06:43,737 --> 00:06:45,772 +Swipe up or down with one finger +to adjust the value. + +126 +00:06:46,607 --> 00:06:49,276 +Tyler: We heard that VoiceOver read +the initial value of the slider, + +127 +00:06:49,309 --> 00:06:52,045 +four slices, +and told us that it's adjustable. + +128 +00:06:52,079 --> 00:06:54,748 +Following VoiceOver's prompt, +we can swipe up to increase + +129 +00:06:54,781 --> 00:06:56,450 +the number of slices selected... + +130 +00:06:56,483 --> 00:06:59,152 +Siri: Five slices. +Six slices. + +131 +00:06:59,186 --> 00:07:02,222 +Tyler: And swipe down to decrease +the number of slices selected. + +132 +00:07:02,256 --> 00:07:04,725 +Siri: Five slices. +Four slices. + +133 +00:07:04,758 --> 00:07:07,361 +Tyler: With these changes in place, +our custom slider component + +134 +00:07:07,394 --> 00:07:08,962 +is now much more accessible. + +135 +00:07:09,930 --> 00:07:13,901 +Now, let's talk about how you can use SSML +in the Web Speech API + +136 +00:07:13,934 --> 00:07:16,970 +to build more rich experiences +for all of your users. + +137 +00:07:17,604 --> 00:07:21,642 +The Web Speech API is made up +of two primary JavaScript interfaces: + +138 +00:07:21,675 --> 00:07:23,911 +SpeechRecognition for audio input, + +139 +00:07:23,944 --> 00:07:26,980 +and SpeechSynthesis +for text-to-speech audio output. + +140 +00:07:28,148 --> 00:07:30,117 +Web Speech gives you the capabilities +to provide + +141 +00:07:30,150 --> 00:07:33,120 +a voice-assisted or voice-only interface +for your web app. + +142 +00:07:34,288 --> 00:07:37,391 +This can be beneficial for users +with motor disabilities, + +143 +00:07:37,424 --> 00:07:39,593 +who may have trouble using other means +of control, + +144 +00:07:39,626 --> 00:07:41,962 +like a mouse, keyboard, or touchscreen. + +145 +00:07:42,696 --> 00:07:46,200 +New to SpeechSynthesis on Safari +is the ability to use SSML + +146 +00:07:46,233 --> 00:07:48,135 +to manipulate the way your text is spoken. + +147 +00:07:49,069 --> 00:07:51,905 +SSML has tons of capabilities. + +148 +00:07:51,939 --> 00:07:54,074 +For example, +you can use the break element + +149 +00:07:54,107 --> 00:07:56,543 +to insert pauses in speech +with a time of your choosing. + +150 +00:07:57,177 --> 00:08:01,215 +You may want to ask your users +to breathe in... and breathe out. + +151 +00:08:02,216 --> 00:08:04,318 +Using the phoneme element, +you can control + +152 +00:08:04,351 --> 00:08:07,087 +the pronunciation of words, +like "tomayto" versus "tomahto." + +153 +00:08:08,755 --> 00:08:11,391 +The prosody element allows +you to manipulate the pitch, + +154 +00:08:11,425 --> 00:08:14,595 +rate, and volume of your spoken text. + +155 +00:08:14,628 --> 00:08:18,065 +And these only scratch +the surface of SSML's capabilities. + +156 +00:08:18,098 --> 00:08:21,835 +To learn more, check out +the SSML specification on w3.org. + +157 +00:08:22,703 --> 00:08:26,406 +Let's put our newfound knowledge +of SSML to use. + +158 +00:08:26,440 --> 00:08:29,009 +For the final question of our quiz, +we ask our students + +159 +00:08:29,042 --> 00:08:31,645 +to choose a radio button +with the correct Spanish translation + +160 +00:08:31,678 --> 00:08:34,381 +of the phrase, "the water." + +161 +00:08:34,414 --> 00:08:36,783 +We can make this question +more engaging by allowing users + +162 +00:08:36,817 --> 00:08:40,687 +to press a button to read the question +and answers with text-to-speech, + +163 +00:08:40,721 --> 00:08:43,056 +using SSML to read +the Spanish phrases + +164 +00:08:43,090 --> 00:08:44,658 +in a Spanish-locale voice. + +165 +00:08:45,526 --> 00:08:48,862 +Let's begin by creating our button, +ensuring to wrap the speaker emoji + +166 +00:08:48,896 --> 00:08:51,265 +in a span with aria-hidden set to true, + +167 +00:08:51,298 --> 00:08:54,434 +since this emoji's description +is not particularly useful here. + +168 +00:08:56,036 --> 00:08:58,438 +Next, let's create +a helper JavaScript function + +169 +00:08:58,472 --> 00:08:59,973 +called wrapWithSSML , + +170 +00:09:00,007 --> 00:09:03,043 +which takes a phrase to speak +and a voice-locale to speak it in. + +171 +00:09:04,144 --> 00:09:06,680 +We'll begin building our SSML string +with the break element + +172 +00:09:06,713 --> 00:09:09,816 +to insert a short pause before each phrase +to build emphasis. + +173 +00:09:10,884 --> 00:09:13,620 +With the prosody element, +we'll specify that we want our phrase + +174 +00:09:13,654 --> 00:09:15,756 +spoken at 80% of the default rate. + +175 +00:09:17,157 --> 00:09:19,459 +And finally, +we can use the lang element to choose + +176 +00:09:19,493 --> 00:09:22,596 +the locale-specific voice +we want our phrase to be spoken in. + +177 +00:09:24,198 --> 00:09:26,967 +And now we'll add a click event listener +to our read question button + +178 +00:09:27,000 --> 00:09:29,837 +and build our SSML string inside. + +179 +00:09:29,870 --> 00:09:32,406 +We begin by wrapping +the entire string in a speak element. + +180 +00:09:33,607 --> 00:09:36,476 +Speak is important because it +indicates to synthesis processors + +181 +00:09:36,510 --> 00:09:39,046 +that anything within +should be considered SSML. + +182 +00:09:40,447 --> 00:09:44,718 +Next, we include our question: +How do you say "the water" in Spanish? + +183 +00:09:45,686 --> 00:09:47,855 +We can use +our wrapWithSSML helper function + +184 +00:09:47,888 --> 00:09:50,090 +to give emphasis +to the phrase being translated + +185 +00:09:50,123 --> 00:09:52,593 +and ensure it's read +in a U.S. English locale-voice. + +186 +00:09:53,594 --> 00:09:57,331 +We'll also use wrapWithSSML +for all four of our potential answers, + +187 +00:09:57,364 --> 00:10:01,101 +providing emphasis and requesting that +they be read in a Spanish locale-voice. + +188 +00:10:02,402 --> 00:10:05,739 +Finally, we can create +a new SpeechSynthesisUtterance object + +189 +00:10:05,772 --> 00:10:08,575 +with our SSML string, +and pass that to the window + +190 +00:10:08,609 --> 00:10:11,144 +SpeechSynthesis speak method +to read it out. + +191 +00:10:13,480 --> 00:10:15,582 +With all of that in place, +let's see what the experience is like + +192 +00:10:15,616 --> 00:10:17,184 +on our web app. + +193 +00:10:17,217 --> 00:10:20,087 +On the page with the final question, +I'll tap the "Read question" button, + +194 +00:10:20,120 --> 00:10:21,855 +and listen. + +195 +00:10:21,889 --> 00:10:26,260 +Siri: How do you say "the water" +in Spanish? + +196 +00:10:26,293 --> 00:10:28,295 +El agua. + +197 +00:10:28,328 --> 00:10:30,564 +La abuela. + +198 +00:10:30,597 --> 00:10:32,900 +La abeja. + +199 +00:10:32,933 --> 00:10:34,902 +El árbol. + +200 +00:10:34,935 --> 00:10:37,771 +Tyler: Thanks to SSML, we've created +a much more engaging experience + +201 +00:10:37,804 --> 00:10:38,839 +for our students. + +202 +00:10:39,973 --> 00:10:42,709 +Another common design pattern +on the web is the modal. + +203 +00:10:43,710 --> 00:10:47,147 +You may use this on your web apps +as a sign-in or sign-up form, + +204 +00:10:47,181 --> 00:10:49,516 +for confirmation dialogs, and more. + +205 +00:10:50,684 --> 00:10:52,953 +One way to provide +an accessible modal experience + +206 +00:10:52,986 --> 00:10:55,489 +is the aria-modal attribute. + +207 +00:10:55,522 --> 00:10:58,025 +With aria-modal="true", +Safari will consider + +208 +00:10:58,058 --> 00:11:02,329 +all accessible elements outside +the modal to be ignored. + +209 +00:11:02,362 --> 00:11:05,866 +Recently, Safari has also added support +for the dialog element. + +210 +00:11:06,567 --> 00:11:09,503 +Dialog makes providing +an accessibility-friendly modal experience + +211 +00:11:09,536 --> 00:11:12,239 +much easier +with standard focus interactions, + +212 +00:11:12,272 --> 00:11:14,741 +out-of-the box handling +of modal closing gestures, + +213 +00:11:14,775 --> 00:11:18,145 +like the Escape key and the scrub gesture +on iOS, and more. + +214 +00:11:18,879 --> 00:11:21,748 +To see this in action, +let's change the "Show score" button + +215 +00:11:21,782 --> 00:11:24,952 +on our quiz assessment web app +to open a dialog with our results. + +216 +00:11:26,920 --> 00:11:30,324 +First things first, +we'll need to create our dialog element. + +217 +00:11:30,357 --> 00:11:33,393 +The markup +could look something like this. + +218 +00:11:33,427 --> 00:11:37,898 +We give the dialog an ID so it can be +referenced later by our show score button. + +219 +00:11:37,931 --> 00:11:40,834 +We also wrap the dialog's contents +in a form with method dialog. + +220 +00:11:41,935 --> 00:11:44,938 +By doing so, any submit type controls, +like our button, + +221 +00:11:44,972 --> 00:11:47,641 +will cause the dialog to close. + +222 +00:11:47,674 --> 00:11:50,878 +We'll also need +a bit of JavaScript to open the modal. + +223 +00:11:50,911 --> 00:11:53,080 +Let's add a click event listener +to our Show Score button + +224 +00:11:53,113 --> 00:11:55,215 +that calls showModal() +on our dialog element. + +225 +00:11:57,484 --> 00:11:59,953 +And now we're ready to try this out. + +226 +00:11:59,987 --> 00:12:03,123 +With VoiceOver active, I'll tap +the "Show score" button to focus it. + +227 +00:12:04,124 --> 00:12:05,459 +Siri: Show score. Button. + +228 +00:12:06,460 --> 00:12:08,462 +Tyler: Then, I'll double-tap +with a single finger + +229 +00:12:08,495 --> 00:12:10,531 +anywhere on the screen +to press the button. + +230 +00:12:11,465 --> 00:12:14,301 +Siri: Show score. Web dialog. +Close button. + +231 +00:12:14,334 --> 00:12:16,270 +Tyler: +And now we have our modal. + +232 +00:12:16,303 --> 00:12:19,573 +I can swipe left to move through +the modal's contents to hear my score. + +233 +00:12:19,606 --> 00:12:22,009 +Siri: You got all six question correct. +Great work! + +234 +00:12:22,442 --> 00:12:24,878 +Tyler: And when I'm done, I can move +back to the close button + +235 +00:12:24,912 --> 00:12:26,280 +by swiping right... + +236 +00:12:26,313 --> 00:12:28,649 +Siri: Close button. + +237 +00:12:28,682 --> 00:12:31,084 +Tyler: And double tap to close +the modal. + +238 +00:12:31,118 --> 00:12:32,886 +Siri: Unchecked. + +239 +00:12:32,920 --> 00:12:34,321 +Tyler: As I mentioned previously, + +240 +00:12:34,354 --> 00:12:36,523 +the dialog element handles +the iOS scrub gesture + +241 +00:12:36,557 --> 00:12:38,759 +for modal closure out of the box. + +242 +00:12:38,792 --> 00:12:42,129 +To demonstrate, I'll re-open the modal +with a double-tap... + +243 +00:12:42,162 --> 00:12:44,398 +Siri: Show score, button. + +244 +00:12:44,431 --> 00:12:46,767 +Web dialog. Close, button. + +245 +00:12:46,800 --> 00:12:48,335 +Tyler: And then perform the scrub gesture + +246 +00:12:48,368 --> 00:12:52,239 +by quickly moving two fingers right, +left, and right across the screen. + +247 +00:12:53,540 --> 00:12:55,075 +Siri: Show score. Button. + +248 +00:12:55,108 --> 00:12:57,044 +Tyler: Okay, so we have +a functional modal, + +249 +00:12:57,077 --> 00:12:59,079 +but we can still do better. + +250 +00:12:59,112 --> 00:13:00,781 +Did you notice +that when we opened the modal, + +251 +00:13:00,814 --> 00:13:04,418 +VoiceOver only read "web dialog, +close, button"? + +252 +00:13:04,451 --> 00:13:06,720 +In this situation, +it would probably make sense + +253 +00:13:06,753 --> 00:13:09,556 +to use an aria-label +or aria-labelledby attribute + +254 +00:13:09,590 --> 00:13:12,860 +to provide more information for users +of assistive technologies. + +255 +00:13:12,893 --> 00:13:15,829 +Since our modal content is short, +simply informing users + +256 +00:13:15,863 --> 00:13:19,433 +of how many answers they got correct, +let's use that for our label. + +257 +00:13:19,466 --> 00:13:24,004 +First, we'll wrap the modal content +in a span with an ID. + +258 +00:13:24,037 --> 00:13:26,039 +Then, we can add +the aria-labelledby attribute + +259 +00:13:26,073 --> 00:13:28,509 +to our dialog pointing +to the modal-content ID. + +260 +00:13:29,543 --> 00:13:32,412 +Let's also explicitly set +the initial modal focus element + +261 +00:13:32,446 --> 00:13:34,848 +to be the close button +with the autofocus attribute. + +262 +00:13:36,016 --> 00:13:38,852 +While this was already the default +behavior for this simple modal, + +263 +00:13:38,886 --> 00:13:41,421 +that may not have been the case +if our modal had more content + +264 +00:13:41,455 --> 00:13:43,423 +or was more complex, +with lots of controls. + +265 +00:13:44,591 --> 00:13:47,227 +For example, +in a modal with a lot of content, + +266 +00:13:47,261 --> 00:13:51,164 +it may have made more sense +to place autofocus on a top-level heading. + +267 +00:13:51,198 --> 00:13:53,467 +As the modal author, +you'll know best as to what will provide + +268 +00:13:53,500 --> 00:13:55,102 +a great experience for your users. + +269 +00:13:56,303 --> 00:13:58,205 +With our new attributes in place, +let's again see + +270 +00:13:58,238 --> 00:14:00,574 +what the experience is like in VoiceOver. + +271 +00:14:00,607 --> 00:14:03,177 +I'll first tap the Show score button +once to focus it... + +272 +00:14:04,077 --> 00:14:05,112 +Siri: Show score, button. + +273 +00:14:06,613 --> 00:14:09,149 +Tyler: And then double-tap to press it. + +274 +00:14:09,183 --> 00:14:13,387 +Siri: You got all six questions correct. +Great work! Web dialog, close button. + +275 +00:14:13,954 --> 00:14:15,822 +Tyler: That's a much better experience. + +276 +00:14:15,856 --> 00:14:19,526 +A VoiceOver user immediately hears +their score thanks to aria-labelledby, + +277 +00:14:19,560 --> 00:14:21,828 +and is already focused +on the close button, + +278 +00:14:21,862 --> 00:14:23,897 +and therefore can double-tap +to leave the modal. + +279 +00:14:23,931 --> 00:14:26,834 +And with that, +it's time to wrap up today's session. + +280 +00:14:26,867 --> 00:14:29,903 +I hope you've learned some techniques +to build rich, accessible web apps, + +281 +00:14:29,937 --> 00:14:32,472 +ensuring you provide a great experience +to all of your users. + +282 +00:14:33,373 --> 00:14:35,509 +Please try these features out +in the latest Safari, + +283 +00:14:35,542 --> 00:14:37,878 +and file any bugs you find +to the WebKit bug tracker + +284 +00:14:37,911 --> 00:14:39,913 +at bugs.webkit.org. + +285 +00:14:40,681 --> 00:14:44,251 +Thanks for joining me on today's whirlwind +tour of modern web accessibility. + +286 +00:14:44,284 --> 00:14:46,787 +Have an amazing WWDC! + +287 +00:14:46,820 --> 00:14:48,889 +[spacey music] + diff --git a/eng/2022 Session 10155 Take ScreenCaptureKit to the next level en.srt b/eng/2022 Session 10155 Take ScreenCaptureKit to the next level en.srt new file mode 100644 index 0000000..0f157ab --- /dev/null +++ b/eng/2022 Session 10155 Take ScreenCaptureKit to the next level en.srt @@ -0,0 +1,2883 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:10,177 +♪ + +3 +00:00:10,177 --> 00:00:12,212 +Meng Yang: Hi, +my name is Meng Yang, + +4 +00:00:12,212 --> 00:00:15,949 +an engineer from GPU Software +here at Apple. + +5 +00:00:15,949 --> 00:00:19,119 +Today I am going cover +a few advanced topics + +6 +00:00:19,119 --> 00:00:21,989 +about ScreenCaptureKit +and how it can take + +7 +00:00:21,989 --> 00:00:26,226 +your app's screen sharing +experience to the next level. + +8 +00:00:26,226 --> 00:00:28,929 +Later, my colleague Drew +will demonstrate + +9 +00:00:28,929 --> 00:00:32,699 +this exciting new API in action. + +10 +00:00:32,699 --> 00:00:37,137 +Screen capture is at the heart +of screen sharing applications + +11 +00:00:37,137 --> 00:00:41,575 +such as Zoom, +Google Meet, SharePlay + +12 +00:00:41,575 --> 00:00:46,346 +and even popular game streaming +services like Twitch, + +13 +00:00:46,346 --> 00:00:50,851 +which have become the new norm +of how we work, study, + +14 +00:00:50,851 --> 00:00:55,656 +collaborate, and socialize +over the past few years. + +15 +00:00:55,656 --> 00:00:58,058 +ScreenCaptureKit +is a brand-new, + +16 +00:00:58,058 --> 00:01:00,794 +high-performance +screen capture framework + +17 +00:01:00,794 --> 00:01:05,565 +built from ground up +with a powerful feature set. + +18 +00:01:05,565 --> 00:01:10,137 +The rich set of features +includes highly customizable + +19 +00:01:10,137 --> 00:01:14,474 +content control that allows you +to easily pick and then choose + +20 +00:01:14,474 --> 00:01:18,045 +any combination +of windows, applications, + +21 +00:01:18,045 --> 00:01:21,515 +and displays to capture. + +22 +00:01:21,515 --> 00:01:24,618 +Ability to capture +up to the screen content's + +23 +00:01:24,618 --> 00:01:28,422 +native resolution +and frame rate. + +24 +00:01:28,422 --> 00:01:32,092 +Dynamic stream property controls +like resolution, + +25 +00:01:32,092 --> 00:01:34,628 +frame rate, pixel format. + +26 +00:01:34,628 --> 00:01:37,497 +And these controls +can be modified on the fly + +27 +00:01:37,497 --> 00:01:40,867 +without recreating the stream. + +28 +00:01:40,867 --> 00:01:44,004 +Capture buffers that are +GPU memory-backed + +29 +00:01:44,004 --> 00:01:47,307 +to reduce memory copies. + +30 +00:01:47,307 --> 00:01:51,211 +Hardware-accelerated +content capture, scaling, + +31 +00:01:51,211 --> 00:01:53,914 +pixel and color +format conversion + +32 +00:01:53,914 --> 00:01:59,953 +to achieve high-performance +capture with reduced CPU usage. + +33 +00:01:59,953 --> 00:02:07,060 +Last but not least, support for +both video and audio capture. + +34 +00:02:07,060 --> 00:02:08,795 +Before getting started, + +35 +00:02:08,795 --> 00:02:11,298 +this talk assumes +you are already familiar + +36 +00:02:11,298 --> 00:02:14,267 +with the basic concepts, +building blocks, + +37 +00:02:14,267 --> 00:02:17,504 +and workflow of +how the framework works. + +38 +00:02:17,504 --> 00:02:21,508 +Please visit the intro session +"Meet ScreenCaptureKit" + +39 +00:02:21,508 --> 00:02:23,510 +to learn more. + +40 +00:02:23,510 --> 00:02:26,246 +In this session, +I am going to talk about + +41 +00:02:26,246 --> 00:02:30,250 +how to capture and display +a single window. + +42 +00:02:30,250 --> 00:02:36,857 +Next, how to add screen content +to full display capture. + +43 +00:02:36,857 --> 00:02:40,961 +How to remove content +from display capture. + +44 +00:02:40,961 --> 00:02:45,032 +I will then show you a few ways +to configure the stream + +45 +00:02:45,032 --> 00:02:48,235 +for different use cases. + +46 +00:02:48,235 --> 00:02:52,372 +And last, you will see a demo +of how ScreenCaptureKit + +47 +00:02:52,372 --> 00:02:56,309 +transformed the screen +and audio capture experience + +48 +00:02:56,309 --> 00:03:03,417 +of OBS Studio, a popular +open source screen capture app. + +49 +00:03:03,417 --> 00:03:06,553 +Now, let's start +with the first example, + +50 +00:03:06,553 --> 00:03:10,223 +and probably +the most common use case: + +51 +00:03:10,223 --> 00:03:14,194 +capture a single window. + +52 +00:03:14,194 --> 00:03:16,897 +This example is going to cover + +53 +00:03:16,897 --> 00:03:20,300 +how to set up +a single window filter; + +54 +00:03:20,300 --> 00:03:22,936 +what to expect +from the stream output + +55 +00:03:22,936 --> 00:03:27,274 +when the captured window +is being resized, occluded, + +56 +00:03:27,274 --> 00:03:30,677 +moved off-screen, or minimized. + +57 +00:03:30,677 --> 00:03:34,347 +You will also learn +how to use per-frame metadata + +58 +00:03:34,347 --> 00:03:38,318 +and how to properly display +the captured window. + +59 +00:03:38,318 --> 00:03:40,287 +Let's dive in. + +60 +00:03:40,287 --> 00:03:43,523 +To capture a single window +that's independent + +61 +00:03:43,523 --> 00:03:45,926 +of which display it's on, + +62 +00:03:45,926 --> 00:03:49,362 +you can start by using +a single window filter + +63 +00:03:49,362 --> 00:03:54,201 +and initialize the filter +with just one window. + +64 +00:03:54,201 --> 00:03:55,902 +In the example here, + +65 +00:03:55,902 --> 00:04:01,341 +the filter is configured to +include a single Safari window. + +66 +00:04:01,341 --> 00:04:06,346 +The video output includes just +that window and nothing else. + +67 +00:04:06,346 --> 00:04:10,150 +No child, pop-up, +or other windows from Safari + +68 +00:04:10,150 --> 00:04:13,186 +will be included. + +69 +00:04:13,186 --> 00:04:17,157 +ScreenCaptureKit's audio capture +policy on the other hand + +70 +00:04:17,157 --> 00:04:20,594 +always works at the app level. + +71 +00:04:20,594 --> 00:04:23,730 +When a single window filter +is used, + +72 +00:04:23,730 --> 00:04:26,800 +all the audio content +from the application + +73 +00:04:26,800 --> 00:04:30,504 +that contains the window +will be captured, + +74 +00:04:30,504 --> 00:04:36,877 +even from those windows that are +not present in the video output. + +75 +00:04:36,877 --> 00:04:40,447 +Now let's take a look +at the code sample. + +76 +00:04:40,447 --> 00:04:43,717 +To create a stream +with a single window, + +77 +00:04:43,717 --> 00:04:47,154 +start by getting +all available content to share + +78 +00:04:47,154 --> 00:04:50,457 +via SCShareableContent. + +79 +00:04:50,457 --> 00:04:55,462 +Next, get the window you want +to share from SCShareableContent + +80 +00:04:55,462 --> 00:04:58,865 +by matching the windowID. + +81 +00:04:58,865 --> 00:05:03,170 +Then, create a SCContentFilter +with the type + +82 +00:05:03,170 --> 00:05:08,575 +desktopIndependentWindow +with the specified SCWindow. + +83 +00:05:08,575 --> 00:05:12,045 +You can further configure +the stream to include audio + +84 +00:05:12,045 --> 00:05:15,382 +as part of the stream output. + +85 +00:05:15,382 --> 00:05:18,051 +Now you are ready +to create a stream + +86 +00:05:18,051 --> 00:05:21,755 +with contentFilter +and streamConfig. + +87 +00:05:21,755 --> 00:05:27,227 +You can then add a StreamOutput +and start the stream. + +88 +00:05:27,227 --> 00:05:31,965 +Let's take a look +at the stream output next. + +89 +00:05:31,965 --> 00:05:36,536 +In the example here, the source +display is on the left + +90 +00:05:36,536 --> 00:05:40,473 +and the stream output +is on the right. + +91 +00:05:40,473 --> 00:05:45,579 +The stream filter includes +a single Safari window. + +92 +00:05:45,579 --> 00:05:49,216 +Now I am going to start +to scroll the Safari window + +93 +00:05:49,216 --> 00:05:52,118 +that's being captured. + +94 +00:05:52,118 --> 00:05:55,055 +The stream output includes +the live content + +95 +00:05:55,055 --> 00:05:57,257 +from the single Safari window + +96 +00:05:57,257 --> 00:06:02,162 +and is updating at the same +cadence as the source window, + +97 +00:06:02,162 --> 00:06:06,199 +up to the source display's +native frame rate. + +98 +00:06:06,199 --> 00:06:10,437 +For example, when the source +window is constantly updating + +99 +00:06:10,437 --> 00:06:14,574 +on a 120Hz display, +the stream output + +100 +00:06:14,574 --> 00:06:20,347 +can also achieve +up to 120 fps update. + +101 +00:06:20,347 --> 00:06:24,551 +You might wonder what happens +when the window resizes. + +102 +00:06:24,551 --> 00:06:27,554 +Please keep in mind +that frequently changing + +103 +00:06:27,554 --> 00:06:29,556 +the stream's output dimension + +104 +00:06:29,556 --> 00:06:32,892 +can lead to additional +memory allocation + +105 +00:06:32,892 --> 00:06:36,029 +and therefore not recommended. + +106 +00:06:36,029 --> 00:06:40,333 +The stream's output dimension +is mostly fixed + +107 +00:06:40,333 --> 00:06:44,337 +and it does not resize +with the source window. + +108 +00:06:44,337 --> 00:06:47,440 +Now let me start to resize +the source window + +109 +00:06:47,440 --> 00:06:51,778 +and see what happens +to the stream's output. + +110 +00:06:51,778 --> 00:06:55,715 +ScreenCaptureKit always +performs hardware scaling + +111 +00:06:55,715 --> 00:07:00,253 +on the captured window so it +never exceeds the frame output + +112 +00:07:00,253 --> 00:07:03,490 +as the source window resizes. + +113 +00:07:03,490 --> 00:07:07,861 +How about windows that are +covered by other windows? + +114 +00:07:07,861 --> 00:07:12,799 +When the source window is +occluded or partially occluded, + +115 +00:07:12,799 --> 00:07:15,302 +the stream output +always includes + +116 +00:07:15,302 --> 00:07:19,105 +the window's full content. + +117 +00:07:19,105 --> 00:07:21,508 +And this also applies +to the case + +118 +00:07:21,508 --> 00:07:24,377 +when the window +is completely off-screen + +119 +00:07:24,377 --> 00:07:28,515 +or moved to other displays. + +120 +00:07:28,515 --> 00:07:33,119 +And for minimized windows, when +the source window is minimized, + +121 +00:07:33,119 --> 00:07:36,623 +the stream output is paused, + +122 +00:07:36,623 --> 00:07:41,828 +and it resumes when the source +window is no longer minimized. + +123 +00:07:41,828 --> 00:07:45,365 +Next, let's move +to audio output. + +124 +00:07:45,365 --> 00:07:47,233 +In this example here, + +125 +00:07:47,233 --> 00:07:50,837 +there are two Safari windows +with audio tracks, + +126 +00:07:50,837 --> 00:07:55,141 +and the window on the left +is being captured. + +127 +00:07:55,141 --> 00:07:59,112 +The video output includes +just the first window, + +128 +00:07:59,112 --> 00:08:01,948 +and the audio tracks +from both Safari windows + +129 +00:08:01,948 --> 00:08:05,352 +will be included +in the audio output. + +130 +00:08:05,352 --> 00:08:07,921 +Let's take a look and listen. + +131 +00:08:07,921 --> 00:08:12,892 +♪ Electronic dance music ♪ + +132 +00:08:12,892 --> 00:08:16,429 +Chef: And I wrote down +my favorite guacamole recipe. + +133 +00:08:16,429 --> 00:08:19,165 +It calls for four avocados. + +134 +00:08:19,165 --> 00:08:21,167 +Meng: With the stream +up and running, + +135 +00:08:21,167 --> 00:08:23,870 +your app receives a frame update + +136 +00:08:23,870 --> 00:08:27,774 +whenever there's +a new frame available. + +137 +00:08:27,774 --> 00:08:30,910 +The frame's output +includes IOSurface + +138 +00:08:30,910 --> 00:08:36,516 +representing the captured frame +and the per-frame metadata. + +139 +00:08:36,516 --> 00:08:42,088 +I'd like to spend some time +talking about metadata. + +140 +00:08:42,088 --> 00:08:45,024 +I am going to show you +examples of metadata + +141 +00:08:45,024 --> 00:08:48,495 +that can be quite useful +for your app. + +142 +00:08:48,495 --> 00:08:54,267 +And these include dirty rects, +content rect, + +143 +00:08:54,267 --> 00:08:58,738 +content scale, and scale factor. + +144 +00:08:58,738 --> 00:09:01,207 +Let's start with dirty rects. + +145 +00:09:01,207 --> 00:09:04,477 +Dirty rects indicate +where the new content is + +146 +00:09:04,477 --> 00:09:06,913 +from the previous frame. + +147 +00:09:06,913 --> 00:09:11,017 +In the example here, the dirty +rects are being highlighted + +148 +00:09:11,017 --> 00:09:15,655 +to illustrate the regions +of frame updates. + +149 +00:09:15,655 --> 00:09:19,325 +Instead of always +encoding the entire frame, + +150 +00:09:19,325 --> 00:09:23,663 +or calculate the delta between +two frames in the encoder, + +151 +00:09:23,663 --> 00:09:27,534 +you can simply use +dirty rects to only encode + +152 +00:09:27,534 --> 00:09:30,403 +and transmit the regions +with new updates + +153 +00:09:30,403 --> 00:09:33,640 +and copy the updates +onto the previous frame + +154 +00:09:33,640 --> 00:09:38,645 +on the receiver side +to generate a new frame. + +155 +00:09:38,645 --> 00:09:43,082 +Dirty rects can be retrieved +from the output CMSampleBuffer's + +156 +00:09:43,082 --> 00:09:49,589 +metadata dictionary +using the matching key. + +157 +00:09:49,589 --> 00:09:54,928 +Now let's move to content rect +and the content scale. + +158 +00:09:54,928 --> 00:09:58,932 +The source window to be captured +is on the left + +159 +00:09:58,932 --> 00:10:02,735 +and the stream output +is on the right. + +160 +00:10:02,735 --> 00:10:05,438 +Since a window can be resized, + +161 +00:10:05,438 --> 00:10:08,775 +the source window's +native backing surface size + +162 +00:10:08,775 --> 00:10:13,079 +often doesn't match +the stream output's dimension. + +163 +00:10:13,079 --> 00:10:16,716 +In the example here, +the captured window has + +164 +00:10:16,716 --> 00:10:22,655 +different aspect ratio from the +frame's output and is bigger. + +165 +00:10:22,655 --> 00:10:29,195 +The captured window is scaled +down to fit into the output. + +166 +00:10:29,195 --> 00:10:33,466 +A content rect, which is +highlighted in green here, + +167 +00:10:33,466 --> 00:10:37,537 +indicates the region of interest +of the captured content + +168 +00:10:37,537 --> 00:10:40,306 +on the stream output. + +169 +00:10:40,306 --> 00:10:44,077 +And the content scale +indicates how much the content + +170 +00:10:44,077 --> 00:10:46,746 +is scaled to fit. + +171 +00:10:46,746 --> 00:10:50,517 +Here the captured Safari window +is scaled down + +172 +00:10:50,517 --> 00:10:55,421 +by 0.77 to fit inside the frame. + +173 +00:10:55,421 --> 00:10:58,491 +Now you can use the metadata +just discussed + +174 +00:10:58,491 --> 00:11:01,294 +to correctly display +the captured window + +175 +00:11:01,294 --> 00:11:06,032 +as close to its native +appearance as possible. + +176 +00:11:06,032 --> 00:11:10,770 +First, let's start by cropping +the content from its output + +177 +00:11:10,770 --> 00:11:13,940 +using the content rect. + +178 +00:11:13,940 --> 00:11:20,613 +Next, scale the content back up +by dividing the content scale. + +179 +00:11:20,613 --> 00:11:24,884 +Now the captured content +is scaled to match one-to-one + +180 +00:11:24,884 --> 00:11:28,054 +in pixel size +as the source window. + +181 +00:11:28,054 --> 00:11:30,623 +But how is the captured window +going to look + +182 +00:11:30,623 --> 00:11:33,560 +on the target display? + +183 +00:11:33,560 --> 00:11:36,696 +To answer that question, +I would like to start + +184 +00:11:36,696 --> 00:11:40,466 +by describing +how scale factor works. + +185 +00:11:40,466 --> 00:11:44,537 +A display's scale factor +indicates the scale ratio + +186 +00:11:44,537 --> 00:11:48,274 +between a display +or window's logical point size + +187 +00:11:48,274 --> 00:11:53,112 +and its backing surface's +pixel size. + +188 +00:11:53,112 --> 00:11:59,152 +A scale factor 2, or a 2x mode, +means every one point onscreen + +189 +00:11:59,152 --> 00:12:03,623 +equals four pixels +on the backing surface. + +190 +00:12:03,623 --> 00:12:06,793 +A window can be moved +from a Retina display + +191 +00:12:06,793 --> 00:12:11,397 +with scale factor 2, +such as in the example here, + +192 +00:12:11,397 --> 00:12:14,701 +to a non-Retina display +with scale factor 1 + +193 +00:12:14,701 --> 00:12:17,403 +while being captured. + +194 +00:12:17,403 --> 00:12:22,275 +With scale factor 1, +each one logical point onscreen + +195 +00:12:22,275 --> 00:12:26,713 +corresponds to one pixel +on the backing surface. + +196 +00:12:26,713 --> 00:12:29,649 +In addition, +the source display might have + +197 +00:12:29,649 --> 00:12:33,219 +mismatched scale factor +from the target display + +198 +00:12:33,219 --> 00:12:38,324 +where the captured content +will be displayed. + +199 +00:12:38,324 --> 00:12:41,894 +In this example, +a window is being captured + +200 +00:12:41,894 --> 00:12:47,033 +from a Retina display on +the left with a scale factor 2 + +201 +00:12:47,033 --> 00:12:52,238 +and to be displayed on a +non-Retina display on the right. + +202 +00:12:52,238 --> 00:12:56,609 +If the captured window is +displayed as-is without scaling + +203 +00:12:56,609 --> 00:12:58,878 +on the target +non-Retina display + +204 +00:12:58,878 --> 00:13:01,848 +with one point +to one pixel mapping, + +205 +00:13:01,848 --> 00:13:06,119 +the window will look +four times as big. + +206 +00:13:06,119 --> 00:13:09,956 +To fix this, you should +always check the scale factor + +207 +00:13:09,956 --> 00:13:13,826 +from the frame's metadata +against the scale factor + +208 +00:13:13,826 --> 00:13:16,663 +of the target display. + +209 +00:13:16,663 --> 00:13:21,067 +When there's a mismatch, scale +the size of the captured content + +210 +00:13:21,067 --> 00:13:24,871 +by the scale factor +before displaying it. + +211 +00:13:24,871 --> 00:13:29,242 +After scaling, the captured +window on the target display + +212 +00:13:29,242 --> 00:13:34,914 +now appears to be the same size +as its source window. + +213 +00:13:34,914 --> 00:13:37,350 +Now let's take a look +at the code, + +214 +00:13:37,350 --> 00:13:39,686 +and it's quite simple. + +215 +00:13:39,686 --> 00:13:44,490 +Content rect, content scale, +and scale factor + +216 +00:13:44,490 --> 00:13:48,561 +can also be retrieved from +the output CMSampleBuffer's + +217 +00:13:48,561 --> 00:13:51,097 +metadata attachment. + +218 +00:13:51,097 --> 00:13:53,733 +You can then use these metadata + +219 +00:13:53,733 --> 00:14:00,740 +to crop and scale the captured +content to display it correctly. + +220 +00:14:00,740 --> 00:14:03,743 +To recap, a single window filter + +221 +00:14:03,743 --> 00:14:06,713 +always includes +full window content + +222 +00:14:06,713 --> 00:14:11,084 +even when the source window +is off-screen or occluded. + +223 +00:14:11,084 --> 00:14:14,821 +It's display +and space independent. + +224 +00:14:14,821 --> 00:14:20,293 +The output is always offset +at the top-left corner. + +225 +00:14:20,293 --> 00:14:25,031 +Pop-up or child windows +are not included. + +226 +00:14:25,031 --> 00:14:29,736 +Consider using metadata +to best display the content. + +227 +00:14:29,736 --> 00:14:35,842 +And the audio includes tracks +from the entire containing app. + +228 +00:14:35,842 --> 00:14:38,745 +Now that you have just learned +about how to capture + +229 +00:14:38,745 --> 00:14:41,180 +and display a single window, + +230 +00:14:41,180 --> 00:14:46,519 +let me move to the next class of +display-based content filters. + +231 +00:14:46,519 --> 00:14:48,488 +In this next example, + +232 +00:14:48,488 --> 00:14:51,290 +you will learn to create +a display-based filter + +233 +00:14:51,290 --> 00:14:55,194 +with windows or apps, +and I will demonstrate + +234 +00:14:55,194 --> 00:15:00,099 +some differences between video- +and audio-filtering rules. + +235 +00:15:00,099 --> 00:15:04,670 +A display-based inclusion filter +specifies which display + +236 +00:15:04,670 --> 00:15:07,406 +you want to capture +content from. + +237 +00:15:07,406 --> 00:15:11,878 +By default, +no windows are captured. + +238 +00:15:11,878 --> 00:15:15,882 +You can choose the content +you want to capture by window. + +239 +00:15:15,882 --> 00:15:20,486 +In the example here, a Safari +window and a Keynote window + +240 +00:15:20,486 --> 00:15:23,389 +are added to the display filter. + +241 +00:15:23,389 --> 00:15:27,226 +The video output includes +just these two windows + +242 +00:15:27,226 --> 00:15:29,295 +placed in a display space + +243 +00:15:29,295 --> 00:15:32,865 +and the audio output +includes all the soundtracks + +244 +00:15:32,865 --> 00:15:37,170 +from Keynote and Safari apps. + +245 +00:15:37,170 --> 00:15:40,206 +This code sample demonstrates +how to create + +246 +00:15:40,206 --> 00:15:44,443 +display-based filters +with included windows. + +247 +00:15:44,443 --> 00:15:48,014 +Start by creating a list +of SCWindows + +248 +00:15:48,014 --> 00:15:52,351 +using SCShareableContent +and windowIDs. + +249 +00:15:52,351 --> 00:15:56,789 +And then, create +a display-based SCContentFilter + +250 +00:15:56,789 --> 00:16:01,494 +with a given display +and a list of included windows. + +251 +00:16:01,494 --> 00:16:03,729 +You can then create a stream + +252 +00:16:03,729 --> 00:16:07,200 +using the filter and +configuration in the same way + +253 +00:16:07,200 --> 00:16:13,172 +as a desktop independent +window and start the stream. + +254 +00:16:13,172 --> 00:16:15,274 +With the stream up and running, + +255 +00:16:15,274 --> 00:16:18,644 +let's take a look +at the stream's output. + +256 +00:16:18,644 --> 00:16:22,849 +The filter is configured +to include two Safari windows, + +257 +00:16:22,849 --> 00:16:26,185 +menu bar, and wallpaper windows. + +258 +00:16:30,356 --> 00:16:32,959 +If a window is moved off-screen, + +259 +00:16:32,959 --> 00:16:37,363 +it will be removed +from the stream output. + +260 +00:16:37,363 --> 00:16:40,800 +When a new Safari window +is created, + +261 +00:16:40,800 --> 00:16:44,170 +the new window doesn't show up +in the stream output + +262 +00:16:44,170 --> 00:16:48,040 +because the new window +is not in the filter. + +263 +00:16:48,040 --> 00:16:52,111 +The same rule also applies +to child or pop-up windows, + +264 +00:16:52,111 --> 00:16:56,315 +which do not show up +in the stream's output. + +265 +00:16:56,315 --> 00:16:59,018 +If you want to ensure +that child windows + +266 +00:16:59,018 --> 00:17:02,889 +are included automatically +in your stream output, + +267 +00:17:02,889 --> 00:17:07,960 +you can use a display-based +filter with included apps. + +268 +00:17:07,960 --> 00:17:12,231 +In this example, adding +the Safari and Keynote apps + +269 +00:17:12,231 --> 00:17:16,736 +to the filter ensures that +the audio and video output + +270 +00:17:16,736 --> 00:17:20,506 +from all the windows and +soundtracks from these two apps + +271 +00:17:20,506 --> 00:17:24,010 +are included in the output + +272 +00:17:24,010 --> 00:17:27,446 +Window exception filters +are a powerful way + +273 +00:17:27,446 --> 00:17:31,117 +of excluding specific windows +from your output + +274 +00:17:31,117 --> 00:17:36,389 +when the filter is specified +as a display with included apps. + +275 +00:17:36,389 --> 00:17:39,392 +For example, +a single Safari window + +276 +00:17:39,392 --> 00:17:42,328 +is removed from the output. + +277 +00:17:42,328 --> 00:17:47,533 +ScreenCaptureKit enables +audio capture at the app level, + +278 +00:17:47,533 --> 00:17:51,570 +so excluding audio +from a single Safari window + +279 +00:17:51,570 --> 00:17:54,740 +is the equivalent +to removing audio tracks + +280 +00:17:54,740 --> 00:17:57,877 +for all Safari apps. + +281 +00:17:57,877 --> 00:18:00,479 +Although the stream's +video output + +282 +00:18:00,479 --> 00:18:03,916 +still includes a Safari window, + +283 +00:18:03,916 --> 00:18:07,987 +all the sound tracks +from Safari apps are removed + +284 +00:18:07,987 --> 00:18:11,490 +and the audio output +includes just the soundtrack + +285 +00:18:11,490 --> 00:18:13,259 +from Keynote. + +286 +00:18:13,259 --> 00:18:17,797 +In the code example here, +we change the SCContentFilter + +287 +00:18:17,797 --> 00:18:21,067 +to include a list of +SCRunningApplications + +288 +00:18:21,067 --> 00:18:23,636 +instead of SCWindows. + +289 +00:18:23,636 --> 00:18:27,807 +If there are individual windows +you want to further exclude, + +290 +00:18:27,807 --> 00:18:33,612 +build a list of SCWindows and +then create an SCContentFilter + +291 +00:18:33,612 --> 00:18:36,615 +using the list of SCApplications + +292 +00:18:36,615 --> 00:18:41,954 +with the list of excepting +windows to exclude. + +293 +00:18:41,954 --> 00:18:45,725 +Let's take a look at what the +stream output looks like now + +294 +00:18:45,725 --> 00:18:48,694 +when new or child windows +are created + +295 +00:18:48,694 --> 00:18:51,964 +by specifying included apps. + +296 +00:18:51,964 --> 00:18:58,070 +This time, Safari app and system +windows are added to the filter. + +297 +00:18:58,070 --> 00:19:02,308 +A new Safari window +is now automatically included + +298 +00:19:02,308 --> 00:19:05,711 +in the stream output +and the same rule applies + +299 +00:19:05,711 --> 00:19:08,881 +to child and pop-up windows. + +300 +00:19:08,881 --> 00:19:12,651 +This can be quite useful +when you are doing a tutorial + +301 +00:19:12,651 --> 00:19:15,621 +and want to demonstrate +the full action + +302 +00:19:15,621 --> 00:19:19,925 +including invoking pop-up +or new windows. + +303 +00:19:19,925 --> 00:19:22,895 +I have just demonstrated +how to add content + +304 +00:19:22,895 --> 00:19:26,632 +to the stream output +through a few different ways. + +305 +00:19:26,632 --> 00:19:28,300 +My next example will show you + +306 +00:19:28,300 --> 00:19:32,938 +how to remove content +from the stream output. + +307 +00:19:32,938 --> 00:19:35,608 +This example +includes a test app + +308 +00:19:35,608 --> 00:19:38,611 +that emulates +a video conferencing app + +309 +00:19:38,611 --> 00:19:43,382 +that contains a preview +of the display being shared. + +310 +00:19:43,382 --> 00:19:47,953 +Because the test app recursively +shows itself in the preview, + +311 +00:19:47,953 --> 00:19:52,758 +it's creating the so-called +mirror hall effect. + +312 +00:19:52,758 --> 00:19:56,595 +Even during full display share, +it's common for screen sharing + +313 +00:19:56,595 --> 00:20:01,434 +applications to remove its +own windows, capture preview, + +314 +00:20:01,434 --> 00:20:05,905 +participant camera view +to avoid the mirror hall effect, + +315 +00:20:05,905 --> 00:20:10,910 +or other system UIs +such as notification windows. + +316 +00:20:10,910 --> 00:20:13,079 +ScreenCaptureKit provides you + +317 +00:20:13,079 --> 00:20:17,116 +with a set of exclusion-based +filters that allow you + +318 +00:20:17,116 --> 00:20:21,153 +to quickly remove content +from display capture. + +319 +00:20:21,153 --> 00:20:25,357 +An exclusion-based display +filter captures all the windows + +320 +00:20:25,357 --> 00:20:28,427 +from the given display +by default. + +321 +00:20:28,427 --> 00:20:32,431 +You can then start to remove +individual windows or apps + +322 +00:20:32,431 --> 00:20:35,968 +by adding them +to the exclusion filter. + +323 +00:20:35,968 --> 00:20:40,306 +For example, you can add +the content capture test app + +324 +00:20:40,306 --> 00:20:46,846 +and Notification Center to the +list of excluded applications. + +325 +00:20:46,846 --> 00:20:49,315 +To create +a display-based filter + +326 +00:20:49,315 --> 00:20:52,384 +excluding a list +of applications, + +327 +00:20:52,384 --> 00:20:56,489 +start by retrieving +SCApplications to exclude + +328 +00:20:56,489 --> 00:20:59,058 +by matching bundle ID. + +329 +00:20:59,058 --> 00:21:02,328 +If there are individual windows +you'd like to cherry-pick + +330 +00:21:02,328 --> 00:21:05,831 +back to the stream output, +you can also build + +331 +00:21:05,831 --> 00:21:10,302 +an optional list of +excepting SCWindows. + +332 +00:21:10,302 --> 00:21:13,606 +And then use a given display, + +333 +00:21:13,606 --> 00:21:16,742 +the list of applications +to exclude, + +334 +00:21:16,742 --> 00:21:22,848 +and a list of excepting windows +to create the content filter. + +335 +00:21:22,848 --> 00:21:26,285 +Let's take a look at the result. + +336 +00:21:26,285 --> 00:21:28,954 +The content capture +test app that's causing + +337 +00:21:28,954 --> 00:21:32,892 +the mirror hall problem +and the notification windows + +338 +00:21:32,892 --> 00:21:36,295 +are both removed +from the stream output. + +339 +00:21:36,295 --> 00:21:38,964 +New or child windows +from these apps + +340 +00:21:38,964 --> 00:21:43,135 +will be automatically +removed as well. + +341 +00:21:43,135 --> 00:21:46,639 +If these removed apps +include any audio, + +342 +00:21:46,639 --> 00:21:50,876 +their audio will be removed +from the audio output. + +343 +00:21:50,876 --> 00:21:54,246 +We've just seen how +to capture a single window, + +344 +00:21:54,246 --> 00:21:58,417 +how to add and remove windows +from a display filter. + +345 +00:21:58,417 --> 00:22:02,288 +Let's move +to stream configuration next. + +346 +00:22:02,288 --> 00:22:05,558 +In the next few examples, +you will learn about + +347 +00:22:05,558 --> 00:22:09,161 +different stream properties +you can configure, + +348 +00:22:09,161 --> 00:22:13,465 +how to set up the stream for +screen capture and streaming, + +349 +00:22:13,465 --> 00:22:18,204 +and how to build a window picker +with live preview. + +350 +00:22:18,204 --> 00:22:22,241 +Let's start with +configuration properties. + +351 +00:22:22,241 --> 00:22:25,211 +These are some of +the common stream properties + +352 +00:22:25,211 --> 00:22:30,649 +you can configure, such as +stream output dimensions, + +353 +00:22:30,649 --> 00:22:33,452 +source and destination rects, + +354 +00:22:33,452 --> 00:22:38,557 +color space, color matrix, +and pixel format, + +355 +00:22:38,557 --> 00:22:43,362 +whether to include cursor, +and frame rate control. + +356 +00:22:43,362 --> 00:22:48,601 +We will take a look at each +property in details next. + +357 +00:22:48,601 --> 00:22:51,237 +Let's start +with output dimension, + +358 +00:22:51,237 --> 00:22:56,508 +which can be specified +as width and height in pixels. + +359 +00:22:56,508 --> 00:22:59,845 +The source display's dimension +and aspect ratio + +360 +00:22:59,845 --> 00:23:04,316 +doesn't always match +the output dimension. + +361 +00:23:04,316 --> 00:23:08,554 +And when this mismatch happens +while capturing a full display, + +362 +00:23:08,554 --> 00:23:13,659 +there will be pillar or +letterbox in the stream output. + +363 +00:23:13,659 --> 00:23:17,830 +You can also specify a source +rect that defines the region + +364 +00:23:17,830 --> 00:23:22,334 +to capture from and the result +will be rendered and scaled + +365 +00:23:22,334 --> 00:23:26,472 +to the destination rect +on the frame output. + +366 +00:23:26,472 --> 00:23:30,342 +ScreenCaptureKit supports +hardware accelerated + +367 +00:23:30,342 --> 00:23:36,248 +color space, color matrix, +and pixel format conversion. + +368 +00:23:36,248 --> 00:23:41,120 +Common BGRA and YUV formats +are supported. + +369 +00:23:41,120 --> 00:23:46,458 +Please visit our developer page +for the full list. + +370 +00:23:46,458 --> 00:23:50,829 +When show cursor is enabled, +the stream output includes + +371 +00:23:50,829 --> 00:23:54,466 +a cursor prerendered +into the frame. + +372 +00:23:54,466 --> 00:23:57,469 +This applies +to all system cursors, + +373 +00:23:57,469 --> 00:24:02,374 +even custom cursor like +the camera-shaped one here. + +374 +00:24:02,374 --> 00:24:04,943 +You can use +minimum frame interval + +375 +00:24:04,943 --> 00:24:08,347 +to control desired +output frame rate. + +376 +00:24:08,347 --> 00:24:12,685 +For example, +when requesting 60 fps, + +377 +00:24:12,685 --> 00:24:16,655 +set the minimal interval +to 1/60. + +378 +00:24:16,655 --> 00:24:21,393 +You will receive frame update +no more than 60 fps, + +379 +00:24:21,393 --> 00:24:26,432 +and no more than +the content's native frame rate. + +380 +00:24:26,432 --> 00:24:29,335 +Queue depth can be specified +to determine + +381 +00:24:29,335 --> 00:24:34,340 +the number of surfaces +in the server-side surface pool. + +382 +00:24:34,340 --> 00:24:38,444 +More surfaces in the pool +can lead to better frame rate + +383 +00:24:38,444 --> 00:24:43,816 +and performance, but it results +in higher system memory usage + +384 +00:24:43,816 --> 00:24:47,286 +and potentially +a latency trade-off, + +385 +00:24:47,286 --> 00:24:50,956 +which I will discuss +in more details later. + +386 +00:24:50,956 --> 00:24:54,293 +ScreenCaptureKit accepts +queue depth range between + +387 +00:24:54,293 --> 00:24:59,531 +three to eight with a default +queue depth of three. + +388 +00:24:59,531 --> 00:25:03,535 +In this example here, +the surface pool is configured + +389 +00:25:03,535 --> 00:25:07,473 +to include four surfaces +available for ScreenCaptureKit + +390 +00:25:07,473 --> 00:25:09,608 +to render to. + +391 +00:25:09,608 --> 00:25:13,612 +The current active surface +is surface 1 + +392 +00:25:13,612 --> 00:25:19,118 +and ScreenCaptureKit is +rendering the next frame to it. + +393 +00:25:19,118 --> 00:25:22,254 +Once surface 1 is complete, + +394 +00:25:22,254 --> 00:25:26,825 +ScreenCaptureKit sends +surface 1 to your app. + +395 +00:25:26,825 --> 00:25:30,529 +Your app is processing +and holding surface 1, + +396 +00:25:30,529 --> 00:25:35,033 +while ScreenCaptureKit +is rendering to surface 2. + +397 +00:25:35,033 --> 00:25:39,371 +Surface 1 is now marked +as unavailable in the pool + +398 +00:25:39,371 --> 00:25:43,242 +since your app +is still using it. + +399 +00:25:43,242 --> 00:25:47,479 +When surface 2 is complete, +it's sent to your app + +400 +00:25:47,479 --> 00:25:51,884 +and ScreenCaptureKit +now renders to surface 3. + +401 +00:25:51,884 --> 00:25:55,654 +But if your app is still +processing surface 1, + +402 +00:25:55,654 --> 00:25:59,591 +it will start to fall behind +as frames are now provided + +403 +00:25:59,591 --> 00:26:03,362 +faster than they +can be processed. + +404 +00:26:03,362 --> 00:26:07,866 +If the surface pool contains +a large number of surfaces, + +405 +00:26:07,866 --> 00:26:10,402 +new surfaces +will start to pile up + +406 +00:26:10,402 --> 00:26:13,105 +and you might need to consider +starting to drop frames + +407 +00:26:13,105 --> 00:26:15,541 +in order to keep up. + +408 +00:26:15,541 --> 00:26:18,877 +In this case, +more surfaces in the pool + +409 +00:26:18,877 --> 00:26:22,948 +can potentially lead +to higher latency. + +410 +00:26:22,948 --> 00:26:25,784 +The number of surfaces +left in the pool + +411 +00:26:25,784 --> 00:26:29,788 +for ScreenCaptureKit to use, +equals the queue depth + +412 +00:26:29,788 --> 00:26:34,493 +minus the number of surfaces +held by your app. + +413 +00:26:34,493 --> 00:26:36,061 +In the example here, + +414 +00:26:36,061 --> 00:26:40,199 +both surface 1 and 2 +are still held by your app. + +415 +00:26:40,199 --> 00:26:45,037 +There are 2 surfaces +left in the surface pool. + +416 +00:26:45,037 --> 00:26:49,041 +After surface 3 is complete +and is sent to your app, + +417 +00:26:49,041 --> 00:26:54,146 +the only available surface +left in the pool is surface 4. + +418 +00:26:54,146 --> 00:26:59,718 +If your app continues to hold +on to surface 1, 2, and 3, + +419 +00:26:59,718 --> 00:27:04,089 +ScreenCaptureKit will soon +run out of surfaces to render to + +420 +00:27:04,089 --> 00:27:07,659 +and you will start to see +frame loss and glitch. + +421 +00:27:07,659 --> 00:27:11,296 +Your app needs to finish +and release surface 1 + +422 +00:27:11,296 --> 00:27:14,800 +before ScreenCaptureKit starts +to render the next frame + +423 +00:27:14,800 --> 00:27:20,472 +after surface 4 +in order to avoid frame loss. + +424 +00:27:20,472 --> 00:27:24,109 +Now your app releases +surface 1 and it's available + +425 +00:27:24,109 --> 00:27:27,679 +for ScreenCaptureKit +to use again. + +426 +00:27:27,679 --> 00:27:31,116 +To recap: there are two rules +your app needs to follow + +427 +00:27:31,116 --> 00:27:35,420 +in order avoid frame latency +and frame loss. + +428 +00:27:35,420 --> 00:27:38,690 +To avoid delayed frame, +you need to be able to process + +429 +00:27:38,690 --> 00:27:42,528 +a frame within +the MinimumFrameInterval. + +430 +00:27:42,528 --> 00:27:45,864 +To avoid frame loss, +the time it takes your app + +431 +00:27:45,864 --> 00:27:50,202 +to release the surfaces back +to the pool must be less than + +432 +00:27:50,202 --> 00:27:55,107 +MinimumFrameInterval +times QueueDepth minus 1, + +433 +00:27:55,107 --> 00:28:00,212 +after which ScreenCaptureKit +runs out of surfaces to use, + +434 +00:28:00,212 --> 00:28:04,716 +enters a stall, and will +start to miss new frames. + +435 +00:28:04,716 --> 00:28:07,052 +Now that you've seen +the various properties + +436 +00:28:07,052 --> 00:28:10,989 +you can configure, +let's dive into some examples + +437 +00:28:10,989 --> 00:28:15,727 +to configure the stream for +screen capture and streaming. + +438 +00:28:15,727 --> 00:28:19,598 +Some screen content +includes videos, games, + +439 +00:28:19,598 --> 00:28:22,768 +or animations that +are constantly updating + +440 +00:28:22,768 --> 00:28:26,204 +and that requires +higher frame rate. + +441 +00:28:26,204 --> 00:28:30,108 +While others include +mostly static text + +442 +00:28:30,108 --> 00:28:32,110 +like the keynote window, + +443 +00:28:32,110 --> 00:28:36,815 +which prioritize higher +resolution over frame rate, + +444 +00:28:36,815 --> 00:28:40,786 +you can live-adjust the +stream's configuration based on + +445 +00:28:40,786 --> 00:28:45,857 +the content being shared +and the networking condition. + +446 +00:28:45,857 --> 00:28:48,694 +In this code example, +you are going to see + +447 +00:28:48,694 --> 00:28:55,300 +how to configure the capture +to stream 4K, 60-fps game. + +448 +00:28:55,300 --> 00:28:58,470 +You can start by setting +the stream output dimension + +449 +00:28:58,470 --> 00:29:01,106 +to 4K in pixel size. + +450 +00:29:01,106 --> 00:29:06,211 +And then, set the output +frame rate to 60 fps + +451 +00:29:06,211 --> 00:29:10,916 +by setting the minimum +frame interval to 1/60. + +452 +00:29:10,916 --> 00:29:18,323 +Next, use pixel format YUV420 +for encoding and streaming. + +453 +00:29:18,323 --> 00:29:20,258 +Set the optional source rect + +454 +00:29:20,258 --> 00:29:25,230 +to just capture +a portion of the screen. + +455 +00:29:25,230 --> 00:29:29,101 +Next, change the background +fill color to black, + +456 +00:29:29,101 --> 00:29:33,372 +and then include a cursor +in the frame output. + +457 +00:29:33,372 --> 00:29:37,542 +Configure surface queue depth +to five for optimal frame rate + +458 +00:29:37,542 --> 00:29:39,778 +and performance. + +459 +00:29:39,778 --> 00:29:45,450 +Last, enable audio +on the output stream. + +460 +00:29:45,450 --> 00:29:48,587 +All the stream configurations +you've just seen + +461 +00:29:48,587 --> 00:29:52,791 +in the previous example can be +dynamically changed on the fly + +462 +00:29:52,791 --> 00:29:55,193 +without recreating the stream. + +463 +00:29:55,193 --> 00:29:59,464 +For example, you can live adjust +some properties such as + +464 +00:29:59,464 --> 00:30:04,302 +output dimension, dynamically +change the frame rate, + +465 +00:30:04,302 --> 00:30:07,472 +and update stream filters. + +466 +00:30:07,472 --> 00:30:10,842 +Here's an example to switch +the output dimension + +467 +00:30:10,842 --> 00:30:14,413 +from 4K down to 720p. + +468 +00:30:14,413 --> 00:30:21,620 +And downgrade the frame rate +from 60 fps to 15 fps. + +469 +00:30:21,620 --> 00:30:25,757 +You can then simply call +updateConfiguration + +470 +00:30:25,757 --> 00:30:28,193 +to apply the new settings +on the fly + +471 +00:30:28,193 --> 00:30:31,530 +without interrupting the stream. + +472 +00:30:31,530 --> 00:30:35,033 +In the last example, +I'd like to walk you through + +473 +00:30:35,033 --> 00:30:39,671 +building a window picker +with live preview. + +474 +00:30:39,671 --> 00:30:41,506 +Here is an example + +475 +00:30:41,506 --> 00:30:45,110 +of what a typical window +picker looks like. + +476 +00:30:45,110 --> 00:30:48,513 +It's common for web conferencing +screen sharing apps + +477 +00:30:48,513 --> 00:30:50,749 +to offer users an option + +478 +00:30:50,749 --> 00:30:55,353 +to choose the exact window +to share. + +479 +00:30:55,353 --> 00:30:58,323 +ScreenCaptureKit provides +an efficient + +480 +00:30:58,323 --> 00:31:02,260 +and high-performance solution +to creating large number + +481 +00:31:02,260 --> 00:31:06,932 +of thumbnail-sized streams +with live content update, + +482 +00:31:06,932 --> 00:31:09,835 +and it's simple to implement. + +483 +00:31:09,835 --> 00:31:12,871 +Let's break it down to see +what it takes to build + +484 +00:31:12,871 --> 00:31:17,008 +a window picker like this +using ScreenCaptureKit. + +485 +00:31:17,008 --> 00:31:20,212 +To set up the picker, +you can start by creating + +486 +00:31:20,212 --> 00:31:23,949 +one single window filter +for each eligible window + +487 +00:31:23,949 --> 00:31:26,952 +that your app +allows the user to pick + +488 +00:31:26,952 --> 00:31:31,890 +with desktop independent window +as the filter type. + +489 +00:31:31,890 --> 00:31:35,127 +Next, set up the stream +configuration + +490 +00:31:35,127 --> 00:31:41,700 +that's thumbnail-sized, 5 fps, +with BGRA pixel format + +491 +00:31:41,700 --> 00:31:48,240 +for onscreen display, default +queue depth, no cursor or audio. + +492 +00:31:48,240 --> 00:31:52,344 +Use single window filter and +the stream configuration here + +493 +00:31:52,344 --> 00:31:57,616 +to create one stream +for each window. + +494 +00:31:57,616 --> 00:32:00,919 +To do this in code, +you can start by getting + +495 +00:32:00,919 --> 00:32:04,623 +the SCShareableContent +by excluding desktop + +496 +00:32:04,623 --> 00:32:07,225 +and system windows. + +497 +00:32:07,225 --> 00:32:10,195 +Next, create a content filter + +498 +00:32:10,195 --> 00:32:16,935 +of type desktop independent +window for each eligible window. + +499 +00:32:16,935 --> 00:32:21,106 +Then, move to +the stream configuration part. + +500 +00:32:21,106 --> 00:32:24,009 +Choose an appropriate +thumbnail size -- + +501 +00:32:24,009 --> 00:32:28,647 +in this example, +it's 284 by 182 -- + +502 +00:32:28,647 --> 00:32:33,919 +and then set the minimum +frame interval to one over five. + +503 +00:32:33,919 --> 00:32:39,391 +With a pixel format of BGRA +for onscreen display, + +504 +00:32:39,391 --> 00:32:43,361 +disable audio and cursor +since we don't need them + +505 +00:32:43,361 --> 00:32:45,263 +in the preview. + +506 +00:32:45,263 --> 00:32:49,401 +And set queue depth to three +because we don't expect updates + +507 +00:32:49,401 --> 00:32:52,337 +that are too often. + +508 +00:32:52,337 --> 00:32:56,308 +With the stream content filter +and the configuration created, + +509 +00:32:56,308 --> 00:32:59,611 +you are now ready +to create the streams. + +510 +00:32:59,611 --> 00:33:02,914 +Create one stream +for each window, + +511 +00:33:02,914 --> 00:33:06,251 +add stream output +for each stream, + +512 +00:33:06,251 --> 00:33:08,954 +and then start the stream. + +513 +00:33:08,954 --> 00:33:13,925 +Last, append it +to the stream list. + +514 +00:33:13,925 --> 00:33:16,795 +This is the window picker +with live preview + +515 +00:33:16,795 --> 00:33:20,832 +created using the sample code +we saw earlier. + +516 +00:33:20,832 --> 00:33:24,502 +Each thumbnail is live updating +and then backed + +517 +00:33:24,502 --> 00:33:29,474 +by an individual stream +with single-window filter. + +518 +00:33:29,474 --> 00:33:32,677 +With ScreenCaptureKit, +you can easily build + +519 +00:33:32,677 --> 00:33:36,314 +a live preview picker like this, +that allows you + +520 +00:33:36,314 --> 00:33:39,851 +to concurrently capture +so much live screen content + +521 +00:33:39,851 --> 00:33:44,823 +simultaneously, without +overburdening the system. + +522 +00:33:44,823 --> 00:33:47,993 +Now let me hand it over +to my colleague, Drew, + +523 +00:33:47,993 --> 00:33:51,129 +who's going to give you +an exciting demo about + +524 +00:33:51,129 --> 00:33:54,766 +OBS adoption +of ScreenCaptureKit. + +525 +00:33:54,766 --> 00:33:56,268 +Drew Mills: Thanks, Meng. + +526 +00:33:56,268 --> 00:33:59,638 +Hi, my name is Drew, and I'm a +Partner Engineer here at Apple. + +527 +00:33:59,638 --> 00:34:02,607 +OBS Studio is an open source +application that allows users + +528 +00:34:02,607 --> 00:34:04,576 +to manage recording +and streaming content + +529 +00:34:04,576 --> 00:34:05,911 +from their computer. + +530 +00:34:05,911 --> 00:34:08,313 +It contains an implementation +of ScreenCaptureKit + +531 +00:34:08,313 --> 00:34:11,016 +that we worked with the project +on integrating this spring. + +532 +00:34:11,016 --> 00:34:13,885 +ScreenCaptureKit was easy +to implement thanks to utilizing + +533 +00:34:13,885 --> 00:34:18,023 +similar code to OBS's existing +CGDisplayStream-based capture. + +534 +00:34:18,023 --> 00:34:20,292 +The ScreenCaptureKit +implementation demonstrates + +535 +00:34:20,292 --> 00:34:22,060 +many of the features discussed + +536 +00:34:22,060 --> 00:34:24,296 +in the "Meet ScreenCaptureKit" +session. + +537 +00:34:24,296 --> 00:34:26,831 +This includes: +capturing an entire desktop, + +538 +00:34:26,831 --> 00:34:28,700 +all of the windows +of an application, + +539 +00:34:28,700 --> 00:34:30,835 +or just one specific window. + +540 +00:34:30,835 --> 00:34:33,371 +ScreenCaptureKit has +lower overhead than OBS's + +541 +00:34:33,371 --> 00:34:36,441 +CGWindowListCreateImage-based +capture. + +542 +00:34:36,441 --> 00:34:39,210 +This means that when capturing +a portion of your screen, + +543 +00:34:39,210 --> 00:34:41,279 +you are left with more +resources that you can use + +544 +00:34:41,279 --> 00:34:43,081 +for producing your content. + +545 +00:34:43,081 --> 00:34:44,382 +Let's dive into a demo + +546 +00:34:44,382 --> 00:34:46,985 +to see what we've +been discussing in action. + +547 +00:34:46,985 --> 00:34:49,254 +On the left, there is +a worst case example + +548 +00:34:49,254 --> 00:34:51,389 +of OBS's Window Capture. + +549 +00:34:51,389 --> 00:34:55,193 +This capture uses the +CGWindowListCreateImage API, + +550 +00:34:55,193 --> 00:34:56,928 +and has significant stuttering. + +551 +00:34:56,928 --> 00:35:01,533 +In our testing, we saw frame +rates dip as low as 7 fps. + +552 +00:35:01,533 --> 00:35:04,135 +Meanwhile, the ScreenCaptureKit +implementation on the right + +553 +00:35:04,135 --> 00:35:06,938 +has a much smoother result, +providing an output video + +554 +00:35:06,938 --> 00:35:09,374 +with significantly +smoother motion. + +555 +00:35:09,374 --> 00:35:13,912 +In this case, delivering 60 fps. + +556 +00:35:13,912 --> 00:35:16,948 +All while OBS uses up +to 15 percent less RAM + +557 +00:35:16,948 --> 00:35:19,751 +than Window Capture. + +558 +00:35:19,751 --> 00:35:23,755 +And while OBS's CPU utilization +is cut by up to half + +559 +00:35:23,755 --> 00:35:28,660 +when using ScreenCaptureKit +instead of OBS's Window Capture. + +560 +00:35:28,660 --> 00:35:30,395 +Let's look at the other +improvements + +561 +00:35:30,395 --> 00:35:33,498 +that ScreenCaptureKit +has to offer OBS users. + +562 +00:35:33,498 --> 00:35:35,633 +I'm still trying to track down +all of the Gold Ranks + +563 +00:35:35,633 --> 00:35:37,369 +in Sayonara Wild Hearts. + +564 +00:35:37,369 --> 00:35:39,037 +I want to show off +my best run, + +565 +00:35:39,037 --> 00:35:40,939 +so I've been +recording my gameplay. + +566 +00:35:40,939 --> 00:35:43,141 +Thanks to ScreenCaptureKit, +I can now capture + +567 +00:35:43,141 --> 00:35:45,110 +direct audio stream +from the game, + +568 +00:35:45,110 --> 00:35:47,212 +so when I get +a notification on my Mac, + +569 +00:35:47,212 --> 00:35:50,048 +it won't ruin my recording's +audio or video. + +570 +00:35:50,048 --> 00:35:51,583 +And this is possible +without having to install + +571 +00:35:51,583 --> 00:35:54,452 +any additional +audio routing software. + +572 +00:35:54,452 --> 00:35:56,721 +♪ + +573 +00:35:56,721 --> 00:35:58,356 +Now, using all +of the enhancements + +574 +00:35:58,356 --> 00:36:00,525 +provided by ScreenCaptureKit +on Apple silicon, + +575 +00:36:00,525 --> 00:36:03,828 +I can stream games like +Taiko no Tatsujin Pop Tap Beat + +576 +00:36:03,828 --> 00:36:06,297 +from my Mac to popular +streaming services. + +577 +00:36:06,297 --> 00:36:08,400 +A new constant bitrate option +for Apple silicon's + +578 +00:36:08,400 --> 00:36:10,435 +hardware encoder +means that I can encode + +579 +00:36:10,435 --> 00:36:12,904 +my streaming content +for services requiring constant + +580 +00:36:12,904 --> 00:36:17,242 +bitrate without significantly +impacting my game's performance. + +581 +00:36:17,242 --> 00:36:19,210 +Now, thanks to +ScreenCaptureKit's + +582 +00:36:19,210 --> 00:36:21,513 +lower resource usage +and encoding offloading, + +583 +00:36:21,513 --> 00:36:23,314 +I have even more +performance available + +584 +00:36:23,314 --> 00:36:24,816 +for the content that matters. + +585 +00:36:24,816 --> 00:36:25,884 +Back to you, Meng. + +586 +00:36:25,884 --> 00:36:28,053 +Meng: Thank you, Drew. + +587 +00:36:28,053 --> 00:36:30,588 +Through the demos and examples, + +588 +00:36:30,588 --> 00:36:34,159 +you learned about advanced +screen content filters. + +589 +00:36:34,159 --> 00:36:38,563 +Several ways to configure the +stream for different use cases. + +590 +00:36:38,563 --> 00:36:42,767 +And how to use per-frame +metadata and correctly display + +591 +00:36:42,767 --> 00:36:44,903 +the captured content. + +592 +00:36:44,903 --> 00:36:49,741 +Some best practices to help you +achieve best performance. + +593 +00:36:49,741 --> 00:36:54,746 +And finally, Drew showcased +the significant capability + +594 +00:36:54,746 --> 00:36:59,884 +and performance improvement +ScreenCaptureKit brought to OBS. + +595 +00:36:59,884 --> 00:37:03,121 +I can't wait to see +how you redefine your app's + +596 +00:37:03,121 --> 00:37:07,192 +screen sharing, streaming, +and collaboration experience + +597 +00:37:07,192 --> 00:37:09,461 +using ScreenCaptureKit. + +598 +00:37:09,461 --> 00:37:11,429 +Thank you for watching! + +599 +00:37:11,429 --> 00:37:14,799 +♪ + diff --git a/eng/2022 Session 10156 Meet ScreenCaptureKit en.srt b/eng/2022 Session 10156 Meet ScreenCaptureKit en.srt new file mode 100644 index 0000000..e4b7947 --- /dev/null +++ b/eng/2022 Session 10156 Meet ScreenCaptureKit en.srt @@ -0,0 +1,1082 @@ +1 +00:00:01,368 --> 00:00:07,374 +[spacey music] + +2 +00:00:09,710 --> 00:00:11,211 +Ernest: Hello and welcome. + +3 +00:00:11,245 --> 00:00:13,480 +My name is Ernest, +and I'm a software engineer + +4 +00:00:13,514 --> 00:00:15,382 +on the ScreenCaptureKit team. + +5 +00:00:15,415 --> 00:00:17,684 +Over the past couple of years, + +6 +00:00:17,718 --> 00:00:20,787 +we all have been more reliant +on remote collaboration, + +7 +00:00:20,821 --> 00:00:22,990 +which often involves screen sharing. + +8 +00:00:25,659 --> 00:00:27,094 +On top of that, + +9 +00:00:27,127 --> 00:00:31,532 +streaming gameplay using +a recording application like OBS Studio, + +10 +00:00:31,565 --> 00:00:33,700 +and content creation as a whole, + +11 +00:00:33,734 --> 00:00:38,839 +has been a continually growing area +for people's education and entertainment. + +12 +00:00:40,574 --> 00:00:43,177 +With this in mind, +we created a framework + +13 +00:00:43,210 --> 00:00:48,115 +that meets developers' needs +for performant and robust screen capture. + +14 +00:00:48,148 --> 00:00:49,616 +Meet ScreenCaptureKit! + +15 +00:00:51,618 --> 00:00:55,055 +ScreenCaptureKit is a brand-new framework +on macOS + +16 +00:00:55,088 --> 00:00:56,823 +that is designed to help you create + +17 +00:00:56,857 --> 00:00:59,259 +your application's +screen sharing experience. + +18 +00:01:00,227 --> 00:01:04,064 +ScreenCaptureKit provides APIs that +will let you choose the content + +19 +00:01:04,097 --> 00:01:05,599 +you want to capture, + +20 +00:01:05,632 --> 00:01:10,170 +with developer controls and toggles +for your application's needs. + +21 +00:01:10,204 --> 00:01:13,941 +And all of the filters and controls +can be updated on the fly. + +22 +00:01:15,409 --> 00:01:18,412 +The framework delivers +high quality and performance + +23 +00:01:18,445 --> 00:01:22,916 +up to the native resolution +and frame rate of your display, + +24 +00:01:22,950 --> 00:01:26,186 +all while having privacy +in mind with global safeguards. + +25 +00:01:27,454 --> 00:01:31,425 +In this session, I'll help you get started +with the ScreenCaptureKit framework. + +26 +00:01:32,559 --> 00:01:34,962 +Once you have the basics down, +take a look + +27 +00:01:34,995 --> 00:01:37,431 +at "Take ScreenCaptureKit +to the next level" + +28 +00:01:37,464 --> 00:01:39,099 +for more advanced topics. + +29 +00:01:40,501 --> 00:01:43,904 +First, I'll go over the key features +of the framework. + +30 +00:01:45,305 --> 00:01:48,942 +Next, I'll cover +the main ScreenCaptureKit constructs + +31 +00:01:48,976 --> 00:01:50,177 +in an API overview. + +32 +00:01:51,512 --> 00:01:54,214 +Then, I'll show you how to set up +your stream + +33 +00:01:54,248 --> 00:01:55,749 +with a filter and configuration. + +34 +00:01:56,850 --> 00:01:59,453 +And finally, +I'll walk you through how to stream + +35 +00:01:59,486 --> 00:02:01,889 +video and audio samples +to your application. + +36 +00:02:03,457 --> 00:02:05,959 +Let's start with the key features +of ScreenCaptureKit. + +37 +00:02:07,294 --> 00:02:11,331 +ScreenCaptureKit lets you specify +the type of content you want to share + +38 +00:02:11,365 --> 00:02:13,467 +or filter out. + +39 +00:02:13,500 --> 00:02:17,804 +You can capture screen content +from any combination of displays, + +40 +00:02:17,838 --> 00:02:22,910 +applications, and windows +as well as the audio that goes with it. + +41 +00:02:25,879 --> 00:02:29,149 +ScreenCaptureKit supports +a variety of developer controls, + +42 +00:02:29,183 --> 00:02:33,787 +including pixel format, color space, + +43 +00:02:33,820 --> 00:02:37,424 +frame rate, and resolution, + +44 +00:02:37,457 --> 00:02:41,862 +and on the audio side, controls +such as sample rate and channel count. + +45 +00:02:44,598 --> 00:02:47,100 +And all of these filters +and configurations + +46 +00:02:47,134 --> 00:02:49,436 +can be adjusted on the fly, + +47 +00:02:49,469 --> 00:02:52,372 +allowing for more flexibility +in application design. + +48 +00:02:54,174 --> 00:02:59,079 +And in order to deliver audio samples +up to 48kHz stereo + +49 +00:02:59,112 --> 00:03:03,650 +and video samples at up to your display's +native resolution and frame rate, + +50 +00:03:03,684 --> 00:03:08,622 +ScreenCaptureKit is performance focused +and leverages the power of Mac GPUs + +51 +00:03:08,655 --> 00:03:13,026 +with a lower CPU overhead +than existing capture methods. + +52 +00:03:13,060 --> 00:03:16,930 +And of course, ScreenCaptureKit +is built with privacy in mind, + +53 +00:03:16,964 --> 00:03:21,335 +providing global privacy safeguards +for all applications using the framework. + +54 +00:03:23,203 --> 00:03:27,941 +The framework will require consent +before capturing video and audio content, + +55 +00:03:27,975 --> 00:03:31,144 +and the choice will be stored +in the Screen Recording privacy setting + +56 +00:03:31,178 --> 00:03:32,746 +in system preferences. + +57 +00:03:34,181 --> 00:03:37,417 +Now that you've seen +what ScreenCaptureKit is all about, + +58 +00:03:37,451 --> 00:03:41,021 +I'll show you some +of the most important concepts in the API. + +59 +00:03:41,054 --> 00:03:44,358 +The ScreenCaptureKit framework +is centered on SCStream. + +60 +00:03:45,559 --> 00:03:49,363 +SCStream handles control methods +like start and stop + +61 +00:03:49,396 --> 00:03:52,799 +and is created along with +SCShareableContent, + +62 +00:03:52,833 --> 00:03:57,104 +SCContentFilter, +and SCStreamConfiguration. + +63 +00:03:58,172 --> 00:04:01,508 +These objects determine +what content you want to capture + +64 +00:04:01,542 --> 00:04:03,410 +and how you want to capture it. + +65 +00:04:04,811 --> 00:04:09,216 +Once created and started, +media samples will be delivered + +66 +00:04:09,249 --> 00:04:12,853 +to your application +through the SCStreamOutput protocol. + +67 +00:04:13,487 --> 00:04:15,589 +I'll explain more about that a bit later. + +68 +00:04:17,391 --> 00:04:22,596 +Now, I'll show you how to use the API +to set up a stream in your application. + +69 +00:04:24,498 --> 00:04:28,368 +Here are the objects you want to get +familiar with when setting up your stream. + +70 +00:04:30,137 --> 00:04:33,607 +These are the objects that +will determine what you capture + +71 +00:04:33,640 --> 00:04:36,276 +and the quality +and the performance of the capture. + +72 +00:04:37,845 --> 00:04:41,381 +The first one I want to go into +is SCShareableContent. + +73 +00:04:44,318 --> 00:04:50,791 +On this desktop, there are windows, +applications, and the display itself. + +74 +00:04:52,259 --> 00:04:55,662 +ScreenCaptureKit has +a corresponding class for each of these + +75 +00:04:55,696 --> 00:04:58,699 +that you can use to build +the content you want to share. + +76 +00:05:01,001 --> 00:05:03,904 +First, let's take a look at SCDisplay. + +77 +00:05:05,105 --> 00:05:10,043 +ScreenCaptureKit categorizes displays +as SCDisplays, with read-only properties + +78 +00:05:10,077 --> 00:05:13,180 +including display identifier + +79 +00:05:13,213 --> 00:05:16,717 +and size properties width and height. + +80 +00:05:19,119 --> 00:05:23,223 +Within the display, there may be +many different running applications, + +81 +00:05:23,257 --> 00:05:27,461 +and each of these will have +a corresponding SCRunningApplication. + +82 +00:05:29,196 --> 00:05:31,865 +SCRunningApplications have +read-only properties + +83 +00:05:31,899 --> 00:05:34,034 +for application-level information + +84 +00:05:34,067 --> 00:05:36,770 +such as bundle identifier, + +85 +00:05:36,803 --> 00:05:40,040 +application name, +and its process identifier. + +86 +00:05:41,909 --> 00:05:45,579 +In the example here, +there will be an SCRunningApplication + +87 +00:05:45,612 --> 00:05:48,048 +for Keynote and Safari. + +88 +00:05:49,249 --> 00:05:52,519 +And, of course, +these applications have windows. + +89 +00:05:53,787 --> 00:05:56,957 +These windows will have +a corresponding SCWindow + +90 +00:05:56,990 --> 00:05:58,392 +with read-only properties + +91 +00:05:58,425 --> 00:06:04,398 +that define the window +such as window id, frame, title, + +92 +00:06:04,431 --> 00:06:07,134 +and if the window +is on screen or minimized. + +93 +00:06:08,602 --> 00:06:11,705 +The SCWindow will also have +an owning application. + +94 +00:06:12,973 --> 00:06:16,009 +In this case, both Safari SCWindows + +95 +00:06:16,043 --> 00:06:18,212 +will have +the same Safari owning application. + +96 +00:06:22,015 --> 00:06:27,221 +SCWindows, SCRunningApplications, +and SCDisplays combine together + +97 +00:06:27,254 --> 00:06:29,823 +to give you the possible content you can +share + +98 +00:06:29,857 --> 00:06:32,059 +in SCShareableContent. + +99 +00:06:32,092 --> 00:06:36,063 +You can get a list +of all shareable content on the device, + +100 +00:06:36,096 --> 00:06:38,765 +or you can specify certain parameters. + +101 +00:06:41,168 --> 00:06:44,137 +Suppose you'd like to list +all the applications and windows + +102 +00:06:44,171 --> 00:06:45,506 +that are on screen + +103 +00:06:45,539 --> 00:06:48,208 +so people can choose +which ones they'd like to share. + +104 +00:06:49,009 --> 00:06:52,379 +Well, ScreenCaptureKit has +a simple API for that. + +105 +00:06:54,815 --> 00:06:57,985 +This short code snippet +is from the capture sample code available + +106 +00:06:58,018 --> 00:06:59,720 +on developer.apple.com. + +107 +00:07:00,754 --> 00:07:05,058 +Only windows that are on screen +are returned with the SCShareableContent, + +108 +00:07:05,092 --> 00:07:11,265 +which includes the associated SCWindows, +SCApplications and SCDisplays. + +109 +00:07:12,566 --> 00:07:14,635 +And now that you have +the shareable content, + +110 +00:07:14,668 --> 00:07:16,170 +you can create a filter. + +111 +00:07:18,205 --> 00:07:21,942 +There are two main types +of SCContentFilters: + +112 +00:07:21,975 --> 00:07:25,712 +A display independent window filter, +which will capture the window + +113 +00:07:25,746 --> 00:07:28,882 +as you move it across multiple displays, + +114 +00:07:28,916 --> 00:07:31,351 +and display dependent filters, + +115 +00:07:31,385 --> 00:07:36,356 +with options to include or exclude +specific windows and applications. + +116 +00:07:37,357 --> 00:07:40,460 +A quick note here is that audio capture +can only be filtered + +117 +00:07:40,494 --> 00:07:41,962 +at an application level. + +118 +00:07:43,063 --> 00:07:46,600 +I'll walk you through some examples +to demonstrate what a filter is + +119 +00:07:48,468 --> 00:07:51,205 +Imagine you're only interested +in sharing a keynote window. + +120 +00:07:53,473 --> 00:07:56,510 +You would choose +a display independent window filter + +121 +00:07:56,543 --> 00:07:59,379 +that will capture the window +as it moves across displays. + +122 +00:08:00,614 --> 00:08:04,651 +Even if you wanted to share +all of the content on a display, + +123 +00:08:04,685 --> 00:08:07,487 +there may be certain content +you'd like to exclude. + +124 +00:08:07,521 --> 00:08:11,058 +For example, you'll want to avoid +the hall of mirrors effect + +125 +00:08:11,091 --> 00:08:13,727 +by excluding your own capture application. + +126 +00:08:16,330 --> 00:08:21,802 +There may also be sensitive information +in a particular window or application, + +127 +00:08:21,835 --> 00:08:24,738 +and you'd want to exclude that +from the capture as well. + +128 +00:08:24,771 --> 00:08:28,609 +All these scenarios will be handled +by SCContentFilter, + +129 +00:08:28,642 --> 00:08:31,111 +so let's jump into the code +and see how to do this. + +130 +00:08:34,214 --> 00:08:36,617 +Here is the code snippet +I showed previously. + +131 +00:08:38,051 --> 00:08:40,587 +After the shareable content is queried, + +132 +00:08:40,621 --> 00:08:44,191 +the code looks for the application +with the same bundleIdentifier + +133 +00:08:44,224 --> 00:08:45,759 +as the capture sample app. + +134 +00:08:46,827 --> 00:08:51,498 +Then, a display dependent content filter +excludes the app from the stream. + +135 +00:08:54,535 --> 00:08:58,472 +In addition to content filters, +ScreenCaptureKit provides quality + +136 +00:08:58,505 --> 00:09:01,475 +and performance controls +that can be adjusted per stream. + +137 +00:09:02,943 --> 00:09:06,213 +These controls can be set +in SCStreamConfiguration. + +138 +00:09:08,182 --> 00:09:13,086 +Some of the video controls include +output resolution, frame rate, + +139 +00:09:13,120 --> 00:09:14,988 +and whether or not +to show the mouse cursor. + +140 +00:09:16,723 --> 00:09:21,161 +On the audio side, you can enable audio, +change the sample rate, + +141 +00:09:21,195 --> 00:09:22,863 +and adjust the channel count. + +142 +00:09:23,997 --> 00:09:28,001 +I'll take you through some scenarios where +these parameters might come into play. + +143 +00:09:29,970 --> 00:09:34,141 +When sharing low-motion screen content +where text clarity is important, + +144 +00:09:34,174 --> 00:09:37,211 +such as from notes or a spreadsheet, + +145 +00:09:37,244 --> 00:09:41,682 +set output resolution of the capture +to 4k, at 10 frames per second. + +146 +00:09:43,350 --> 00:09:45,752 +And because the content doesn't have +any audio, + +147 +00:09:45,786 --> 00:09:47,454 +you can leave audio disabled. + +148 +00:09:48,522 --> 00:09:50,791 +But in the case of high motion content, + +149 +00:09:50,824 --> 00:09:53,827 +such as sharing a video +of a recent vacation, + +150 +00:09:53,861 --> 00:09:57,264 +you should prioritize +frame rate over resolution + +151 +00:09:57,297 --> 00:10:00,033 +by lowering the output resolution +to 1080p + +152 +00:10:00,067 --> 00:10:02,769 +and increasing +the frames per second to 60. + +153 +00:10:04,471 --> 00:10:07,107 +And since cursor movement +could be distracting, + +154 +00:10:07,140 --> 00:10:08,742 +you may want to hide the cursor. + +155 +00:10:10,344 --> 00:10:14,815 +You can also have audio capture enabled +for a more immersive experience. + +156 +00:10:16,383 --> 00:10:19,353 +All of these controls can be set +through the different properties + +157 +00:10:19,386 --> 00:10:20,988 +in SCStreamConfiguration. + +158 +00:10:24,424 --> 00:10:27,961 +Here's one possible configuration +for sharing high motion content. + +159 +00:10:29,563 --> 00:10:34,034 +In this code sample, the output resolution +of the capture is set to 1080p. + +160 +00:10:34,935 --> 00:10:39,973 +Then, the minimum frame interval +is set to 1/60 in order to capture + +161 +00:10:40,007 --> 00:10:41,441 +at 60 frames per second. + +162 +00:10:42,643 --> 00:10:45,979 +And finally, the stream configuration +will hide the cursor. + +163 +00:10:47,681 --> 00:10:52,719 +On the audio side, first enable audio +by setting capturesAudio to true, + +164 +00:10:52,753 --> 00:10:58,125 +then, set the sample rate to 48kHz +and the channel count to 2. + +165 +00:11:00,027 --> 00:11:04,164 +With an SCContentFilter +and an SCStreamConfiguration, + +166 +00:11:04,198 --> 00:11:06,867 +you have the information you need +to set up screen capture + +167 +00:11:06,900 --> 00:11:08,468 +to your application's needs. + +168 +00:11:09,536 --> 00:11:12,339 +Together, you can now create an SCStream. + +169 +00:11:15,342 --> 00:11:16,977 +Let's go back to the overview. + +170 +00:11:18,212 --> 00:11:20,214 +You will need to initialize the stream + +171 +00:11:20,247 --> 00:11:22,583 +with your desired filter +and configuration. + +172 +00:11:23,784 --> 00:11:26,053 +And you can also pass in +an optional delegate + +173 +00:11:26,086 --> 00:11:27,688 +in order to handle errors. + +174 +00:11:29,022 --> 00:11:33,427 +Once set up, you can call start capture, +and ScreenCaptureKit will provide + +175 +00:11:33,460 --> 00:11:37,064 +the SCStream with samples +when they are available. + +176 +00:11:38,365 --> 00:11:43,237 +With a filter and configuration created, +starting a stream in code is easy. + +177 +00:11:43,270 --> 00:11:44,271 +Let me show you. + +178 +00:11:47,307 --> 00:11:50,410 +Once again, with the filter +and configuration you want, + +179 +00:11:50,444 --> 00:11:52,980 +you can initialize an SCStream object. + +180 +00:11:54,648 --> 00:11:59,119 +In the capture sample project, self +is passed as the error handling delegate. + +181 +00:12:01,054 --> 00:12:05,125 +With an SCStream created, +you can now call startCapture. + +182 +00:12:06,894 --> 00:12:09,396 +Once you've initialized +and started a stream, + +183 +00:12:09,429 --> 00:12:12,599 +the next step is to get media samples +to your application. + +184 +00:12:16,136 --> 00:12:19,239 +Audio and video samples are +sent to your application + +185 +00:12:19,273 --> 00:12:21,441 +in the form of CMSampleBuffers. + +186 +00:12:22,910 --> 00:12:26,146 +In order to get those media samples +from your stream, + +187 +00:12:26,180 --> 00:12:27,648 +you will need to add an object + +188 +00:12:27,681 --> 00:12:31,385 +that implements the +SCStreamOutput protocol to your stream. + +189 +00:12:32,553 --> 00:12:36,890 +When you add your stream output, +you may also specify a handler queue. + +190 +00:12:38,825 --> 00:12:41,728 +This may be useful if you want +your sample to be delivered + +191 +00:12:41,762 --> 00:12:45,265 +in a particular queue +without needing an extra dispatch. + +192 +00:12:47,434 --> 00:12:51,038 +If you don't specify a queue, +a default queue will be used. + +193 +00:12:54,775 --> 00:12:59,746 +With a stream started and an output added, +ScreenCaptureKit will provide a callback + +194 +00:12:59,780 --> 00:13:01,415 +when a new sample is available. + +195 +00:13:02,683 --> 00:13:06,119 +Now, I'll show you how +to get media samples in code. + +196 +00:13:09,089 --> 00:13:12,793 +Here's an implementation +of the SCStreamOutputProtocol + +197 +00:13:12,826 --> 00:13:15,729 +which will be called +when new media samples are available. + +198 +00:13:17,297 --> 00:13:21,068 +ScreenCaptureKit delivers +these samples as CMSampleBuffers + +199 +00:13:21,101 --> 00:13:23,837 +and provides the stream and sample type. + +200 +00:13:25,873 --> 00:13:31,078 +After implementing sample buffer handlers, +you simply need to add your streamOutput. + +201 +00:13:32,713 --> 00:13:35,816 +And with that, media samples +from the stream, + +202 +00:13:35,849 --> 00:13:39,419 +with the content you want, +in the format you want, + +203 +00:13:39,453 --> 00:13:41,355 +will be delivered to your application. + +204 +00:13:44,157 --> 00:13:48,128 +ScreenCaptureKit delivers samples +in the form of a CMSampleBuffers, + +205 +00:13:48,161 --> 00:13:50,564 +so let's talk a little bit +about how to use them. + +206 +00:13:53,267 --> 00:13:57,304 +On the video side, +the CMSampleBuffer is IOSurface backed. + +207 +00:13:58,805 --> 00:14:00,974 +ScreenCaptureKit +also provides attachments + +208 +00:14:01,008 --> 00:14:04,678 +to the CMSampleBuffer +in SCStreamFrameInfo. + +209 +00:14:06,980 --> 00:14:10,450 +This attachment provides information +about the video sample you're receiving. + +210 +00:14:12,219 --> 00:14:15,389 +Check frame status +for the current state of the stream. + +211 +00:14:15,422 --> 00:14:19,626 +A complete frame status indicates +that there is a new video frame. + +212 +00:14:19,660 --> 00:14:22,896 +An idle frame status +means the video sample hasn't changed, + +213 +00:14:22,930 --> 00:14:25,065 +so there's no new IOSurface. + +214 +00:14:26,233 --> 00:14:30,637 +Otherwise, the sample provided +is like any CMSampleBuffer, + +215 +00:14:30,671 --> 00:14:33,941 +so you can use existing +CMSampleBuffer utilities. + +216 +00:14:35,209 --> 00:14:39,079 +ScreenCaptureKit includes APIs +to help you get filtered screen + +217 +00:14:39,112 --> 00:14:40,647 +audio and video content. + +218 +00:14:41,481 --> 00:14:43,584 +On top of that, +the framework provides + +219 +00:14:43,617 --> 00:14:47,087 +many different developer controls +to suit your application's needs. + +220 +00:14:48,455 --> 00:14:50,924 +I also covered some basics +to get you started + +221 +00:14:50,958 --> 00:14:53,894 +with the different screen capture +experiences you will create. + +222 +00:14:56,463 --> 00:14:59,833 +With the release of ScreenCaptureKit, +older capture frameworks + +223 +00:14:59,867 --> 00:15:04,571 +CGDisplayStream and CGWindowList +will be deprecated in the future. + +224 +00:15:06,240 --> 00:15:08,075 +I hope you're as excited as I am + +225 +00:15:08,108 --> 00:15:10,143 +with this introduction +of ScreenCaptureKit! + +226 +00:15:11,845 --> 00:15:14,281 +When you're ready to look +at more advanced topics, + +227 +00:15:14,314 --> 00:15:17,684 +please hop over to "Take ScreenCaptureKit +to the next level." + +228 +00:15:18,785 --> 00:15:20,420 +Thanks for watching! + +229 +00:15:20,454 --> 00:15:22,523 +[spacey music] + diff --git a/eng/2022 Session 10157 What's new in SF Symbols 4 en.srt b/eng/2022 Session 10157 What's new in SF Symbols 4 en.srt new file mode 100644 index 0000000..087eb58 --- /dev/null +++ b/eng/2022 Session 10157 What's new in SF Symbols 4 en.srt @@ -0,0 +1,1396 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,943 --> 00:00:11,812 +Hi, my name is Thalia, + +3 +00:00:11,845 --> 00:00:15,749 +and today we will learn what's new +in SF Symbols. + +4 +00:00:15,782 --> 00:00:20,120 +A symbol is one of the most effective +single pieces of graphic communication. + +5 +00:00:20,153 --> 00:00:23,390 +If you ever want to represent a feeling, +an object, + +6 +00:00:23,423 --> 00:00:27,694 +an action, or a concept, +symbols are an excellent way to do so. + +7 +00:00:27,728 --> 00:00:30,564 +Symbols are used frequently +and consistently, + +8 +00:00:30,597 --> 00:00:32,633 +becoming second nature to us. + +9 +00:00:32,666 --> 00:00:35,435 +Without relying on them, +we will find it very difficult + +10 +00:00:35,469 --> 00:00:37,171 +to navigate our surroundings. + +11 +00:00:37,204 --> 00:00:41,441 +As a result, they become +an essential part of interface design, + +12 +00:00:41,475 --> 00:00:45,846 +as symbols are an excellent way +to aid in communication. + +13 +00:00:45,879 --> 00:00:50,684 +A symbol can also bring many benefits +by being a means of interaction, + +14 +00:00:50,717 --> 00:00:53,854 +being space-efficient, + +15 +00:00:53,887 --> 00:00:56,590 +enhancing the aesthetic appeal, + +16 +00:00:56,623 --> 00:01:00,294 +and engaging us by being user-friendly. + +17 +00:01:00,327 --> 00:01:04,965 +What is so great about a symbol +is that it can transcend many languages. + +18 +00:01:04,998 --> 00:01:06,733 +They can be universal. + +19 +00:01:06,767 --> 00:01:11,505 +As a result, symbols can unite people +in communicating shared ideas, + +20 +00:01:11,538 --> 00:01:16,777 +helping engage with others on a deeper +level than is possible with just words. + +21 +00:01:16,810 --> 00:01:20,681 +At Apple, we care deeply +about making the user interface + +22 +00:01:20,714 --> 00:01:22,816 +and the overall experience better. + +23 +00:01:22,850 --> 00:01:25,118 +That's why we created SF Symbols, + +24 +00:01:25,152 --> 00:01:27,721 +an extensive library of iconography + +25 +00:01:27,754 --> 00:01:32,292 +designed to integrate seamlessly +with San Francisco, the system font, + +26 +00:01:32,326 --> 00:01:35,796 +providing a powerful +and flexible design resource + +27 +00:01:35,829 --> 00:01:39,867 +for creating experiences +on all Apple platforms. + +28 +00:01:39,900 --> 00:01:42,836 +SF Symbols is designed +with typography in mind. + +29 +00:01:42,870 --> 00:01:47,307 +It has awesome features +like different weights, scales, + +30 +00:01:47,341 --> 00:01:51,879 +outlined and filled variants, +encapsulated shapes, and alignments. + +31 +00:01:51,912 --> 00:01:55,682 +To learn more about these features +and when it's best to use them, + +32 +00:01:55,716 --> 00:02:00,687 +check out last year's video +from WWDC to get you up to speed. + +33 +00:02:00,721 --> 00:02:04,091 +Today, we will take a look +at the new repertoire + +34 +00:02:04,124 --> 00:02:07,261 +and the new categories in "New symbols." + +35 +00:02:07,294 --> 00:02:11,732 +In "Rendering modes," we will review +the benefits of adding colors to symbols, + +36 +00:02:11,765 --> 00:02:17,471 +and we have a new configuration to +help make the symbol's behavior automatic. + +37 +00:02:17,504 --> 00:02:20,874 +We have a new exciting feature +called "Variable Color." + +38 +00:02:20,908 --> 00:02:25,579 +Here we will explore the use of color +to make a symbol more dynamic. + +39 +00:02:25,612 --> 00:02:30,150 +And finally, we have a more efficient way +of annotating symbols. + +40 +00:02:30,184 --> 00:02:33,687 +We will learn more about it +in "Unified annotations." + +41 +00:02:33,720 --> 00:02:35,489 +So let's get started. + +42 +00:02:35,522 --> 00:02:38,659 +The SF Symbols library +keeps growing each year, + +43 +00:02:38,692 --> 00:02:42,129 +with newer categories +and symbols to choose from. + +44 +00:02:42,162 --> 00:02:44,331 +There are some great additions for Home, + +45 +00:02:44,364 --> 00:02:48,869 +including lights, blinds, +windows, and doors. + +46 +00:02:48,902 --> 00:02:52,806 +We even have light switches +and power outlets. + +47 +00:02:52,840 --> 00:02:55,609 +There are new furniture and appliances. + +48 +00:02:55,642 --> 00:02:57,978 +And new health symbols. + +49 +00:02:58,011 --> 00:03:02,783 +And this year, our fitness figures +are available for you to use. + +50 +00:03:02,816 --> 00:03:06,153 +We have expanded +the library's currency symbols. + +51 +00:03:06,186 --> 00:03:09,756 +And we have many new objects +to choose from. + +52 +00:03:09,790 --> 00:03:13,126 +And, of course, +we keep expanding our localized symbols, + +53 +00:03:13,160 --> 00:03:18,699 +with new ones covering different scripts +and right-to-left writing systems. + +54 +00:03:18,732 --> 00:03:21,835 +There are over 700 new symbols +to choose from, + +55 +00:03:21,869 --> 00:03:27,040 +making SF Symbols a library +of more than 4,000 unique symbols. + +56 +00:03:27,074 --> 00:03:28,876 +That is amazing. + +57 +00:03:28,909 --> 00:03:32,513 +With all these new additions, +we wanted to help you navigate + +58 +00:03:32,546 --> 00:03:34,982 +through all the symbols +in the SF Symbols app, + +59 +00:03:35,015 --> 00:03:39,486 +so we've added five new categories +that we think will be super helpful: + +60 +00:03:39,520 --> 00:03:41,989 +Camera & Photos, + +61 +00:03:42,022 --> 00:03:43,957 +Accessibility, + +62 +00:03:43,991 --> 00:03:46,026 +Privacy & Security, + +63 +00:03:46,059 --> 00:03:49,496 +Home, and Fitness. + +64 +00:03:49,530 --> 00:03:53,367 +And remember that in the app, +you can always create your own collection + +65 +00:03:53,400 --> 00:03:57,638 +with a selection of symbols +that best suits your needs. + +66 +00:03:57,671 --> 00:04:02,075 +Now, let's have a quick reminder +about the different rendering modes. + +67 +00:04:02,109 --> 00:04:04,778 +As you may know, in SF Symbols, + +68 +00:04:04,811 --> 00:04:07,047 +there are four rendering modes +to choose from, + +69 +00:04:07,080 --> 00:04:12,519 +each one providing a greater control +over how color is applied to a symbol. + +70 +00:04:12,553 --> 00:04:16,323 +Monochrome is the most neutral +of all the rendering modes. + +71 +00:04:16,356 --> 00:04:18,825 +It gives a uniform and consistent look, + +72 +00:04:18,859 --> 00:04:21,428 +and it is the rendering mode +that most closely reflects + +73 +00:04:21,461 --> 00:04:24,965 +the typographic nature of SF Symbols. + +74 +00:04:24,998 --> 00:04:28,936 +Hierarchical is the rendering mode +that provides a subtle emphasis + +75 +00:04:28,969 --> 00:04:32,606 +while having a single color hue +drive the overall aesthetic. + +76 +00:04:32,639 --> 00:04:37,177 +We can apply depth by highlighting +the most important shape of a symbol + +77 +00:04:37,211 --> 00:04:40,981 +or differentiate the foreground +and background elements. + +78 +00:04:41,014 --> 00:04:44,618 +That way, we create a visual hierarchy +by emphasizing + +79 +00:04:44,651 --> 00:04:48,589 +the essential part or parts of a symbol. + +80 +00:04:48,622 --> 00:04:52,893 +Palette uses two or more contrasting +colors to give elements of a symbol + +81 +00:04:52,926 --> 00:04:54,895 +more prominence and versatility, + +82 +00:04:54,928 --> 00:04:57,598 +allowing for the symbols to be customized + +83 +00:04:57,631 --> 00:05:01,468 +to integrate with the color palette +of the environment. + +84 +00:05:01,502 --> 00:05:05,172 +Palette helps the symbols +have a contrasting and unique look + +85 +00:05:05,205 --> 00:05:08,809 +without sacrificing the overall aesthetic. + +86 +00:05:08,842 --> 00:05:12,779 +And Multicolor is the rendering mode +that represents the intrinsic + +87 +00:05:12,813 --> 00:05:15,249 +or native color of a symbol. + +88 +00:05:15,282 --> 00:05:19,753 +This rendering mode uses a range of colors +that can be applied to a symbol + +89 +00:05:19,786 --> 00:05:23,390 +to describe the appearance of an object +in the physical world, + +90 +00:05:23,423 --> 00:05:25,692 +or it can use colors +to emphasize the meaning + +91 +00:05:25,726 --> 00:05:28,061 +that the symbol is trying to convey. + +92 +00:05:28,095 --> 00:05:32,432 +You can use Multicolor when +the symbols are very prominent in the UI, + +93 +00:05:32,466 --> 00:05:38,038 +as it will help create a color narrative +that will relate to the symbol forms. + +94 +00:05:38,071 --> 00:05:42,376 +Until now, if you didn't explicitly +specify a rendering mode, + +95 +00:05:42,409 --> 00:05:44,478 +you would get Monochrome by default. + +96 +00:05:44,511 --> 00:05:47,514 +But this year, +we've made it easier to display symbols + +97 +00:05:47,548 --> 00:05:52,419 +in a rendering mode that best highlights +each symbol's unique characteristics. + +98 +00:05:52,452 --> 00:05:54,888 +Symbols now feature +a preferred rendering mode, + +99 +00:05:54,922 --> 00:05:57,624 +which can vary between symbols. + +100 +00:05:57,658 --> 00:06:01,495 +And we call this behavior +Automatic Rendering. + +101 +00:06:01,528 --> 00:06:05,432 +When selected, it provides +the preferred rendering mode configuration + +102 +00:06:05,465 --> 00:06:09,903 +for each symbol +without having to specify it manually. + +103 +00:06:09,937 --> 00:06:12,639 +For example, when Automatic is selected, + +104 +00:06:12,673 --> 00:06:16,143 +the camera filters symbol +will opt into Hierarchical, + +105 +00:06:16,176 --> 00:06:20,514 +as it is the rendering mode that conveys +a more precise visual representation + +106 +00:06:20,547 --> 00:06:23,917 +by highlighting the opacities +that reference the translucency + +107 +00:06:23,951 --> 00:06:27,654 +of the physical camera lenses and filters. + +108 +00:06:27,688 --> 00:06:29,590 +Here's another example. + +109 +00:06:29,623 --> 00:06:31,692 +When Automatic is selected, + +110 +00:06:31,725 --> 00:06:36,263 +the SharePlay symbol participates +in the Hierarchical rendering mode. + +111 +00:06:36,296 --> 00:06:41,068 +This behavior lets the person's shape +stand prominently in the foreground + +112 +00:06:41,101 --> 00:06:44,838 +while the waves play a secondary role +in the background. + +113 +00:06:44,872 --> 00:06:48,108 +Again, +this emphasizes the symbol's concept, + +114 +00:06:48,141 --> 00:06:50,944 +as the SharePlay feature +is mainly a way to share + +115 +00:06:50,978 --> 00:06:53,981 +and connect with friends and family. + +116 +00:06:54,014 --> 00:06:57,918 +In most cases, +Automatic will be the best way to go. + +117 +00:06:57,951 --> 00:07:00,587 +But you always have to be aware +of the context. + +118 +00:07:00,621 --> 00:07:05,526 +For example, the AirPods Pro symbol +will render as Hierarchical + +119 +00:07:05,559 --> 00:07:07,561 +when Automatic is selected. + +120 +00:07:07,594 --> 00:07:10,731 +But in this context, +the symbol is very small + +121 +00:07:10,764 --> 00:07:15,502 +and has low contrast +when presented on this background. + +122 +00:07:15,536 --> 00:07:19,606 +Remember that rendering modes +can still be explicitly specified + +123 +00:07:19,640 --> 00:07:24,678 +for a uniform appearance +across symbols in a particular context. + +124 +00:07:24,711 --> 00:07:28,215 +So in this case, +Monochrome will be the best choice + +125 +00:07:28,248 --> 00:07:33,053 +as it is more legible +and has fewer details at small sizes. + +126 +00:07:33,086 --> 00:07:38,959 +So always make sure to specify the most +suitable rendering mode configuration. + +127 +00:07:38,992 --> 00:07:42,529 +The different rendering modes +apply color to symbols + +128 +00:07:42,563 --> 00:07:46,900 +to present visual solutions +for a wide range of circumstances. + +129 +00:07:46,934 --> 00:07:51,438 +Color is a powerful tool, +and we can explore it even further. + +130 +00:07:51,471 --> 00:07:54,141 +Some symbols are more dynamic in nature. + +131 +00:07:54,174 --> 00:07:56,910 +If we analyze their visual representation, + +132 +00:07:56,944 --> 00:07:59,780 +we can notice two main characteristics: + +133 +00:07:59,813 --> 00:08:05,385 +first, their paths or shapes convey +varying levels of strength, + +134 +00:08:05,419 --> 00:08:08,422 +and second, +they rely on color to communicate + +135 +00:08:08,455 --> 00:08:11,625 +their status changing over time. + +136 +00:08:11,658 --> 00:08:14,361 +This year, +we're expanding the use of color + +137 +00:08:14,394 --> 00:08:18,432 +by introducing a new feature +called Variable Color. + +138 +00:08:18,465 --> 00:08:22,202 +We've arranged the symbol's vector paths +into layers + +139 +00:08:22,236 --> 00:08:25,606 +and organized those layers +in sequential order, + +140 +00:08:25,639 --> 00:08:29,576 +creating a new method of +distributing color through these layers. + +141 +00:08:29,610 --> 00:08:32,579 +This allows us +to convey different levels of strength + +142 +00:08:32,613 --> 00:08:34,882 +or communicate a sequence over time, + +143 +00:08:34,915 --> 00:08:38,952 +which is dictated by the nature +of the design of the symbol. + +144 +00:08:38,986 --> 00:08:42,256 +One important thing to know is that, +in Variable Color, + +145 +00:08:42,289 --> 00:08:46,159 +some symbols have all paths +participating in the sequence, + +146 +00:08:46,193 --> 00:08:51,565 +but for other symbols, +only some of the paths may opt-in. + +147 +00:08:51,598 --> 00:08:54,601 +Let's look at a few examples. + +148 +00:08:54,635 --> 00:08:58,772 +With Variable Color, +we want the paths representing the iPhone + +149 +00:08:58,805 --> 00:09:01,408 +to opt-out of the variable sequence, + +150 +00:09:01,441 --> 00:09:05,445 +and we want to highlight the paths +representing the radio waves. + +151 +00:09:05,479 --> 00:09:09,917 +This will help differentiate the stages +that describe the levels of strength + +152 +00:09:09,950 --> 00:09:12,920 +of the radio signal of the phone. + +153 +00:09:12,953 --> 00:09:16,924 +It's important to know that we define +how we want to group the paths. + +154 +00:09:16,957 --> 00:09:19,359 +For example, +it wouldn't make much sense + +155 +00:09:19,393 --> 00:09:23,030 +to highlight the waves +from left to right in this case. + +156 +00:09:23,063 --> 00:09:26,767 +So instead, we can group the paths +following the sequence + +157 +00:09:26,800 --> 00:09:30,170 +that best conveys +the radio signal strength: + +158 +00:09:30,204 --> 00:09:33,440 +we have the two smaller waves +in one layer, + +159 +00:09:33,473 --> 00:09:36,743 +and we have the two bigger waves +in a different layer. + +160 +00:09:36,777 --> 00:09:41,915 +And as mentioned, the phone does +not participate in the variable sequence. + +161 +00:09:41,949 --> 00:09:46,486 +This time, let's look at a symbol +that we are very familiar with. + +162 +00:09:46,520 --> 00:09:50,691 +In most cases, this symbol is paired +with a built-in slider + +163 +00:09:50,724 --> 00:09:54,695 +that syncs with the states +represented in the symbol. + +164 +00:09:54,728 --> 00:09:57,631 +The waves highlight their paths +following a sequence + +165 +00:09:57,664 --> 00:10:00,434 +dictated by the user +controlling the slider, + +166 +00:10:00,467 --> 00:10:04,505 +increasing or decreasing the volume level. + +167 +00:10:04,538 --> 00:10:06,340 +Like in the iPhone example, + +168 +00:10:06,373 --> 00:10:09,710 +we have a path that doesn't participate +in the sequence, + +169 +00:10:09,743 --> 00:10:12,546 +which is the shape illustrating +the speaker, + +170 +00:10:12,579 --> 00:10:15,883 +and we have paths +that do participate in the sequence, + +171 +00:10:15,916 --> 00:10:19,820 +which are the three waves +that define the volume's strength: + +172 +00:10:19,853 --> 00:10:24,191 +low volume, mid-volume, +and high volume. + +173 +00:10:24,224 --> 00:10:27,027 +These paths are organized into layers, + +174 +00:10:27,060 --> 00:10:32,065 +and the selected layers opt-in +to the Variable Color feature. + +175 +00:10:32,099 --> 00:10:35,936 +We represent their strength +with percentage values: + +176 +00:10:35,969 --> 00:10:38,105 +0% is entirely off, + +177 +00:10:38,138 --> 00:10:42,376 +anything above 0% +will highlight part of the symbol, + +178 +00:10:42,409 --> 00:10:48,582 +and the whole symbol will be fully +highlighted as the value approaches 100%. + +179 +00:10:48,615 --> 00:10:51,385 +One important thing to know +is that Variable Color + +180 +00:10:51,418 --> 00:10:53,654 +is not meant to create depth; + +181 +00:10:53,687 --> 00:10:58,125 +instead, it's meant to highlight +a sequence of steps or stages + +182 +00:10:58,158 --> 00:11:00,561 +that the symbol can represent. + +183 +00:11:00,594 --> 00:11:03,497 +For example, +let's imagine I need a symbol + +184 +00:11:03,530 --> 00:11:06,366 +to represent people's capacity +inside a room. + +185 +00:11:06,400 --> 00:11:08,969 +This symbol looks great for that. + +186 +00:11:09,002 --> 00:11:11,638 +Let's look at it in more detail. + +187 +00:11:11,672 --> 00:11:15,275 +We don't want to highlight +just one part of the symbol. + +188 +00:11:15,309 --> 00:11:20,747 +Instead, we need to think of the symbol +as a sequence or a range. + +189 +00:11:20,781 --> 00:11:23,283 +My goal is to cover different states: + +190 +00:11:23,317 --> 00:11:25,352 +the room is empty, + +191 +00:11:25,385 --> 00:11:28,222 +the room has few people in it, + +192 +00:11:28,255 --> 00:11:32,659 +the room is half-full, +and the room is at full capacity. + +193 +00:11:32,693 --> 00:11:36,129 +So now, when I opt into Variable Color, + +194 +00:11:36,163 --> 00:11:38,699 +I can easily see +the graphic representation + +195 +00:11:38,732 --> 00:11:41,602 +of the different states +I'm trying to convey. + +196 +00:11:41,635 --> 00:11:45,772 +And there are no limits to how +many paths can opt into the feature. + +197 +00:11:45,806 --> 00:11:48,008 +There can be just one or many. + +198 +00:11:48,041 --> 00:11:52,012 +You decide what is the design strategy +that best suits your needs. + +199 +00:11:52,045 --> 00:11:56,617 +If you want to represent the strength +level of shapes that follow a sequence, + +200 +00:11:56,650 --> 00:11:59,920 +like waves, rays, ellipsis, and layers, + +201 +00:11:59,953 --> 00:12:02,890 +you can do so with Variable Color. + +202 +00:12:02,923 --> 00:12:07,561 +Variable Color is opacity based +and it's available in all rendering modes. + +203 +00:12:09,029 --> 00:12:14,168 +This year, we have made annotations for +custom symbols faster and easier to use + +204 +00:12:14,201 --> 00:12:18,705 +by creating a unified layer +structure across all rendering modes. + +205 +00:12:18,739 --> 00:12:21,775 +I love baking, +and I was thinking of designing an app + +206 +00:12:21,808 --> 00:12:23,977 +with cupcake-only recipes. + +207 +00:12:24,011 --> 00:12:26,713 +I wanted to create my own custom symbols, + +208 +00:12:26,747 --> 00:12:29,683 +so I've designed a set +that will cover my needs. + +209 +00:12:29,716 --> 00:12:33,287 +This will be an excellent opportunity +to show you how to annotate symbols + +210 +00:12:33,320 --> 00:12:35,989 +with the unified layer structure approach + +211 +00:12:36,023 --> 00:12:40,194 +and how to annotate symbols +that participate in Variable Color. + +212 +00:12:40,227 --> 00:12:43,197 +Let's use these two as an example. + +213 +00:12:43,230 --> 00:12:47,134 +Before we start annotating, +there are a few things to consider: + +214 +00:12:47,167 --> 00:12:52,472 +we need to keep the hierarchy in mind +and make sure we outline the z-order. + +215 +00:12:52,506 --> 00:12:58,545 +The z-order refers to the order +of the paths in a symbol along the z-axis. + +216 +00:12:58,579 --> 00:13:02,583 +There are also two new concepts +you need to be aware of: + +217 +00:13:02,616 --> 00:13:05,085 +Draw and Erase. + +218 +00:13:05,118 --> 00:13:08,989 +These are used to help define the way +a layer renders. + +219 +00:13:09,022 --> 00:13:12,559 +For example, +here we have the paths of a symbol + +220 +00:13:12,593 --> 00:13:16,163 +representing a square +overlapping a circle. + +221 +00:13:16,196 --> 00:13:19,066 +When selecting the layers +containing the square, + +222 +00:13:19,099 --> 00:13:21,068 +if we choose the Draw option, + +223 +00:13:21,101 --> 00:13:24,638 +the layer will draw the paths +contained in that layer. + +224 +00:13:24,671 --> 00:13:26,974 +If we choose the Erase option, + +225 +00:13:27,007 --> 00:13:30,310 +the layer will erase the path +containing the layer, + +226 +00:13:30,344 --> 00:13:33,347 +affecting how the symbol is rendered. + +227 +00:13:33,380 --> 00:13:36,149 +Now, let's start annotating the cupcake. + +228 +00:13:36,183 --> 00:13:39,953 +As a first step, we need to organize +the paths in layers + +229 +00:13:39,987 --> 00:13:42,723 +to create the desired hierarchy. + +230 +00:13:42,756 --> 00:13:47,361 +If I analyze it closely, +I can see four main shapes: + +231 +00:13:47,394 --> 00:13:50,564 +the frosting, the cupcake base, + +232 +00:13:50,597 --> 00:13:53,033 +the badge, and the plus. + +233 +00:13:53,066 --> 00:13:56,403 +You can add as many paths as needed +to a layer. + +234 +00:13:56,436 --> 00:13:59,773 +So in this case, +the frosting will be defined + +235 +00:13:59,806 --> 00:14:02,476 +by a layer with three different paths, + +236 +00:14:02,509 --> 00:14:07,447 +and the rest of the layers will be defined +by just one path each. + +237 +00:14:07,481 --> 00:14:12,085 +Organizing the shapes in this way +allows us to have more flexibility + +238 +00:14:12,119 --> 00:14:15,722 +when annotating one structure +for all rendering modes. + +239 +00:14:15,756 --> 00:14:18,892 +Now we have all the information +we need in one place + +240 +00:14:18,926 --> 00:14:21,428 +to customize the symbol as needed. + +241 +00:14:22,396 --> 00:14:26,166 +Now we can start annotating +all rendering modes. + +242 +00:14:26,200 --> 00:14:28,735 +Let's look at Multicolor first. + +243 +00:14:28,769 --> 00:14:31,038 +I already have the shapes set up, + +244 +00:14:31,071 --> 00:14:33,774 +so I just need to choose the right colors. + +245 +00:14:33,807 --> 00:14:36,210 +Red Velvet is my favorite flavor, + +246 +00:14:36,243 --> 00:14:41,215 +so I'll choose white for the frosting +and red for the cupcake base. + +247 +00:14:41,248 --> 00:14:44,818 +Now I will follow the same logic +that SF Symbols defines + +248 +00:14:44,852 --> 00:14:47,554 +for the plus badges in Multicolor: + +249 +00:14:47,588 --> 00:14:50,824 +green for the badge +and white for the plus. + +250 +00:14:50,858 --> 00:14:54,294 +Okay, this is looking great so far. + +251 +00:14:54,328 --> 00:14:56,296 +Now, let's focus on the badge. + +252 +00:14:56,330 --> 00:15:00,834 +This is where the unified annotation +approach is more apparent. + +253 +00:15:00,868 --> 00:15:03,203 +We already defined Multicolor. + +254 +00:15:03,237 --> 00:15:06,406 +Now let's look at Hierarchical +and Palette. + +255 +00:15:06,440 --> 00:15:09,843 +Because of the use of hierarchy +in this rendering modes, + +256 +00:15:09,877 --> 00:15:12,379 +I would expect the badge to be Primary, + +257 +00:15:12,412 --> 00:15:17,217 +which will render it white +on a black background, like this one. + +258 +00:15:17,251 --> 00:15:21,355 +Now I need the plus shape +to erase part of the badge. + +259 +00:15:21,388 --> 00:15:24,224 +This is where the Erase selection +is helpful. + +260 +00:15:24,258 --> 00:15:27,394 +I will be able to see the badge +rendered as desired + +261 +00:15:27,427 --> 00:15:32,566 +by erasing a part of a shape +when the layers overlap. + +262 +00:15:32,599 --> 00:15:36,170 +Finally, I just need +the Monochrome annotation. + +263 +00:15:36,203 --> 00:15:39,406 +Because there is no added complexity +in this rendering mode, + +264 +00:15:39,439 --> 00:15:41,308 +I will follow the same logic, + +265 +00:15:41,341 --> 00:15:46,213 +making the plus shape +erase part of the badge shape. + +266 +00:15:46,246 --> 00:15:48,115 +I'm almost done now. + +267 +00:15:48,148 --> 00:15:50,918 +I just need a few extra details. + +268 +00:15:50,951 --> 00:15:53,987 +For Hierarchical and Palette, +I just need to annotate + +269 +00:15:54,021 --> 00:15:57,191 +the rest of the cupcake shape +as Secondary. + +270 +00:15:57,224 --> 00:16:01,528 +For Palette, I will choose a color +to give a bit of contrast. + +271 +00:16:01,562 --> 00:16:04,364 +And for Monochrome, +I just need to make sure + +272 +00:16:04,398 --> 00:16:07,668 +I have the remaining shapes +opting into Draw. + +273 +00:16:09,503 --> 00:16:11,038 +That's all I need. + +274 +00:16:11,071 --> 00:16:14,942 +The cupcake is ready and customized +for all rendering modes. + +275 +00:16:16,777 --> 00:16:19,213 +Now let's look at the kitchen timer. + +276 +00:16:19,246 --> 00:16:23,217 +The paths represent the time passing, +and since this is a sequence, + +277 +00:16:23,250 --> 00:16:27,221 +it's a great candidate +to participate in Variable Color. + +278 +00:16:27,254 --> 00:16:30,390 +We can use the same strategy +to annotate this symbol, + +279 +00:16:30,424 --> 00:16:33,961 +but instead of grouping +the timer paths in one layer, + +280 +00:16:33,994 --> 00:16:37,764 +we need to split them +with each in its own layer. + +281 +00:16:37,798 --> 00:16:40,367 +This is because +we need to organize the shapes + +282 +00:16:40,400 --> 00:16:42,703 +to allow us to recreate the sequence + +283 +00:16:42,736 --> 00:16:47,307 +that will help us communicate +the different states of the symbol. + +284 +00:16:47,341 --> 00:16:51,745 +And remember that Variable Color +works in all rendering modes. + +285 +00:16:51,778 --> 00:16:55,015 +If you want to learn more about +the new Variable Color feature + +286 +00:16:55,048 --> 00:16:57,050 +and the SF Symbols app, + +287 +00:16:57,084 --> 00:17:02,523 +check out Paul's talk +"Adopt Variable Color in SF Symbols.” + +288 +00:17:02,556 --> 00:17:05,826 +You can find the new beta version +of the SF Symbols app, + +289 +00:17:05,859 --> 00:17:09,329 +where you can explore +the new unified annotation approach + +290 +00:17:09,363 --> 00:17:13,100 +and access the hundreds of new symbols +and fantastic new features. + +291 +00:17:13,133 --> 00:17:17,671 +Check out developer.apple.com/sf-symbols. + +292 +00:17:17,704 --> 00:17:22,209 +From Automatic Rendering behavior +to the Variable Color dynamic nature, + +293 +00:17:22,242 --> 00:17:25,646 +SF Symbols are an extremely powerful tool +to use + +294 +00:17:25,679 --> 00:17:28,682 +when implementing symbols in your UIs. + +295 +00:17:28,715 --> 00:17:32,486 +And this year, +SF Symbols is even more powerful, + +296 +00:17:32,519 --> 00:17:36,223 +with features that define +a spectrum of expression. + +297 +00:17:36,256 --> 00:17:37,891 +Thank you for joining today. + +298 +00:17:37,925 --> 00:17:41,929 +I hope you enjoyed learning +about what's new in SF Symbols. + diff --git a/eng/2022 Session 10159 Scale compute workloads across Apple GPUs en.srt b/eng/2022 Session 10159 Scale compute workloads across Apple GPUs en.srt new file mode 100644 index 0000000..1ef96f7 --- /dev/null +++ b/eng/2022 Session 10159 Scale compute workloads across Apple GPUs en.srt @@ -0,0 +1,1796 @@ +1 +00:00:00,434 --> 00:00:06,440 +[upbeat music] + +2 +00:00:09,309 --> 00:00:11,478 +- Hello and welcome. + +3 +00:00:11,512 --> 00:00:13,413 +My name is Marco Giordano, + +4 +00:00:13,447 --> 00:00:17,050 +and I'm with the GPU Software +Engineering team here at Apple. + +5 +00:00:17,084 --> 00:00:19,620 +In this session I'll talk to you about + +6 +00:00:19,653 --> 00:00:23,357 +how to scale workloads +across Apple M1 GPUs. + +7 +00:00:23,390 --> 00:00:26,727 +If you work on complex compute +workloads and want to know how to + +8 +00:00:26,760 --> 00:00:31,031 +take full advantage of Apple silicon +hardware and achieve great scaling, + +9 +00:00:31,064 --> 00:00:33,433 +this talk is the one for you. + +10 +00:00:33,467 --> 00:00:36,837 +I will start by discussing +compute scalability concepts + +11 +00:00:36,870 --> 00:00:42,276 +and how applications can naturally scale +performance across the M1 GPU family. + +12 +00:00:42,309 --> 00:00:45,846 +And then, I'll share +step-by-step "how-tos" + +13 +00:00:45,879 --> 00:00:48,782 +and talk about what tools are available + +14 +00:00:48,815 --> 00:00:52,252 +to maximize compute scaling +for your workloads. + +15 +00:00:52,286 --> 00:00:55,122 +Let's start by understanding +what scalability is + +16 +00:00:55,155 --> 00:00:57,991 +and why it is important +for your workload. + +17 +00:00:59,293 --> 00:01:02,829 +The Apple M1 GPU was designed +from the ground up to scale + +18 +00:01:02,863 --> 00:01:08,535 +and to let your workload achieve excellent +performance across the entire SOC family. + +19 +00:01:08,569 --> 00:01:13,407 +The same GPU supporting all Metal 3 +features scales from your 8-core iPad + +20 +00:01:13,440 --> 00:01:16,476 +all the way to your 64-core Mac Studio. + +21 +00:01:17,544 --> 00:01:20,214 +To take advantage +of the high level of scaling, + +22 +00:01:20,247 --> 00:01:24,551 +having an app optimized for M1 +is a great starting point. + +23 +00:01:24,585 --> 00:01:29,089 +Many prominent pro apps have already +been optimized for Apple M1 + +24 +00:01:29,122 --> 00:01:33,260 +and have been experiencing +excellent scaling across all devices. + +25 +00:01:34,962 --> 00:01:39,933 +For example, here we have +Affinity Photo and DaVinci Resolve-- + +26 +00:01:39,967 --> 00:01:43,971 +photo and video editors from +the post-production industry. + +27 +00:01:44,004 --> 00:01:47,608 +These apps are achieving great scaling. + +28 +00:01:47,641 --> 00:01:53,447 +Let's define what scalability really means +and how you can achieve "ideal" scaling. + +29 +00:01:53,480 --> 00:01:58,385 +GPU workload scalability +is the capacity to improve performance + +30 +00:01:58,418 --> 00:02:01,555 +with an increased number of GPU cores. + +31 +00:02:01,588 --> 00:02:04,658 +The chart on the right +shows application speed-up + +32 +00:02:04,691 --> 00:02:07,194 +with an increasing GPU cores count. + +33 +00:02:07,227 --> 00:02:10,964 +Linear proportion improvement +is considered ideal. + +34 +00:02:12,232 --> 00:02:16,403 +However, while working on your app, +you might notice a type of scaling + +35 +00:02:16,436 --> 00:02:20,307 +which hits a plateau and scales with +diminishing returns, + +36 +00:02:20,340 --> 00:02:24,945 +or doesn't scale at all +due to gaps in the GPU timeline. + +37 +00:02:25,579 --> 00:02:29,983 +Or you might see another type scaling +where the performance improves + +38 +00:02:30,017 --> 00:02:32,819 +but not uniformly across the stack + +39 +00:02:32,853 --> 00:02:37,157 +where the workload +is hitting some GPU limiters, + +40 +00:02:37,191 --> 00:02:42,930 +like here, between 24 to 32 +or 48 to 64 cores. + +41 +00:02:44,431 --> 00:02:48,569 +Your goal is to get as close as +possible to linear scaling, + +42 +00:02:48,602 --> 00:02:51,338 +and I will show you +the tools and techniques + +43 +00:02:51,371 --> 00:02:55,108 +to identify bottlenecks and +achieve the result you want. + +44 +00:02:56,276 --> 00:03:01,682 +In the next section I will discuss the +approaches to maximize GPU scaling. + +45 +00:03:01,715 --> 00:03:06,954 +For every workload, you should +first identify where the bottleneck is. + +46 +00:03:06,987 --> 00:03:11,225 +Workloads can be limited either by +computation or bandwidth. + +47 +00:03:11,258 --> 00:03:14,027 +During the optimization process, + +48 +00:03:14,061 --> 00:03:16,930 +you might end up +bouncing between one and the other. + +49 +00:03:16,964 --> 00:03:21,635 +If you are computational-bound, +you might try to shift some of the load + +50 +00:03:21,668 --> 00:03:26,039 +to leverage memory +to reduce computation, or vice versa. + +51 +00:03:26,073 --> 00:03:29,576 +Bottlenecks can shift when you scale up. + +52 +00:03:29,610 --> 00:03:32,379 +One good solution could be +using Apple frameworks + +53 +00:03:32,412 --> 00:03:34,982 +like MPS or MPSGraph. + +54 +00:03:35,015 --> 00:03:37,184 +if you can leverage their primitives, + +55 +00:03:37,217 --> 00:03:41,522 +we made sure every compute kernel +runs best on all the hardware. + +56 +00:03:41,555 --> 00:03:44,725 +However, you +can't replace everything with MPS, + +57 +00:03:44,758 --> 00:03:48,529 +so it is critical to profile +and understand your workload. + +58 +00:03:50,297 --> 00:03:51,164 +I will first cover three items +that can help minimize GPU gaps: + +59 +00:03:54,868 --> 00:03:58,839 +Improve your work distribution, +eliminate GPU timeline gaps, + +60 +00:03:58,872 --> 00:04:01,508 +and atomics operation considerations. + +61 +00:04:02,476 --> 00:04:06,613 +Then I will explain +how to optimize for GPU limiters + +62 +00:04:06,647 --> 00:04:10,951 +by first investigating the effect of +compute grid shapes + +63 +00:04:10,984 --> 00:04:13,387 +and memory layouts of your workload + +64 +00:04:13,420 --> 00:04:18,892 +and finally by looking +at a specific example in Blender Cycles. + +65 +00:04:18,926 --> 00:04:22,362 +Start by focusing on minimizing GPU gaps. + +66 +00:04:22,396 --> 00:04:26,967 +This kind of scaling can be the result +of the GPU not being fully utilized, + +67 +00:04:27,000 --> 00:04:30,070 +with gaps in the GPU timeline +where the hardware is idle. + +68 +00:04:32,139 --> 00:04:35,976 +Let's see if we can improve scaling by +investigating work distribution. + +69 +00:04:37,544 --> 00:04:41,715 +Small workloads +usually do not saturate the whole GPU, + +70 +00:04:41,748 --> 00:04:44,418 +and kernel synchronization has its cost, + +71 +00:04:44,451 --> 00:04:47,955 +so both can prevent proper scaling. + +72 +00:04:47,988 --> 00:04:53,093 +It is very important to understand how +the workload gets mapped to the hardware, + +73 +00:04:53,126 --> 00:04:54,928 +so let's talk about it. + +74 +00:04:55,696 --> 00:05:00,267 +A workload is dispatched in the form +of a 3D grid of threadgroups. + +75 +00:05:00,300 --> 00:05:04,137 +Threadgroups are uniformly +distributed to the GPU cores + +76 +00:05:04,171 --> 00:05:08,976 +and have access to threadgroup memory, +which is limited in size, + +77 +00:05:09,009 --> 00:05:11,945 +but very fast, local to the GPU core. + +78 +00:05:12,913 --> 00:05:16,783 +A single threadgroup is further +broken down into SIMD-groups, + +79 +00:05:16,817 --> 00:05:21,088 +which are also known as waves or warps +in other compute dialects. + +80 +00:05:21,989 --> 00:05:26,059 +Checking the "threadExecutionWidth" +on the compute pipeline state object + +81 +00:05:26,093 --> 00:05:28,161 +will return the SIMD width, + +82 +00:05:28,195 --> 00:05:31,765 +and on all Apple GPUs, +it is equal to 32. + +83 +00:05:33,033 --> 00:05:37,337 +Threadgroups can have up to +1024 threads per threadgroup + +84 +00:05:37,371 --> 00:05:41,575 +and threads can share up to +32K of threadgroup memory. + +85 +00:05:42,876 --> 00:05:45,679 +To keep the GPU busy, +there should be enough work to do + +86 +00:05:45,712 --> 00:05:47,347 +on all the GPU cores. + +87 +00:05:48,749 --> 00:05:51,618 +Here is an example of a grid to dispatch. + +88 +00:05:51,652 --> 00:05:54,555 +Threadgroups are dispatched +to GPU Clusters + +89 +00:05:54,588 --> 00:05:58,091 +and distributed among GPU cores. + +90 +00:05:59,159 --> 00:06:01,228 +If there are too few threadgroups, + +91 +00:06:01,261 --> 00:06:04,565 +the workload +won't fully saturate the machine. + +92 +00:06:04,598 --> 00:06:06,033 +Here's how to fix this. + +93 +00:06:08,101 --> 00:06:11,438 +Start by computing +how many threads the workload produces + +94 +00:06:11,471 --> 00:06:15,475 +and roughly see if the dispatch +will saturate the whole machine. + +95 +00:06:16,410 --> 00:06:22,082 +For relatively complex kernels, 1K to 2K +concurrent threads per shader core + +96 +00:06:22,115 --> 00:06:24,651 +is considered a very good occupancy, + +97 +00:06:24,685 --> 00:06:30,591 +so take 1 to 2K threads per GPU core as +a rule of thumb. + +98 +00:06:30,624 --> 00:06:35,596 +Now you can compute if you have enough +work to fully saturate the hardware. + +99 +00:06:35,629 --> 00:06:39,032 +The table here shows the lowest +recommended number of threads + +100 +00:06:39,066 --> 00:06:41,301 +to saturate different SOCs. + +101 +00:06:43,670 --> 00:06:45,739 +Another thing to consider +would be avoiding + +102 +00:06:45,772 --> 00:06:48,909 +using unnecessarily large +threadgroup sizes. + +103 +00:06:48,942 --> 00:06:54,481 +Making threadgroups smaller will map +the load to the hardware more uniformly. + +104 +00:06:54,515 --> 00:06:58,752 +Using larger threadgroups might prevent +a more uniform distribution, + +105 +00:06:58,785 --> 00:07:01,588 +leading to imbalance in the GPU cores. + +106 +00:07:02,890 --> 00:07:05,926 +It's best to use +the smallest multiple of the SIMD width + +107 +00:07:05,959 --> 00:07:07,995 +that maps well to your workload. + +108 +00:07:08,629 --> 00:07:12,165 +By using smaller threadgroups, +the GPU has more opportunities + +109 +00:07:12,199 --> 00:07:14,668 +to better balance its workload. + +110 +00:07:16,170 --> 00:07:19,039 +Please always check your +kernel runtime performance + +111 +00:07:19,072 --> 00:07:21,942 +with Xcode or Instruments GPU Tools. + +112 +00:07:23,443 --> 00:07:28,882 +In this GPU capture, for example, there is +a kernel performing some computation. + +113 +00:07:28,916 --> 00:07:32,653 +Occupancy is pretty low, +which is unexpected. + +114 +00:07:32,686 --> 00:07:36,089 +The compiler statistics show +that max theoretical occupancy, + +115 +00:07:36,123 --> 00:07:40,060 +which is new in Xcode 14, is 100%. + +116 +00:07:40,093 --> 00:07:43,997 +This indicates there might not +be enough threads--and indeed, + +117 +00:07:44,031 --> 00:07:48,569 +we can see the algorithms starts +to dispatch fewer and fewer threads, + +118 +00:07:48,602 --> 00:07:50,604 +not saturating the machine anymore. + +119 +00:07:51,805 --> 00:07:54,975 +Low occupancy +might have several other causes. + +120 +00:07:55,008 --> 00:08:01,014 +To get all the details, check the +Metal Compute on MacBook Pro Tech talk. + +121 +00:08:01,849 --> 00:08:05,152 +OK, now that workload +is correctly distributed, + +122 +00:08:05,185 --> 00:08:08,655 +it's time to make sure the GPU +is always busy. + +123 +00:08:09,957 --> 00:08:13,393 +Under-utilizing the GPU +never leads to ideal scaling, + +124 +00:08:13,427 --> 00:08:18,131 +and the worst case +of under-utilizing is keeping it idle. + +125 +00:08:18,165 --> 00:08:21,768 +The GPU can be idle +because of GPU timeline gaps. + +126 +00:08:23,971 --> 00:08:26,807 +Consider this example. + +127 +00:08:26,840 --> 00:08:30,110 +Here is a workload +using only 50% of the GPU + +128 +00:08:30,143 --> 00:08:34,248 +due to work serialization +between CPU and GPU. + +129 +00:08:34,281 --> 00:08:39,152 +In this case, overall task duration +is the sum of CPU and GPU work + +130 +00:08:39,186 --> 00:08:41,455 +with no overlaps. + +131 +00:08:42,489 --> 00:08:46,727 +Doubling the GPU cores makes +the GPU track complete faster, + +132 +00:08:46,760 --> 00:08:49,796 +but the CPU track is not affected. + +133 +00:08:49,830 --> 00:08:56,103 +Overall performance increases only by 33%, +far from ideal scaling. + +134 +00:08:57,237 --> 00:09:02,676 +If the GPU cores are doubled again, +the workload is even faster on the GPU, + +135 +00:09:02,709 --> 00:09:08,315 +but overall latency is reduced by +only 60% compared to the original time! + +136 +00:09:08,348 --> 00:09:13,053 +So GPU cores scaling brings +diminishing returns in such cases. + +137 +00:09:13,086 --> 00:09:16,089 +This is far from ideal. +Let's fix it! + +138 +00:09:17,858 --> 00:09:23,397 +This Instrument trace from a M1 pro +shows big GPU timeline gaps, + +139 +00:09:23,430 --> 00:09:26,667 +and this will clearly prevent +proper scaling. + +140 +00:09:28,035 --> 00:09:31,738 +On M1 Ultra the same workload +is indeed a bit faster, + +141 +00:09:31,772 --> 00:09:34,441 +but the GPU idle time became higher + +142 +00:09:34,474 --> 00:09:38,278 +and the workload is not scaling well. + +143 +00:09:38,312 --> 00:09:41,348 +The big gaps are caused by +CPU synchronization + +144 +00:09:41,381 --> 00:09:44,651 +using the waitUntilCompleted +on the command buffer. + +145 +00:09:45,552 --> 00:09:49,189 +After changing the waiting logic +and removing serialization, + +146 +00:09:49,223 --> 00:09:53,327 +the GPU became fully utilized, +which is great. + +147 +00:09:54,661 --> 00:09:56,029 +Comparing the workload scaling + +148 +00:09:56,063 --> 00:09:57,030 +before and after, + +149 +00:09:57,064 --> 00:09:58,765 +we can state that the scaling + +150 +00:09:58,799 --> 00:10:01,635 +became much closer to the ideal scaling. + +151 +00:10:03,403 --> 00:10:06,773 +In the previous example, +it was possible to remove + +152 +00:10:06,807 --> 00:10:09,810 +CPU/GPU synchronization altogether, + +153 +00:10:09,843 --> 00:10:15,482 +however this is not always the case, +due to your application nature. + +154 +00:10:15,516 --> 00:10:20,587 +There are other approaches you can take +to reduce idle time. + +155 +00:10:20,621 --> 00:10:23,724 +Use MTLSharedEvents to signal the CPU, + +156 +00:10:23,757 --> 00:10:27,961 +pipeline more work, +consider using GPU-driven encoding, + +157 +00:10:27,995 --> 00:10:30,497 +and using concurrent dispatches. + +158 +00:10:30,531 --> 00:10:35,335 +So let's discuss those approaches +to minimize GPU timeline gaps. + +159 +00:10:35,369 --> 00:10:37,905 +Some of them might fit your workflow. + +160 +00:10:39,139 --> 00:10:44,378 +Waiting on the CPU for GPU completion +leads to not ideal scaling. + +161 +00:10:44,411 --> 00:10:46,947 +If your application +is using WaitUntilCompleted, + +162 +00:10:46,980 --> 00:10:50,584 +you might want to try to use +MTLSharedEvents instead. + +163 +00:10:51,919 --> 00:10:54,555 +MTLSharedEvents have lower overhead + +164 +00:10:54,588 --> 00:10:57,724 +and can help you reduce the timeline gaps. + +165 +00:10:57,758 --> 00:10:58,959 +The next thing to consider + +166 +00:10:58,992 --> 00:11:01,228 +is pipelining the workload. + +167 +00:11:02,329 --> 00:11:04,498 +If the algorithm has the data necessary + +168 +00:11:04,531 --> 00:11:06,633 +for the next batch to work on, + +169 +00:11:06,667 --> 00:11:09,937 +it's possible to encode one or +more batches in advance + +170 +00:11:09,970 --> 00:11:12,973 +before waiting on the MTLSharedEvents. + +171 +00:11:13,006 --> 00:11:15,843 +By doing so, +the GPU will not become drained + +172 +00:11:15,876 --> 00:11:18,312 +and will always have work to process. + +173 +00:11:19,713 --> 00:11:23,116 +If work can't be encoded +in advance on the same queue, + +174 +00:11:23,150 --> 00:11:26,787 +consider using a second queue +to overlap work. + +175 +00:11:26,820 --> 00:11:30,424 +Using multiple queues allows you +to submit independent work, + +176 +00:11:30,457 --> 00:11:33,026 +and they do not stall +the other submission thread + +177 +00:11:33,060 --> 00:11:35,462 +when waiting on an event. + +178 +00:11:35,495 --> 00:11:40,067 +This way, the GPU has the chance +to keep receiving and processing work. + +179 +00:11:41,602 --> 00:11:46,807 +In some cases, an algorithm +can encode work directly from the GPU. + +180 +00:11:47,841 --> 00:11:49,576 +Using indirect command buffer, + +181 +00:11:49,610 --> 00:11:53,380 +you can move the encoding of the +next batch directly on the GPU, + +182 +00:11:53,413 --> 00:11:56,416 +avoiding any need for synchronization. + +183 +00:11:56,450 --> 00:12:00,153 +For more details about indirect +command buffers, please check + +184 +00:12:00,187 --> 00:12:02,456 +"Modern Rendering with Metal." + +185 +00:12:02,489 --> 00:12:06,527 +The workload now removes +or minimizes expensive synchronizations + +186 +00:12:06,560 --> 00:12:09,763 +between CPU and GPU as much as possible. + +187 +00:12:09,796 --> 00:12:15,035 +But even with a busy GPU timeline, +scaling challenges may still exist. + +188 +00:12:15,068 --> 00:12:17,137 +Let's investigate. + +189 +00:12:17,171 --> 00:12:19,973 +This graph +is from an image processing workload + +190 +00:12:20,007 --> 00:12:23,744 +where images are processed +1 frame at a time. + +191 +00:12:23,777 --> 00:12:28,982 +A lot of back-to-back compute serial +dispatches can also limit scaling. + +192 +00:12:29,016 --> 00:12:31,718 +The GPU is busy, +but kernel synchronization + +193 +00:12:31,752 --> 00:12:36,223 +has a cost and additionally, +every dispatch has a small ramp up + +194 +00:12:36,256 --> 00:12:38,392 +where the threadgroups +are being distributed + +195 +00:12:38,425 --> 00:12:41,361 +and not yet saturating the cores. + +196 +00:12:41,395 --> 00:12:45,032 +Likewise, when threadgroups +finish and retire, + +197 +00:12:45,065 --> 00:12:49,303 +there might not be enough work +to fully saturate the cores anymore. + +198 +00:12:49,336 --> 00:12:54,174 +In this situation, the advice is to +overlap independent work when possible. + +199 +00:12:54,208 --> 00:12:56,810 +Let's see a visual example. + +200 +00:12:56,844 --> 00:13:00,747 +Here we have a workload processing +two images, one after the other. + +201 +00:13:00,781 --> 00:13:04,017 +Normally, kernels need to +synchronize between each other. + +202 +00:13:04,051 --> 00:13:07,688 +However, this is not the only +way to schedule work. + +203 +00:13:07,721 --> 00:13:13,327 +You can interleave independent work of +two images using concurrent dispatches. + +204 +00:13:13,360 --> 00:13:16,730 +Here the driver is able to +interleave different work, + +205 +00:13:16,763 --> 00:13:19,299 +thanks to concurrent dispatches. + +206 +00:13:19,333 --> 00:13:22,402 +We can see that the two kernels +that previously were back-to-back + +207 +00:13:22,436 --> 00:13:27,140 +are now separated +by some independent work. + +208 +00:13:27,174 --> 00:13:30,644 +However, +when you use MTLDispatchTypeConcurrent, + +209 +00:13:30,677 --> 00:13:33,780 +barriers must be put in manually. + +210 +00:13:33,814 --> 00:13:38,118 +Concurrent dispatches enable the +driver to pack the work more tightly, + +211 +00:13:38,151 --> 00:13:42,222 +hiding most of the synchronization cost +between dependent kernels, + +212 +00:13:42,256 --> 00:13:47,427 +as well as fill the ramp up +and tail end of the various kernels. + +213 +00:13:47,461 --> 00:13:50,764 +This optimization greatly improved +the workload performance + +214 +00:13:50,797 --> 00:13:55,402 +and scaling when +moving from M1 Max to M1 Ultra. + +215 +00:13:55,435 --> 00:13:59,506 +The workload runs 30% faster +with two images interleaved, + +216 +00:13:59,540 --> 00:14:04,811 +70% faster with 3 images in parallel, +compared to the previous scaling. + +217 +00:14:07,014 --> 00:14:11,652 +It's important to carefully consider +atomic operations that kernels are doing. + +218 +00:14:11,685 --> 00:14:15,422 +Let's make sure it is made in +the most efficient way. + +219 +00:14:15,455 --> 00:14:19,126 +Atomic operation +allows reading and writing data + +220 +00:14:19,159 --> 00:14:22,229 +from multiple threads in a safe manner. + +221 +00:14:22,262 --> 00:14:26,333 +Global atomics +are coherent across the whole GPU. + +222 +00:14:26,366 --> 00:14:29,937 +When many threads attempt to +read and write the same global value, + +223 +00:14:29,970 --> 00:14:32,472 +this leads to contention. + +224 +00:14:32,506 --> 00:14:38,445 +Increasing numbers of GPU cores doesn't +help and in fact leads to more contention. + +225 +00:14:38,478 --> 00:14:41,682 +Let's investigate how you can +improve atomics behavior + +226 +00:14:41,715 --> 00:14:44,051 +in an algorithm with an example. + +227 +00:14:45,719 --> 00:14:49,356 +Here is a reduction algorithm, +where all of the values in a buffer + +228 +00:14:49,389 --> 00:14:51,792 +will be summed up together. + +229 +00:14:51,825 --> 00:14:54,928 +The simplest approach is to perform +an atomic add operation + +230 +00:14:54,962 --> 00:14:57,464 +per thread in main memory. + +231 +00:14:57,497 --> 00:15:02,603 +However, this is not ideal because +that puts a great level of pressure + +232 +00:15:02,636 --> 00:15:08,008 +on a single value in main memory, +effectively serializing each memory write. + +233 +00:15:09,376 --> 00:15:11,945 +There are two things that the +hardware offers to help with + +234 +00:15:11,979 --> 00:15:14,381 +atomic memory contention: + +235 +00:15:14,414 --> 00:15:16,950 +Simd-group instruction +and threadgroup atomics. + +236 +00:15:18,585 --> 00:15:24,291 +SIMD instructions like prefix_exlusive sum +and simd_min and many more + +237 +00:15:24,324 --> 00:15:28,161 +allow to do operations and +exchange memory between registers + +238 +00:15:28,195 --> 00:15:31,932 +in a SIMD-group +without round trip to memory. + +239 +00:15:31,965 --> 00:15:35,769 +Threadgroup atomics are fulfilled by +the threadgroup memory. + +240 +00:15:35,802 --> 00:15:38,839 +Each GPU core +has its own threadgroup memory + +241 +00:15:38,872 --> 00:15:42,476 +allowing to scale +with the number of GPU cores. + +242 +00:15:42,509 --> 00:15:46,847 +Let's see how these two features +can help you improve your workload. + +243 +00:15:48,549 --> 00:15:50,617 +Here we have +the same reduction problem, + +244 +00:15:50,651 --> 00:15:54,321 +but this time it start using +a SIMD-group instruction, + +245 +00:15:54,354 --> 00:15:56,390 +an inclusive memory sum. + +246 +00:15:56,423 --> 00:16:01,094 +Such operation will leave the sum +of all the numbers in the SIMD-group + +247 +00:16:01,128 --> 00:16:03,664 +in the last thread. + +248 +00:16:03,697 --> 00:16:08,001 +The last thread from each SIMD-group +can then perform a single atomic add + +249 +00:16:08,035 --> 00:16:12,739 +in threadgroup memory to reduce +all SIMD-groups to a single value + +250 +00:16:12,773 --> 00:16:14,908 +in threadgroup memory. + +251 +00:16:14,942 --> 00:16:19,246 +In this way, using SIMD-group +instruction and threadgroup memory, + +252 +00:16:19,279 --> 00:16:24,484 +a whole threadgroup was reduced +without touching main memory at all. + +253 +00:16:24,518 --> 00:16:28,488 +Each group will be able to reduce +independently and in parallel. + +254 +00:16:29,823 --> 00:16:32,926 +Now that each threadgroup has +been reduced to a single value, + +255 +00:16:32,960 --> 00:16:35,229 +one thread per threadgroup can perform + +256 +00:16:35,262 --> 00:16:37,898 +a single atomic in main memory. + +257 +00:16:37,931 --> 00:16:39,666 +Not only this requires only + +258 +00:16:39,700 --> 00:16:41,201 +one atomic per threadgroup, + +259 +00:16:41,235 --> 00:16:43,136 +but since threadgroups complete + +260 +00:16:43,170 --> 00:16:44,771 +at different times, + +261 +00:16:44,805 --> 00:16:47,107 +it scatters atomics over time, + +262 +00:16:47,140 --> 00:16:50,677 +reducing memory contention even further. + +263 +00:16:50,711 --> 00:16:54,114 +To recap, +to maximize atomics effectiveness, + +264 +00:16:54,147 --> 00:16:59,119 +try to leverage memory locality, +try to use SIMD-group operation, + +265 +00:16:59,152 --> 00:17:02,756 +as well as to leverage +threadgroup memory atomics. + +266 +00:17:02,789 --> 00:17:07,628 +All this should greatly help reduce atomic +operation pressure that prevents scaling. + +267 +00:17:08,829 --> 00:17:15,035 +Now that GPU gaps are fixed, it's time +to see if the scaling is closer to ideal. + +268 +00:17:15,068 --> 00:17:19,706 +GPU Limiters in Xcode and +Metal System Trace help to optimize + +269 +00:17:19,740 --> 00:17:25,179 +any bottlenecks and inefficiencies in GPU +cores execution pipeline. + +270 +00:17:25,212 --> 00:17:28,382 +For example, +inefficient memory access patterns + +271 +00:17:28,415 --> 00:17:33,020 +always cause high Last Level Cache +or Memory Management Unit, + +272 +00:17:33,053 --> 00:17:36,723 +or MMU limiters, +and pretty low utilizations. + +273 +00:17:36,757 --> 00:17:43,497 +The first thing to address is the way +to tune threadgroups and memory layout. + +274 +00:17:43,530 --> 00:17:46,200 +The key in reducing memory span +and divergence + +275 +00:17:46,233 --> 00:17:50,537 +is to have a clear understanding +of the workload memory access pattern, + +276 +00:17:50,571 --> 00:17:54,007 +both spatially and temporally. + +277 +00:17:54,041 --> 00:17:58,312 +Once that's understood, +there are two possible tuning directions: + +278 +00:17:58,345 --> 00:18:01,915 +Re-organize the data layout to improve +data access locality, + +279 +00:18:01,949 --> 00:18:05,953 +or tune the access pattern +to better match the data layout + +280 +00:18:05,986 --> 00:18:09,056 +and improve memory and cache locality. + +281 +00:18:09,089 --> 00:18:10,490 +Let's see an example. + +282 +00:18:12,659 --> 00:18:16,663 +Here it is a memory buffer +where the data is laid out horizontally, + +283 +00:18:16,697 --> 00:18:18,732 +one row after the other. + +284 +00:18:18,765 --> 00:18:21,802 +However, when the compute kernel +is dispatched, + +285 +00:18:21,835 --> 00:18:24,404 +it is common to have a 2D like pattern + +286 +00:18:24,438 --> 00:18:27,374 +with square threadgroups +being distributed, + +287 +00:18:27,407 --> 00:18:29,843 +which is quite spatially localized. + +288 +00:18:29,877 --> 00:18:34,414 +This access pattern and data layout +is not great for data locality. + +289 +00:18:36,216 --> 00:18:39,653 +For example, when the first +SIMD-group access the data, + +290 +00:18:39,686 --> 00:18:42,322 +the requests are packed in a cache lines. + +291 +00:18:42,356 --> 00:18:44,992 +Most of the cache line won't be used, + +292 +00:18:45,025 --> 00:18:50,063 +however still +occupying space in the cache. + +293 +00:18:50,097 --> 00:18:53,100 +Re-arrange the data to fit +the access pattern better, + +294 +00:18:53,133 --> 00:18:56,837 +where, for example, +instead of spanning the whole row, + +295 +00:18:56,870 --> 00:18:59,940 +it is localized into stripes. + +296 +00:19:01,008 --> 00:19:04,144 +With this new memory layout, +a threadgroup will be able + +297 +00:19:04,178 --> 00:19:07,080 +to utilize +most of the data that will be requested + +298 +00:19:07,114 --> 00:19:11,585 +in a cache line, reducing divergence +and improving cache efficiency. + +299 +00:19:12,586 --> 00:19:16,089 +The other option is to change +how the 3D grid is dispatched + +300 +00:19:16,123 --> 00:19:19,126 +to better fit the current data layout. + +301 +00:19:19,159 --> 00:19:23,664 +Try to play with the threadgroup +size to create groups that map better + +302 +00:19:23,697 --> 00:19:27,768 +to your memory layout, +like a more rectangular shape, + +303 +00:19:27,801 --> 00:19:29,670 +for example. + +304 +00:19:29,703 --> 00:19:33,440 +In this case, the access pattern +is aligned with the memory layout, + +305 +00:19:33,473 --> 00:19:36,977 +giving a much higher cache efficiency. + +306 +00:19:37,010 --> 00:19:41,415 +You might need to experiment +to find the best fit for your workload. + +307 +00:19:41,448 --> 00:19:44,418 +Sometimes +you might need to make trade-offs, + +308 +00:19:44,451 --> 00:19:49,756 +sacrifice thread divergence +for memory locality, or vice versa, + +309 +00:19:49,790 --> 00:19:54,661 +change your data layout, grid dispatch, +or a combination of them all. + +310 +00:19:54,695 --> 00:19:57,631 +Every workload and access pattern +is different. + +311 +00:20:00,100 --> 00:20:03,270 +Now that you're aware of ways +to improve memory locality, + +312 +00:20:03,303 --> 00:20:06,273 +let's see a more concrete example +in Blender Cycles. + +313 +00:20:08,408 --> 00:20:13,680 +Cycles is Blender's physically-based +path tracer for production rendering. + +314 +00:20:13,714 --> 00:20:17,885 +It is designed to provide physically +based results out-of-the-box, + +315 +00:20:17,918 --> 00:20:22,422 +with artistic control and flexible +shading nodes for production needs. + +316 +00:20:24,258 --> 00:20:30,230 +This Instrument trace clearly shows low +Read Bandwidth, high Top GPU Limiter, + +317 +00:20:30,264 --> 00:20:34,368 +high Cache Limiter +and low Last Level Cache Utilization. + +318 +00:20:36,170 --> 00:20:41,975 +Keeping bandwidth and MMU limiters +in control is important for scaling. + +319 +00:20:42,009 --> 00:20:45,679 +If your Top limiter is the Last +Level Cache or MMU, + +320 +00:20:45,712 --> 00:20:50,551 +you need to reduce your memory span +and maximize data locality. + +321 +00:20:50,584 --> 00:20:52,152 +Let's see an example. + +322 +00:20:53,887 --> 00:20:57,958 +Cycles use sorting of data +to try to reduce divergence. + +323 +00:20:57,991 --> 00:21:01,762 +It does that by sorting +the ray hits by material type. + +324 +00:21:01,795 --> 00:21:04,131 +This is great to reduce thread divergence, + +325 +00:21:04,164 --> 00:21:07,401 +but it increases +spatial memory divergence, + +326 +00:21:07,434 --> 00:21:11,004 +resulting in a high MMU Limiter. + +327 +00:21:11,038 --> 00:21:14,641 +To help with this, we experimented with +partitioning the memory range + +328 +00:21:14,675 --> 00:21:17,711 +before sorting to increase data locality. + +329 +00:21:17,744 --> 00:21:18,579 +Let's visualize it. + +330 +00:21:18,612 --> 00:21:24,051 +When rays are shot into the scene +to simulate light traveling, + +331 +00:21:24,084 --> 00:21:28,055 +they hit objects and data is +collected into buffers. + +332 +00:21:28,088 --> 00:21:30,958 +At the point of intersection +we know many things-- + +333 +00:21:30,991 --> 00:21:34,728 +the material type that was +hit, like glass, metal and so on, + +334 +00:21:34,761 --> 00:21:39,099 +the intersection position, +the ray, and more. + +335 +00:21:39,132 --> 00:21:43,303 +For simplicity, +let's focus on the material type only. + +336 +00:21:43,337 --> 00:21:45,906 +Here are the materials +in a buffer in memory. + +337 +00:21:47,341 --> 00:21:49,910 +Since a lot of data +is gathered per ray hit, + +338 +00:21:49,943 --> 00:21:53,380 +the memory buffer can become quite large. + +339 +00:21:53,413 --> 00:21:55,616 +To avoid moving a lot of memory around, + +340 +00:21:55,649 --> 00:22:00,521 +populate a list of indices +and sort those instead. + +341 +00:22:00,554 --> 00:22:03,957 +After the sort, +the indices for the same material type + +342 +00:22:03,991 --> 00:22:06,560 +are now packed closed together. + +343 +00:22:06,593 --> 00:22:11,932 +SIMD-groups can start loading +the indices and process the materials. + +344 +00:22:11,965 --> 00:22:15,269 +The SIMD-group will use the index +to load the corresponding data + +345 +00:22:15,302 --> 00:22:16,837 +in the original buffer. + +346 +00:22:18,472 --> 00:22:22,576 +However, the SIMD-group would be +reading across the whole memory span, + +347 +00:22:22,609 --> 00:22:25,279 +putting pressure on the MMU. + +348 +00:22:25,312 --> 00:22:27,981 +Let's investigate the new approach. + +349 +00:22:28,015 --> 00:22:31,985 +The memory range is partitioned +in an idealized partition + +350 +00:22:32,019 --> 00:22:36,390 +that simply won't +let indices from different partition mix. + +351 +00:22:36,423 --> 00:22:41,962 +When sorting, it is apparent that +the range of data accessed is contained + +352 +00:22:41,995 --> 00:22:46,733 +inside the partition instead of spanning +the full memory range like before. + +353 +00:22:46,767 --> 00:22:52,739 +It is a trade-off and balance between +thread divergence and memory divergence. + +354 +00:22:52,773 --> 00:22:55,609 +The number of partitions +and size that is ideal + +355 +00:22:55,642 --> 00:22:58,078 +is highly dependent on the workload. + +356 +00:22:58,111 --> 00:23:02,216 +You might have to experiment +to see which one works best. + +357 +00:23:02,249 --> 00:23:07,421 +Let's take another Metal System Trace +and let see if the workload improved. + +358 +00:23:07,454 --> 00:23:11,124 +Here we see the limiters and +utilizations for the optimized version. + +359 +00:23:11,158 --> 00:23:17,364 +The Top Performance limiter went down, +as well as Last Level Cache limiter. + +360 +00:23:17,397 --> 00:23:22,436 +As a result, bandwidth and shader runtime +substantially improved. + +361 +00:23:22,469 --> 00:23:24,137 +Let's see how much. + +362 +00:23:24,171 --> 00:23:28,976 +Top limiter and LLC limiter +reduced by around 20%. + +363 +00:23:29,009 --> 00:23:32,145 +That means data flow is more efficient. + +364 +00:23:32,179 --> 00:23:35,215 +GPU Read bandwidth increased +significantly, + +365 +00:23:35,249 --> 00:23:38,652 +allowing to push more data +to the GPU cores. + +366 +00:23:39,987 --> 00:23:43,557 +Overall, increasing +memory locality with this experiment + +367 +00:23:43,590 --> 00:23:48,896 +improved performance between +10 to 30%, depending on the scene. + +368 +00:23:48,929 --> 00:23:54,034 +This was just an example of many ways you +can try to improve memory access pattern. + +369 +00:23:54,067 --> 00:23:58,472 +Keep experimenting and optimizing +for the Top Performance Limiter. + +370 +00:23:58,505 --> 00:24:02,476 +The GPU tools have more useful +counters to tune for. + +371 +00:24:03,677 --> 00:24:08,916 +Xcode has a new theoretical occupancy +in the compiler statistic window. + +372 +00:24:08,949 --> 00:24:15,556 +Both Xcode and Instruments now have +several MMU related limiters and counters, + +373 +00:24:15,589 --> 00:24:20,060 +specifically a new MMU Limiter, +MMU Utilization Counter, + +374 +00:24:20,093 --> 00:24:23,130 +and MMU TLB Miss Rate Counter. + +375 +00:24:24,431 --> 00:24:27,301 +I have covered a lot of ground today. + +376 +00:24:27,334 --> 00:24:31,972 +I discussed GPU scalability and how +bottlenecks can shift when scaling up, + +377 +00:24:32,005 --> 00:24:36,009 +and how the tools can help +you find and fix scalability issues. + +378 +00:24:36,043 --> 00:24:40,147 +I also discussed how you might +need to experiment and make trade-offs + +379 +00:24:40,180 --> 00:24:43,417 +to get the best result +for your application. + +380 +00:24:43,450 --> 00:24:47,855 +I am looking forward to seeing all +your great apps scale amazingly well + +381 +00:24:47,888 --> 00:24:48,956 +on Apple silicon. + +382 +00:24:48,989 --> 00:24:50,624 +Thank you for watching. + diff --git a/eng/2022 Session 10160 Program Metal in C++ with metal-cpp en.srt b/eng/2022 Session 10160 Program Metal in C++ with metal-cpp en.srt new file mode 100644 index 0000000..4f4be57 --- /dev/null +++ b/eng/2022 Session 10160 Program Metal in C++ with metal-cpp en.srt @@ -0,0 +1,2474 @@ +1 +00:00:00,100 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,676 +♪ + +3 +00:00:09,676 --> 00:00:11,311 +Hi, my name is Keyi Yu, + +4 +00:00:11,311 --> 00:00:14,014 +and I'm an engineer +from the Metal Ecosystem team. + +5 +00:00:14,014 --> 00:00:17,017 +Today, it's my pleasure +to introduce metal-cpp. + +6 +00:00:17,017 --> 00:00:21,955 +We created metal-cpp for anyone +who uses C++ and wants to build + +7 +00:00:21,955 --> 00:00:25,058 +Metal applications +for Apple platforms. + +8 +00:00:25,058 --> 00:00:27,594 +Metal-cpp is +a low-overhead library + +9 +00:00:27,594 --> 00:00:31,698 +that connects your C++ +applications to Metal. + +10 +00:00:31,698 --> 00:00:35,702 +First, I'll start with +an overview of what metal-cpp is + +11 +00:00:35,702 --> 00:00:39,706 +and how it works, +and then I'll cover some details + +12 +00:00:39,706 --> 00:00:43,310 +about the lifecycles +for Objective-C objects. + +13 +00:00:43,310 --> 00:00:47,781 +C++ and Objective-C handle +lifecycles a bit differently, + +14 +00:00:47,781 --> 00:00:52,219 +and I'll show you how to handle +those differences. + +15 +00:00:52,219 --> 00:00:55,756 +Xcode and metal-cpp +have some great utilities + +16 +00:00:55,756 --> 00:00:58,392 +that can help you manage +the object lifecycles + +17 +00:00:58,392 --> 00:01:01,094 +in your apps. + +18 +00:01:01,094 --> 00:01:05,098 +And finally, I'll show you +how to integrate C++ code + +19 +00:01:05,098 --> 00:01:09,102 +with Objective-C classes. + +20 +00:01:09,102 --> 00:01:13,340 +So here's a look at metal-cpp +and how it works. + +21 +00:01:13,340 --> 00:01:16,343 +Metal is the foundation +for accelerated graphics + +22 +00:01:16,343 --> 00:01:20,314 +and compute on Apple platforms, +enabling your apps and games + +23 +00:01:20,314 --> 00:01:23,317 +to tap into the incredible power +of the GPU. + +24 +00:01:23,317 --> 00:01:26,553 +It was originally designed +using the powerful features + +25 +00:01:26,553 --> 00:01:29,323 +and the conventions +offered by Objective-C. + +26 +00:01:29,323 --> 00:01:34,061 +But if your code base is in C++, +you may need something to bridge + +27 +00:01:34,061 --> 00:01:37,464 +between your code +and Metal's Objective-C code. + +28 +00:01:37,464 --> 00:01:39,999 +Introducing metal-cpp! + +29 +00:01:39,999 --> 00:01:43,337 +It serves as a hub +between your C++ application + +30 +00:01:43,337 --> 00:01:45,605 +and Objective-C Metal. + +31 +00:01:45,605 --> 00:01:48,208 +With metal-cpp +in your application, + +32 +00:01:48,208 --> 00:01:51,678 +you can use Metal classes +and functions in C++, + +33 +00:01:51,678 --> 00:01:54,915 +and metal-cpp can help you +call Objective-C functions + +34 +00:01:54,915 --> 00:01:56,450 +in runtime. + +35 +00:01:56,450 --> 00:02:00,520 +metal-cpp is a lightweight +Metal C++ wrapper. + +36 +00:02:00,520 --> 00:02:03,657 +I say it's lightweight, +because it's implemented + +37 +00:02:03,657 --> 00:02:07,627 +as a header-only library +with inline function calls. + +38 +00:02:07,627 --> 00:02:11,331 +It provides 100 percent +coverage of the Metal API + +39 +00:02:11,331 --> 00:02:14,835 +by implementing a one-to-one +mapping of C++ calls + +40 +00:02:14,835 --> 00:02:16,770 +to Objective-C APIs. + +41 +00:02:16,770 --> 00:02:20,007 +To do this, metal-cpp wraps +parts of the Foundation + +42 +00:02:20,007 --> 00:02:22,442 +and CoreAnimation frameworks. + +43 +00:02:22,442 --> 00:02:24,711 +It's open source +under Apache 2 License, + +44 +00:02:24,711 --> 00:02:27,214 +so you can modify the library +and include it + +45 +00:02:27,214 --> 00:02:29,649 +to your applications, easily. + +46 +00:02:29,649 --> 00:02:32,853 +metal-cpp uses C +to call directly + +47 +00:02:32,853 --> 00:02:35,622 +into the Objective-C runtime. + +48 +00:02:35,622 --> 00:02:38,959 +This is the exact same mechanism +that the Objective-C compiler + +49 +00:02:38,959 --> 00:02:42,963 +uses to execute +Objective-C methods. + +50 +00:02:42,963 --> 00:02:47,167 +So this wrapper +introduces little overhead. + +51 +00:02:47,167 --> 00:02:51,438 +Since metal-cpp implements +a one-to-one mapping of C++ + +52 +00:02:51,438 --> 00:02:53,440 +to Objective-C calls, + +53 +00:02:53,440 --> 00:02:57,077 +it follows the same Cocoa +memory-management rules. + +54 +00:02:57,077 --> 00:02:59,946 +I will discuss this +in more detail later. + +55 +00:02:59,946 --> 00:03:02,215 +This one-to-one mapping +also allows + +56 +00:03:02,215 --> 00:03:05,318 +all of the developer tools +to work seamlessly, + +57 +00:03:05,318 --> 00:03:10,791 +including GPU Frame Capture +and the Xcode debugger. + +58 +00:03:10,791 --> 00:03:13,760 +These are the series of calls +necessary to draw a triangle + +59 +00:03:13,760 --> 00:03:15,695 +with metal-cpp. + +60 +00:03:15,695 --> 00:03:19,499 +If you are familiar with C++, +it's a good time to learn Metal, + +61 +00:03:19,499 --> 00:03:22,869 +because you don't need to worry +about language syntax. + +62 +00:03:22,869 --> 00:03:26,306 +If you've already used Metal +with Objective-C, + +63 +00:03:26,306 --> 00:03:29,443 +in terms of function calls, +there's very little difference + +64 +00:03:29,443 --> 00:03:31,912 +between the Objective-C +interface of Metal + +65 +00:03:31,912 --> 00:03:33,780 +and metal-cpp. + +66 +00:03:33,780 --> 00:03:38,852 +I am going to demonstrate +how easy it is to use metal-cpp. + +67 +00:03:38,852 --> 00:03:41,555 +First, I create +a command buffer, + +68 +00:03:41,555 --> 00:03:45,258 +which I will fill with commands +for the GPU to execute. + +69 +00:03:45,258 --> 00:03:48,495 +I can simply use +the raw pointer in C++ + +70 +00:03:48,495 --> 00:03:51,665 +as a mapping to ID +in Objective-C. + +71 +00:03:51,665 --> 00:03:54,234 +I can create a render +command encoder + +72 +00:03:54,234 --> 00:03:57,504 +and write render commands +with a command buffer. + +73 +00:03:57,504 --> 00:04:00,540 +The C++ function +renderCommandEncoder + +74 +00:04:00,540 --> 00:04:02,642 +and the Objective-C method + +75 +00:04:02,642 --> 00:04:06,980 +renderCommandEncoder +WithDescriptor are the same. + +76 +00:04:06,980 --> 00:04:09,749 +The only differences +are the name conventions + +77 +00:04:09,749 --> 00:04:11,551 +of the languages. + +78 +00:04:11,551 --> 00:04:14,321 +I then set a render pipeline +state object + +79 +00:04:14,321 --> 00:04:17,257 +which contains the vertex +and fragment shaders + +80 +00:04:17,257 --> 00:04:19,860 +and various other +rendering states. + +81 +00:04:19,860 --> 00:04:24,631 +Then I encode my draw call +to render a single triangle. + +82 +00:04:24,631 --> 00:04:25,999 +Then I indicate that + +83 +00:04:25,999 --> 00:04:29,402 +I've finished encoding +render commands. + +84 +00:04:29,402 --> 00:04:33,340 +I present the drawable, so the +triangle is displayed onscreen. + +85 +00:04:33,340 --> 00:04:36,409 +Finally, I commit +my command buffer. + +86 +00:04:36,409 --> 00:04:40,847 +This tells the GPU that it can +begin executing my commands. + +87 +00:04:40,847 --> 00:04:43,783 +Obviously, metal-cpp +and Objective-C Metal + +88 +00:04:43,783 --> 00:04:45,485 +are almost the same. + +89 +00:04:45,485 --> 00:04:48,155 +You don't need to worry +about language syntax now + +90 +00:04:48,155 --> 00:04:49,956 +with metal-cpp, + +91 +00:04:49,956 --> 00:04:52,726 +you can directly look into +the Metal documentation + +92 +00:04:52,726 --> 00:04:56,463 +to learn the concepts +and usage of Metal. + +93 +00:04:56,463 --> 00:04:58,098 +You may have already played + +94 +00:04:58,098 --> 00:05:00,500 +with this deferred lighting +sample before. + +95 +00:05:00,500 --> 00:05:03,703 +We now provide a new version +of this deferred lighting sample + +96 +00:05:03,703 --> 00:05:06,206 +which uses metal-cpp. + +97 +00:05:06,206 --> 00:05:09,509 +We hope this can help you learn +how to code with metal-cpp + +98 +00:05:09,509 --> 00:05:11,077 +in practice. + +99 +00:05:11,077 --> 00:05:13,380 +I'm also excited +to introduce a series + +100 +00:05:13,380 --> 00:05:17,851 +of incremental C++ samples +that introduces the Metal API + +101 +00:05:17,851 --> 00:05:21,488 +and shows you how to accomplish +different tasks with it. + +102 +00:05:25,158 --> 00:05:28,695 +So now that you know +a little bit about metal-cpp, + +103 +00:05:28,695 --> 00:05:30,463 +how do you actually use it? + +104 +00:05:30,463 --> 00:05:32,933 +We published +metal-cpp last year. + +105 +00:05:32,933 --> 00:05:35,302 +Here's the webpage where +you can find the downloads + +106 +00:05:35,302 --> 00:05:36,937 +and instructions. + +107 +00:05:36,937 --> 00:05:39,906 +Let me show you the steps +you will need to take. + +108 +00:05:39,906 --> 00:05:41,908 +After downloading metal-cpp, + +109 +00:05:41,908 --> 00:05:44,878 +you should tell Xcode +where to find it. + +110 +00:05:44,878 --> 00:05:49,616 +Here, I put metal-cpp +under the current project. + +111 +00:05:49,616 --> 00:05:53,019 +Then, you need to set +C++17 or higher + +112 +00:05:53,019 --> 00:05:56,990 +as the C++ language dialect. + +113 +00:05:56,990 --> 00:05:59,859 +Next, add three frameworks +to the project: + +114 +00:05:59,859 --> 00:06:03,997 +Foundation, QuartzCore, +and Metal. + +115 +00:06:03,997 --> 00:06:05,532 +Now there's only one thing +left to do + +116 +00:06:05,532 --> 00:06:09,336 +before using C++ interfaces +of those frameworks. + +117 +00:06:09,336 --> 00:06:12,138 +There are three headers +in metal-cpp. + +118 +00:06:12,138 --> 00:06:14,841 +Since metal-cpp is +a header-only library, + +119 +00:06:14,841 --> 00:06:17,444 +you need to generate +their implementations + +120 +00:06:17,444 --> 00:06:19,846 +before importing +the header files. + +121 +00:06:19,846 --> 00:06:24,985 +To do this, define three macros: +NS_PRIVATE_IMPLEMENTATION, + +122 +00:06:24,985 --> 00:06:29,656 +CA_PRIVATE_IMPLEMENTATION, +AND MTL_PRIVATE_IMPLEMENTATION. + +123 +00:06:29,656 --> 00:06:32,692 +If you are interested in what +metal-cpp does with the macros + +124 +00:06:32,692 --> 00:06:33,994 +under the hood, + +125 +00:06:33,994 --> 00:06:37,631 +please check out header bridge +files in the metal-cpp folder. + +126 +00:06:37,631 --> 00:06:39,499 +You can use +the headers separately + +127 +00:06:39,499 --> 00:06:42,168 +or put them in a single header. + +128 +00:06:42,168 --> 00:06:45,071 +You can import the header files +whenever you need them. + +129 +00:06:45,071 --> 00:06:48,908 +But remember, +do not define the NS, CA, + +130 +00:06:48,908 --> 00:06:52,746 +or MTL_PRIVATE_IMPLEMENTATION +macros more than once. + +131 +00:06:52,746 --> 00:06:56,950 +Otherwise, you may cause +duplicate definition errors. + +132 +00:06:56,950 --> 00:06:59,286 +To use metal-cpp effectively, + +133 +00:06:59,286 --> 00:07:02,322 +you'll need to know Cocoa's +memory management rules, + +134 +00:07:02,322 --> 00:07:05,425 +how to use the great utilities +that can help you + +135 +00:07:05,425 --> 00:07:07,994 +manage object lifecycles, +and how to design + +136 +00:07:07,994 --> 00:07:10,497 +your application architecture +when you interface + +137 +00:07:10,497 --> 00:07:12,732 +with other frameworks. + +138 +00:07:12,732 --> 00:07:16,336 +I'll start with object +lifecycle management. + +139 +00:07:16,336 --> 00:07:18,638 +During your application's +operation, + +140 +00:07:18,638 --> 00:07:21,908 +you typically need to allocate +and release memory. + +141 +00:07:21,908 --> 00:07:24,811 +You also need to manage +command buffers, + +142 +00:07:24,811 --> 00:07:27,213 +pipeline objects, and resources. + +143 +00:07:27,213 --> 00:07:29,182 +To help manage this memory, + +144 +00:07:29,182 --> 00:07:33,019 +Objective-C and Cocoa objects +include a reference count. + +145 +00:07:33,019 --> 00:07:36,089 +This is also present +in metal-cpp. + +146 +00:07:36,089 --> 00:07:39,025 +Reference counting helps you +manage your memory. + +147 +00:07:39,025 --> 00:07:40,627 +Using reference counting, + +148 +00:07:40,627 --> 00:07:43,596 +all objects contain +a retainCount property. + +149 +00:07:43,596 --> 00:07:45,732 +Components in an app +increase the count + +150 +00:07:45,732 --> 00:07:48,568 +to keep objects +they're interacting with alive + +151 +00:07:48,568 --> 00:07:51,304 +and decrease it +when they are done with them. + +152 +00:07:51,304 --> 00:07:53,473 +When the retainCount hits zero, + +153 +00:07:53,473 --> 00:07:56,176 +the runtime +deallocates the object. + +154 +00:07:56,176 --> 00:07:59,279 +There are two types of reference +counting in Objective-C. + +155 +00:07:59,279 --> 00:08:02,615 +One is called +Manual Retain-Release -- MRR; + +156 +00:08:02,615 --> 00:08:06,252 +the other is Automatic +Reference Counting -- ARC. + +157 +00:08:06,252 --> 00:08:08,688 +When compiling code +with the ARC feature, + +158 +00:08:08,688 --> 00:08:11,257 +the compiler takes +the references you create + +159 +00:08:11,257 --> 00:08:13,293 +and automatically inserts calls + +160 +00:08:13,293 --> 00:08:16,429 +to the underlying +memory-management mechanism. + +161 +00:08:16,429 --> 00:08:20,233 +metal-cpp objects are manually +retained and released. + +162 +00:08:20,233 --> 00:08:23,303 +So you need to understand +Cocoa's conventions + +163 +00:08:23,303 --> 00:08:26,973 +to know when to retain +and release objects. + +164 +00:08:26,973 --> 00:08:31,478 +Unlike creating objects in C++, +metal-cpp objects + +165 +00:08:31,478 --> 00:08:34,948 +are neither created with new +nor destroyed with delete. + +166 +00:08:34,948 --> 00:08:38,785 +With Cocoa's conventions, +you own any object you create + +167 +00:08:38,785 --> 00:08:43,423 +with methods starting +with the alloc, new, copy, + +168 +00:08:43,423 --> 00:08:45,892 +mutableCopy, or create. + +169 +00:08:45,892 --> 00:08:49,562 +You can take ownership +of an object using retain. + +170 +00:08:49,562 --> 00:08:53,333 +When you no longer need it, +you must relinquish ownership + +171 +00:08:53,333 --> 00:08:54,868 +of an object you own. + +172 +00:08:54,868 --> 00:08:58,972 +You can release it immediately +or release it afterwards. + +173 +00:08:58,972 --> 00:09:02,475 +You must not relinquish +ownership of an object + +174 +00:09:02,475 --> 00:09:05,912 +you do not own +as you risk a double free. + +175 +00:09:05,912 --> 00:09:08,081 +Next, I'll walk through +an example + +176 +00:09:08,081 --> 00:09:10,150 +of these Cocoa conventions. + +177 +00:09:10,150 --> 00:09:14,154 +In class A, a method uses alloc +to create an object + +178 +00:09:14,154 --> 00:09:16,756 +and init to initialize +this object. + +179 +00:09:16,756 --> 00:09:20,960 +Remember, never call init +on an object twice. + +180 +00:09:20,960 --> 00:09:22,896 +Class A takes the ownership + +181 +00:09:22,896 --> 00:09:25,832 +and is responsible +for deallocating it. + +182 +00:09:25,832 --> 00:09:29,302 +Now the retain count +for this object is one. + +183 +00:09:29,302 --> 00:09:33,373 +Next, class B uses retain +to get the object + +184 +00:09:33,373 --> 00:09:35,975 +and takes ownership +of this object. + +185 +00:09:35,975 --> 00:09:39,279 +So far, I have two objects +that share the ownership + +186 +00:09:39,279 --> 00:09:42,582 +of this object represented +by the orange cube. + +187 +00:09:42,582 --> 00:09:44,651 +The retain count +increases by one. + +188 +00:09:46,519 --> 00:09:49,355 +Class A doesn't need +this object anymore, + +189 +00:09:49,355 --> 00:09:53,126 +so class A should manually +call release for it. + +190 +00:09:53,126 --> 00:09:56,830 +As a result, the retain count +decreases by one. + +191 +00:09:56,830 --> 00:10:00,066 +Now, only class B +owns the object. + +192 +00:10:00,066 --> 00:10:03,670 +OK, finally, class B wants +to release this object too. + +193 +00:10:03,670 --> 00:10:08,374 +Now the retain count is zero, +so the runtime frees the object. + +194 +00:10:08,374 --> 00:10:11,144 +Here's a situation where +a method in class B + +195 +00:10:11,144 --> 00:10:13,513 +returns an object. + +196 +00:10:13,513 --> 00:10:16,616 +You still need this object +in the rest of the programs. + +197 +00:10:16,616 --> 00:10:19,552 +In other words, you want +to relinquish ownership + +198 +00:10:19,552 --> 00:10:22,222 +of an object in a method +in class B, + +199 +00:10:22,222 --> 00:10:26,226 +but you don't want it to be +deallocated immediately. + +200 +00:10:26,226 --> 00:10:30,263 +In this case, you should call +autorelease in class B. + +201 +00:10:30,263 --> 00:10:33,333 +The retain count is still one +after you call autorelease, + +202 +00:10:33,333 --> 00:10:36,836 +and thus, you can still use +the object later. + +203 +00:10:36,836 --> 00:10:38,238 +Here's the question: + +204 +00:10:38,238 --> 00:10:41,574 +since class B does not own +this object anymore, + +205 +00:10:41,574 --> 00:10:44,577 +who is responsible +for deallocating it? + +206 +00:10:44,577 --> 00:10:47,580 +The Foundation Framework +provides an important object, + +207 +00:10:47,580 --> 00:10:49,649 +called the AutoreleasePool. + +208 +00:10:49,649 --> 00:10:54,387 +The Autorelease API puts the +object into an AutoreleasePool. + +209 +00:10:54,387 --> 00:10:58,992 +Now, the AutoreleasePool takes +the ownership of the object. + +210 +00:10:58,992 --> 00:11:02,028 +The AutoreleasePool decrements +the receiver's retain count + +211 +00:11:02,028 --> 00:11:04,797 +when the AutoreleasePool +is destroyed. + +212 +00:11:04,797 --> 00:11:08,835 +You are not the only one who +can create autoreleased objects. + +213 +00:11:08,835 --> 00:11:11,304 +Metal creates several +autoreleased objects + +214 +00:11:11,304 --> 00:11:13,473 +as part of its operation. + +215 +00:11:13,473 --> 00:11:16,175 +All methods that create +temporary objects + +216 +00:11:16,175 --> 00:11:18,211 +add them to AutoreleasePools + +217 +00:11:18,211 --> 00:11:21,014 +by calling autorelease +under the hood. + +218 +00:11:21,014 --> 00:11:24,717 +It is the AutoreleasePool's +responsibility to release them. + +219 +00:11:24,717 --> 00:11:27,320 +In other words, +with an AutoreleasePool, + +220 +00:11:27,320 --> 00:11:30,156 +you can code +in a more elegant way. + +221 +00:11:30,156 --> 00:11:33,526 +You can have an AutoreleasePool +for the main application. + +222 +00:11:33,526 --> 00:11:36,429 +We also encourage you +to create and manage + +223 +00:11:36,429 --> 00:11:38,932 +additional AutoreleasePools +at smaller scopes + +224 +00:11:38,932 --> 00:11:41,401 +to reduce your program's +working set. + +225 +00:11:41,401 --> 00:11:45,204 +You also need AutoreleasePools +for every thread you create. + +226 +00:11:45,204 --> 00:11:48,508 +Here's an example showing +how to use an AutoreleasePool + +227 +00:11:48,508 --> 00:11:51,077 +and autoreleased objects. + +228 +00:11:51,077 --> 00:11:53,680 +In this sample, +an AutoreleasePool + +229 +00:11:53,680 --> 00:11:57,917 +is created by alloc, which +means you take the ownership + +230 +00:11:57,917 --> 00:12:00,587 +and it should be +manually released. + +231 +00:12:00,587 --> 00:12:03,289 +Now we have an AutoreleasePool. + +232 +00:12:03,289 --> 00:12:05,124 +As we discussed +in the beginning, + +233 +00:12:05,124 --> 00:12:07,927 +you should create +a command buffer. + +234 +00:12:07,927 --> 00:12:11,130 +It's not created +with alloc or create, + +235 +00:12:11,130 --> 00:12:13,566 +so you don't own it. + +236 +00:12:13,566 --> 00:12:19,706 +Instead, it's an autoreleased +object created by Metal. + +237 +00:12:19,706 --> 00:12:22,942 +This command buffer will be put +into the AutoreleasePool. + +238 +00:12:22,942 --> 00:12:26,613 +It's the AutoreleasePool's +responsibility to deallocate it. + +239 +00:12:26,613 --> 00:12:28,181 +You can use it as you wish + +240 +00:12:28,181 --> 00:12:31,551 +until you release +the AutoreleasePool. + +241 +00:12:31,551 --> 00:12:35,655 +Then you need to create +a RenderPassDescriptor. + +242 +00:12:35,655 --> 00:12:39,192 +This RenderPassDescriptor will +be put into the AutoreleasePool + +243 +00:12:39,192 --> 00:12:40,393 +as well. + +244 +00:12:40,393 --> 00:12:43,196 +Same to RenderCommandEncoder. + +245 +00:12:43,196 --> 00:12:46,899 +It's also an autoreleased object +created by Metal. + +246 +00:12:46,899 --> 00:12:49,936 +Don't forget +this currentDrawable object. + +247 +00:12:49,936 --> 00:12:53,606 +It will be put into +the AutoreleasePool too. + +248 +00:12:53,606 --> 00:12:56,943 +At the end of the piece of code, +I use pPool-release + +249 +00:12:56,943 --> 00:12:59,112 +to release the AutoreleasePool. + +250 +00:12:59,112 --> 00:13:02,115 +Before being deallocated, +the AutoreleasePool + +251 +00:13:02,115 --> 00:13:04,651 +releases everything +that it owns, + +252 +00:13:04,651 --> 00:13:07,920 +in this case, it releases +the CommandBuffer, + +253 +00:13:07,920 --> 00:13:12,058 +RenderPassDescriptor, +RenderCommandEncoder, + +254 +00:13:12,058 --> 00:13:14,060 +and currentDrawable. + +255 +00:13:14,060 --> 00:13:16,863 +Then the AutoreleasePool +is released. + +256 +00:13:16,863 --> 00:13:19,532 +So far, you got to know +Cocoa's conventions, + +257 +00:13:19,532 --> 00:13:22,669 +autoreleased objects, +and AutoreleasePools. + +258 +00:13:22,669 --> 00:13:26,439 +It's important to correctly +manage object lifecycles + +259 +00:13:26,439 --> 00:13:29,842 +to avoid memory leaks +and zombie objects, + +260 +00:13:29,842 --> 00:13:32,612 +and we have great tools +to help you avoid + +261 +00:13:32,612 --> 00:13:35,014 +and debug these issues. + +262 +00:13:35,014 --> 00:13:37,183 +I'll focus on two utilities: + +263 +00:13:37,183 --> 00:13:40,586 +NS::SharedPtr and NSZombie. + +264 +00:13:40,586 --> 00:13:44,457 +NS::SharedPtr is a new utility +that can help you manage + +265 +00:13:44,457 --> 00:13:46,626 +the object lifecycle. + +266 +00:13:46,626 --> 00:13:48,995 +You can find it under +Foundation framework + +267 +00:13:48,995 --> 00:13:51,064 +in the metal-cpp folder. + +268 +00:13:51,064 --> 00:13:53,332 +Notice that it is not +exactly the same + +269 +00:13:53,332 --> 00:13:55,268 +as std:shared_ptr. + +270 +00:13:55,268 --> 00:13:59,639 +So there's no dependency +on the C++ standard library + +271 +00:13:59,639 --> 00:14:03,042 +and no extra cost +on storing the reference count. + +272 +00:14:03,042 --> 00:14:05,778 +Here's what NS::SharedPtr +is like. + +273 +00:14:05,778 --> 00:14:08,614 +Transfer and retain functions +clearly express + +274 +00:14:08,614 --> 00:14:11,484 +the intent of consuming +an object. + +275 +00:14:11,484 --> 00:14:13,920 +Transfer creates a SharedPtr + +276 +00:14:13,920 --> 00:14:16,756 +without increasing +the pointee's referenceCount, + +277 +00:14:16,756 --> 00:14:21,027 +effectively transferring +ownership to the SharedPtr. + +278 +00:14:21,027 --> 00:14:25,531 +The retain function sends a +retain to the passed-in object. + +279 +00:14:25,531 --> 00:14:28,101 +Use this function +to keep alive objects + +280 +00:14:28,101 --> 00:14:29,936 +that are in AutoreleasePools + +281 +00:14:29,936 --> 00:14:32,538 +and to express +that the pointer's owner + +282 +00:14:32,538 --> 00:14:36,542 +has a vested interest +in the pointee's lifecycle. + +283 +00:14:36,542 --> 00:14:38,411 +You can access +the underlying object + +284 +00:14:38,411 --> 00:14:42,749 +as expected via get +and via the operator-. + +285 +00:14:42,749 --> 00:14:45,752 +SharedPtr copy, +move, construction, + +286 +00:14:45,752 --> 00:14:48,788 +and assignment work as expected, + +287 +00:14:48,788 --> 00:14:51,524 +with copy increasing +the retainCount. + +288 +00:14:51,524 --> 00:14:54,560 +Moves are fast and +do not affect the retain count + +289 +00:14:54,560 --> 00:14:56,429 +in the general case. + +290 +00:14:56,429 --> 00:14:59,599 +SharedPtrs always send +exactly one release + +291 +00:14:59,599 --> 00:15:02,034 +to the pointee +on destruction. + +292 +00:15:02,034 --> 00:15:03,703 +You can avoid this if you want + +293 +00:15:03,703 --> 00:15:06,239 +by calling +the detach function. + +294 +00:15:06,239 --> 00:15:07,707 +Going back to the top, + +295 +00:15:07,707 --> 00:15:10,176 +it's important to know +the differences between + +296 +00:15:10,176 --> 00:15:14,747 +creating a pointer +by transferring or retaining it. + +297 +00:15:14,747 --> 00:15:17,083 +So for TransferPtr, + +298 +00:15:17,083 --> 00:15:22,789 +suppose I have an MRR object, +with a reference count of 1. + +299 +00:15:22,789 --> 00:15:25,358 +After I pass it +to the TransferPtr function, + +300 +00:15:25,358 --> 00:15:28,327 +the SharedPtr takes +ownership of the object, + +301 +00:15:28,327 --> 00:15:30,963 +but its retainCount +doesn't change. + +302 +00:15:30,963 --> 00:15:32,799 +When the pointer +goes out of scope, + +303 +00:15:32,799 --> 00:15:34,867 +the SharedPtr's destructor runs + +304 +00:15:34,867 --> 00:15:37,637 +and calls release +on the MRR object, + +305 +00:15:37,637 --> 00:15:40,139 +which decrements +the retainCount to 0. + +306 +00:15:40,139 --> 00:15:43,276 +Another function +is NS::RetainPtr. + +307 +00:15:43,276 --> 00:15:46,012 +When you want to avoid +deallocating an object + +308 +00:15:46,012 --> 00:15:47,780 +because you want +to use it later, + +309 +00:15:47,780 --> 00:15:50,216 +you should use NS::RetainPtr. + +310 +00:15:50,216 --> 00:15:55,388 +Suppose we have this MRR object; +the retainCount is one. + +311 +00:15:55,388 --> 00:15:58,024 +After we pass it +to the RetainPtr function, + +312 +00:15:58,024 --> 00:16:00,726 +the retainCount increases +by one. + +313 +00:16:00,726 --> 00:16:02,261 +After running out of the scope, + +314 +00:16:02,261 --> 00:16:06,499 +this RetainPtr calls release +for this MRR object. + +315 +00:16:06,499 --> 00:16:09,101 +So the retainCount is one. + +316 +00:16:09,101 --> 00:16:12,405 +In general, NS::TransferPtr +takes the ownership + +317 +00:16:12,405 --> 00:16:13,940 +of an object for you. + +318 +00:16:13,940 --> 00:16:17,443 +But NS::RetainPtr +helps you retain an object + +319 +00:16:17,443 --> 00:16:20,746 +when you don't want it +to be deallocated. + +320 +00:16:20,746 --> 00:16:24,016 +When you pass an object +to these two functions, + +321 +00:16:24,016 --> 00:16:27,320 +NS::TransferPtr doesn't change +the reference count + +322 +00:16:27,320 --> 00:16:31,557 +but NS::RetainPtr increases +reference count by one + +323 +00:16:31,557 --> 00:16:34,493 +as it calls retain for you +under the hood. + +324 +00:16:34,493 --> 00:16:36,829 +The destructor +of these two functions + +325 +00:16:36,829 --> 00:16:39,498 +both call release +for the passed-in object + +326 +00:16:39,498 --> 00:16:43,269 +and, therefore, reference +count decreases by one. + +327 +00:16:43,269 --> 00:16:45,571 +If the reference count +hits zero, + +328 +00:16:45,571 --> 00:16:48,207 +the object will be freed +in runtime. + +329 +00:16:48,207 --> 00:16:51,744 +Here's an example +of NS::TransferPtr. + +330 +00:16:51,744 --> 00:16:53,479 +When I talked about +the render pass, + +331 +00:16:53,479 --> 00:16:55,681 +which drew a single triangle, + +332 +00:16:55,681 --> 00:16:58,484 +I needed this +render pipeline state. + +333 +00:16:58,484 --> 00:17:02,355 +Here are the calls to create +a render pipeline state object. + +334 +00:17:02,355 --> 00:17:05,791 +These are the attributes that a +render pipeline descriptor needs. + +335 +00:17:05,791 --> 00:17:07,927 +According to +Cocoa's conventions, + +336 +00:17:07,927 --> 00:17:12,198 +since these calls starts +with new and alloc, + +337 +00:17:12,198 --> 00:17:14,066 +I own these objects. + +338 +00:17:14,066 --> 00:17:17,603 +So I need to call release +for these objects. + +339 +00:17:17,603 --> 00:17:20,940 +With NS::SharedPtr, +I don't need to call release + +340 +00:17:20,940 --> 00:17:23,042 +for those MRR objects + +341 +00:17:23,042 --> 00:17:27,747 +because NS::SharedPtrs takes +the ownership of these objects. + +342 +00:17:27,747 --> 00:17:33,219 +So here, I pass raw pointers +to the TransferPtr function. + +343 +00:17:33,219 --> 00:17:36,289 +After doing that, +there's no need to call release + +344 +00:17:36,289 --> 00:17:38,724 +as I did in the previous slide. + +345 +00:17:38,724 --> 00:17:40,660 +If you are familiar with ARC, + +346 +00:17:40,660 --> 00:17:44,363 +you may find that MRR +used with NS::SharedPtr + +347 +00:17:44,363 --> 00:17:46,399 +is similar to using ARC. + +348 +00:17:46,399 --> 00:17:48,467 +You may encounter +use-after-free bugs + +349 +00:17:48,467 --> 00:17:50,536 +when handling memory manually. + +350 +00:17:50,536 --> 00:17:52,838 +They occur when you are trying +to use an object + +351 +00:17:52,838 --> 00:17:55,207 +which has been already released. + +352 +00:17:55,207 --> 00:17:58,311 +NSZombie is a good way +to check for those bugs. + +353 +00:17:58,311 --> 00:18:00,313 +When use-after-free +bugs occur, + +354 +00:18:00,313 --> 00:18:04,450 +it triggers a breakpoint and +provides you with a stack trace. + +355 +00:18:04,450 --> 00:18:06,485 +You can enable Zombies +very easily + +356 +00:18:06,485 --> 00:18:08,321 +with an environment variable. + +357 +00:18:08,321 --> 00:18:11,223 +Just set NSZombieEnabled +to YES. + +358 +00:18:11,223 --> 00:18:13,826 +Or If you're using Xcode, + +359 +00:18:13,826 --> 00:18:16,028 +you can enable Zombies +in a scheme. + +360 +00:18:16,028 --> 00:18:18,431 +Here's how it works. + +361 +00:18:18,431 --> 00:18:21,834 +I want to create a new +render pipeline state object + +362 +00:18:21,834 --> 00:18:25,071 +with the same +render pipeline settings. + +363 +00:18:25,071 --> 00:18:28,841 +So in this +newRenderPipelineState function, + +364 +00:18:28,841 --> 00:18:32,812 +I reuse the pDesc object. + +365 +00:18:34,814 --> 00:18:38,684 +After clicking on run, +Xcode triggers a breakpoint + +366 +00:18:38,684 --> 00:18:40,886 +and shows me the stack trace. + +367 +00:18:40,886 --> 00:18:43,723 +That means +I got something wrong. + +368 +00:18:43,723 --> 00:18:47,193 +Hm, what's the problem? + +369 +00:18:47,193 --> 00:18:53,999 +Maybe NSZombie can help here, +so I enable NSZombie in scheme. + +370 +00:18:57,003 --> 00:19:01,307 +When I run the program again, +NSZombie triggers a breakpoint. + +371 +00:19:01,307 --> 00:19:04,243 +I get something new +in the console output: + +372 +00:19:04,243 --> 00:19:09,181 +"message sent +to deallocated instance." + +373 +00:19:09,181 --> 00:19:12,885 +Oh, I reused an object +that I have already released. + +374 +00:19:12,885 --> 00:19:16,255 +And it's +the render pipeline descriptor. + +375 +00:19:16,255 --> 00:19:19,025 +So I need to use this +render pipeline descriptor + +376 +00:19:19,025 --> 00:19:21,227 +before calling release. + +377 +00:19:21,227 --> 00:19:24,096 +By doing that, +I fix the problem. + +378 +00:19:24,096 --> 00:19:28,034 +More tools and details +are covered in this year's talk, + +379 +00:19:28,034 --> 00:19:31,270 +"Profile and optimize +your game's memory." + +380 +00:19:31,270 --> 00:19:34,607 +For example, you can learn +how to track retainCount + +381 +00:19:34,607 --> 00:19:37,143 +in allocations in instruments. + +382 +00:19:37,143 --> 00:19:40,546 +Feel free to check out +other tools on Apple platforms. + +383 +00:19:40,546 --> 00:19:43,449 +You will find out that they +can help you debug your game + +384 +00:19:43,449 --> 00:19:45,618 +and improve performance. + +385 +00:19:45,618 --> 00:19:50,456 +Now you know how to manage +object lifecycles in metal-cpp. + +386 +00:19:50,456 --> 00:19:53,492 +But you may still need to +interface with other frameworks, + +387 +00:19:53,492 --> 00:19:56,695 +like game controller +and audio. + +388 +00:19:56,695 --> 00:19:59,231 +These are still in Objective-C. + +389 +00:19:59,231 --> 00:20:01,534 +How can you interface +with those APIs + +390 +00:20:01,534 --> 00:20:05,704 +and design an elegant +application architecture? + +391 +00:20:05,704 --> 00:20:09,175 +Say you wrote a ViewController +in Objective-C, + +392 +00:20:09,175 --> 00:20:13,279 +but you wrote a renderer in C++ +with metal-cpp. + +393 +00:20:13,279 --> 00:20:16,315 +You need to call renderer +methods, like draw, + +394 +00:20:16,315 --> 00:20:18,317 +from the ViewController. + +395 +00:20:18,317 --> 00:20:22,221 +The challenge here is to nicely +separate the two languages + +396 +00:20:22,221 --> 00:20:25,624 +but have them work together. + +397 +00:20:25,624 --> 00:20:29,028 +The solution is to create +an adapter class + +398 +00:20:29,028 --> 00:20:32,965 +which calls C++ +from Objective-C files. + +399 +00:20:32,965 --> 00:20:37,136 +By doing this, you can focus +on Objective-C or C++ + +400 +00:20:37,136 --> 00:20:40,439 +in files where +you implement features. + +401 +00:20:40,439 --> 00:20:41,640 +For example, + +402 +00:20:41,640 --> 00:20:45,578 +I can create a RendererAdapter +class in Objective-C. + +403 +00:20:45,578 --> 00:20:48,047 +And down in the implementation, + +404 +00:20:48,047 --> 00:20:51,450 +I add an Objective-C method +so that I can call it + +405 +00:20:51,450 --> 00:20:53,886 +directly from +the ViewController. + +406 +00:20:53,886 --> 00:20:55,621 +Inside of the interface, + +407 +00:20:55,621 --> 00:21:00,459 +I declare a C++ pointer +to a renderer object. + +408 +00:21:00,459 --> 00:21:02,795 +Inside the body of the method, + +409 +00:21:02,795 --> 00:21:06,999 +I directly call +the renderer's C++ method. + +410 +00:21:06,999 --> 00:21:10,002 +This method needs to pass +the MTK::View + +411 +00:21:10,002 --> 00:21:13,672 +as a C++ object +into the draw method, + +412 +00:21:13,672 --> 00:21:18,010 +so it casts the view +as a C++ type + +413 +00:21:18,010 --> 00:21:20,112 +by using the __bridge keyword. + +414 +00:21:20,112 --> 00:21:23,415 +I'll talk more +about this cast later. + +415 +00:21:23,415 --> 00:21:27,887 +In contrast, you need to call +MTKView which is written + +416 +00:21:27,887 --> 00:21:32,625 +with Objective-C in Renderer +which is written with C++. + +417 +00:21:32,625 --> 00:21:35,060 +It's challenging as well. + +418 +00:21:35,060 --> 00:21:39,131 +Similarly, the solution +is to create an adapter class. + +419 +00:21:39,131 --> 00:21:41,967 +With this class, in C++ files, + +420 +00:21:41,967 --> 00:21:47,039 +you can call Objective-C methods +using C++ interface. + +421 +00:21:47,039 --> 00:21:50,943 +For example, I can create +a ViewAdapter class. + +422 +00:21:50,943 --> 00:21:53,879 +I write the interfaces in C++, + +423 +00:21:53,879 --> 00:21:55,548 +so in the Renderer class, + +424 +00:21:55,548 --> 00:21:58,918 +I can call those C++ +view methods easily. + +425 +00:21:58,918 --> 00:22:00,553 +While in the implementation, + +426 +00:22:00,553 --> 00:22:03,756 +I call Objective-C methods +from MTKView, + +427 +00:22:03,756 --> 00:22:08,394 +including currentDrawable +and depthStencilTexture. + +428 +00:22:08,394 --> 00:22:11,697 +You may notice there're some +__bridge keywords here. + +429 +00:22:11,697 --> 00:22:14,400 +I use them to cast between +metal-cpp objects + +430 +00:22:14,400 --> 00:22:16,402 +and Objective-C objects. + +431 +00:22:16,402 --> 00:22:18,003 +As you learned in the beginning, + +432 +00:22:18,003 --> 00:22:21,440 +metal-cpp objects are manually +retained and released, + +433 +00:22:21,440 --> 00:22:24,176 +but objects created +by Objective-C + +434 +00:22:24,176 --> 00:22:27,046 +use automatic +reference counting. + +435 +00:22:27,046 --> 00:22:30,816 +You need to move objects +from MRR to ARC + +436 +00:22:30,816 --> 00:22:33,786 +and from ARC to MRR. + +437 +00:22:33,786 --> 00:22:35,521 +Here are three types +of bridge casting + +438 +00:22:35,521 --> 00:22:39,558 +which can help you cast +between Objective-C and C++. + +439 +00:22:39,558 --> 00:22:42,561 +They can also help you +transfer ownership + +440 +00:22:42,561 --> 00:22:45,464 +_bridge casting casts +between Objective-C + +441 +00:22:45,464 --> 00:22:47,733 +and metal-cpp objects. + +442 +00:22:47,733 --> 00:22:52,104 +There is no transfer of +ownership between them. + +443 +00:22:52,104 --> 00:22:55,741 +__bridge_retained casting +casts an Objective-C pointer + +444 +00:22:55,741 --> 00:23:00,746 +to a metal-cpp pointer and +takes the ownership from ARC. + +445 +00:23:00,746 --> 00:23:03,749 +__bridge_transfer casting moves +a metal-cpp pointer + +446 +00:23:03,749 --> 00:23:07,753 +to Objective-C and transfers +the ownership to ARC. + +447 +00:23:07,753 --> 00:23:10,189 +Going back to the problem, +you need to cast + +448 +00:23:10,189 --> 00:23:13,792 +between metal-cpp objects +and Objective-C objects. + +449 +00:23:13,792 --> 00:23:16,262 +If there's no transfer +of ownership, + +450 +00:23:16,262 --> 00:23:18,731 +you can use __bridge cast. + +451 +00:23:18,731 --> 00:23:23,002 +If you want to cast from +metal-cpp to Objective-C objects + +452 +00:23:23,002 --> 00:23:25,704 +and transfer +the ownership to Objective-C, + +453 +00:23:25,704 --> 00:23:29,074 +you should use +__bridge_transfer cast. + +454 +00:23:29,074 --> 00:23:32,444 +If you want to cast from +Objective-C to metal-cpp objects + +455 +00:23:32,444 --> 00:23:34,913 +and take the ownership +out of ARC, + +456 +00:23:34,913 --> 00:23:37,283 +you should use +__bridge_retained cast. + +457 +00:23:37,283 --> 00:23:40,085 +Here's a case when +I have to use MetalKit + +458 +00:23:40,085 --> 00:23:42,388 +to leverage +the asset loading code. + +459 +00:23:42,388 --> 00:23:45,491 +That means in my C++ +application, + +460 +00:23:45,491 --> 00:23:49,161 +I need a texture +as a metal-cpp object, + +461 +00:23:49,161 --> 00:23:53,465 +but it is created +by Objective-C methods. + +462 +00:23:53,465 --> 00:23:57,136 +I need the ability to transfer +ownership out of ARC + +463 +00:23:57,136 --> 00:23:59,605 +so I can manually release it. + +464 +00:23:59,605 --> 00:24:00,839 +And in this case, + +465 +00:24:00,839 --> 00:24:06,045 +I need to pick +__bridge_retained cast. + +466 +00:24:06,045 --> 00:24:10,015 +I have this C++ function that +loads a texture from the catalog + +467 +00:24:10,015 --> 00:24:13,819 +and I want to return +a metal-cpp texture. + +468 +00:24:13,819 --> 00:24:16,822 +But inside, I'm calling +some Objective-C functions + +469 +00:24:16,822 --> 00:24:19,758 +in MetalKit. + +470 +00:24:19,758 --> 00:24:23,929 +I need to define the options +that a texture loader needs. + +471 +00:24:23,929 --> 00:24:29,068 +Then I create a texture loader +by calling an Objective-C method + +472 +00:24:29,068 --> 00:24:31,170 +from MetalKit. + +473 +00:24:31,170 --> 00:24:35,474 +With that loader, +I can create a texture object + +474 +00:24:35,474 --> 00:24:38,377 +and load a texture +from the catalog. + +475 +00:24:38,377 --> 00:24:41,347 +This method is also +an Objective-C method + +476 +00:24:41,347 --> 00:24:43,215 +from MetalKit. + +477 +00:24:43,215 --> 00:24:46,452 +Now I have +an Objective-C type texture, + +478 +00:24:46,452 --> 00:24:49,922 +I need to cast it +to the metal-cpp object + +479 +00:24:49,922 --> 00:24:52,191 +and take it out of ARC. + +480 +00:24:52,191 --> 00:24:54,993 +With these steps in mind, +it's time to code, + +481 +00:24:54,993 --> 00:24:58,664 +and I'll show you +how casting works in practice. + +482 +00:24:58,664 --> 00:25:01,667 +First step is to define +the texture loader options + +483 +00:25:01,667 --> 00:25:03,902 +that a texture loader needs. + +484 +00:25:03,902 --> 00:25:07,806 +I can safely cast the metal-cpp +storage mode and usage + +485 +00:25:07,806 --> 00:25:09,742 +to the Objective-C type, + +486 +00:25:09,742 --> 00:25:13,946 +as the metal-cpp type +defines them to the same values. + +487 +00:25:13,946 --> 00:25:16,849 +Here I create a texture loader. + +488 +00:25:16,849 --> 00:25:20,252 +I have a device that +is a metal-cpp object, + +489 +00:25:20,252 --> 00:25:24,757 +and I need to pass it +to the initWithDevice method. + +490 +00:25:24,757 --> 00:25:28,961 +Because the metal-cpp object +is an Objective-C object, + +491 +00:25:28,961 --> 00:25:31,730 +I can cast it +like a toll-free object. + +492 +00:25:31,730 --> 00:25:35,200 +There is no transfer +of ownership. + +493 +00:25:35,200 --> 00:25:37,302 +Now I use +the texture loader options + +494 +00:25:37,302 --> 00:25:39,938 +and a texture loader +to create a texture. + +495 +00:25:39,938 --> 00:25:44,476 +And I want to return the loaded +texture as a metal-cpp object. + +496 +00:25:44,476 --> 00:25:46,812 +So I need +to take it out of ARC + +497 +00:25:46,812 --> 00:25:50,616 +and cast it to the corresponding +pointer type. + +498 +00:25:50,616 --> 00:25:53,252 +This is done with +a __bridge_retained cast. + +499 +00:25:53,252 --> 00:25:55,287 +After this, +I can use this texture + +500 +00:25:55,287 --> 00:25:57,423 +as any metal-cpp object. + +501 +00:25:57,423 --> 00:26:00,025 +I am responsible +for releasing it. + +502 +00:26:00,025 --> 00:26:03,929 +In this section, +I provided an adapter pattern + +503 +00:26:03,929 --> 00:26:06,432 +which can help you handle +two different languages + +504 +00:26:06,432 --> 00:26:08,300 +in your program. + +505 +00:26:08,300 --> 00:26:13,038 +I also showed how to interface +with Objective-C and C++ + +506 +00:26:13,038 --> 00:26:15,174 +with three types of casts. + +507 +00:26:15,174 --> 00:26:18,710 +To summarize, +metal-cpp is a lightweight + +508 +00:26:18,710 --> 00:26:22,581 +and very efficient +Metal C++ wrapper. + +509 +00:26:22,581 --> 00:26:26,285 +I talked about how to manage +object lifecycles + +510 +00:26:26,285 --> 00:26:29,021 +when using metal-cpp, + +511 +00:26:29,021 --> 00:26:31,557 +how to interface +with Objective-C + +512 +00:26:31,557 --> 00:26:32,958 +in an elegant manner, + +513 +00:26:32,958 --> 00:26:37,162 +and how our developer tools +can help you debug. + +514 +00:26:37,162 --> 00:26:41,567 +Download metal-cpp and play with +all the amazing samples now! + +515 +00:26:41,567 --> 00:26:44,203 +See what you can create +with Metal. + +516 +00:26:44,203 --> 00:26:47,206 +We look forward to seeing +your C++ applications + +517 +00:26:47,206 --> 00:26:49,575 +running across +all Apple platforms. + +518 +00:26:49,575 --> 00:26:51,410 +Thanks for watching! + +519 +00:26:51,410 --> 00:26:55,614 +♪ + diff --git a/eng/2022 Session 10162 Transform your geometry with Metal mesh shaders en.srt b/eng/2022 Session 10162 Transform your geometry with Metal mesh shaders en.srt new file mode 100644 index 0000000..41a04b7 --- /dev/null +++ b/eng/2022 Session 10162 Transform your geometry with Metal mesh shaders en.srt @@ -0,0 +1,1442 @@ +1 +00:00:01,034 --> 00:00:07,040 +[upbeat music] + +2 +00:00:09,309 --> 00:00:11,411 +- Hi! My name is Andrei. + +3 +00:00:11,445 --> 00:00:15,282 +I'm a GPU software engineer +with Metal Frameworks team. + +4 +00:00:15,315 --> 00:00:19,686 +Today, I'm excited to introduce to you +Metal mesh shaders. + +5 +00:00:19,720 --> 00:00:23,290 +Mesh shaders is the new +flexible pipeline in Metal + +6 +00:00:23,323 --> 00:00:26,927 +for GPU-driven +geometry creation and processing. + +7 +00:00:26,960 --> 00:00:31,231 +It improves on the vertex/fragment +pipeline, adding a lot of flexibility + +8 +00:00:31,265 --> 00:00:34,968 +and removing the limitations +of per-vertex processing. + +9 +00:00:35,002 --> 00:00:38,605 +It has multiple applications, +, but not limited to: + +10 +00:00:38,639 --> 00:00:41,108 +fine-grained geometry culling, + +11 +00:00:41,141 --> 00:00:44,478 +scalable procedural geometry +creation on the GPU, + +12 +00:00:44,511 --> 00:00:47,080 +and allowing custom geometry inputs + +13 +00:00:47,114 --> 00:00:49,283 +such as compressed vertex streams, + +14 +00:00:49,316 --> 00:00:52,553 +meshlets and complex procedural +algorithms. + +15 +00:00:52,586 --> 00:00:55,289 +I would like to +cover these three things today. + +16 +00:00:55,322 --> 00:00:59,359 +First, I will go over what +Metal mesh shaders are. + +17 +00:00:59,393 --> 00:01:03,430 +Then, I would like to tell you about +two mesh shaders use cases. + +18 +00:01:03,463 --> 00:01:06,800 +Mesh shaders are great +for generating procedural geometry + +19 +00:01:06,834 --> 00:01:09,102 +such as rendering procedural hair. + +20 +00:01:09,136 --> 00:01:13,207 +Mesh shaders also help +improve scene processing and rendering. + +21 +00:01:13,240 --> 00:01:15,909 +A primary example of this +is using mesh shaders + +22 +00:01:15,943 --> 00:01:18,979 +to implement +GPU-driven meshlet culling. + +23 +00:01:19,012 --> 00:01:21,348 +Let's start with introducing mesh shaders. + +24 +00:01:21,381 --> 00:01:23,383 +Here is the Stanford Bunny, + +25 +00:01:23,417 --> 00:01:27,855 +which represents a typical mesh +that you could render on the GPU. + +26 +00:01:27,888 --> 00:01:31,458 +In order to render this mesh, +the vertex and index data + +27 +00:01:31,491 --> 00:01:34,962 +would first have to be placed +in the device memory. + +28 +00:01:34,995 --> 00:01:39,733 +You would then have to use render +command encoder to execute a draw call. + +29 +00:01:39,766 --> 00:01:44,304 +A traditional rendering pipeline +consists of three fundamental stages: + +30 +00:01:44,338 --> 00:01:49,176 +A programmable vertex shader stage, +a fixed-function rasterization stage, + +31 +00:01:49,209 --> 00:01:52,346 +and a programmable fragment shader +stage. + +32 +00:01:52,379 --> 00:01:54,848 +The vertex shader stage +would take geometry + +33 +00:01:54,882 --> 00:01:57,751 +from device memory +as an input and process it. + +34 +00:01:57,784 --> 00:02:00,888 +The rasterizer would produce +screen space fragments, + +35 +00:02:00,921 --> 00:02:04,391 +and the fragment shader would +shade them to produce the final image. + +36 +00:02:04,424 --> 00:02:08,862 +This pipeline has been, and still is, +serving its purpose extremely well. + +37 +00:02:08,896 --> 00:02:13,300 +However, it lacks flexibility +and has certain limitations. + +38 +00:02:13,333 --> 00:02:15,335 +Let's step through an example. + +39 +00:02:15,369 --> 00:02:19,606 +Imagine that you want to generate +some procedural geometry on the GPU. + +40 +00:02:19,640 --> 00:02:25,179 +For example, you've decided +to add procedural fur to this bunny. + +41 +00:02:25,212 --> 00:02:30,117 +Let me show you how this task is handled +by the traditional geometry pipeline. + +42 +00:02:30,150 --> 00:02:33,487 +Traditionally, +in order to generate procedural geometry, + +43 +00:02:33,520 --> 00:02:37,057 +you would also need to have +a compute command encoder + +44 +00:02:37,090 --> 00:02:40,627 +that would perform +a compute kernel dispatch. + +45 +00:02:40,661 --> 00:02:44,064 +The compute kernel would take +the original mesh as an input, + +46 +00:02:44,097 --> 00:02:49,036 +generate procedural geometry and +output it back into device memory. + +47 +00:02:49,069 --> 00:02:52,806 +You would then use a render command +encoder to execute a draw call + +48 +00:02:52,840 --> 00:02:57,444 +that would take procedural geometry +as an input and produce a final image . + +49 +00:02:57,477 --> 00:03:00,547 +Not only does this approach require +two command encoders, + +50 +00:03:00,581 --> 00:03:03,050 +it also requires you +to allocate additional memory + +51 +00:03:03,083 --> 00:03:05,519 +to store the procedural geometry. + +52 +00:03:05,552 --> 00:03:09,056 +In case of indirect draw calls +or high expansion factors, + +53 +00:03:09,089 --> 00:03:13,327 +the amount of this memory can +be quite high and hard to predict. + +54 +00:03:13,360 --> 00:03:18,799 +There is also a barrier between the two +encoders, serializing work across the GPU. + +55 +00:03:18,832 --> 00:03:21,735 +Metal mesh shaders addresses all +of those issues. + +56 +00:03:21,768 --> 00:03:24,538 +Mesh shaders is a new geometry pipeline + +57 +00:03:24,571 --> 00:03:29,209 +that replaces the vertex shader stage +with two new programmable stages-- + +58 +00:03:29,243 --> 00:03:32,179 +the object shader stage +and the mesh shader stage. + +59 +00:03:32,212 --> 00:03:36,383 +In this example, the object shader +would take geometry as an input , + +60 +00:03:36,416 --> 00:03:40,087 +process it and output some data-- +which we call "payload"-- + +61 +00:03:40,120 --> 00:03:41,955 +to the mesh shader. + +62 +00:03:41,989 --> 00:03:45,058 +It is up to you +to decide what this data is. + +63 +00:03:45,092 --> 00:03:50,264 +The mesh shader, in turn, would use +this data to generate procedural geometry. + +64 +00:03:50,297 --> 00:03:54,635 +This procedural geometry +would only exist inside the draw call, + +65 +00:03:54,668 --> 00:03:58,338 +so it would not require you +to allocate any device memory. + +66 +00:03:58,372 --> 00:04:02,075 +It would be pipelined +straight to the rasterizer + +67 +00:04:02,109 --> 00:04:06,613 +and then to a fragment shader +that would produce the final image. + +68 +00:04:06,647 --> 00:04:10,517 +Mesh draw calls are performed using +the same type of render command encoder + +69 +00:04:10,551 --> 00:04:12,819 +as the traditional draw calls. + +70 +00:04:12,853 --> 00:04:16,924 +Mesh draw calls and traditional +draw calls can be mixed and matched. + +71 +00:04:16,957 --> 00:04:20,527 +Now, let's take a look at +two new programmable stages. + +72 +00:04:22,029 --> 00:04:23,964 +In contrast to vertex shaders, + +73 +00:04:23,997 --> 00:04:27,634 +object and mesh shaders +are similar to compute kernels. + +74 +00:04:27,668 --> 00:04:30,571 +They are launched +in grids of thread groups. + +75 +00:04:30,604 --> 00:04:33,941 +Each thread group is a grid +of individual threads that, + +76 +00:04:33,974 --> 00:04:37,878 +like compute threads, +can communicate with each other. + +77 +00:04:37,911 --> 00:04:41,949 +Additionally, each object thread group +can spawn a mesh grid + +78 +00:04:41,982 --> 00:04:45,819 +and programmatically define the size of +the mesh grid it launches, + +79 +00:04:45,853 --> 00:04:48,255 +providing plenty of flexibility. + +80 +00:04:48,288 --> 00:04:53,293 +Each object thread group passes +payload data to the mesh grid it spawns. + +81 +00:04:53,327 --> 00:04:57,231 +As the name suggests, +the object stage processes objects. + +82 +00:04:57,264 --> 00:05:01,201 +Object is an abstract concept that +you can define according to your needs. + +83 +00:05:01,235 --> 00:05:04,705 +It can be a scene model, +a part of a scene model or, + +84 +00:05:04,738 --> 00:05:09,743 +for example, a region of space where +you want to generate procedural geometry. + +85 +00:05:09,776 --> 00:05:12,546 +The mesh stage is designed +to build meshes + +86 +00:05:12,579 --> 00:05:15,516 +and send geometry data +directly to the rasterizer. + +87 +00:05:15,549 --> 00:05:19,987 +The next two examples will cover +the relation between objects and meshes. + +88 +00:05:20,020 --> 00:05:23,657 +The first one is using mesh shaders +to implement hair rendering. + +89 +00:05:23,690 --> 00:05:28,262 +To simplify this task, instead of +the Bunny model, I'll use a simple plane. + +90 +00:05:28,295 --> 00:05:32,432 +To generate a patch of hair, +I'll divide the input geometry into tiles, + +91 +00:05:32,466 --> 00:05:35,636 +where each tile will calculate +a level of detail + +92 +00:05:35,669 --> 00:05:38,405 +and the number of strands +it needs to produce, + +93 +00:05:38,438 --> 00:05:41,742 +and then generate +each individual strand of hair. + +94 +00:05:41,775 --> 00:05:45,112 +Let me show you how to +procedurally generate hair on this plane + +95 +00:05:45,145 --> 00:05:46,780 +using mesh shaders. + +96 +00:05:46,813 --> 00:05:49,016 +The plane can be split into tiles, + +97 +00:05:49,049 --> 00:05:52,252 +where each tile corresponds +to an object threadgroup. + +98 +00:05:52,286 --> 00:05:55,722 +Each object threadgroup will +calculate the number of hair strands + +99 +00:05:55,756 --> 00:05:59,693 +and generate the curve control +points for each strand. + +100 +00:05:59,726 --> 00:06:01,762 +This will become the payload. + +101 +00:06:01,795 --> 00:06:04,765 +Our object threadgroup then launches +a mesh grid, + +102 +00:06:04,798 --> 00:06:08,202 +where each mesh threadgroup +represents a single strand of hair. + +103 +00:06:08,235 --> 00:06:11,538 +Each mesh threadgroup +outputs the mesh to the rasterizer. + +104 +00:06:11,572 --> 00:06:14,308 +The new geometry pipeline +allows you to map + +105 +00:06:14,341 --> 00:06:16,476 +your geometry processing +closely to hardware + +106 +00:06:16,510 --> 00:06:21,782 +and enables you to take full advantage +of all the threads your GPU offers. + +107 +00:06:21,815 --> 00:06:25,552 +In a mesh render pipeline, +input geometry is split into tiles + +108 +00:06:25,586 --> 00:06:27,487 +for the object shader grid. + +109 +00:06:27,521 --> 00:06:31,525 +Each object shader threadgroup +can independently generate a payload + +110 +00:06:31,558 --> 00:06:33,260 +and launch a mesh grid. + +111 +00:06:33,293 --> 00:06:37,464 +Each mesh shader threadgroup from +the grid generates a metal::mesh + +112 +00:06:37,497 --> 00:06:41,034 +which is further processed in +the rest of the rendering pipeline. + +113 +00:06:41,068 --> 00:06:46,006 +Let's take a closer look at +the data produced by each of those stages. + +114 +00:06:46,039 --> 00:06:48,709 +The payload is defined +in the object shader. + +115 +00:06:48,742 --> 00:06:51,245 +Each object threadgroup +passes the customized payload + +116 +00:06:51,278 --> 00:06:55,349 +to the generated mesh grid +that an object threadgroup spawns. + +117 +00:06:55,382 --> 00:07:00,187 +In the case of hair rendering, the payload +consists of the curve control points. + +118 +00:07:00,220 --> 00:07:03,924 +Meanwhile, the mesh shader +outputs vertex and primitive data + +119 +00:07:03,957 --> 00:07:08,795 +through a new metal::mesh type, which +I will discuss in further detail in a bit. + +120 +00:07:09,963 --> 00:07:12,332 +The object and mesh stages +output mesh data + +121 +00:07:12,366 --> 00:07:15,369 +that is consumed by rest of the pipeline. + +122 +00:07:15,402 --> 00:07:18,572 +Similar to the vertex output +from the traditional pipeline, + +123 +00:07:18,605 --> 00:07:21,708 +the mesh data is first +consumed by the rasterizer, + +124 +00:07:21,742 --> 00:07:25,546 +then the fragment shader executes. + +125 +00:07:25,579 --> 00:07:30,184 +Let's take a deeper dive into how +to set up a hair rendering mesh pipeline. + +126 +00:07:30,217 --> 00:07:34,755 +First, the plane that is to be +covered in hair is split into tiles, + +127 +00:07:34,788 --> 00:07:38,025 +where each tile corresponds +to an object threadgroup. + +128 +00:07:38,058 --> 00:07:41,228 +The object threadgroup +determines the mesh grid size + +129 +00:07:41,261 --> 00:07:45,332 +and initializes the payload data +that it passes to the mesh grid. + +130 +00:07:45,365 --> 00:07:50,771 +In this case, the tile has six strands +of hair and generates a 3x2 mesh grid, + +131 +00:07:50,804 --> 00:07:53,740 +along with a curve +payload data for each strand. + +132 +00:07:54,408 --> 00:07:57,077 +Each threadgroup can generate +unique mesh grid sizes. + +133 +00:07:57,110 --> 00:07:59,913 +For the next threadgroup, +only four strands of hair + +134 +00:07:59,947 --> 00:08:01,615 +need to be generated, + +135 +00:08:01,648 --> 00:08:03,750 +so a 2x2 mesh grid is set + +136 +00:08:03,784 --> 00:08:07,654 +along with initializing curve +payload data for 4 strands. + +137 +00:08:07,688 --> 00:08:11,592 +This is how the object shader +that implements this approach looks like. + +138 +00:08:11,625 --> 00:08:14,361 +The object attribute +has been added to Metal + +139 +00:08:14,394 --> 00:08:17,564 +to specify what code +is an object shader. + +140 +00:08:17,598 --> 00:08:21,235 +In addition to the payload +attribute and object_data address space, + +141 +00:08:21,268 --> 00:08:24,738 +allow payload arguments +to be used in shaders. + +142 +00:08:26,139 --> 00:08:31,378 +The mesh grid properties argument +is used to encode the mesh grid size. + +143 +00:08:31,411 --> 00:08:34,214 +The next step is pipeline initialization. + +144 +00:08:34,248 --> 00:08:38,018 +First, allocate the +mesh render pipeline descriptor, + +145 +00:08:38,051 --> 00:08:43,991 +then initialize the object function and +specify the desired payload length, + +146 +00:08:44,024 --> 00:08:47,494 +along with the maximum number +of threads per threadgroup. + +147 +00:08:47,528 --> 00:08:50,430 +There are certain +constraints on object shaders. + +148 +00:08:50,464 --> 00:08:54,434 +Payload format and contents +are fully customizable. + +149 +00:08:54,468 --> 00:08:58,939 +However, payload size can't exceed +the limit of 16 kilobytes. + +150 +00:08:58,972 --> 00:09:01,542 +Also, the maximum number of +mesh threadgroups + +151 +00:09:01,575 --> 00:09:06,013 +that each object threadgroup +produces can't exceed 1024. + +152 +00:09:06,046 --> 00:09:08,882 +The next step after getting +the object shader stage ready + +153 +00:09:08,916 --> 00:09:11,718 +is initializing the mesh shader stage. + +154 +00:09:11,752 --> 00:09:15,222 +The mesh shader has the user +defined payload as an input. + +155 +00:09:15,255 --> 00:09:19,860 +In this example, the payload +is the set of curve control points. + +156 +00:09:19,893 --> 00:09:22,529 +Each mesh threadgroup produces +a metal::mesh, + +157 +00:09:22,563 --> 00:09:25,098 +which is a single strand of hair. + +158 +00:09:25,132 --> 00:09:29,236 +The output mesh of the mesh shader +must have a metal::mesh type. + +159 +00:09:29,269 --> 00:09:33,640 +A metal::mesh is a built-in structure +in Metal that provides you an interface + +160 +00:09:33,674 --> 00:09:38,879 +to output vertex and primitive data +to the rasterizer and fragment shader. + +161 +00:09:38,912 --> 00:09:43,784 +Each metal::mesh defines a vertex +data type, much like the output type + +162 +00:09:43,817 --> 00:09:45,319 +of a vertex shader, + +163 +00:09:45,352 --> 00:09:49,356 +a primitive data type, +the maximum number of vertices, + +164 +00:09:49,389 --> 00:09:51,725 +the maximum number of primitives, + +165 +00:09:51,758 --> 00:09:57,364 +and finally, the mesh topology-- +either point, line, or triangle. + +166 +00:09:57,397 --> 00:10:00,968 +The mesh attribute was added to +Metal shading language + +167 +00:10:01,001 --> 00:10:04,004 +to specify what code is a mesh shader. + +168 +00:10:04,037 --> 00:10:07,841 +Metal::mesh is used as an output +structure in the mesh shader. + +169 +00:10:09,943 --> 00:10:13,213 +Mesh shaders are great for GPU-driven +geometry processing + +170 +00:10:13,247 --> 00:10:15,849 +as they allow you to produce +these metal::meshes on the fly + +171 +00:10:15,883 --> 00:10:18,318 +for the rasterizer to consume. + +172 +00:10:18,352 --> 00:10:21,455 +Mesh shaders leverage the metal::mesh +to their advantage + +173 +00:10:21,488 --> 00:10:24,324 +so you can put more +processing into render commands + +174 +00:10:24,358 --> 00:10:27,027 +without having additional compute passes. + +175 +00:10:27,060 --> 00:10:31,265 +Encoding a mesh is done across threads +within the same thread group. + +176 +00:10:31,298 --> 00:10:35,836 +In this example, the first 9 threads +of a thread group will encode the vertex, + +177 +00:10:35,869 --> 00:10:39,873 +index, +and primitive data of this hair strand. + +178 +00:10:39,907 --> 00:10:45,279 +Threads 0 through 4 +each encode one vertex in the mesh. + +179 +00:10:45,312 --> 00:10:50,017 +The remaining threads in the thread +group do not encode vertices in the mesh. + +180 +00:10:50,050 --> 00:10:55,355 +Next, all 9 threads encode one index +into the mesh indices. + +181 +00:10:57,291 --> 00:11:03,697 +Next, the first three threads encode +primitive data for the three triangles. + +182 +00:11:03,730 --> 00:11:07,234 +The rest of the threads +don't encode any primitive data. + +183 +00:11:07,267 --> 00:11:12,940 +And lastly, one thread shall encode +the primitive count for the metal::mesh. + +184 +00:11:12,973 --> 00:11:16,076 +Let me show you the source code +for this mesh shader. + +185 +00:11:16,109 --> 00:11:19,780 +The mesh shader is organized to avoid +as much divergence in threads + +186 +00:11:19,813 --> 00:11:23,283 +as possible--following the +same steps to encode vertex, + +187 +00:11:23,317 --> 00:11:26,486 +index and primitive data, + +188 +00:11:26,520 --> 00:11:28,856 +and finally the primitive count. + +189 +00:11:30,858 --> 00:11:34,394 +Let's switch back to initializing +the mesh pipeline descriptor. + +190 +00:11:34,428 --> 00:11:37,764 +On the mesh pipeline descriptor, +the mesh function + +191 +00:11:37,798 --> 00:11:41,301 +along with the maximum threads +per mesh thread group, is set. + +192 +00:11:41,335 --> 00:11:45,239 +There are limits that metal::mesh +structure needs to adhere to. + +193 +00:11:45,272 --> 00:11:47,841 +Metal::mesh shaders +have the following limits: + +194 +00:11:47,875 --> 00:11:54,414 +metal::mesh supports up to 256 +vertices and up to 512 primitives. + +195 +00:11:54,448 --> 00:11:58,352 +The total size of metal::mesh +cannot exceed 16 kilobytes. + +196 +00:11:58,385 --> 00:12:01,255 +Now that the mesh grid has +generated the metal::meshes, + +197 +00:12:01,288 --> 00:12:06,493 +these are then fed to the rasterizer +and finally the fragment shader is run. + +198 +00:12:06,527 --> 00:12:09,530 +So, similar to the traditional +render pipeline, + +199 +00:12:09,563 --> 00:12:13,066 +the fragment function +is set on the mesh pipeline descriptor. + +200 +00:12:13,100 --> 00:12:15,502 +Now that the descriptor has +been initialized, + +201 +00:12:15,536 --> 00:12:18,572 +the pipeline state is created + +202 +00:12:18,605 --> 00:12:22,209 +through the "make render pipeline +state with mesh descriptor" method + +203 +00:12:22,242 --> 00:12:23,744 +on the Metal device. + +204 +00:12:23,777 --> 00:12:29,082 +Encoding a mesh pipeline is very similar +to encoding a traditional draw call. + +205 +00:12:29,116 --> 00:12:31,718 +The pipeline state is set on the encoder. + +206 +00:12:31,752 --> 00:12:34,788 +Each stage in the pipeline +can have resources bound. + +207 +00:12:34,821 --> 00:12:38,258 +In this example the bound resources are: + +208 +00:12:38,292 --> 00:12:40,861 +an object buffer to an object stage, + +209 +00:12:40,894 --> 00:12:42,629 +a texture to a mesh stage, + +210 +00:12:42,663 --> 00:12:46,667 +and a fragment buffer to a fragment stage. + +211 +00:12:46,700 --> 00:12:51,872 +Next, I'm defining a few constants that +I'll need to launch the mesh pipeline: + +212 +00:12:51,905 --> 00:12:54,141 +object grid dimensions, + +213 +00:12:54,174 --> 00:12:56,643 +number of threads per object threadgroup, + +214 +00:12:56,677 --> 00:12:59,546 +number of threads per mesh threadgroup, + +215 +00:12:59,580 --> 00:13:02,149 +and use these constants to encode the draw + +216 +00:13:02,182 --> 00:13:04,718 +through the new +"draw mesh threadgroups" method. + +217 +00:13:04,751 --> 00:13:07,754 +The same approach that is used to +render a plane of hair + +218 +00:13:07,788 --> 00:13:09,823 +can be applied to the whole bunny + +219 +00:13:09,857 --> 00:13:13,360 +to procedurally generate fur +through a mesh pipeline. + +220 +00:13:13,393 --> 00:13:16,630 +Next, let's look at another way +to use mesh shaders. + +221 +00:13:16,663 --> 00:13:19,233 +Mesh shaders can be used to +efficiently process + +222 +00:13:19,266 --> 00:13:23,003 +and render large amounts of +geometry using meshlet culling. + +223 +00:13:23,036 --> 00:13:26,373 +The basis for this technique +is splitting the scene meshes up + +224 +00:13:26,406 --> 00:13:28,909 +into smaller pieces called meshlets. + +225 +00:13:33,213 --> 00:13:36,884 +Splitting scene geometry into meshlets +increases the granularity of the scene, + +226 +00:13:36,917 --> 00:13:39,753 +allowing more +efficient and fine-grained culling. + +227 +00:13:39,786 --> 00:13:42,856 +This allows you to greatly +reduce geometry overhead. + +228 +00:13:42,890 --> 00:13:44,858 +Leveraging meshlet granularity processing + +229 +00:13:44,892 --> 00:13:47,594 +allows for efficient occlusion +and culling algorithms + +230 +00:13:47,628 --> 00:13:51,632 +such as screen space +occlusion culling and normal filtering. + +231 +00:13:51,665 --> 00:13:54,835 +You can use mesh shaders to +implement a fully GPU-driven culling + +232 +00:13:54,868 --> 00:13:56,069 +and rendering pipeline. + +233 +00:13:56,937 --> 00:13:59,873 +Here is a traditional GPU-driven pipeline + +234 +00:13:59,907 --> 00:14:02,476 +that performs scene processing and +rendering + +235 +00:14:02,509 --> 00:14:05,779 +using one compute and one render pass. + +236 +00:14:05,812 --> 00:14:07,748 +The scene data is split into meshlets + +237 +00:14:07,781 --> 00:14:10,150 +and fed into the compute pass, + +238 +00:14:10,184 --> 00:14:12,853 +which is responsible for frustum culling, + +239 +00:14:12,886 --> 00:14:14,188 +LOD selection, + +240 +00:14:14,221 --> 00:14:17,157 +and encoding the draws to device memory. + +241 +00:14:17,191 --> 00:14:20,561 +The render pass then executes +the draw commands for the scene + +242 +00:14:20,594 --> 00:14:23,030 +and produces the final image. + +243 +00:14:23,063 --> 00:14:27,334 +Using mesh shaders, it is possible +to remove synchronization points + +244 +00:14:27,367 --> 00:14:31,138 +and avoid the intermediate +draw commands by merging two passes + +245 +00:14:31,171 --> 00:14:33,607 +into a single mesh shader dispatch. + +246 +00:14:33,640 --> 00:14:35,375 +Let me show you how it can be done. + +247 +00:14:35,409 --> 00:14:37,878 +Here is a single render pass + +248 +00:14:37,911 --> 00:14:39,947 +that executes a mesh shader dispatch. + +249 +00:14:39,980 --> 00:14:42,149 +The object shader performs frustum culling + +250 +00:14:42,182 --> 00:14:45,586 +and calculates LODs for +each of the visible meshlets. + +251 +00:14:45,619 --> 00:14:50,257 +The payload to the mesh shader is a list +of meshlet IDs that should be encoded. + +252 +00:14:50,290 --> 00:14:53,527 +The mesh shader then encodes +the metal::mesh objects + +253 +00:14:53,560 --> 00:14:55,863 +that shall be rasterized and shaded. + +254 +00:14:55,896 --> 00:14:58,465 +The final image is then +shaded in the fragment shader, + +255 +00:14:58,498 --> 00:15:00,801 +identical to the traditional pipeline. + +256 +00:15:00,834 --> 00:15:02,536 +The geometry processing is done entirely + +257 +00:15:02,569 --> 00:15:04,304 +within the mesh threadgroups command + +258 +00:15:04,338 --> 00:15:05,973 +and within a single encoder. + +259 +00:15:06,006 --> 00:15:08,242 +There is no longer a need +for an intermediate buffer + +260 +00:15:08,275 --> 00:15:09,810 +to store these draw commands, + +261 +00:15:09,843 --> 00:15:12,746 +as the triangle data +is encoded in the mesh shader. + +262 +00:15:13,881 --> 00:15:15,983 +Let's turn our attention to culling now-- + +263 +00:15:16,016 --> 00:15:19,152 +specifically, +an implementation of meshlet culling. + +264 +00:15:19,186 --> 00:15:22,956 +The scene consists of models +represented by the shapes here. + +265 +00:15:22,990 --> 00:15:27,628 +In this implementation, each model of the +scene will become part of the object grid. + +266 +00:15:27,661 --> 00:15:30,564 +The mesh grids spawned by +the object shader threadgroups + +267 +00:15:30,597 --> 00:15:32,299 +will consist of meshlets-- + +268 +00:15:32,332 --> 00:15:35,636 +patches of triangles +that make up the surface of the model. + +269 +00:15:35,669 --> 00:15:38,038 +The new geometry pipeline +is very flexible. + +270 +00:15:38,071 --> 00:15:41,708 +It is up to you to decide how to +map your scene onto an object grid. + +271 +00:15:41,742 --> 00:15:45,379 +In this example, I'm mapping +each model to an object threadgroup, + +272 +00:15:45,412 --> 00:15:48,982 +but you can use the mapping +that better suits your task. + +273 +00:15:49,016 --> 00:15:51,919 +Now, the object shader will determine +the visibility of meshlets + +274 +00:15:51,952 --> 00:15:53,554 +using the viewing frustum + +275 +00:15:53,587 --> 00:15:58,058 +and dispatch work only for +what will be presented in the final image. + +276 +00:15:58,091 --> 00:16:00,794 +Let's focus on two models from the scene. + +277 +00:16:00,827 --> 00:16:04,498 +The object shader launches mesh grids +based on determined visibility. + +278 +00:16:04,531 --> 00:16:09,570 +The mesh shader then processes +the meshlets and constructs metal::meshes. + +279 +00:16:09,603 --> 00:16:12,773 +The programmable mesh grid +size enables flexible dispatching + +280 +00:16:12,806 --> 00:16:16,410 +so only visible meshlets +get processed by the mesh shader. + +281 +00:16:16,443 --> 00:16:21,315 +This reduces the time spent processing +unseen geometry later in the pipeline. + +282 +00:16:21,348 --> 00:16:25,485 +The fixed function rasterizer only +receives surfaces that are known to be + +283 +00:16:25,519 --> 00:16:27,621 +visible and will reduce time spent + +284 +00:16:27,654 --> 00:16:29,957 +processing and clipping out +unseen geometry. + +285 +00:16:30,791 --> 00:16:35,262 +Finally, the programmable fragment shader +is invoked and produces the final image. + +286 +00:16:35,996 --> 00:16:38,465 +As you can see, +there are a wide variety of problems + +287 +00:16:38,498 --> 00:16:41,101 +that the new geometry pipeline +allows you to address + +288 +00:16:41,134 --> 00:16:45,439 +such as creating procedural meshes or +making your draw calls more efficient, + +289 +00:16:45,472 --> 00:16:48,308 +as demonstrated +in this meshlet culling example. + +290 +00:16:48,342 --> 00:16:52,346 +Metal now includes a new geometry +pipeline that is modern and flexible. + +291 +00:16:52,379 --> 00:16:56,283 +It is now easier than ever before +to create procedural geometry, + +292 +00:16:56,316 --> 00:16:59,253 +as demonstrated +in the hair rendering example. + +293 +00:16:59,286 --> 00:17:02,489 +Additionally, +the possibilities for GPU-driven work + +294 +00:17:02,523 --> 00:17:04,691 +in a single render pass have expanded + +295 +00:17:04,725 --> 00:17:08,529 +without requiring additional compute +passes or intermediate buffers, + +296 +00:17:08,562 --> 00:17:10,964 +as seen in the meshlet culling demo. + +297 +00:17:11,899 --> 00:17:16,270 +This new geometry pipeline is available +in Family7 and Mac2 devices. + +298 +00:17:18,138 --> 00:17:21,575 +To help you start learning and +experimenting with mesh shaders, + +299 +00:17:21,608 --> 00:17:24,344 +a sample code is available +on Apple developer web site + +300 +00:17:24,378 --> 00:17:27,080 +that shows how to use the new API. + +301 +00:17:27,114 --> 00:17:29,583 +I'm excited to see +how you use this feature + +302 +00:17:29,616 --> 00:17:32,853 +and utilize the massively +parallel nature of Apple GPUs + +303 +00:17:32,886 --> 00:17:35,022 +to fit your geometry processing needs. + +304 +00:17:35,055 --> 00:17:36,857 +Thank you so much for watching! + diff --git a/eng/2022 Session 10166 Explore App Tracking Transparency en.srt b/eng/2022 Session 10166 Explore App Tracking Transparency en.srt new file mode 100644 index 0000000..f02b9df --- /dev/null +++ b/eng/2022 Session 10166 Explore App Tracking Transparency en.srt @@ -0,0 +1,1368 @@ +1 +00:00:00,167 --> 00:00:03,704 +♪ instrumental hip hop music ♪ + +2 +00:00:03,704 --> 00:00:09,676 +♪ + +3 +00:00:09,676 --> 00:00:12,279 +Hi, I'm Julia +from Privacy Engineering, + +4 +00:00:12,279 --> 00:00:16,516 +and welcome to "Explore +App Tracking Transparency." + +5 +00:00:16,516 --> 00:00:20,787 +At Apple, we believe privacy +is a fundamental human right. + +6 +00:00:20,787 --> 00:00:22,823 +Part of engineering +great privacy + +7 +00:00:22,823 --> 00:00:25,259 +is giving people +choices and control + +8 +00:00:25,259 --> 00:00:27,427 +over how their data is used. + +9 +00:00:27,427 --> 00:00:28,929 +When people have these choices + +10 +00:00:28,929 --> 00:00:32,165 +and understand how their data +will be linked or shared, + +11 +00:00:32,165 --> 00:00:36,803 +they are more likely to trust +and engage with your app. + +12 +00:00:36,803 --> 00:00:38,705 +That's why, +beginning last year, + +13 +00:00:38,705 --> 00:00:42,809 +App Store policy requires apps +to receive users' permission + +14 +00:00:42,809 --> 00:00:45,779 +before tracking users +across apps and websites + +15 +00:00:45,779 --> 00:00:48,115 +owned by other companies +by adopting + +16 +00:00:48,115 --> 00:00:51,551 +the AppTrackingTransparency +framework. + +17 +00:00:51,551 --> 00:00:54,554 +Today, I'm going to talk to you +about when and how to adopt + +18 +00:00:54,554 --> 00:00:57,090 +App Tracking Transparency. + +19 +00:00:57,090 --> 00:00:59,526 +First, I'll start with some +background on tracking + +20 +00:00:59,526 --> 00:01:02,496 +to help you understand if +and when your app needs to adopt + +21 +00:01:02,496 --> 00:01:05,866 +the AppTrackingTransparency +framework. + +22 +00:01:05,866 --> 00:01:08,335 +Then, I'll highlight some +key things to keep in mind + +23 +00:01:08,335 --> 00:01:11,338 +when adopting the framework +in practice. + +24 +00:01:11,338 --> 00:01:14,441 +Let's get started with some +background on tracking. + +25 +00:01:14,441 --> 00:01:18,512 +So, how is tracking defined +for App Tracking Transparency? + +26 +00:01:18,512 --> 00:01:21,815 +Tracking refers to linking +user or device data + +27 +00:01:21,815 --> 00:01:24,718 +collected from your app +with user or device data + +28 +00:01:24,718 --> 00:01:27,421 +collected from other +companies' apps, websites, + +29 +00:01:27,421 --> 00:01:29,990 +or offline properties +for targeted advertising + +30 +00:01:29,990 --> 00:01:32,993 +or advertising-measurement +purposes. + +31 +00:01:32,993 --> 00:01:35,996 +Tracking also refers +to sharing user or device data + +32 +00:01:35,996 --> 00:01:39,132 +with data brokers. + +33 +00:01:39,132 --> 00:01:41,001 +Let's talk through +some example scenarios + +34 +00:01:41,001 --> 00:01:44,271 +to better understand +how tracking is defined. + +35 +00:01:44,271 --> 00:01:46,540 +First, let's look +at an advertising scenario + +36 +00:01:46,540 --> 00:01:49,910 +that doesn't involve tracking. + +37 +00:01:49,910 --> 00:01:52,646 +Suppose I download +an app called Pal About, + +38 +00:01:52,646 --> 00:01:54,881 +and the Pal About app +has a feature + +39 +00:01:54,881 --> 00:01:57,150 +that lets me search +for places and events + +40 +00:01:57,150 --> 00:01:59,152 +that are happening nearby. + +41 +00:01:59,152 --> 00:02:02,022 +Now suppose I use Pal About +to search for places + +42 +00:02:02,022 --> 00:02:05,225 +that serve waffles near me, +which results in Pal About + +43 +00:02:05,225 --> 00:02:08,228 +storing waffles +as an interest of mine. + +44 +00:02:08,228 --> 00:02:11,531 +Pal About later wants to show +an ad for breakfast places + +45 +00:02:11,531 --> 00:02:14,534 +targeting people +who like breakfast foods. + +46 +00:02:14,534 --> 00:02:17,971 +Using the data Pal About stores +about me from my searches, + +47 +00:02:17,971 --> 00:02:21,041 +Pal About shows me +the breakfast ad. + +48 +00:02:21,041 --> 00:02:24,378 +In this example, +Pal About doesn't link my data + +49 +00:02:24,378 --> 00:02:26,446 +with any data +from an app or website + +50 +00:02:26,446 --> 00:02:29,549 +owned by another company +to show me the breakfast ad, + +51 +00:02:29,549 --> 00:02:34,354 +so this scenario would +not be considered tracking. + +52 +00:02:34,354 --> 00:02:37,524 +For another example that +wouldn't be considered tracking, + +53 +00:02:37,524 --> 00:02:40,961 +suppose the company that owns +Pal About -- Pal About Inc. -- + +54 +00:02:40,961 --> 00:02:44,398 +has another app that I use +called Pal About Plus. + +55 +00:02:44,398 --> 00:02:48,101 +And Pal About's server links +together data collected about me + +56 +00:02:48,101 --> 00:02:51,471 +in Pal About Plus, +like my interest in tacos, + +57 +00:02:51,471 --> 00:02:55,008 +with data collected about me +in Pal About. + +58 +00:02:55,008 --> 00:02:58,545 +After linking this data, +Pal About shows me an ad + +59 +00:02:58,545 --> 00:03:01,882 +for a taco truck using +the fact I like tacos + +60 +00:03:01,882 --> 00:03:04,484 +collected from Pal About Plus. + +61 +00:03:04,484 --> 00:03:07,220 +In this example, +the Pal About app doesn't need + +62 +00:03:07,220 --> 00:03:10,690 +to get my permission to track +because it isn't tracking. + +63 +00:03:10,690 --> 00:03:13,460 +Pal About doesn't link +my data from Pal About + +64 +00:03:13,460 --> 00:03:15,495 +with any data +from an app or website + +65 +00:03:15,495 --> 00:03:17,831 +owned by another company. + +66 +00:03:17,831 --> 00:03:20,934 +Let's now consider a scenario +that would require Pal About + +67 +00:03:20,934 --> 00:03:23,170 +to get permission to track. + +68 +00:03:23,170 --> 00:03:25,238 +Suppose there's +a food delivery app I use + +69 +00:03:25,238 --> 00:03:28,809 +that's owned by a different +company than Pal About. + +70 +00:03:28,809 --> 00:03:30,343 +And I've used +the food delivery app + +71 +00:03:30,343 --> 00:03:32,646 +to place orders late at night. + +72 +00:03:32,646 --> 00:03:34,681 +When I signed up for +the food delivery app, + +73 +00:03:34,681 --> 00:03:37,851 +I gave the app +my email address -- + +74 +00:03:37,851 --> 00:03:41,321 +the same email address I used +to sign up for Pal About + +75 +00:03:41,321 --> 00:03:45,425 +and that Pal About stores +for my account. + +76 +00:03:45,425 --> 00:03:47,527 +The food delivery app +includes code + +77 +00:03:47,527 --> 00:03:49,229 +that shares my email address + +78 +00:03:49,229 --> 00:03:53,633 +and the fact I often order +at night with Pal About. + +79 +00:03:53,633 --> 00:03:57,237 +The Pal About server uses my +email address to link together + +80 +00:03:57,237 --> 00:04:00,540 +my interest in waffles, +collected by the Pal About app, + +81 +00:04:00,540 --> 00:04:02,142 +to the fact I order at night, + +82 +00:04:02,142 --> 00:04:05,345 +collected in +the food delivery app. + +83 +00:04:05,345 --> 00:04:08,048 +Finally, Pal About +uses the combination + +84 +00:04:08,048 --> 00:04:10,917 +of my ordering habits +and my interest in waffles + +85 +00:04:10,917 --> 00:04:12,552 +to show me +an ad for a restaurant + +86 +00:04:12,552 --> 00:04:15,322 +that serves all-day breakfast. + +87 +00:04:15,322 --> 00:04:17,491 +This scenario +would require Pal About + +88 +00:04:17,491 --> 00:04:19,459 +to request permission to track + +89 +00:04:19,459 --> 00:04:22,129 +because it linked user data +from Pal About -- + +90 +00:04:22,129 --> 00:04:23,363 +my email address -- + +91 +00:04:23,363 --> 00:04:25,532 +with another company's +user data -- + +92 +00:04:25,532 --> 00:04:27,868 +my email and habit +of ordering at night -- + +93 +00:04:27,868 --> 00:04:30,170 +for advertising purposes. + +94 +00:04:30,170 --> 00:04:33,473 +In this example, data is +linked together across apps + +95 +00:04:33,473 --> 00:04:36,042 +with an email address. + +96 +00:04:36,042 --> 00:04:39,012 +Even if the email address +or another user identifier + +97 +00:04:39,012 --> 00:04:42,182 +is hashed before it is used +to link data, + +98 +00:04:42,182 --> 00:04:45,318 +it would still require +permission to track + +99 +00:04:45,318 --> 00:04:48,421 +because it would still be +linking data about a user + +100 +00:04:48,421 --> 00:04:52,859 +from the app with another +company's data about that user. + +101 +00:04:52,859 --> 00:04:56,329 +The type of identifier +and whether or not it is hashed + +102 +00:04:56,329 --> 00:04:59,432 +doesn't change the fact +it is being used for tracking, + +103 +00:04:59,432 --> 00:05:03,303 +which is what +requires permission. + +104 +00:05:03,303 --> 00:05:04,838 +Another thing +you'll need to consider + +105 +00:05:04,838 --> 00:05:08,141 +to determine if your app needs +to request permission to track + +106 +00:05:08,141 --> 00:05:12,812 +is how third-party SDKs use +and share data from your app. + +107 +00:05:12,812 --> 00:05:13,980 +As a developer, + +108 +00:05:13,980 --> 00:05:17,417 +you're responsible for +the behavior of your whole app. + +109 +00:05:17,417 --> 00:05:20,887 +Returning to our example, +suppose the Pal About developer + +110 +00:05:20,887 --> 00:05:23,056 +hasn't written +any code themselves + +111 +00:05:23,056 --> 00:05:25,358 +that would require +permission to track, + +112 +00:05:25,358 --> 00:05:28,361 +but would like to include +a third-party SDK in their app + +113 +00:05:28,361 --> 00:05:31,398 +for advertising-measurement +purposes. + +114 +00:05:31,398 --> 00:05:33,867 +Whether Pal About +needs permission to track + +115 +00:05:33,867 --> 00:05:37,571 +in order to include the SDK +depends on whether or not + +116 +00:05:37,571 --> 00:05:40,640 +the SDK combines +user data from Pal About + +117 +00:05:40,640 --> 00:05:45,145 +with user data from other +companies' apps or websites. + +118 +00:05:45,145 --> 00:05:49,449 +For example, if the SDK +shares user data from Pal About + +119 +00:05:49,449 --> 00:05:52,519 +to provide analytics +about ads in Pal About, + +120 +00:05:52,519 --> 00:05:55,789 +but doesn't link the user data +it collects from Pal About + +121 +00:05:55,789 --> 00:05:57,991 +with user data +from other companies, + +122 +00:05:57,991 --> 00:06:02,762 +it doesn't require +permission to track. + +123 +00:06:02,762 --> 00:06:04,531 +Now suppose instead, + +124 +00:06:04,531 --> 00:06:09,035 +the SDK shares user data from +Pal About with an ad network, + +125 +00:06:09,035 --> 00:06:12,072 +and the ad network +links the data it receives + +126 +00:06:12,072 --> 00:06:13,974 +about how I use Pal About + +127 +00:06:13,974 --> 00:06:16,810 +with data about ads I saw +in other companies' apps + +128 +00:06:16,810 --> 00:06:20,547 +to compare the impact +of ad campaigns in those apps. + +129 +00:06:20,547 --> 00:06:23,483 +This requires Pal About +to request users' permission + +130 +00:06:23,483 --> 00:06:26,920 +to track because this SDK +is tracking. + +131 +00:06:26,920 --> 00:06:30,123 +This is considered tracking +regardless of whether Pal About + +132 +00:06:30,123 --> 00:06:32,859 +uses the SDK +for those purposes, + +133 +00:06:32,859 --> 00:06:36,429 +or even if Pal About +only gets aggregate reporting + +134 +00:06:36,429 --> 00:06:39,132 +after Pal About +users' data is linked + +135 +00:06:39,132 --> 00:06:42,202 +with other companies' +users' data. + +136 +00:06:42,202 --> 00:06:45,238 +If you're unsure about +whether an SDK you want to use + +137 +00:06:45,238 --> 00:06:46,906 +would contain code +that would require + +138 +00:06:46,906 --> 00:06:48,842 +App Tracking Transparency, + +139 +00:06:48,842 --> 00:06:52,345 +you should ask the developer +of that SDK. + +140 +00:06:52,345 --> 00:06:55,382 +This responsibility applies +not just to SDKs, + +141 +00:06:55,382 --> 00:06:59,519 +but to any libraries or +third-party code your app uses. + +142 +00:06:59,519 --> 00:07:03,623 +So far, we've looked at examples +that involve linking user data. + +143 +00:07:03,623 --> 00:07:05,158 +Now let's look +at another scenario + +144 +00:07:05,158 --> 00:07:06,860 +that's considered tracking: + +145 +00:07:06,860 --> 00:07:09,863 +sharing user or device data +with data brokers. + +146 +00:07:09,863 --> 00:07:13,166 +First, how are data brokers +defined? + +147 +00:07:13,166 --> 00:07:16,569 +Data brokers are defined by law +in some jurisdictions. + +148 +00:07:16,569 --> 00:07:19,539 +But in general, +a data broker is a company + +149 +00:07:19,539 --> 00:07:22,575 +that regularly collects +and sells, licenses, + +150 +00:07:22,575 --> 00:07:25,111 +or otherwise discloses +to third parties + +151 +00:07:25,111 --> 00:07:27,981 +the personal information +of particular end users + +152 +00:07:27,981 --> 00:07:31,451 +with whom the business does +not have a direct relationship. + +153 +00:07:31,451 --> 00:07:35,622 +Let's look at sharing data with +a data broker in an example. + +154 +00:07:35,622 --> 00:07:38,858 +Suppose the Pal About app +includes client code + +155 +00:07:38,858 --> 00:07:40,794 +that sends my interest +in waffles + +156 +00:07:40,794 --> 00:07:44,097 +and an account identifier +to a data broker. + +157 +00:07:44,097 --> 00:07:46,132 +This scenario counts as tracking + +158 +00:07:46,132 --> 00:07:48,101 +whether or not +the data that is shared + +159 +00:07:48,101 --> 00:07:50,370 +is linked with data +from other companies + +160 +00:07:50,370 --> 00:07:53,239 +for advertising +or advertising measurement. + +161 +00:07:53,239 --> 00:07:55,575 +Sharing of user data +with a data broker + +162 +00:07:55,575 --> 00:07:59,379 +requires permission to track. + +163 +00:07:59,379 --> 00:08:01,648 +And even if +Pal About client code + +164 +00:08:01,648 --> 00:08:04,250 +doesn't directly send +my account identifier + +165 +00:08:04,250 --> 00:08:06,986 +and my interest in waffles +to the data broker, + +166 +00:08:06,986 --> 00:08:10,357 +but instead this interest +is sent to Pal About's server + +167 +00:08:10,357 --> 00:08:12,525 +and the server +later shares accounts + +168 +00:08:12,525 --> 00:08:15,295 +interested in waffles +with the data broker, + +169 +00:08:15,295 --> 00:08:17,864 +this would require +getting permission to track + +170 +00:08:17,864 --> 00:08:20,066 +even though my device +isn't communicating + +171 +00:08:20,066 --> 00:08:22,836 +with the data broker directly. + +172 +00:08:22,836 --> 00:08:25,105 +We've now talked through +how the definition of tracking + +173 +00:08:25,105 --> 00:08:27,507 +applies to some +example scenarios. + +174 +00:08:27,507 --> 00:08:30,477 +For more information about +how App Tracking Transparency + +175 +00:08:30,477 --> 00:08:32,312 +defines tracking, +you can visit + +176 +00:08:32,312 --> 00:08:35,448 +the User Privacy +and Data Use page. + +177 +00:08:35,448 --> 00:08:37,584 +Now, if you've determined +that your app + +178 +00:08:37,584 --> 00:08:39,352 +would like to track users, + +179 +00:08:39,352 --> 00:08:42,288 +you'll need to ask for +and obtain the user's permission + +180 +00:08:42,288 --> 00:08:43,957 +before you do so. + +181 +00:08:43,957 --> 00:08:45,658 +Here's how. + +182 +00:08:45,658 --> 00:08:48,762 +To ask for permission +for your app to track, + +183 +00:08:48,762 --> 00:08:51,598 +you'll need to present +the app tracking authorization + +184 +00:08:51,598 --> 00:08:53,800 +request prompt by calling + +185 +00:08:53,800 --> 00:08:57,504 +the requestTrackingAuthorization +method. + +186 +00:08:57,504 --> 00:09:00,707 +Calling this method will cause +a system permission alert -- + +187 +00:09:00,707 --> 00:09:05,211 +like this one for Pal About -- +to appear over your app. + +188 +00:09:05,211 --> 00:09:07,046 +This is a one-time prompt. + +189 +00:09:07,046 --> 00:09:09,449 +The system will remember +the user's choice + +190 +00:09:09,449 --> 00:09:10,650 +and won't prompt again + +191 +00:09:10,650 --> 00:09:14,421 +unless the app is uninstalled +and reinstalled. + +192 +00:09:14,421 --> 00:09:17,123 +The next thing you'll +need to do is provide + +193 +00:09:17,123 --> 00:09:23,363 +a NSUserTrackingUsageDescription +key in your app's info.plist. + +194 +00:09:23,363 --> 00:09:26,866 +The string provided here will +be shown in the system prompt + +195 +00:09:26,866 --> 00:09:30,470 +and informs the user why +the app is requesting permission + +196 +00:09:30,470 --> 00:09:34,207 +to use data for tracking +the user or the device. + +197 +00:09:34,207 --> 00:09:37,877 +A great purpose string is clear, +concise, and helps users + +198 +00:09:37,877 --> 00:09:41,281 +understand why they are +being asked to allow tracking. + +199 +00:09:41,281 --> 00:09:44,217 +This string doesn't need +to include the app's name, + +200 +00:09:44,217 --> 00:09:46,719 +because the system +will automatically identify + +201 +00:09:46,719 --> 00:09:49,055 +the requesting app +and display the app name + +202 +00:09:49,055 --> 00:09:51,391 +in the system prompt. + +203 +00:09:51,391 --> 00:09:54,327 +If you don't include +a usage-description string, + +204 +00:09:54,327 --> 00:09:57,864 +your app will crash +when the system prompt is shown. + +205 +00:09:57,864 --> 00:10:01,634 +Finally, use +trackingAuthorizationStatus + +206 +00:10:01,634 --> 00:10:04,404 +to determine the user's +app-tracking permission status + +207 +00:10:04,404 --> 00:10:06,639 +for your app. + +208 +00:10:06,639 --> 00:10:09,242 +If a user has selected +Allow for this app, + +209 +00:10:09,242 --> 00:10:11,678 +then you have their permission +to link their activity + +210 +00:10:11,678 --> 00:10:14,614 +in that app across +other apps and websites + +211 +00:10:14,614 --> 00:10:17,250 +as long as their +tracking authorization status + +212 +00:10:17,250 --> 00:10:19,652 +remains authorized. + +213 +00:10:19,652 --> 00:10:22,021 +Users can change +and grant or revoke + +214 +00:10:22,021 --> 00:10:25,291 +their tracking authorization +at any time, + +215 +00:10:25,291 --> 00:10:29,329 +so make sure your app checks the +tracking authorization status + +216 +00:10:29,329 --> 00:10:32,932 +each time it is launched +and only continues to track + +217 +00:10:32,932 --> 00:10:35,602 +when the value of the +tracking authorization status + +218 +00:10:35,602 --> 00:10:37,804 +is authorized. + +219 +00:10:37,804 --> 00:10:41,341 +Users can control whether apps +have their permission to track + +220 +00:10:41,341 --> 00:10:43,877 +on a per-app basis. + +221 +00:10:43,877 --> 00:10:46,312 +Just because a user +has given one of your apps + +222 +00:10:46,312 --> 00:10:49,282 +permission to track doesn't mean +you have their permission + +223 +00:10:49,282 --> 00:10:52,986 +to track in another app +owned by the same company. + +224 +00:10:52,986 --> 00:10:56,689 +Different apps must each +individually request permission + +225 +00:10:56,689 --> 00:10:59,526 +from the user +for that particular app + +226 +00:10:59,526 --> 00:11:01,961 +before data from +that app can be linked + +227 +00:11:01,961 --> 00:11:04,097 +to apps or websites +owned by other companies + +228 +00:11:04,097 --> 00:11:07,634 +for marketing or advertising. + +229 +00:11:07,634 --> 00:11:09,102 +If your app doesn't have + +230 +00:11:09,102 --> 00:11:11,371 +tracking authorization +for a user, + +231 +00:11:11,371 --> 00:11:14,307 +there are a couple things +to keep in mind. + +232 +00:11:14,307 --> 00:11:17,210 +First, per the App Store +review guidelines, + +233 +00:11:17,210 --> 00:11:19,979 +your app must not gate +any of its functionality + +234 +00:11:19,979 --> 00:11:23,716 +on whether the user agrees +to allow tracking. + +235 +00:11:23,716 --> 00:11:27,554 +Second, the IDFA API +will return all zeros + +236 +00:11:27,554 --> 00:11:31,190 +if the user has asked your app +not to track. + +237 +00:11:31,190 --> 00:11:33,226 +If a user has opted +out of tracking, + +238 +00:11:33,226 --> 00:11:35,962 +there are nontracking +alternatives for advertising + +239 +00:11:35,962 --> 00:11:38,498 +or advertising measurement +for your app. + +240 +00:11:38,498 --> 00:11:42,035 +For example, your app could +choose to serve first-party ads + +241 +00:11:42,035 --> 00:11:43,836 +or contextual ads. + +242 +00:11:43,836 --> 00:11:47,140 +And for advertising measurement, +we continue to build and improve + +243 +00:11:47,140 --> 00:11:50,276 +privacy preserving +ad-attribution technologies + +244 +00:11:50,276 --> 00:11:52,512 +that ad networks can adopt. + +245 +00:11:52,512 --> 00:11:54,814 +For more information +about recent improvements + +246 +00:11:54,814 --> 00:11:58,718 +to SKAdNetwork and private click +measurement, you can refer to + +247 +00:11:58,718 --> 00:12:01,287 +"Meet privacy preserving +ad attribution" + +248 +00:12:01,287 --> 00:12:04,824 +and "What's new in SKAdNetwork." + +249 +00:12:04,824 --> 00:12:07,760 +You'll also need to declare +what data your app uses + +250 +00:12:07,760 --> 00:12:11,931 +to track for display in your +app's privacy nutrition label. + +251 +00:12:11,931 --> 00:12:13,933 +Filling out your +privacy nutrition label + +252 +00:12:13,933 --> 00:12:16,202 +when submitting your app +to the App Store + +253 +00:12:16,202 --> 00:12:18,404 +and getting permission +to track using + +254 +00:12:18,404 --> 00:12:20,773 +the AppTrackingTransparency +framework + +255 +00:12:20,773 --> 00:12:23,443 +are two separate steps +that are both required + +256 +00:12:23,443 --> 00:12:26,746 +if your app would like to use +data for tracking. + +257 +00:12:26,746 --> 00:12:29,115 +For more information +about nutrition labels + +258 +00:12:29,115 --> 00:12:30,883 +and how to fill them out +for your app, + +259 +00:12:30,883 --> 00:12:34,220 +see "Create your +Privacy Nutrition Label." + +260 +00:12:34,220 --> 00:12:37,523 +Finally, let's talk +about fingerprinting. + +261 +00:12:37,523 --> 00:12:40,560 +With permission, +tracking is allowed. + +262 +00:12:40,560 --> 00:12:43,496 +But fingerprinting +is never allowed. + +263 +00:12:43,496 --> 00:12:45,698 +Regardless of whether +a user gives your app + +264 +00:12:45,698 --> 00:12:48,201 +permission to track, +fingerprinting -- + +265 +00:12:48,201 --> 00:12:49,902 +or using signals +from the device + +266 +00:12:49,902 --> 00:12:52,405 +to try to identify +the device or user -- + +267 +00:12:52,405 --> 00:12:53,539 +is not allowed + +268 +00:12:53,539 --> 00:12:57,677 +per the Apple Developer Program +License Agreement. + +269 +00:12:57,677 --> 00:13:01,114 +Some examples of user or device +data used for fingerprinting + +270 +00:13:01,114 --> 00:13:03,549 +include properties +of a user's web browser + +271 +00:13:03,549 --> 00:13:05,018 +and its configuration, + +272 +00:13:05,018 --> 00:13:07,253 +the user's device +and its configuration, + +273 +00:13:07,253 --> 00:13:11,557 +the user's location, or +the user's network connection. + +274 +00:13:11,557 --> 00:13:14,060 +Collecting any data +solely for the purpose + +275 +00:13:14,060 --> 00:13:18,197 +of generating a fingerprint +is also not allowed. + +276 +00:13:18,197 --> 00:13:20,800 +It's important people +have transparency and control + +277 +00:13:20,800 --> 00:13:23,870 +over how their data +is used for tracking. + +278 +00:13:23,870 --> 00:13:26,005 +We hope that by tuning in +to this session, + +279 +00:13:26,005 --> 00:13:28,174 +you now have the tools +you need to determine + +280 +00:13:28,174 --> 00:13:31,277 +when and how to give people +that control by adopting + +281 +00:13:31,277 --> 00:13:33,713 +the AppTrackingTransparency +framework. + +282 +00:13:33,713 --> 00:13:35,148 +Thanks for watching. + +283 +00:13:35,148 --> 00:13:39,352 +♪ + diff --git a/eng/2022 Session 10167 Create your Privacy Nutrition Label en.srt b/eng/2022 Session 10167 Create your Privacy Nutrition Label en.srt new file mode 100644 index 0000000..1cb1831 --- /dev/null +++ b/eng/2022 Session 10167 Create your Privacy Nutrition Label en.srt @@ -0,0 +1,1017 @@ +1 +00:00:09,309 --> 00:00:11,678 +Hi, I'm Ben, +from Privacy Engineering, + +2 +00:00:11,712 --> 00:00:14,815 +and welcome +to Create your Privacy Nutrition Label. + +3 +00:00:14,848 --> 00:00:17,818 +At WWDC 2020, + +4 +00:00:17,851 --> 00:00:19,853 +we announced privacy nutrition labels +to provide people + +5 +00:00:19,887 --> 00:00:23,891 +with an easily glanceable +and understandable summary + +6 +00:00:23,924 --> 00:00:27,828 +about how the apps they use +collect and use data. + +7 +00:00:27,861 --> 00:00:32,032 +In this talk, we'll discuss strategies +for creating an accurate label + +8 +00:00:32,065 --> 00:00:33,667 +and highlight some definitions + +9 +00:00:33,700 --> 00:00:36,904 +and examples to keep in mind +as you build your label. + +10 +00:00:37,938 --> 00:00:40,974 +Nutrition labels are one of many ways +you can communicate + +11 +00:00:41,008 --> 00:00:42,776 +your app's privacy practices, + +12 +00:00:42,809 --> 00:00:46,680 +in addition to a privacy policy +and App Privacy Report. + +13 +00:00:46,713 --> 00:00:51,552 +Labels can help you explain to people +how you use the data your app collects. + +14 +00:00:51,585 --> 00:00:54,188 +Additionally, sharing a label is +a requirement + +15 +00:00:54,221 --> 00:00:56,523 +for all apps in the App Store. + +16 +00:00:56,557 --> 00:00:59,793 +On an app's nutrition label, +you can see data the app collects + +17 +00:00:59,826 --> 00:01:03,297 +that is used for tracking, +data that is linked to identity, + +18 +00:01:03,330 --> 00:01:06,033 +or associated with an account or profile, + +19 +00:01:06,066 --> 00:01:08,135 +and data that is not linked to identity. + +20 +00:01:08,936 --> 00:01:12,439 +In each section, the categories +of data collected are shown. + +21 +00:01:13,040 --> 00:01:17,578 +Alternatively, if data is not collected, +an alternate label is shown. + +22 +00:01:18,312 --> 00:01:20,747 +From an app's product page +in the App Store, + +23 +00:01:20,781 --> 00:01:24,184 +you can also view a more detailed version +of the nutrition label + +24 +00:01:24,218 --> 00:01:27,354 +that includes the specific types of data +the app collects + +25 +00:01:27,387 --> 00:01:29,189 +and how that data is used. + +26 +00:01:29,990 --> 00:01:34,328 +Today we'll be looking +at nutrition labels in more detail. + +27 +00:01:34,361 --> 00:01:37,231 +We'll walk through the process +for how to create your label + +28 +00:01:37,264 --> 00:01:38,498 +in App Store Connect, + +29 +00:01:38,532 --> 00:01:41,168 +and I'll share some resources +along the way. + +30 +00:01:42,870 --> 00:01:46,039 +Additionally, we'll provide +some more detailed definitions + +31 +00:01:46,073 --> 00:01:50,377 +and examples to help you +answer privacy questions for your app. + +32 +00:01:51,178 --> 00:01:54,715 +Let's begin with label creation process. + +33 +00:01:54,748 --> 00:01:58,585 +To create your label, you'll be asked +about what data your app collects, + +34 +00:01:58,619 --> 00:02:03,090 +use cases for each type of data, +and how that data is stored. + +35 +00:02:03,123 --> 00:02:06,026 +You might want to start +with creating an inventory + +36 +00:02:06,059 --> 00:02:08,729 +of your app's features +and data collection. + +37 +00:02:08,762 --> 00:02:12,266 +An inventory can be a helpful resource +for the next step, + +38 +00:02:12,299 --> 00:02:15,469 +where you'll enter information +about your app's privacy practices + +39 +00:02:15,502 --> 00:02:17,604 +into App Store Connect. + +40 +00:02:17,638 --> 00:02:21,742 +After you submit your label, +and as you update your data practices, + +41 +00:02:21,775 --> 00:02:24,344 +you should keep your label updated +as appropriate. + +42 +00:02:25,379 --> 00:02:29,016 +Lets dive right in and look +at how you can go about crafting + +43 +00:02:29,049 --> 00:02:30,851 +an inventory for your app. + +44 +00:02:31,818 --> 00:02:34,254 +When you submit +your label to App Store Connect, + +45 +00:02:34,288 --> 00:02:37,691 +you'll answer some questions +about how your app uses data. + +46 +00:02:37,724 --> 00:02:41,361 +You'll be asked about the categories +of data collected by your app, + +47 +00:02:41,395 --> 00:02:43,530 +the use cases for the data, + +48 +00:02:43,564 --> 00:02:45,699 +whether the data is linked to identity, + +49 +00:02:45,732 --> 00:02:48,135 +and whether the data is used for tracking. + +50 +00:02:49,102 --> 00:02:51,905 +However, as a developer, +you might not be thinking + +51 +00:02:51,939 --> 00:02:53,941 +about data categories in your app. + +52 +00:02:53,974 --> 00:02:56,143 +You're probably thinking about features, + +53 +00:02:56,176 --> 00:02:59,046 +and if you're not sure where to start +on building the nutrition label, + +54 +00:02:59,079 --> 00:03:00,981 +list out your app's features. + +55 +00:03:01,915 --> 00:03:05,085 +Then consider what data powers +each feature, + +56 +00:03:05,118 --> 00:03:09,323 +including what data is retained +and how that data is used. + +57 +00:03:09,356 --> 00:03:12,426 +You can then keep this list +as a reference for when you're asked + +58 +00:03:12,459 --> 00:03:15,462 +to enter specific details +into App Store Connect. + +59 +00:03:15,495 --> 00:03:18,532 +We recommend finding a framework +to document this information + +60 +00:03:18,565 --> 00:03:21,702 +that works for your app, +and you may have many strategies + +61 +00:03:21,735 --> 00:03:24,438 +to build an inventory +or other documentation. + +62 +00:03:25,506 --> 00:03:28,575 +When creating this inventory, +there are a number of resources + +63 +00:03:28,609 --> 00:03:30,811 +that you can use along the way. + +64 +00:03:30,844 --> 00:03:34,281 +Make sure to reach out to the stakeholders +working on your app. + +65 +00:03:34,314 --> 00:03:36,884 +For example, +you can check with your marketing team + +66 +00:03:36,917 --> 00:03:38,852 +to understand what data they use + +67 +00:03:38,886 --> 00:03:42,623 +and work with legal counsel to ensure +you document all data uses + +68 +00:03:42,656 --> 00:03:44,758 +described in your app's privacy policy. + +69 +00:03:45,526 --> 00:03:49,863 +We also recommend consulting +internal documentation in this process. + +70 +00:03:50,797 --> 00:03:53,267 +Consider looking +at your app's network traffic + +71 +00:03:53,300 --> 00:03:57,304 +using App Privacy Report +or a network proxy tool. + +72 +00:03:57,337 --> 00:04:01,308 +You can use this information to learn +what domains your app is contacting + +73 +00:04:01,341 --> 00:04:03,877 +and follow up with the owners +of those endpoints + +74 +00:04:03,911 --> 00:04:06,146 +to understand how data is being used. + +75 +00:04:06,180 --> 00:04:09,883 +Keep in mind that while a network audit +may be helpful in discovering + +76 +00:04:09,917 --> 00:04:12,986 +where your app sends data, +it is not comprehensive, + +77 +00:04:13,020 --> 00:04:15,789 +and you should use it +in combination with other strategies. + +78 +00:04:16,690 --> 00:04:20,127 +You should also audit any data +you retain on a server, + +79 +00:04:20,160 --> 00:04:22,129 +such as by reviewing database schemas + +80 +00:04:22,162 --> 00:04:24,464 +and checking what systems +have access to data. + +81 +00:04:26,200 --> 00:04:29,203 +Additionally, make sure to check +with any partners + +82 +00:04:29,236 --> 00:04:31,271 +that may be processing your app's data, + +83 +00:04:31,305 --> 00:04:34,541 +as you are responsible +for declaring the collection practices + +84 +00:04:34,575 --> 00:04:36,777 +of all data collected from your app, + +85 +00:04:36,810 --> 00:04:39,847 +including data collected +by third-party SDKs, + +86 +00:04:39,880 --> 00:04:43,984 +analytics tools, advertising networks, +or other external vendors. + +87 +00:04:44,885 --> 00:04:48,522 +Many SDKs provide documentation +of their privacy practices, + +88 +00:04:48,555 --> 00:04:51,859 +and some provide specific guidance +for nutrition labels + +89 +00:04:51,892 --> 00:04:55,395 +which you can use to ensure +your label is comprehensive. + +90 +00:04:56,463 --> 00:04:59,366 +When building this inventory, +you might also discover + +91 +00:04:59,399 --> 00:05:02,202 +that there is data that you're collecting +that you don't need. + +92 +00:05:02,236 --> 00:05:06,473 +Inventorying your privacy practices +can be a way to identify opportunities + +93 +00:05:06,507 --> 00:05:10,477 +to make changes to your app's practices +by minimizing data collection, + +94 +00:05:10,511 --> 00:05:14,581 +processing data on device, +and storing data not linked to identity. + +95 +00:05:15,382 --> 00:05:19,853 +For some new technologies you can use, +see What's New In Privacy. + +96 +00:05:19,887 --> 00:05:23,991 +Once you've built up an inventory +or documented your data practices + +97 +00:05:24,024 --> 00:05:25,592 +through your preferred process, + +98 +00:05:25,626 --> 00:05:28,795 +you'll work though responding +to the questions in App Store Connect. + +99 +00:05:29,796 --> 00:05:34,101 +In App Store Connect, +account holders, admins, and app mangers + +100 +00:05:34,134 --> 00:05:36,503 +can enter your app's privacy label. + +101 +00:05:36,537 --> 00:05:39,773 +From your app's page, +open the App Privacy section. + +102 +00:05:40,574 --> 00:05:44,578 +First, you'll be asked +about whether your app collects data. + +103 +00:05:44,611 --> 00:05:48,682 +Data is considered collected when it +is transmitted off device in a way + +104 +00:05:48,715 --> 00:05:52,386 +that is accessible for longer than +to service the request in real time. + +105 +00:05:52,419 --> 00:05:56,456 +So if you or any of your partners +retain data about interaction with the app + +106 +00:05:56,490 --> 00:05:59,560 +on a server, +such as server logs, a user profile, + +107 +00:05:59,593 --> 00:06:02,629 +or analytics, +your app likely collects data. + +108 +00:06:04,531 --> 00:06:08,101 +We designed labels +to describe all data apps collect. + +109 +00:06:08,135 --> 00:06:10,704 +You should declare all data +collected by your app, + +110 +00:06:10,737 --> 00:06:14,374 +even if people agree elsewhere +to the data collection or use. + +111 +00:06:15,342 --> 00:06:17,711 +Labels are a supplement +to a privacy policy + +112 +00:06:17,744 --> 00:06:18,779 +and any requirements + +113 +00:06:18,812 --> 00:06:21,849 +for user transparency +or consent for data collection. + +114 +00:06:22,516 --> 00:06:26,653 +We recommend working with legal counsel +to evaluate those requirements. + +115 +00:06:26,687 --> 00:06:29,823 +If you do collect data, +you'll be asked to declare + +116 +00:06:29,857 --> 00:06:32,125 +the categories of data +collected from your app, + +117 +00:06:32,159 --> 00:06:35,162 +such as email address, +phone number, or payment info. + +118 +00:06:36,230 --> 00:06:38,832 +You'll then be able to preview +your label in progress + +119 +00:06:38,866 --> 00:06:41,668 +and provide more detail +for each data category. + +120 +00:06:42,269 --> 00:06:44,505 +Let's work on phone number. + +121 +00:06:44,538 --> 00:06:48,342 +For each category of data, +you'll be asked to declare what use cases + +122 +00:06:48,375 --> 00:06:49,810 +the data collection supports, + +123 +00:06:49,843 --> 00:06:52,679 +such as analytics +or product personalization. + +124 +00:06:53,614 --> 00:06:55,649 +You'll then be asked to disclose + +125 +00:06:55,682 --> 00:06:57,851 +whether data is linked +to a user's identity. + +126 +00:06:59,553 --> 00:07:03,824 +Data is considered linked to identity +if it is associated with an account, + +127 +00:07:03,857 --> 00:07:06,059 +device, or user profile. + +128 +00:07:06,093 --> 00:07:09,263 +This can be an opportunity +for you to evaluate whether you need + +129 +00:07:09,296 --> 00:07:13,000 +to store data linked to identity, +and whether you can store data + +130 +00:07:13,033 --> 00:07:16,303 +in a way that isn't linkable +to any account or profile. + +131 +00:07:17,304 --> 00:07:20,741 +Finally, you'll be asked +whether each data type is used + +132 +00:07:20,774 --> 00:07:22,309 +for tracking purposes. + +133 +00:07:23,043 --> 00:07:26,680 +"Tracking" refers to linking data +collected from your app + +134 +00:07:26,713 --> 00:07:31,185 +about a particular end user or device, +such as a user identifier, + +135 +00:07:31,218 --> 00:07:35,222 +device identifier, or profile, +with third-party data + +136 +00:07:35,255 --> 00:07:38,825 +for targeted advertising +or advertising measurement purposes, + +137 +00:07:38,859 --> 00:07:41,161 +or sharing data collected from your app + +138 +00:07:41,195 --> 00:07:44,998 +about a particular end user +or device with a data broker. + +139 +00:07:45,799 --> 00:07:49,403 +As mentioned earlier, +nutrition labels are intended to reflect + +140 +00:07:49,436 --> 00:07:51,672 +all data your app may collect, + +141 +00:07:51,705 --> 00:07:54,074 +even for features where people +using your app + +142 +00:07:54,107 --> 00:07:57,344 +agree separately +to data collection or use. + +143 +00:07:57,377 --> 00:08:02,416 +Ensure you disclose any data categories +used for tracking on your nutrition label. + +144 +00:08:02,449 --> 00:08:05,485 +Additionally, +as all tracking must be with permission, + +145 +00:08:05,519 --> 00:08:09,389 +adopt App Tracking Transparency +if appropriate for your app. + +146 +00:08:10,290 --> 00:08:15,128 +For more information about tracking, +see Explore App Tracking Transparency. + +147 +00:08:17,531 --> 00:08:21,235 +Once you've submitted this information +for all data your app collects, + +148 +00:08:21,268 --> 00:08:24,238 +you'll be able +to preview and publish your label. + +149 +00:08:24,271 --> 00:08:29,142 +Your label will be published immediately, +independent from any updates to your app. + +150 +00:08:30,377 --> 00:08:32,179 +Now that you've created your label, + +151 +00:08:32,212 --> 00:08:34,982 +let's consider +when you'll need to update it. + +152 +00:08:35,015 --> 00:08:37,417 +You can update your label at any time, + +153 +00:08:37,451 --> 00:08:40,621 +and aren't required to release +a new version of your app. + +154 +00:08:40,654 --> 00:08:42,823 +We recommend re-evaluating +your app's label + +155 +00:08:42,856 --> 00:08:45,826 +when releasing new features +and on an ongoing basis. + +156 +00:08:46,426 --> 00:08:48,495 +As you change how your app uses data, + +157 +00:08:48,529 --> 00:08:51,064 +ensure your label remains up to date. + +158 +00:08:51,098 --> 00:08:53,066 +If you're adding new features to your app, + +159 +00:08:53,100 --> 00:08:57,871 +implementing new or updated integrations +with third-party partners or SDKs, + +160 +00:08:57,905 --> 00:09:00,541 +or using already-collected data +in new ways, + +161 +00:09:00,574 --> 00:09:04,077 +make sure to evaluate +whether any label changes are needed. + +162 +00:09:04,111 --> 00:09:08,916 +Now that we've worked through the process +of building your privacy nutrition label, + +163 +00:09:08,949 --> 00:09:14,087 +we'll discuss some additional guidance +around policy definitions and examples. + +164 +00:09:14,121 --> 00:09:18,392 +All of this information is available +in the Apple Developer documentation. + +165 +00:09:18,425 --> 00:09:23,430 +Today I'll be highlighting a few examples +based on our experience at Apple + +166 +00:09:23,463 --> 00:09:26,333 +building nutrition labels +for our own apps, + +167 +00:09:26,366 --> 00:09:29,403 +and from some questions +we have heard from developers. + +168 +00:09:30,037 --> 00:09:33,907 +You might wonder +how to disclose use of IP address. + +169 +00:09:33,941 --> 00:09:36,677 +IP addresses can be used +for multiple purposes, + +170 +00:09:36,710 --> 00:09:41,181 +including as an identifier +or to infer approximate location. + +171 +00:09:41,215 --> 00:09:44,051 +Our guidance is to declare +the collection categories + +172 +00:09:44,084 --> 00:09:47,554 +based on what the IP address is used for. + +173 +00:09:47,588 --> 00:09:51,024 +For example, if you use IP address +to show local content + +174 +00:09:51,058 --> 00:09:54,728 +or for location analytics, +declare location. + +175 +00:09:55,429 --> 00:09:59,266 +Another area to note is the categories +including product interaction. + +176 +00:10:00,200 --> 00:10:04,004 +Product Interaction covers data +collected about the user's interactions + +177 +00:10:04,037 --> 00:10:07,941 +inside the app, such as information +about which screens people open. + +178 +00:10:08,976 --> 00:10:11,979 +Browsing history refers +to collection of activity + +179 +00:10:12,012 --> 00:10:15,849 +that is not part of the app, +such as an in-app browser. + +180 +00:10:15,883 --> 00:10:19,119 +Search History is searches +performed within the app + +181 +00:10:19,152 --> 00:10:23,190 +for any content, +both in the app or in in-app browsers. + +182 +00:10:24,191 --> 00:10:27,127 +Labels are intended +to comprehensively describe + +183 +00:10:27,160 --> 00:10:28,862 +the app's primary functionality + +184 +00:10:28,896 --> 00:10:31,765 +and features encountered +by all of your app's users. + +185 +00:10:32,599 --> 00:10:35,035 +However, there are certain types +of collection + +186 +00:10:35,068 --> 00:10:37,271 +that are optional to disclose. + +187 +00:10:37,304 --> 00:10:40,541 +Collection that is infrequent, optional, +and independent + +188 +00:10:40,574 --> 00:10:43,043 +from the app's primary functionality, + +189 +00:10:43,076 --> 00:10:46,413 +clearly discloses all collection +at submission time, + +190 +00:10:46,446 --> 00:10:52,085 +and has limited use purposes, for example, +not used for tracking or advertising, + +191 +00:10:52,119 --> 00:10:54,021 +may be optional to disclose. + +192 +00:10:54,821 --> 00:10:58,158 +Feedback forms and report-a-problem flows +are some features + +193 +00:10:58,192 --> 00:10:59,927 +that may meet these requirements. + +194 +00:11:00,961 --> 00:11:04,264 +Full details and requirements +for the optional disclosure policy + +195 +00:11:04,298 --> 00:11:08,202 +are available on the "App privacy details +on the App Store" page + +196 +00:11:08,235 --> 00:11:10,637 +in Apple Developer documentation. + +197 +00:11:10,671 --> 00:11:12,439 +And that's a wrap! + +198 +00:11:12,472 --> 00:11:16,410 +Here are four things to keep +in mind while building your label. + +199 +00:11:16,443 --> 00:11:20,180 +Make sure to reach out to all stakeholders +working on your app + +200 +00:11:20,214 --> 00:11:24,051 +to inventory your collection +and ensure your label is accurate. + +201 +00:11:25,118 --> 00:11:28,255 +Remember to include +all collection from your app, + +202 +00:11:28,288 --> 00:11:30,791 +including from SDKs and other partners. + +203 +00:11:31,892 --> 00:11:36,763 +If applicable to your app, ensure +you request permission for tracking, + +204 +00:11:36,797 --> 00:11:39,967 +in addition to disclosing +any use on your nutrition label. + +205 +00:11:40,934 --> 00:11:45,305 +Finally, when you update your app +or change how you use data, + +206 +00:11:45,339 --> 00:11:47,040 +ensure your label is updated. + +207 +00:11:48,175 --> 00:11:50,077 +Thanks for joining me today. + +208 +00:11:50,110 --> 00:11:52,746 +Privacy Nutrition Labels +are a way to build trust + +209 +00:11:52,779 --> 00:11:54,548 +with people who use your app + +210 +00:11:54,581 --> 00:11:57,518 +and help people +understand how you use data. + +211 +00:11:57,551 --> 00:12:00,921 +The Apple Developer Documentation +and App Store Connect + +212 +00:12:00,954 --> 00:12:03,824 +have even more information +to reference throughout the process + +213 +00:12:03,857 --> 00:12:06,059 +of building and updating your label. + +214 +00:12:06,960 --> 00:12:09,263 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 10169 Design App Shortcuts en.srt b/eng/2022 Session 10169 Design App Shortcuts en.srt new file mode 100644 index 0000000..55cde58 --- /dev/null +++ b/eng/2022 Session 10169 Design App Shortcuts en.srt @@ -0,0 +1,2205 @@ +1 +00:00:00,100 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:10,444 +♪ + +3 +00:00:10,444 --> 00:00:11,879 +Hi, I'm Lynn, + +4 +00:00:11,879 --> 00:00:14,081 +a design producer +for Siri and Shortcuts. + +5 +00:00:14,081 --> 00:00:15,983 +And today, I'm going +to tell you a bit more + +6 +00:00:15,983 --> 00:00:17,317 +about the new App Intents +framework + +7 +00:00:17,317 --> 00:00:21,154 +and how you can use it +to design great App Shortcuts. + +8 +00:00:21,154 --> 00:00:23,757 +Here at Apple, we know +that people love using Siri + +9 +00:00:23,757 --> 00:00:27,160 +and Spotlight to accelerate +common tasks across devices, + +10 +00:00:27,160 --> 00:00:28,595 +like this. + +11 +00:00:30,264 --> 00:00:31,832 +Remind me to buy garlic. + +12 +00:00:34,735 --> 00:00:36,737 +Set a timer for 20 minutes. + +13 +00:00:44,111 --> 00:00:46,613 +These habitual tasks +can be easily completed + +14 +00:00:46,613 --> 00:00:49,616 +outside their respective apps, +and making them available + +15 +00:00:49,616 --> 00:00:52,452 +via Siri and Spotlight +gives people flexibility + +16 +00:00:52,452 --> 00:00:54,221 +in how they interact +with their device + +17 +00:00:54,221 --> 00:00:56,857 +and how they get things done. + +18 +00:00:56,857 --> 00:01:00,260 +Shortcuts are how you can offer +people that same flexibility + +19 +00:01:00,260 --> 00:01:04,231 +to accomplish key tasks +from your app throughout the OS. + +20 +00:01:04,231 --> 00:01:07,134 +And all shortcuts begin +with a fundamental component + +21 +00:01:07,134 --> 00:01:10,103 +called actions, which +represent an individual task + +22 +00:01:10,103 --> 00:01:12,039 +that people can complete +with your app, + +23 +00:01:12,039 --> 00:01:16,109 +such as creating a reminder +or sending a message. + +24 +00:01:16,109 --> 00:01:19,546 +This foundation can be used +in a couple different ways. + +25 +00:01:19,546 --> 00:01:22,783 +First, for custom shortcuts, +which people can create + +26 +00:01:22,783 --> 00:01:24,818 +using one or more actions +from apps; + +27 +00:01:24,818 --> 00:01:27,387 +that is, your app and others. + +28 +00:01:27,387 --> 00:01:30,757 +Or an action can be used +to create an App Shortcut, + +29 +00:01:30,757 --> 00:01:32,559 +created by +app developers like you, + +30 +00:01:32,559 --> 00:01:35,128 +containing one action +from your app. + +31 +00:01:35,128 --> 00:01:38,098 +And while in the past, +people needed to find and tap + +32 +00:01:38,098 --> 00:01:40,434 +the Add to Siri button +to enable each new app shortcut + +33 +00:01:40,434 --> 00:01:44,304 +you created, +now, with iOS 16, + +34 +00:01:44,304 --> 00:01:46,139 +the App Shortcuts +you create for your app + +35 +00:01:46,139 --> 00:01:49,776 +will be automatically available +as soon as your app is installed. + +36 +00:01:49,776 --> 00:01:52,813 +That means they'll be +available in Siri, + +37 +00:01:52,813 --> 00:01:54,548 +visible in Spotlight, + +38 +00:01:54,548 --> 00:01:56,216 +and featured +in the Shortcuts app, + +39 +00:01:56,216 --> 00:01:58,185 +making it easier than ever +for people to access + +40 +00:01:58,185 --> 00:02:02,389 +their favorite features +from your app throughout the OS. + +41 +00:02:02,389 --> 00:02:04,591 +Today I'm going to walk through +a few different areas + +42 +00:02:04,591 --> 00:02:07,394 +to help you create +unforgettable App Shortcuts. + +43 +00:02:07,394 --> 00:02:08,929 +I'll start with how to select +the right features + +44 +00:02:08,929 --> 00:02:11,031 +from your app +to turn into shortcuts, + +45 +00:02:11,031 --> 00:02:12,699 +and how to name them. + +46 +00:02:12,699 --> 00:02:14,801 +Then, I'll walk through +some best practices + +47 +00:02:14,801 --> 00:02:16,303 +for presenting your visuals + +48 +00:02:16,303 --> 00:02:18,638 +and for collecting +required information. + +49 +00:02:18,638 --> 00:02:21,108 +And finally, I'll let you know +how you can tell people + +50 +00:02:21,108 --> 00:02:22,943 +about your new shortcuts. + +51 +00:02:22,943 --> 00:02:25,545 +So let's jump in. + +52 +00:02:25,545 --> 00:02:27,848 +Now your app +has a wide range of features. + +53 +00:02:27,848 --> 00:02:29,449 +But we know that some things +lend themselves + +54 +00:02:29,449 --> 00:02:31,985 +better than others +to becoming great shortcuts. + +55 +00:02:31,985 --> 00:02:33,887 +So what should you look for? + +56 +00:02:33,887 --> 00:02:36,189 +Think about the features +people love in your app, + +57 +00:02:36,189 --> 00:02:39,159 +and then focus on tasks +that are self-contained, + +58 +00:02:39,159 --> 00:02:41,895 +meaning they can be completed +outside your app; + +59 +00:02:41,895 --> 00:02:43,130 +and straightforward, + +60 +00:02:43,130 --> 00:02:45,432 +in that they're efficient +to get through. + +61 +00:02:45,432 --> 00:02:47,401 +To give these principles +a bit more context, + +62 +00:02:47,401 --> 00:02:49,603 +let's take a look at +a sample meditation app. + +63 +00:02:49,603 --> 00:02:52,773 +We'll just call it "Meditation." + +64 +00:02:52,773 --> 00:02:54,641 +Now a great +App Shortcut is one + +65 +00:02:54,641 --> 00:02:57,611 +that actually doesn't require +your app in focus at all. + +66 +00:02:57,611 --> 00:03:00,013 +You're looking for a lightweight +task that can be completed + +67 +00:03:00,013 --> 00:03:04,217 +via Siri or via search +in its entirety. + +68 +00:03:04,217 --> 00:03:07,120 +For our new Meditation app, +that'd be something like this. + +69 +00:03:08,989 --> 00:03:12,025 +Start sleep meditation. + +70 +00:03:12,025 --> 00:03:14,995 +Voiceover: Take a deep breath, +close your eyes, + +71 +00:03:14,995 --> 00:03:16,563 +and leave the day behind. + +72 +00:03:19,733 --> 00:03:22,035 +It's easy to kick this off +without opening the app, + +73 +00:03:22,035 --> 00:03:24,271 +and it just starts playing. + +74 +00:03:24,271 --> 00:03:26,406 +You also want to make sure +the shortcuts you author + +75 +00:03:26,406 --> 00:03:27,774 +are straightforward. + +76 +00:03:27,774 --> 00:03:29,810 +They should feel effortless +to get through. + +77 +00:03:29,810 --> 00:03:32,579 +Tasks that require a lot +of input may be too tedious + +78 +00:03:32,579 --> 00:03:34,815 +and time-consuming +for a shortcut. + +79 +00:03:34,815 --> 00:03:40,153 +For example, a lengthy, +multistep survey like this one + +80 +00:03:40,153 --> 00:03:43,490 +wouldn't be appropriate +for an App Shortcut. + +81 +00:03:43,490 --> 00:03:46,326 +Instead, focus on +uncomplicated tasks + +82 +00:03:46,326 --> 00:03:48,061 +that people can succeed +at quickly, + +83 +00:03:48,061 --> 00:03:50,864 +and that they can remember even +when they're not using your app, + +84 +00:03:50,864 --> 00:03:52,799 +like this. + +85 +00:04:02,109 --> 00:04:04,778 +So the best features from +your app to turn into shortcuts + +86 +00:04:04,778 --> 00:04:07,481 +are self-contained +and straightforward. + +87 +00:04:07,481 --> 00:04:09,716 +They can be completed +without the app in focus + +88 +00:04:09,716 --> 00:04:12,018 +and are uncomplicated +to get through. + +89 +00:04:12,018 --> 00:04:15,055 +And all in all, there may not +be so many of these. + +90 +00:04:15,055 --> 00:04:18,291 +The maximum you can create +is 10, but in most cases, + +91 +00:04:18,291 --> 00:04:20,093 +the key features of your app +can be captured + +92 +00:04:20,093 --> 00:04:22,963 +in two to five +high-quality app shortcuts, + +93 +00:04:22,963 --> 00:04:24,631 +and we strongly recommend +keeping your set + +94 +00:04:24,631 --> 00:04:27,400 +as focused as possible. + +95 +00:04:27,400 --> 00:04:29,936 +Now, as important +as selecting the right features + +96 +00:04:29,936 --> 00:04:32,439 +to become app shortcuts, +is authoring a great phrase + +97 +00:04:32,439 --> 00:04:34,608 +to name them. + +98 +00:04:34,608 --> 00:04:37,744 +This name is really the hero +phrase for your shortcut. + +99 +00:04:37,744 --> 00:04:40,947 +It appears in the Shortcuts app +under your app's header, + +100 +00:04:40,947 --> 00:04:43,683 +it's how your shortcut will be +referenced in Spotlight + +101 +00:04:43,683 --> 00:04:47,087 +and it's what people will say +to Siri to invoke your shortcut. + +102 +00:04:47,087 --> 00:04:49,055 +So where to start? + +103 +00:04:49,055 --> 00:04:50,857 +The first important thing +to remember + +104 +00:04:50,857 --> 00:04:54,895 +when you're crafting that hero +phrase is to keep it brief. + +105 +00:04:54,895 --> 00:04:56,029 +You want your +app shortcut phrase + +106 +00:04:56,029 --> 00:04:57,430 +to be easy to remember + +107 +00:04:57,430 --> 00:05:00,667 +and to clearly communicate +its function. + +108 +00:05:00,667 --> 00:05:03,537 +As a requirement, you'll need +to include your app name. + +109 +00:05:03,537 --> 00:05:05,505 +But you can be +creative about it! + +110 +00:05:05,505 --> 00:05:07,274 +See if you can incorporate +your app name + +111 +00:05:07,274 --> 00:05:09,342 +directly into +the invocation phrase, + +112 +00:05:09,342 --> 00:05:12,112 +like here with Voice Memo + +113 +00:05:12,112 --> 00:05:14,614 +or here with Panera. + +114 +00:05:14,614 --> 00:05:17,717 +And the app name in this phrase +can be your official app name + +115 +00:05:17,717 --> 00:05:21,221 +or any of the alternative names +you submitted to the App Store. + +116 +00:05:21,221 --> 00:05:23,990 +In this case, Panera Bread +is the official app name, + +117 +00:05:23,990 --> 00:05:27,460 +but the Panera synonym feels +more natural in the phrase. + +118 +00:05:27,460 --> 00:05:31,665 +And here's how that could look +with our app, Meditation. + +119 +00:05:31,665 --> 00:05:35,135 +Let's dig a little deeper +with the Voice Memo example. + +120 +00:05:35,135 --> 00:05:38,905 +I'll turn on my transcriptions +so we can see my speech. + +121 +00:05:38,905 --> 00:05:41,241 +Remember that the name +you choose for your App Shortcut + +122 +00:05:41,241 --> 00:05:44,244 +is also what people will say to +Siri to invoke it, and you need + +123 +00:05:44,244 --> 00:05:46,546 +to provide thoughtful +natural language variations + +124 +00:05:46,546 --> 00:05:48,715 +to ensure it works +for whatever similar phrases + +125 +00:05:48,715 --> 00:05:50,250 +people might say. + +126 +00:05:50,250 --> 00:05:54,454 +For example, for this shortcut, +named Record Voice Memo, + +127 +00:05:54,454 --> 00:05:56,756 +I would need +to explicitly specify + +128 +00:05:56,756 --> 00:06:00,227 +"Start Voice Memo" +and "New Voice Memo" + +129 +00:06:00,227 --> 00:06:03,930 +as synonyms, +otherwise they won't work. + +130 +00:06:03,930 --> 00:06:05,732 +Be sufficiently thorough here. + +131 +00:06:05,732 --> 00:06:08,134 +You want to try to capture +all the alternative phrases + +132 +00:06:08,134 --> 00:06:09,869 +people might say. + +133 +00:06:09,869 --> 00:06:12,539 +But -- and this is +a bit of a judgment call -- + +134 +00:06:12,539 --> 00:06:14,841 +make sure you're not straying +too far from the core purpose + +135 +00:06:14,841 --> 00:06:16,876 +of your shortcut +and entering into a new meaning + +136 +00:06:16,876 --> 00:06:18,178 +with these phrases. + +137 +00:06:18,178 --> 00:06:21,948 +In this case, the intent is +truly to create not to save. + +138 +00:06:21,948 --> 00:06:25,151 +And remember, you will have +to go through this exercise + +139 +00:06:25,151 --> 00:06:27,621 +of providing thorough synonyms +for every language + +140 +00:06:27,621 --> 00:06:30,156 +that your app is published +to ensure a robust experience + +141 +00:06:30,156 --> 00:06:31,891 +around the world. + +142 +00:06:34,494 --> 00:06:35,929 +So I've talked about +the importance + +143 +00:06:35,929 --> 00:06:38,765 +of keeping your app shortcut +name brief and memorable, + +144 +00:06:38,765 --> 00:06:41,534 +creative strategies +for incorporating your app name, + +145 +00:06:41,534 --> 00:06:43,436 +and the value of generating +thorough synonyms + +146 +00:06:43,436 --> 00:06:46,640 +and translating those synonyms +to other languages. + +147 +00:06:46,640 --> 00:06:47,941 +The last thing +I want to talk about + +148 +00:06:47,941 --> 00:06:49,643 +when it comes +to naming your shortcut + +149 +00:06:49,643 --> 00:06:52,879 +is how and when to use +a dynamic parameter. + +150 +00:06:52,879 --> 00:06:54,781 +You can now use +a dynamic parameter + +151 +00:06:54,781 --> 00:06:56,549 +directly in the name +of your shortcut + +152 +00:06:56,549 --> 00:06:58,551 +to create multiple variations +on the phrase + +153 +00:06:58,551 --> 00:07:01,688 +that people can say to Siri +or see in Spotlight. + +154 +00:07:01,688 --> 00:07:03,790 +Let's take a look at an example. + +155 +00:07:03,790 --> 00:07:06,293 +So for our Meditation app, +the dynamic parameter + +156 +00:07:06,293 --> 00:07:08,728 +could be different types +of meditation. + +157 +00:07:08,728 --> 00:07:10,930 +And you can see how +this provides the flexibility + +158 +00:07:10,930 --> 00:07:14,234 +to directly ask +for those different types. + +159 +00:07:14,234 --> 00:07:16,970 +But there are a couple things +to note about this parameter. + +160 +00:07:16,970 --> 00:07:19,839 +First, you can only have +one dynamic parameter + +161 +00:07:19,839 --> 00:07:22,275 +in your phrase, +and it can only be used + +162 +00:07:22,275 --> 00:07:24,978 +to select from a finite list. + +163 +00:07:24,978 --> 00:07:26,946 +You want to make sure +that other values in the list + +164 +00:07:26,946 --> 00:07:29,482 +are predictable since people +won't have this list of options + +165 +00:07:29,482 --> 00:07:31,618 +in front of them for reference. + +166 +00:07:31,618 --> 00:07:34,254 +This is a good example because +people are likely familiar + +167 +00:07:34,254 --> 00:07:36,956 +with the different meditation +types from using the app, + +168 +00:07:36,956 --> 00:07:38,992 +and it's also a small list. + +169 +00:07:38,992 --> 00:07:41,261 +Other good examples +might be recent restaurants + +170 +00:07:41,261 --> 00:07:45,332 +that are front of mind or +different rooms in the home. + +171 +00:07:45,332 --> 00:07:48,068 +A bad example would be +something with infinite options, + +172 +00:07:48,068 --> 00:07:49,703 +like a time value. + +173 +00:07:49,703 --> 00:07:51,805 +You don't want to use +a dynamic parameter here, + +174 +00:07:51,805 --> 00:07:53,807 +but instead, prompt +for additional information + +175 +00:07:53,807 --> 00:07:55,041 +as needed. + +176 +00:07:55,041 --> 00:07:57,677 +I'll talk more +about that in a bit. + +177 +00:07:57,677 --> 00:07:59,546 +Last thing to note +is that this list + +178 +00:07:59,546 --> 00:08:02,515 +of possible parameter values +can be updated any time + +179 +00:08:02,515 --> 00:08:04,651 +your app is open, +so you can ensure + +180 +00:08:04,651 --> 00:08:08,988 +it consistently contains +up-to-date and relevant values. + +181 +00:08:08,988 --> 00:08:11,257 +Now, each parameter value, + +182 +00:08:11,257 --> 00:08:13,360 +in combination +with the App Intent, + +183 +00:08:13,360 --> 00:08:16,596 +creates a unique variant +of your app shortcut. + +184 +00:08:16,596 --> 00:08:18,298 +So for starting a meditation, + +185 +00:08:18,298 --> 00:08:20,867 +you'd have this +unparameterized version + +186 +00:08:20,867 --> 00:08:25,138 +and each parameterized version, +like Start Sleep Meditation, + +187 +00:08:25,138 --> 00:08:28,908 +Start Gratitude Meditation, +and all the others. + +188 +00:08:28,908 --> 00:08:30,477 +These are automatically +generated + +189 +00:08:30,477 --> 00:08:34,114 +and displayed in the Shortcuts +app, as you see here, + +190 +00:08:34,114 --> 00:08:37,684 +and shown as individual rows +in Spotlight. + +191 +00:08:37,684 --> 00:08:40,153 +These parameters are also +visible in the Shortcuts editor + +192 +00:08:40,153 --> 00:08:43,490 +when you provide a parameter +summary for your action. + +193 +00:08:43,490 --> 00:08:45,058 +When someone taps +on the parameter, + +194 +00:08:45,058 --> 00:08:48,228 +your options will appear +in a menu like this. + +195 +00:08:48,228 --> 00:08:50,230 +To learn more about actions +and how they appear + +196 +00:08:50,230 --> 00:08:51,364 +in the Shortcuts editor, + +197 +00:08:51,364 --> 00:08:54,000 +check out this talk from last +year on designing great actions + +198 +00:08:54,000 --> 00:08:57,437 +for Shortcuts, Siri, +and Suggestions. + +199 +00:08:57,437 --> 00:08:59,339 +The last important consideration +when it comes + +200 +00:08:59,339 --> 00:09:02,342 +to including a dynamic parameter +is making sure it's clear + +201 +00:09:02,342 --> 00:09:05,044 +which part of the phrase +is a parameter. + +202 +00:09:05,044 --> 00:09:07,914 +As I mentioned, you can only +have one dynamic parameter + +203 +00:09:07,914 --> 00:09:11,484 +per app shortcut, but if you had +this as your app shortcut, + +204 +00:09:11,484 --> 00:09:14,788 +the second part of +the phrase -- "nature sounds" -- + +205 +00:09:14,788 --> 00:09:17,590 +seems like a second parameter +that can be changed. + +206 +00:09:17,590 --> 00:09:19,726 +This could make people +think they can say + +207 +00:09:19,726 --> 00:09:22,429 +"gratitude meditation +with ocean sounds," + +208 +00:09:22,429 --> 00:09:24,063 +when in fact, only +the first part of the phrase + +209 +00:09:24,063 --> 00:09:25,632 +can be changed. + +210 +00:09:25,632 --> 00:09:28,501 +The solution here +is to keep it simple. + +211 +00:09:28,501 --> 00:09:31,037 +If it feels too complicated +when you say it aloud, + +212 +00:09:31,037 --> 00:09:32,772 +it probably is. + +213 +00:09:32,772 --> 00:09:34,007 +And if that additional +information + +214 +00:09:34,007 --> 00:09:35,375 +is absolutely required, + +215 +00:09:35,375 --> 00:09:37,310 +you can ask for it +in a subsequent step, + +216 +00:09:37,310 --> 00:09:41,047 +and I'll provide some tips +on how to do that later. + +217 +00:09:41,047 --> 00:09:42,582 +Looking back at everything +I mentioned + +218 +00:09:42,582 --> 00:09:45,385 +about selecting features, +naming your shortcut, + +219 +00:09:45,385 --> 00:09:48,555 +and using dynamic parameters, +one theme stands out, + +220 +00:09:48,555 --> 00:09:50,623 +and that's to make it memorable. + +221 +00:09:50,623 --> 00:09:52,959 +Within your app, +people can explore, + +222 +00:09:52,959 --> 00:09:55,929 +get drawn into features, +and learn new flows. + +223 +00:09:55,929 --> 00:09:57,864 +But with shortcuts, +your main goal is to create + +224 +00:09:57,864 --> 00:10:01,334 +a few focused experiences +that people can quickly learn, + +225 +00:10:01,334 --> 00:10:04,404 +remember, +and grow to depend on. + +226 +00:10:04,404 --> 00:10:06,206 +Now let's take a look at +some visual considerations + +227 +00:10:06,206 --> 00:10:08,908 +for your app shortcuts. + +228 +00:10:08,908 --> 00:10:10,944 +Custom Snippets +and Live Activities + +229 +00:10:10,944 --> 00:10:13,646 +provide you with an opportunity +to present information, + +230 +00:10:13,646 --> 00:10:16,749 +request clarifications, +and surface your app identity, + +231 +00:10:16,749 --> 00:10:18,985 +and I want to share some tips +for making them as beautiful + +232 +00:10:18,985 --> 00:10:21,521 +and effective as possible. + +233 +00:10:21,521 --> 00:10:24,991 +Unlike most of your apps that +likely use opaque backgrounds, + +234 +00:10:24,991 --> 00:10:27,827 +snippets use +a semitranslucent material. + +235 +00:10:27,827 --> 00:10:30,463 +Place elements from your app +on top of this material + +236 +00:10:30,463 --> 00:10:33,399 +instead of filling the visual +with an opaque background. + +237 +00:10:33,399 --> 00:10:35,168 +You can see an example here, +with elements + +238 +00:10:35,168 --> 00:10:37,036 +from the Weather app +integrating seamlessly + +239 +00:10:37,036 --> 00:10:39,706 +into the Siri snippet. + +240 +00:10:39,706 --> 00:10:42,242 +And when you draw your text, +use vibrant label colors + +241 +00:10:42,242 --> 00:10:45,678 +to guarantee great contrast +over the translucent background. + +242 +00:10:45,678 --> 00:10:47,146 +Doing this ensures +that your text + +243 +00:10:47,146 --> 00:10:50,950 +will automatically look great +in Dark Mode too. + +244 +00:10:50,950 --> 00:10:53,987 +Now there are two ways +to show results in iOS 16: + +245 +00:10:53,987 --> 00:10:56,956 +Live Activities +and Custom Snippets. + +246 +00:10:56,956 --> 00:10:58,691 +If you're not sure +which one to use, + +247 +00:10:58,691 --> 00:11:00,093 +think about whether +people would benefit + +248 +00:11:00,093 --> 00:11:02,562 +from continuous access +to this information, + +249 +00:11:02,562 --> 00:11:04,664 +such as transit progress +for a food order + +250 +00:11:04,664 --> 00:11:06,533 +or a timer counting down. + +251 +00:11:06,533 --> 00:11:09,202 +If so, use a Live Activity. + +252 +00:11:09,202 --> 00:11:11,437 +This will make the content +continuously glanceable, + +253 +00:11:11,437 --> 00:11:14,507 +even on the Lock Screen, +until the event has ended. + +254 +00:11:14,507 --> 00:11:17,343 +If not, and the shortcut +is a self-contained action + +255 +00:11:17,343 --> 00:11:21,447 +or piece of information, +use a Custom Snippet. + +256 +00:11:21,447 --> 00:11:23,449 +Let's take a closer look +at the iOS snippet + +257 +00:11:23,449 --> 00:11:25,785 +and a couple +of the key elements it contains. + +258 +00:11:25,785 --> 00:11:28,488 +Here, you see +the supporting dialog. + +259 +00:11:28,488 --> 00:11:29,856 +This is what Siri speaks + +260 +00:11:29,856 --> 00:11:33,793 +and is intended to accompany +your custom visual. + +261 +00:11:33,793 --> 00:11:36,963 +Together, they communicate +all the necessary information + +262 +00:11:36,963 --> 00:11:39,165 +for this step of the shortcut. + +263 +00:11:39,165 --> 00:11:42,101 +Now by default, your custom +visual will consistently appear + +264 +00:11:42,101 --> 00:11:43,903 +with supporting dialog. + +265 +00:11:43,903 --> 00:11:46,706 +But in this example, you can +see that the supporting dialog + +266 +00:11:46,706 --> 00:11:49,676 +is fully redundant +with your visual response. + +267 +00:11:49,676 --> 00:11:51,945 +When that's the case, +you should suppress this dialog + +268 +00:11:51,945 --> 00:11:56,149 +in your source code +so that it won't be shown. + +269 +00:11:56,149 --> 00:11:59,552 +But the presentation of your +information goes beyond iOS, + +270 +00:11:59,552 --> 00:12:01,854 +and you'll need to think about +other devices in the ecosystem + +271 +00:12:01,854 --> 00:12:03,456 +as well. + +272 +00:12:03,456 --> 00:12:06,125 +For voice-only products, +like AirPods, + +273 +00:12:06,125 --> 00:12:09,362 +Siri will read out +the full dialog you provide. + +274 +00:12:09,362 --> 00:12:10,697 +For these platforms, +it's important + +275 +00:12:10,697 --> 00:12:14,200 +that your full dialog contain +all the critical information + +276 +00:12:14,200 --> 00:12:17,870 +from both your custom visual +and any supporting dialog. + +277 +00:12:17,870 --> 00:12:19,772 +Be sure to provide +both types of dialog, + +278 +00:12:19,772 --> 00:12:22,308 +so that people have access +to all the information they need + +279 +00:12:22,308 --> 00:12:23,943 +regardless of which device +they choose + +280 +00:12:23,943 --> 00:12:26,846 +to interact with your shortcut. + +281 +00:12:26,846 --> 00:12:28,081 +And for the first time, + +282 +00:12:28,081 --> 00:12:31,084 +Apple Watch now supports +Custom Snippets as well. + +283 +00:12:31,084 --> 00:12:32,952 +This means you should take +a look at your shortcuts + +284 +00:12:32,952 --> 00:12:35,288 +in watchOS 9 +and make sure they look great + +285 +00:12:35,288 --> 00:12:37,390 +on that platform as well. + +286 +00:12:37,390 --> 00:12:39,325 +And know that this may +require you to make changes + +287 +00:12:39,325 --> 00:12:40,493 +to your layout. + +288 +00:12:40,493 --> 00:12:42,128 +Here you can see +how the Coffee app + +289 +00:12:42,128 --> 00:12:44,497 +repositioned the address line +on watchOS + +290 +00:12:44,497 --> 00:12:48,768 +to fall beneath the header +to help with readability. + +291 +00:12:48,768 --> 00:12:51,571 +Just to recap +these first few visual tips. + +292 +00:12:51,571 --> 00:12:53,072 +I walked through how +to integrate elements + +293 +00:12:53,072 --> 00:12:55,942 +from your app into +the translucent background, + +294 +00:12:55,942 --> 00:12:58,478 +the importance of using +vibrant label colors, + +295 +00:12:58,478 --> 00:13:01,447 +the appropriate time to use +snippets versus live activities, + +296 +00:13:01,447 --> 00:13:03,316 +and how to adapt +your dialog and visuals + +297 +00:13:03,316 --> 00:13:06,319 +for different platforms +and contexts. + +298 +00:13:06,319 --> 00:13:08,421 +Now, let's talk about Spotlight. + +299 +00:13:08,421 --> 00:13:12,992 +In iOS 16, your app's shortcuts +will also appear in Spotlight. + +300 +00:13:12,992 --> 00:13:16,462 +If someone searches for your +app name, one app shortcut -- + +301 +00:13:16,462 --> 00:13:18,831 +the first one in +your App Shortcuts array -- + +302 +00:13:18,831 --> 00:13:23,436 +will appear as a Siri Suggestion +below your app as a top hit. + +303 +00:13:23,436 --> 00:13:25,872 +They can also search directly +for the name of the shortcut, + +304 +00:13:25,872 --> 00:13:28,074 +and any app shortcut +that matches the search term + +305 +00:13:28,074 --> 00:13:30,109 +will appear. + +306 +00:13:30,109 --> 00:13:32,478 +Finally, if your app +is a Siri Suggestion + +307 +00:13:32,478 --> 00:13:34,347 +when someone +first launches Spotlight, + +308 +00:13:34,347 --> 00:13:36,582 +your top shortcut +will also appear here, + +309 +00:13:36,582 --> 00:13:39,986 +even before anything has been +typed into the search field. + +310 +00:13:39,986 --> 00:13:41,821 +This is all really exciting, +because it provides + +311 +00:13:41,821 --> 00:13:44,457 +an opportunity for people +to learn about your shortcuts + +312 +00:13:44,457 --> 00:13:47,760 +just by looking for your app +or by using their phone. + +313 +00:13:47,760 --> 00:13:50,229 +You'll also notice +that each unique app shortcut + +314 +00:13:50,229 --> 00:13:53,332 +is decorated with a symbol +on the right side. + +315 +00:13:53,332 --> 00:13:55,468 +Be sure to review +the SF Symbols library + +316 +00:13:55,468 --> 00:13:57,704 +and select one for each +of your app shortcuts + +317 +00:13:57,704 --> 00:14:01,174 +that accurately reflects +its intent. + +318 +00:14:01,174 --> 00:14:05,078 +Also, know that the order of +your actions and your parameters + +319 +00:14:05,078 --> 00:14:07,146 +will influence the order +your app shortcuts appear + +320 +00:14:07,146 --> 00:14:08,948 +in Spotlight. + +321 +00:14:08,948 --> 00:14:11,517 +Actions can only be reordered +with app updates, + +322 +00:14:11,517 --> 00:14:13,986 +but as I mentioned earlier, +the order of your parameters + +323 +00:14:13,986 --> 00:14:16,055 +is truly dynamic +and can be updated + +324 +00:14:16,055 --> 00:14:18,291 +any time your app is open. + +325 +00:14:18,291 --> 00:14:21,627 +This in turn impacts +how your shortcuts are shown. + +326 +00:14:21,627 --> 00:14:24,697 +Here, you see that +Reorder Coffee App Cappuccino + +327 +00:14:24,697 --> 00:14:27,900 +is first in the Shortcuts app +and takes the single slot + +328 +00:14:27,900 --> 00:14:29,869 +in Spotlight. + +329 +00:14:29,869 --> 00:14:32,739 +But maybe this customer ordered +a chai latte more recently, + +330 +00:14:32,739 --> 00:14:35,341 +and you want that to be +your suggestion in Spotlight. + +331 +00:14:35,341 --> 00:14:38,277 +You can make it the first item +returned by your entity query + +332 +00:14:38,277 --> 00:14:41,047 +or dynamic options provider, +and it will become first + +333 +00:14:41,047 --> 00:14:44,717 +in the Shortcuts app +and in Spotlight. + +334 +00:14:44,717 --> 00:14:47,019 +And as the number +of parameters increases, + +335 +00:14:47,019 --> 00:14:50,323 +this prioritization may become +even more important. + +336 +00:14:50,323 --> 00:14:52,158 +But be sure to pick +a meaningful heuristic -- + +337 +00:14:52,158 --> 00:14:55,294 +such as recency or frequency -- +to drive the order, + +338 +00:14:55,294 --> 00:14:58,998 +so that it doesn't feel +random or unpredictable. + +339 +00:14:58,998 --> 00:15:01,868 +And one last thing -- +pick a color for your shortcuts + +340 +00:15:01,868 --> 00:15:03,603 +in the Shortcuts app. + +341 +00:15:03,603 --> 00:15:05,838 +We have a bunch of great colors +for you to choose from, + +342 +00:15:05,838 --> 00:15:08,474 +and all your shortcuts will use +this color in the app. + +343 +00:15:08,474 --> 00:15:10,877 +So pick one that complements +your app icon nicely + +344 +00:15:10,877 --> 00:15:13,780 +and don't just stick +with the default. + +345 +00:15:13,780 --> 00:15:17,683 +Now, as quick and contained +as your shortcuts aspire to be, + +346 +00:15:17,683 --> 00:15:18,918 +there may still be times +when you need + +347 +00:15:18,918 --> 00:15:22,288 +to collect some information +in order to see a task though. + +348 +00:15:22,288 --> 00:15:24,490 +I'm going to walk through +a few different ways to do that + +349 +00:15:24,490 --> 00:15:26,959 +and when to use each one. + +350 +00:15:26,959 --> 00:15:30,396 +Take a phrase like this one that +doesn't contain a parameter. + +351 +00:15:30,396 --> 00:15:32,465 +Sometimes, you can make +an informed decision + +352 +00:15:32,465 --> 00:15:35,802 +and assume a value and +then proceed with the action. + +353 +00:15:35,802 --> 00:15:36,702 +One example here + +354 +00:15:36,702 --> 00:15:39,906 +might be starting a meditation +that was already in progress. + +355 +00:15:39,906 --> 00:15:42,675 +But other times, in the face +of an ambiguous request, + +356 +00:15:42,675 --> 00:15:45,611 +you'll need to ask +for more information. + +357 +00:15:45,611 --> 00:15:48,447 +When possible, try to make +a meaningful assumption + +358 +00:15:48,447 --> 00:15:51,651 +and present it as an option +for people to confirm. + +359 +00:15:51,651 --> 00:15:54,487 +This is called +a Parameter Confirmation. + +360 +00:15:54,487 --> 00:15:57,857 +These assumptions could be based +on remembering a prior selection + +361 +00:15:57,857 --> 00:16:00,660 +or based on something +like the most popular option. + +362 +00:16:00,660 --> 00:16:03,262 +Parameter Confirmations +are a nice, efficient way + +363 +00:16:03,262 --> 00:16:07,400 +to be sure you have the detail +you need and continue on. + +364 +00:16:07,400 --> 00:16:09,435 +But let's say the person has +never gone through this shortcut + +365 +00:16:09,435 --> 00:16:13,105 +before and you don't have +a single parameter to offer. + +366 +00:16:13,105 --> 00:16:16,042 +Another option is +to provide a short list. + +367 +00:16:16,042 --> 00:16:18,711 +This is called a disambiguation +and helps people + +368 +00:16:18,711 --> 00:16:20,446 +that are unfamiliar +with the shortcut + +369 +00:16:20,446 --> 00:16:22,448 +learn the possible values. + +370 +00:16:22,448 --> 00:16:24,550 +Just keep in mind +that this is best for lists + +371 +00:16:24,550 --> 00:16:26,552 +of five values or less, + +372 +00:16:26,552 --> 00:16:29,722 +because in voice-only +modalities, such as AirPods, + +373 +00:16:29,722 --> 00:16:32,892 +Siri will read +this entire list aloud. + +374 +00:16:32,892 --> 00:16:34,360 +What kind of meditation? + +375 +00:16:34,360 --> 00:16:40,199 +Focus, Gratitude, Walking, +Compassion, or Sleep? + +376 +00:16:40,199 --> 00:16:42,735 +Sometimes, you need +to collect an open-ended value + +377 +00:16:42,735 --> 00:16:44,871 +that isn't compatible +with a short list, + +378 +00:16:44,871 --> 00:16:48,074 +like a number, a location, +or a string. + +379 +00:16:48,074 --> 00:16:51,844 +In these situations, you can +use an open-ended request. + +380 +00:16:51,844 --> 00:16:53,512 +This has the fewest +guardrails though, + +381 +00:16:53,512 --> 00:16:55,248 +since there are +no suggested values + +382 +00:16:55,248 --> 00:16:57,550 +and people can say +anything in response, + +383 +00:16:57,550 --> 00:16:59,452 +so make sure it's clear +what type of information + +384 +00:16:59,452 --> 00:17:01,020 +your app is expecting. + +385 +00:17:01,020 --> 00:17:02,021 +In this case, + +386 +00:17:02,021 --> 00:17:05,558 +it's apparent that the response +should be a time value. + +387 +00:17:05,558 --> 00:17:08,261 +The App Intent framework does +provide a set of common options + +388 +00:17:08,261 --> 00:17:12,198 +for these open-ended requests, +such as numerical values, + +389 +00:17:12,198 --> 00:17:15,234 +dates, or time values. + +390 +00:17:15,234 --> 00:17:17,670 +If your required parameter +aligns with one of these, + +391 +00:17:17,670 --> 00:17:19,472 +definitely select it. + +392 +00:17:19,472 --> 00:17:22,008 +This will allow you to benefit +from certain built-in dialog + +393 +00:17:22,008 --> 00:17:23,342 +and visual patterns, + +394 +00:17:23,342 --> 00:17:25,211 +as well as Siri's natural +language understanding + +395 +00:17:25,211 --> 00:17:27,980 +as it pertains to these types +of information. + +396 +00:17:27,980 --> 00:17:31,083 +If not, you can use +a custom entity. + +397 +00:17:31,083 --> 00:17:33,819 +For more information on how +to implement app entities, + +398 +00:17:33,819 --> 00:17:37,690 +check out this year's talk +called "Dive into App Intents." + +399 +00:17:37,690 --> 00:17:41,027 +Lastly, even if you have all +the information you require, + +400 +00:17:41,027 --> 00:17:43,496 +you may still want to do +one final confirmation -- + +401 +00:17:43,496 --> 00:17:45,564 +what we call +an "intent confirmation" -- + +402 +00:17:45,564 --> 00:17:48,100 +before proceeding +with your shortcut. + +403 +00:17:48,100 --> 00:17:50,169 +The guidance here is +to only use this step + +404 +00:17:50,169 --> 00:17:53,906 +for consequential actions, +be it a financial transaction, + +405 +00:17:53,906 --> 00:17:56,275 +a destructive action +like deleting content, + +406 +00:17:56,275 --> 00:17:58,277 +or an action that may +just feel high risk, + +407 +00:17:58,277 --> 00:18:01,180 +like sending a calendar invite +to a big group. + +408 +00:18:01,180 --> 00:18:03,783 +Use these appropriately +but sparingly, + +409 +00:18:03,783 --> 00:18:06,152 +as the goal of getting through a +shortcut as quickly as possible + +410 +00:18:06,152 --> 00:18:08,654 +always applies. + +411 +00:18:08,654 --> 00:18:10,756 +One last detail +on intent confirmations + +412 +00:18:10,756 --> 00:18:13,592 +is that they will always have a +pair of buttons in the snippet, + +413 +00:18:13,592 --> 00:18:16,128 +offering to either +proceed with -- or cancel -- + +414 +00:18:16,128 --> 00:18:17,930 +the proposed action. + +415 +00:18:17,930 --> 00:18:20,333 +When confirming an intent, +buttons should contain a verb + +416 +00:18:20,333 --> 00:18:22,868 +reiterating the action +that is about to be taken + +417 +00:18:22,868 --> 00:18:26,672 +and not ambiguous words +like "confirm." + +418 +00:18:26,672 --> 00:18:29,175 +The App Intents framework +provides a really helpful set + +419 +00:18:29,175 --> 00:18:32,078 +of default verbs +with corresponding synonyms. + +420 +00:18:32,078 --> 00:18:34,447 +You can see +a few examples here. + +421 +00:18:34,447 --> 00:18:37,950 +If your intent maps to one +of these, go ahead and use it. + +422 +00:18:37,950 --> 00:18:41,020 +If you have a specific intent +that falls outside this group, + +423 +00:18:41,020 --> 00:18:43,189 +you can provide +a custom string instead + +424 +00:18:43,189 --> 00:18:44,890 +but be sure to provide +synonyms as well + +425 +00:18:44,890 --> 00:18:48,194 +so that Siri understands +all the similar phrases. + +426 +00:18:48,194 --> 00:18:50,229 +Last but not least, +once you've created + +427 +00:18:50,229 --> 00:18:52,698 +an awesome set of app shortcuts, +there are a few places + +428 +00:18:52,698 --> 00:18:56,035 +to surface them +for people to discover. + +429 +00:18:56,035 --> 00:18:58,537 +First, in place of the old +Add to Siri button + +430 +00:18:58,537 --> 00:19:01,173 +is a new tip for you +to feature in your app. + +431 +00:19:01,173 --> 00:19:03,142 +It's important that +you carefully select moments + +432 +00:19:03,142 --> 00:19:05,511 +within your app to surface +these tips, at a time + +433 +00:19:05,511 --> 00:19:08,080 +when people are likely +to benefit from the education, + +434 +00:19:08,080 --> 00:19:11,217 +such as immediately before +or after completing an action + +435 +00:19:11,217 --> 00:19:13,619 +that they may want to repeat +in the future. + +436 +00:19:13,619 --> 00:19:16,989 +And where appropriate, +make your tip dismissible. + +437 +00:19:16,989 --> 00:19:19,325 +Respect people's desire to +remove this helpful information + +438 +00:19:19,325 --> 00:19:22,061 +from their workflow. + +439 +00:19:22,061 --> 00:19:24,897 +We've also provided a button +that you can place in your app + +440 +00:19:24,897 --> 00:19:27,800 +to link to the Shortcuts app +directly to the landing page + +441 +00:19:27,800 --> 00:19:30,770 +that lists all +your app shortcuts. + +442 +00:19:30,770 --> 00:19:32,438 +It's an exciting year +for shortcuts, + +443 +00:19:32,438 --> 00:19:34,573 +with the introduction +of App Shortcuts, + +444 +00:19:34,573 --> 00:19:37,176 +created by you, +and seamlessly available + +445 +00:19:37,176 --> 00:19:41,647 +in Siri, Shortcuts, +and Spotlight. + +446 +00:19:41,647 --> 00:19:44,083 +With dynamic parameters +in the invocation phrase, + +447 +00:19:44,083 --> 00:19:47,553 +Custom Snippets on watchOS, +new tips, and more, + +448 +00:19:47,553 --> 00:19:49,889 +there's a lot to explore. + +449 +00:19:49,889 --> 00:19:51,624 +And with all the info +I walked through today + +450 +00:19:51,624 --> 00:19:53,959 +on selecting and naming +great app shortcuts, + +451 +00:19:53,959 --> 00:19:55,961 +refining your presentation +and flow, + +452 +00:19:55,961 --> 00:19:57,863 +and including tips +for discoverability, + +453 +00:19:57,863 --> 00:19:58,864 +you're well on your way + +454 +00:19:58,864 --> 00:20:01,100 +to an amazing +App Shortcuts experience. + +455 +00:20:01,100 --> 00:20:02,601 +We can't wait to see +what you create, + +456 +00:20:02,601 --> 00:20:04,703 +and have a great +rest of WWDC. + +457 +00:20:04,703 --> 00:20:09,275 +♪ + diff --git a/eng/2022 Session 10170 Implement App Shortcuts with App Intents en.srt b/eng/2022 Session 10170 Implement App Shortcuts with App Intents en.srt new file mode 100644 index 0000000..acf3163 --- /dev/null +++ b/eng/2022 Session 10170 Implement App Shortcuts with App Intents en.srt @@ -0,0 +1,2342 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ (Mellow instrumental +hip-hop music) ♪ + +2 +00:00:03,003 --> 00:00:09,710 +♪ + +3 +00:00:09,710 --> 00:00:11,678 +Hi, my name is Michael Sumner. + +4 +00:00:11,678 --> 00:00:15,148 +I'm a software engineer +working on Siri and App Intents. + +5 +00:00:15,148 --> 00:00:17,117 +In this session, +I want to talk to you + +6 +00:00:17,117 --> 00:00:19,219 +about creating app shortcuts +for your app, + +7 +00:00:19,219 --> 00:00:22,155 +using the new +App Intents framework. + +8 +00:00:22,155 --> 00:00:25,425 +I'll start with an overview +of what App Shortcuts are + +9 +00:00:25,425 --> 00:00:28,095 +and how they relate +to App Intents. + +10 +00:00:28,095 --> 00:00:31,665 +Then, I'll walk through creating +an app shortcut in Swift, + +11 +00:00:31,665 --> 00:00:34,301 +and adding a parameter. + +12 +00:00:34,301 --> 00:00:38,205 +Finally, I'll cover how to make +your app shortcut discoverable, + +13 +00:00:38,205 --> 00:00:41,775 +so users can benefit +from your hard work. + +14 +00:00:41,775 --> 00:00:44,177 +Let's get started with +the App Intents framework + +15 +00:00:44,177 --> 00:00:46,280 +and App Shortcuts. + +16 +00:00:46,280 --> 00:00:48,982 +People use shortcuts +to create multistep workflows + +17 +00:00:48,982 --> 00:00:51,785 +with your apps that can be used +from the Shortcuts app + +18 +00:00:51,785 --> 00:00:53,553 +and from Siri. + +19 +00:00:53,553 --> 00:00:56,790 +Until now, someone first +had to set up a shortcut + +20 +00:00:56,790 --> 00:00:59,826 +via an Add to Siri button +or the Shortcuts app + +21 +00:00:59,826 --> 00:01:02,429 +before they could use +your intent. + +22 +00:01:02,429 --> 00:01:05,032 +We're excited to introduce +App Shortcuts, + +23 +00:01:05,032 --> 00:01:08,268 +which require zero user setup. + +24 +00:01:08,268 --> 00:01:10,871 +This makes it easier than ever +for people to benefit + +25 +00:01:10,871 --> 00:01:12,773 +from your shortcuts. + +26 +00:01:12,773 --> 00:01:14,808 +By integrating +with App Shortcuts, + +27 +00:01:14,808 --> 00:01:16,677 +intents from your app +will be available + +28 +00:01:16,677 --> 00:01:19,346 +as soon as your app +is installed. + +29 +00:01:19,346 --> 00:01:21,982 +This makes it easy for someone +to discover and use + +30 +00:01:21,982 --> 00:01:24,251 +your app's functionality. + +31 +00:01:24,251 --> 00:01:26,687 +They no longer need +to head to the Shortcuts app + +32 +00:01:26,687 --> 00:01:31,024 +or use an Add to Siri button +to set anything up. + +33 +00:01:31,024 --> 00:01:33,760 +App Shortcuts, +like user-built shortcuts, + +34 +00:01:33,760 --> 00:01:35,662 +can be run from +the Shortcuts app, + +35 +00:01:35,662 --> 00:01:38,198 +Spotlight, and Siri. + +36 +00:01:38,198 --> 00:01:40,901 +This gives people multiple ways +to discover and interact + +37 +00:01:40,901 --> 00:01:44,638 +with your application from +various places in the system. + +38 +00:01:44,638 --> 00:01:46,873 +For example, +when searching in Spotlight, + +39 +00:01:46,873 --> 00:01:48,442 +your app shortcut +will be displayed + +40 +00:01:48,442 --> 00:01:52,045 +right in the search results +for easy access. + +41 +00:01:52,045 --> 00:01:53,947 +By implementing App Shortcuts, + +42 +00:01:53,947 --> 00:01:57,417 +your users will be able to +interact with your app in quick, + +43 +00:01:57,417 --> 00:01:59,853 +lightweight interactions +that make it easier for them + +44 +00:01:59,853 --> 00:02:03,724 +to complete their task +and be on their way. + +45 +00:02:03,724 --> 00:02:06,226 +My team is working on an app -- +Meditation -- + +46 +00:02:06,226 --> 00:02:08,428 +that helps users meditate +by guiding them + +47 +00:02:08,428 --> 00:02:11,031 +through a set of audio prompts +and sounds + +48 +00:02:11,031 --> 00:02:13,667 +to help them focus +on what matters. + +49 +00:02:13,667 --> 00:02:17,871 +Today, to start a meditation, +users have to launch the app, + +50 +00:02:17,871 --> 00:02:22,242 +log in, and find the meditation +session that they want to run. + +51 +00:02:22,242 --> 00:02:24,244 +By integrating +with App Shortcuts, + +52 +00:02:24,244 --> 00:02:27,581 +my users can quickly access +these features from anywhere + +53 +00:02:27,581 --> 00:02:30,217 +just by asking Siri. + +54 +00:02:30,217 --> 00:02:32,919 +And by making it faster +to start a session, + +55 +00:02:32,919 --> 00:02:36,223 +users can integrate meditation +into their daily routine, + +56 +00:02:36,223 --> 00:02:38,125 +either in the morning +before work + +57 +00:02:38,125 --> 00:02:43,830 +or in the evening to help +wind down after a long day. + +58 +00:02:43,830 --> 00:02:46,500 +Alright, let's dive right in +to the code needed + +59 +00:02:46,500 --> 00:02:51,204 +to create an App Intent and +turn it into an app shortcut. + +60 +00:02:51,204 --> 00:02:52,873 +Unlike previous shortcuts, + +61 +00:02:52,873 --> 00:02:57,244 +App Shortcuts are built with +the new App Intents framework. + +62 +00:02:57,244 --> 00:02:59,813 +App Intents is a new, +Swift-only framework + +63 +00:02:59,813 --> 00:03:02,182 +built from the ground up +to make it faster + +64 +00:03:02,182 --> 00:03:05,318 +and easier to build +great intents. + +65 +00:03:05,318 --> 00:03:07,187 +With App Intents, +everything is defined + +66 +00:03:07,187 --> 00:03:09,189 +right in your Swift source code, + +67 +00:03:09,189 --> 00:03:12,125 +instead of a separate +metadata file. + +68 +00:03:12,125 --> 00:03:14,628 +This removes any +code generation steps + +69 +00:03:14,628 --> 00:03:17,330 +and allows you to stay focused +without switching contexts + +70 +00:03:17,330 --> 00:03:20,634 +between the source editor +and the metadata editor. + +71 +00:03:20,634 --> 00:03:22,169 +They're also easier +to code review + +72 +00:03:22,169 --> 00:03:24,638 +and solve merge conflicts. + +73 +00:03:24,638 --> 00:03:26,039 +To build App Shortcuts, + +74 +00:03:26,039 --> 00:03:28,575 +you'll need to write +an AppShortcutsProvider + +75 +00:03:28,575 --> 00:03:31,845 +that lists the phrases +and other metadata needed + +76 +00:03:31,845 --> 00:03:35,615 +to turn your App Intent +into a full-fledged shortcut. + +77 +00:03:35,615 --> 00:03:37,818 +And note that because +these are set up + +78 +00:03:37,818 --> 00:03:40,454 +without any user interaction, +you'll need to include + +79 +00:03:40,454 --> 00:03:44,024 +your application's name +in the trigger phrase. + +80 +00:03:44,024 --> 00:03:46,326 +Intents are defined +as Swift structs, + +81 +00:03:46,326 --> 00:03:49,262 +that implement +the AppIntent protocol. + +82 +00:03:49,262 --> 00:03:52,165 +A basic intent has +just two requirements -- + +83 +00:03:52,165 --> 00:03:54,701 +a title, which is used +to display your intent + +84 +00:03:54,701 --> 00:03:58,438 +in the Shortcuts app, +and a method called perform. + +85 +00:03:58,438 --> 00:04:01,074 +The perform method is where +you run your intent's logic + +86 +00:04:01,074 --> 00:04:03,210 +and return a result. + +87 +00:04:03,210 --> 00:04:05,946 +Additionally, you can trigger +prompts for the user + +88 +00:04:05,946 --> 00:04:08,648 +and await their response. + +89 +00:04:08,648 --> 00:04:11,852 +In this intent, I'll start +the default meditation session + +90 +00:04:11,852 --> 00:04:14,788 +using my app's +MeditationService. + +91 +00:04:14,788 --> 00:04:16,990 +Because the perform method +is async, + +92 +00:04:16,990 --> 00:04:21,294 +I can run asynchronous code +to start my session. + +93 +00:04:21,294 --> 00:04:22,896 +Once the session has started, + +94 +00:04:22,896 --> 00:04:25,832 +I'll return a dialog +that is shown to the user. + +95 +00:04:25,832 --> 00:04:27,467 +And if your app is localized, + +96 +00:04:27,467 --> 00:04:32,539 +you'll want to localize this +string in all of your locales. + +97 +00:04:32,539 --> 00:04:34,608 +So far, with what I've built, + +98 +00:04:34,608 --> 00:04:37,677 +the StartMeditationIntent +will appear in the Shortcuts app + +99 +00:04:37,677 --> 00:04:39,813 +when authoring a shortcut. + +100 +00:04:39,813 --> 00:04:42,249 +A motivated user +can take this intent + +101 +00:04:42,249 --> 00:04:45,418 +and create a shortcut +that will kick off a session. + +102 +00:04:45,418 --> 00:04:46,653 +In this shortcut, + +103 +00:04:46,653 --> 00:04:51,191 +I've also added a second intent +to enable a Focus. + +104 +00:04:51,191 --> 00:04:54,194 +By default, my App Intent +is rendered using the title + +105 +00:04:54,194 --> 00:04:56,830 +I specified in my source code. + +106 +00:04:56,830 --> 00:04:59,499 +To customize the rendering +for your action, + +107 +00:04:59,499 --> 00:05:04,004 +be sure to add a parameter +summary to your App Intent. + +108 +00:05:04,004 --> 00:05:05,906 +Parameter summaries +allow you to customize + +109 +00:05:05,906 --> 00:05:10,410 +the look of your intent, +as well as show values inline. + +110 +00:05:10,410 --> 00:05:14,681 +However, this intent works great +as a shortcut all by itself. + +111 +00:05:14,681 --> 00:05:17,417 +Ideally, someone would be able +to run my intent + +112 +00:05:17,417 --> 00:05:20,754 +without first having to author +a shortcut at all. + +113 +00:05:20,754 --> 00:05:22,422 +By creating an app shortcut, + +114 +00:05:22,422 --> 00:05:25,892 +I can perform this setup step +on behalf of the user, + +115 +00:05:25,892 --> 00:05:27,994 +so they can start +using my intent + +116 +00:05:27,994 --> 00:05:31,164 +as soon as the app is installed. + +117 +00:05:31,164 --> 00:05:32,899 +Now that I've written an intent, + +118 +00:05:32,899 --> 00:05:35,735 +I'll create +an app shortcut for it. + +119 +00:05:35,735 --> 00:05:37,437 +Similar to an intent, + +120 +00:05:37,437 --> 00:05:39,773 +App Shortcuts are defined +in Swift code, + +121 +00:05:39,773 --> 00:05:43,410 +by implementing the +AppShortcutsProvider protocol. + +122 +00:05:43,410 --> 00:05:44,978 +To implement the protocol, + +123 +00:05:44,978 --> 00:05:47,514 +I'll simply create +a single getter that returns + +124 +00:05:47,514 --> 00:05:50,850 +all the app shortcuts +I want to set up for the user. + +125 +00:05:50,850 --> 00:05:53,720 +Note that in total, +your app can have a maximum + +126 +00:05:53,720 --> 00:05:55,922 +of 10 app shortcuts. + +127 +00:05:55,922 --> 00:06:00,093 +However, most apps +only need a few. + +128 +00:06:00,093 --> 00:06:02,429 +So I'll create +a single AppShortcut + +129 +00:06:02,429 --> 00:06:05,098 +for my StartMeditationIntent. + +130 +00:06:05,098 --> 00:06:08,635 +To get started, I'll pass +an instance of my intent. + +131 +00:06:08,635 --> 00:06:11,137 +If my intent's initializer +took parameters, + +132 +00:06:11,137 --> 00:06:14,074 +I could specify values here. + +133 +00:06:14,074 --> 00:06:17,143 +Second, I'll create an array +of spoken phrases + +134 +00:06:17,143 --> 00:06:20,413 +that will invoke +my AppShortcut from Siri. + +135 +00:06:20,413 --> 00:06:23,149 +You'll notice that rather than +writing my application's name + +136 +00:06:23,149 --> 00:06:24,584 +directly in the string, + +137 +00:06:24,584 --> 00:06:27,787 +I used a special +.applicationName token. + +138 +00:06:27,787 --> 00:06:31,258 +This allows Siri to insert not +only my application's main name, + +139 +00:06:31,258 --> 00:06:35,996 +but also any app name synonyms +that I've configured. + +140 +00:06:35,996 --> 00:06:39,432 +Because users may say different +phrases to start meditation, + +141 +00:06:39,432 --> 00:06:42,636 +I'll provide a few more +alternative phrases here. + +142 +00:06:42,636 --> 00:06:44,137 +If your app is localized, + +143 +00:06:44,137 --> 00:06:46,840 +you'll need to localize +these phrases as well. + +144 +00:06:48,842 --> 00:06:51,711 +Great. So now when someone +wants to mediate, + +145 +00:06:51,711 --> 00:06:55,081 +they just stay to Siri, +"Start a meditation." + +146 +00:06:55,081 --> 00:06:57,751 +Siri will call +the StartMeditationIntent + +147 +00:06:57,751 --> 00:07:00,887 +and speak the dialog +that I returned. + +148 +00:07:00,887 --> 00:07:04,257 +Also, if someone searches +for my app in Spotlight, + +149 +00:07:04,257 --> 00:07:06,559 +they'll see the first +App Shortcut I've listed + +150 +00:07:06,559 --> 00:07:08,161 +in my code. + +151 +00:07:08,161 --> 00:07:09,829 +When the user taps the result, + +152 +00:07:09,829 --> 00:07:11,631 +the shortcut +will immediately run + +153 +00:07:11,631 --> 00:07:14,000 +without launching +the application. + +154 +00:07:14,000 --> 00:07:15,935 +It's important to note +that if your intent + +155 +00:07:15,935 --> 00:07:19,906 +does trigger an app launch, +it won't be shown in Spotlight. + +156 +00:07:19,906 --> 00:07:22,709 +So, with just a very small +amount of code, + +157 +00:07:22,709 --> 00:07:25,478 +I've now made it much, +much simpler for my users + +158 +00:07:25,478 --> 00:07:28,982 +to meditate with my app. + +159 +00:07:28,982 --> 00:07:31,751 +But right now, Siri shows +a default view + +160 +00:07:31,751 --> 00:07:33,820 +whenever running my intent. + +161 +00:07:33,820 --> 00:07:36,623 +This is OK, but I'd really +like to show the user + +162 +00:07:36,623 --> 00:07:40,293 +more information when they run +my app shortcut. + +163 +00:07:40,293 --> 00:07:43,163 +To do this, I'll need +to implement a custom view + +164 +00:07:43,163 --> 00:07:47,901 +that Siri can show whenever +my intent is run. + +165 +00:07:47,901 --> 00:07:49,969 +Views in the App Intents +framework are built + +166 +00:07:49,969 --> 00:07:54,841 +using SwiftUI and leverage the +same view technology as widgets. + +167 +00:07:54,841 --> 00:07:57,977 +This means you, as a developer, +don't need to build + +168 +00:07:57,977 --> 00:08:01,181 +a separate UI extension +for your custom view. + +169 +00:08:01,181 --> 00:08:03,316 +Instead, you can simply +return the view + +170 +00:08:03,316 --> 00:08:05,685 +when running your intent. + +171 +00:08:05,685 --> 00:08:08,188 +It's important to think about +the specific constraints + +172 +00:08:08,188 --> 00:08:10,190 +this brings for your views. + +173 +00:08:10,190 --> 00:08:13,026 +Just like widgets, +custom App Intent views + +174 +00:08:13,026 --> 00:08:17,530 +can't include things like +interactivity or animations. + +175 +00:08:17,530 --> 00:08:21,801 +Make sure to take this into +account when designing your UI. + +176 +00:08:21,801 --> 00:08:26,206 +App Intents supports showing +custom UI at three phases: + +177 +00:08:26,206 --> 00:08:29,142 +value confirmation, +intent confirmation, + +178 +00:08:29,142 --> 00:08:31,478 +and after the intent +is finished. + +179 +00:08:31,478 --> 00:08:34,247 +For my app, I'm going to return +a custom view + +180 +00:08:34,247 --> 00:08:36,816 +at the end of running my intent. + +181 +00:08:36,816 --> 00:08:38,885 +If you're using +these other prompts, + +182 +00:08:38,885 --> 00:08:41,521 +be sure to think about how +you can integrate custom UI + +183 +00:08:41,521 --> 00:08:44,257 +at those steps too. + +184 +00:08:44,257 --> 00:08:48,328 +Finally, as I mentioned, +displaying custom UI is easy. + +185 +00:08:48,328 --> 00:08:52,065 +You simply need to return +your view from your intent. + +186 +00:08:52,065 --> 00:08:55,235 +Alright, time to jump +into some code. + +187 +00:08:55,235 --> 00:08:57,203 +Adding a custom view is easy. + +188 +00:08:57,203 --> 00:09:01,241 +As I mentioned, I'll just return +the view alongside my dialog. + +189 +00:09:01,241 --> 00:09:03,276 +The App Intents framework +will take care + +190 +00:09:03,276 --> 00:09:06,846 +of presenting my view +inside the Siri snippet. + +191 +00:09:06,846 --> 00:09:09,215 +Keep in mind that your views +are going to be shown + +192 +00:09:09,215 --> 00:09:11,551 +alongside other Siri views, + +193 +00:09:11,551 --> 00:09:14,654 +like the snippet title +or confirmation buttons. + +194 +00:09:14,654 --> 00:09:16,289 +So you'll want +your snippet's design + +195 +00:09:16,289 --> 00:09:19,559 +to feel at home inside of Siri. + +196 +00:09:19,559 --> 00:09:22,695 +Up next, let's check out how +I can extend an app shortcut + +197 +00:09:22,695 --> 00:09:26,766 +to include parameters. + +198 +00:09:26,766 --> 00:09:28,601 +In my previous implementation, + +199 +00:09:28,601 --> 00:09:32,238 +I chose to start +the default meditation session. + +200 +00:09:32,238 --> 00:09:35,575 +But my app includes +many great session types, + +201 +00:09:35,575 --> 00:09:37,811 +and users will want to start +a particular session + +202 +00:09:37,811 --> 00:09:39,746 +they have in mind. + +203 +00:09:39,746 --> 00:09:42,415 +Ideally, my user would be able +to specify the session + +204 +00:09:42,415 --> 00:09:45,251 +they want to start +when running my intent. + +205 +00:09:45,251 --> 00:09:46,820 +To support these use cases, + +206 +00:09:46,820 --> 00:09:49,622 +I'll need to extend my intent +by adding a parameter + +207 +00:09:49,622 --> 00:09:54,461 +that captures the session +the user wants to run. + +208 +00:09:54,461 --> 00:09:55,728 +To add a parameter, + +209 +00:09:55,728 --> 00:09:58,698 +I first need to define +the parameter's type. + +210 +00:09:58,698 --> 00:10:01,034 +I'll create +a MeditationSession struct + +211 +00:10:01,034 --> 00:10:03,870 +that has the relevant +information for a session. + +212 +00:10:03,870 --> 00:10:05,138 +I'll include a name, + +213 +00:10:05,138 --> 00:10:06,873 +and I'll give it +an identifier field, + +214 +00:10:06,873 --> 00:10:10,009 +which can be a UUID. + +215 +00:10:10,009 --> 00:10:13,079 +To use this struct +as a parameter for my intent, + +216 +00:10:13,079 --> 00:10:16,983 +I also need to implement +the AppEntity protocol. + +217 +00:10:16,983 --> 00:10:19,152 +Implementing +the AppEntity protocol + +218 +00:10:19,152 --> 00:10:22,055 +tells the App Intents framework +about my type + +219 +00:10:22,055 --> 00:10:24,524 +and lets me specify +additional information, + +220 +00:10:24,524 --> 00:10:27,527 +like how the entity +is displayed. + +221 +00:10:27,527 --> 00:10:31,130 +The entity protocol requires +that my type has an identifier, + +222 +00:10:31,130 --> 00:10:32,866 +which I've already provided. + +223 +00:10:32,866 --> 00:10:37,370 +I could use other types as well, +like integers or strings. + +224 +00:10:37,370 --> 00:10:39,072 +I also need to provide +some information + +225 +00:10:39,072 --> 00:10:41,207 +on how to display my entity. + +226 +00:10:41,207 --> 00:10:42,809 +This will be used +in the Shortcuts app + +227 +00:10:42,809 --> 00:10:46,246 +and other places +where my entity is shown. + +228 +00:10:46,246 --> 00:10:49,449 +Finally, I need to wire up +a default query. + +229 +00:10:49,449 --> 00:10:52,252 +I'll call my query +the MeditationSessionQuery, + +230 +00:10:52,252 --> 00:10:55,388 +and I'll implement it next. + +231 +00:10:55,388 --> 00:10:58,057 +In order to work with my entity, +the App Intents framework + +232 +00:10:58,057 --> 00:10:59,859 +needs to be able +to look up my entities + +233 +00:10:59,859 --> 00:11:01,928 +based on their identifier. + +234 +00:11:01,928 --> 00:11:03,229 +To make this possible, + +235 +00:11:03,229 --> 00:11:06,633 +the EntityQuery protocol +defines just one requirement: + +236 +00:11:06,633 --> 00:11:08,568 +a function +that takes identifiers + +237 +00:11:08,568 --> 00:11:11,237 +and returns matching entities. + +238 +00:11:11,237 --> 00:11:13,573 +I'll implement this function +by looking up the sessions + +239 +00:11:13,573 --> 00:11:16,709 +in my SessionManager. + +240 +00:11:16,709 --> 00:11:19,646 +Next, I'll update +my StartMeditationIntent + +241 +00:11:19,646 --> 00:11:21,748 +to add a parameter. + +242 +00:11:21,748 --> 00:11:23,016 +Parameters are +straightforward; + +243 +00:11:23,016 --> 00:11:25,852 +they are just a normal +property on my struct. + +244 +00:11:25,852 --> 00:11:28,488 +But to tell App Intents +about my parameter, + +245 +00:11:28,488 --> 00:11:31,991 +I also need to add the +@Parameter property wrapper. + +246 +00:11:31,991 --> 00:11:33,993 +This property wrapper +lets App Intents know + +247 +00:11:33,993 --> 00:11:37,330 +that the session property +is part of my intent. + +248 +00:11:37,330 --> 00:11:39,532 +I can also specify +additional metadata + +249 +00:11:39,532 --> 00:11:44,404 +in the Parameter property +wrapper, like the display name. + +250 +00:11:44,404 --> 00:11:47,173 +Now that I've added +a parameter to my intent, + +251 +00:11:47,173 --> 00:11:50,443 +I need to ask the user which +session they'd like to run. + +252 +00:11:50,443 --> 00:11:52,745 +The App Intents framework +has robust support + +253 +00:11:52,745 --> 00:11:54,714 +for asking users +follow-up questions + +254 +00:11:54,714 --> 00:11:58,184 +to gather values +for my intent's parameters. + +255 +00:11:58,184 --> 00:12:02,121 +These prompts will be displayed +anywhere my intent is run. + +256 +00:12:02,121 --> 00:12:05,058 +When run from Siri, Siri will +speak out the questions, + +257 +00:12:05,058 --> 00:12:07,827 +and ask the user +to speak the answer. + +258 +00:12:07,827 --> 00:12:09,729 +In Spotlight +and the Shortcuts app, + +259 +00:12:09,729 --> 00:12:11,864 +the user will be presented +with the same prompt + +260 +00:12:11,864 --> 00:12:14,667 +in a touch-driven UI. + +261 +00:12:14,667 --> 00:12:19,138 +App Intents supports +three types of value prompts. + +262 +00:12:19,138 --> 00:12:23,643 +Disambiguations asks the user +to select from a fixed list. + +263 +00:12:23,643 --> 00:12:26,079 +Disambiguations are great +to present the user + +264 +00:12:26,079 --> 00:12:28,581 +when you have small +fixed set of options + +265 +00:12:28,581 --> 00:12:32,452 +for a parameter +in your intent. + +266 +00:12:32,452 --> 00:12:34,687 +Value prompts allow you +to ask the user + +267 +00:12:34,687 --> 00:12:37,056 +for an open-ended value. + +268 +00:12:37,056 --> 00:12:39,926 +These are great for types +like strings or integers, + +269 +00:12:39,926 --> 00:12:42,996 +which can take any value. + +270 +00:12:42,996 --> 00:12:45,632 +Finally, confirmation +asks the user + +271 +00:12:45,632 --> 00:12:48,668 +to verify a particular value +and can be helpful + +272 +00:12:48,668 --> 00:12:50,503 +if you want to double-check +with the user + +273 +00:12:50,503 --> 00:12:53,039 +that you understand +their intent. + +274 +00:12:53,039 --> 00:12:55,375 +Prompting for values is +a great way to make intents + +275 +00:12:55,375 --> 00:12:58,578 +more flexible and allows you +to gather more information + +276 +00:12:58,578 --> 00:13:00,179 +from the user. + +277 +00:13:00,179 --> 00:13:03,182 +But they also slow down +the conversation, + +278 +00:13:03,182 --> 00:13:06,586 +and can frustrate users +if you use them too often. + +279 +00:13:06,586 --> 00:13:09,889 +For more insight into +designing great intents, + +280 +00:13:09,889 --> 00:13:11,357 +check out the session titled + +281 +00:13:11,357 --> 00:13:15,595 +"Design App Shortcuts" +from Lynn. + +282 +00:13:15,595 --> 00:13:18,464 +All right, now that I've added +the session parameter + +283 +00:13:18,464 --> 00:13:22,035 +to the StartMeditationIntent, +I'll go ahead and add logic + +284 +00:13:22,035 --> 00:13:25,772 +to my perform method +to prompt for this value. + +285 +00:13:25,772 --> 00:13:28,841 +In my app, I have a small +fixed number of sessions + +286 +00:13:28,841 --> 00:13:30,410 +the user can run. + +287 +00:13:30,410 --> 00:13:32,779 +If the session +isn't already specified, + +288 +00:13:32,779 --> 00:13:35,081 +I'll retrieve the list +from my SessionManager + +289 +00:13:35,081 --> 00:13:38,084 +and present a disambiguation +to the user. + +290 +00:13:38,084 --> 00:13:41,354 +Using the display representation +for each of my sessions, + +291 +00:13:41,354 --> 00:13:44,390 +App Intents will format +sessions into list items + +292 +00:13:44,390 --> 00:13:46,859 +and display them +to the user. + +293 +00:13:46,859 --> 00:13:48,094 +When the user picks one, + +294 +00:13:48,094 --> 00:13:51,497 +the selected item +will be returned to me. + +295 +00:13:51,497 --> 00:13:54,634 +I'll pass the selected session +to my MeditationService, + +296 +00:13:54,634 --> 00:13:56,736 +which will start +the session. + +297 +00:13:56,736 --> 00:13:59,305 +I can then return a dialog +to let the user know + +298 +00:13:59,305 --> 00:14:01,541 +that the intent has started. + +299 +00:14:01,541 --> 00:14:03,976 +Since the user +provided a session, + +300 +00:14:03,976 --> 00:14:06,012 +it's a good idea to put the name +of the session + +301 +00:14:06,012 --> 00:14:11,217 +in the dialog so the user knows +we understood their request. + +302 +00:14:11,217 --> 00:14:15,221 +Great, so now when my users +say, "Start a Meditation," + +303 +00:14:15,221 --> 00:14:17,990 +my app can prompt the user +for the particular session + +304 +00:14:17,990 --> 00:14:19,859 +they want to run. + +305 +00:14:19,859 --> 00:14:21,894 +However, as I +mentioned before, + +306 +00:14:21,894 --> 00:14:26,432 +users prefer Siri interactions +that are quick and to the point. + +307 +00:14:26,432 --> 00:14:29,469 +Ideally, I'd be able +to let my users tell Siri + +308 +00:14:29,469 --> 00:14:32,438 +the session they'd like +to run in the initial phrase, + +309 +00:14:32,438 --> 00:14:35,174 +rather than +in a follow-up question. + +310 +00:14:35,174 --> 00:14:37,643 +Well, I have good news. + +311 +00:14:37,643 --> 00:14:40,413 +App Shortcuts has support +for extending trigger phrases + +312 +00:14:40,413 --> 00:14:43,216 +with predefined parameters. + +313 +00:14:43,216 --> 00:14:46,018 +By implementing +parameterized phrases, + +314 +00:14:46,018 --> 00:14:48,187 +my app can support +utterances like + +315 +00:14:48,187 --> 00:14:52,959 +"Start a calming meditation" +or "Start a walking meditation." + +316 +00:14:52,959 --> 00:14:54,293 +Parameters are great +when you have + +317 +00:14:54,293 --> 00:14:56,929 +a fixed set of well-known +parameter values + +318 +00:14:56,929 --> 00:15:00,032 +that you can specify +to Siri ahead of time. + +319 +00:15:00,032 --> 00:15:04,003 +For my app, +I'll use my session names. + +320 +00:15:04,003 --> 00:15:07,440 +Parameters are not meant +for open-ended values. + +321 +00:15:07,440 --> 00:15:11,010 +For example, it's not possible +to gather an arbitrary string + +322 +00:15:11,010 --> 00:15:13,513 +from the user +in the initial utterance. + +323 +00:15:13,513 --> 00:15:15,715 +So, my app couldn't support +a phrase + +324 +00:15:15,715 --> 00:15:18,518 +like "Search my +diary for X," + +325 +00:15:18,518 --> 00:15:22,121 +where X could be +any input from the user. + +326 +00:15:22,121 --> 00:15:25,491 +Instead, parameter values +are specified ahead of time, + +327 +00:15:25,491 --> 00:15:27,393 +when your app is running. + +328 +00:15:27,393 --> 00:15:30,630 +Let's implement +some parameterized phrases. + +329 +00:15:30,630 --> 00:15:32,899 +To implement parameterized +phrases in my app, + +330 +00:15:32,899 --> 00:15:35,535 +I need to make +a few changes. + +331 +00:15:35,535 --> 00:15:38,704 +First, I'll update the query +for my SessionEntity + +332 +00:15:38,704 --> 00:15:41,007 +to implement the +suggestedResults() method + +333 +00:15:41,007 --> 00:15:45,444 +to return the list of entities +for my parameterized shortcut. + +334 +00:15:45,444 --> 00:15:48,381 +Second, I'll need to notify +the App Intents framework + +335 +00:15:48,381 --> 00:15:51,651 +when the list of available +SessionEntities has changed. + +336 +00:15:51,651 --> 00:15:53,119 +This allows +the App Intents framework + +337 +00:15:53,119 --> 00:15:56,756 +to create new shortcut phrases +for use in Siri. + +338 +00:15:56,756 --> 00:15:59,292 +I'll do this by updating +my app's model layer + +339 +00:15:59,292 --> 00:16:00,860 +to notify +the App Intents framework + +340 +00:16:00,860 --> 00:16:03,729 +whenever my session list +changes. + +341 +00:16:03,729 --> 00:16:06,899 +Finally, I'll add some new +phrases to my App Shortcut + +342 +00:16:06,899 --> 00:16:08,701 +that reference +the session parameter + +343 +00:16:08,701 --> 00:16:12,171 +on my StartMeditationIntent. + +344 +00:16:12,171 --> 00:16:15,808 +So first, I'll update +the MeditationSessionQuery + +345 +00:16:15,808 --> 00:16:18,945 +by implementing the +suggestedEntities function. + +346 +00:16:18,945 --> 00:16:21,214 +The App Intents framework +uses the sessions + +347 +00:16:21,214 --> 00:16:25,318 +returned from this function to +create parameterized shortcuts. + +348 +00:16:25,318 --> 00:16:28,354 +It's important to note that +while this method is optional, + +349 +00:16:28,354 --> 00:16:30,356 +if I don't implement +this method, + +350 +00:16:30,356 --> 00:16:34,861 +I won't get any +parameterized shortcuts at all. + +351 +00:16:34,861 --> 00:16:37,697 +Second, I'll need to update +my app's model layer + +352 +00:16:37,697 --> 00:16:39,699 +to notify +the App Intents framework + +353 +00:16:39,699 --> 00:16:42,935 +whenever my list +of sessions changes. + +354 +00:16:42,935 --> 00:16:46,172 +In my app, I infrequently +publish new session types + +355 +00:16:46,172 --> 00:16:48,875 +that I fetch from the server +in the background. + +356 +00:16:48,875 --> 00:16:51,043 +I'll update my SessionModel +to call the + +357 +00:16:51,043 --> 00:16:53,846 +updateAppShortcutParameters() +method + +358 +00:16:53,846 --> 00:16:56,616 +any time I receive +new sessions. + +359 +00:16:56,616 --> 00:16:59,185 +This method is provided +by the App Intents framework; + +360 +00:16:59,185 --> 00:17:01,554 +you don't need +to implement it yourself. + +361 +00:17:01,554 --> 00:17:04,891 +When called, App Intents +will invoke your entity's query + +362 +00:17:04,891 --> 00:17:09,195 +to gather the list of parameters +for your shortcut phrases. + +363 +00:17:09,195 --> 00:17:12,265 +Finally, I'll add new phrases +for my App Shortcut + +364 +00:17:12,265 --> 00:17:15,601 +that include the session +keypath on my intent. + +365 +00:17:15,601 --> 00:17:18,271 +The App Intents framework +will combine this phrase + +366 +00:17:18,271 --> 00:17:21,574 +with all of the sessions +returned from my query. + +367 +00:17:21,574 --> 00:17:25,678 +The text used for each value is +pulled from the title property + +368 +00:17:25,678 --> 00:17:29,248 +on the SessionEntity's +display representation. + +369 +00:17:29,248 --> 00:17:31,350 +Just like before, +I'll want to include + +370 +00:17:31,350 --> 00:17:35,421 +a few different ways that users +might phrase my App Shortcut. + +371 +00:17:35,421 --> 00:17:37,523 +This ensures +a smoother experience + +372 +00:17:37,523 --> 00:17:41,360 +if the user doesn't remember +your preferred phrase. + +373 +00:17:41,360 --> 00:17:44,931 +All right, I now have a great, +full-featured App Shortcut, + +374 +00:17:44,931 --> 00:17:48,034 +and I can't wait for my users +to give it a try. + +375 +00:17:48,034 --> 00:17:50,336 +But in order +for that to happen, + +376 +00:17:50,336 --> 00:17:55,675 +I need to do some work to help +users discover my new Shortcut. + +377 +00:17:55,675 --> 00:17:59,478 +The first thing I want to talk +about is picking great phrases. + +378 +00:17:59,478 --> 00:18:03,516 +Great phrases for App Shortcuts +are short and memorable. + +379 +00:18:03,516 --> 00:18:05,751 +Users will have a lot +of apps on their phone + +380 +00:18:05,751 --> 00:18:07,353 +that support App Shortcuts; + +381 +00:18:07,353 --> 00:18:09,822 +and in practice, +users can have a hard time + +382 +00:18:09,822 --> 00:18:12,792 +remembering exactly how +to phrase their shortcuts. + +383 +00:18:12,792 --> 00:18:17,997 +So where possible, keep your +phrases short and to the point. + +384 +00:18:17,997 --> 00:18:20,132 +Along these lines, +if your app name + +385 +00:18:20,132 --> 00:18:22,468 +can be used +as a noun or verb, + +386 +00:18:22,468 --> 00:18:25,304 +consider using it that way +in your phrase. + +387 +00:18:25,304 --> 00:18:28,174 +In my app, I've used +Meditation like a noun, + +388 +00:18:28,174 --> 00:18:31,677 +so that the phrase +can be short and memorable. + +389 +00:18:31,677 --> 00:18:36,782 +Finally, app name synonyms +can help your users immensely. + +390 +00:18:36,782 --> 00:18:38,250 +If users call your app + +391 +00:18:38,250 --> 00:18:40,653 +something other than +your app's display name, + +392 +00:18:40,653 --> 00:18:44,557 +you'll want to consider adding +an app name synonym for it. + +393 +00:18:44,557 --> 00:18:48,361 +iOS 11 added support +for app name synonyms. + +394 +00:18:48,361 --> 00:18:53,065 +If you haven't created one, now +may be a great time to do so. + +395 +00:18:53,065 --> 00:18:54,700 +The next thing +I want to talk about + +396 +00:18:54,700 --> 00:18:58,371 +is the Siri Tip +and the Shortcuts link. + +397 +00:18:58,371 --> 00:19:01,640 +Because App Shortcuts +don't require any user setup, + +398 +00:19:01,640 --> 00:19:04,777 +discoverability is vital +for users to find + +399 +00:19:04,777 --> 00:19:07,613 +and use your App Shortcuts. + +400 +00:19:07,613 --> 00:19:10,016 +With App Shortcuts, +users no longer need + +401 +00:19:10,016 --> 00:19:13,419 +the Add to Siri button +to add your Shortcut. + +402 +00:19:13,419 --> 00:19:14,787 +It's already added! + +403 +00:19:14,787 --> 00:19:18,024 +However, we don't want to lose +the discoverability benefits + +404 +00:19:18,024 --> 00:19:20,259 +that the Add to Siri button +provided. + +405 +00:19:20,259 --> 00:19:24,697 +With that in mind, we've +created a new Siri Tip view. + +406 +00:19:24,697 --> 00:19:27,166 +This view works great +anywhere you may have used + +407 +00:19:27,166 --> 00:19:29,902 +the Add To Siri button +in the past. + +408 +00:19:29,902 --> 00:19:34,106 +The Tip view is available +in both SwiftUI and UIKit. + +409 +00:19:34,106 --> 00:19:35,908 +And we've provided +a number of styles + +410 +00:19:35,908 --> 00:19:40,012 +so that the Tip looks great +in any application. + +411 +00:19:40,012 --> 00:19:42,615 +Siri Tips are best placed +contextually, + +412 +00:19:42,615 --> 00:19:45,518 +when they're relevant +to the content onscreen. + +413 +00:19:45,518 --> 00:19:48,454 +If a user just placed +an order in your app, + +414 +00:19:48,454 --> 00:19:50,589 +consider showing a Tip +for your Shortcut + +415 +00:19:50,589 --> 00:19:52,958 +that provides +the order status. + +416 +00:19:52,958 --> 00:19:55,227 +Siri Tips should be +placed thoughtfully, + +417 +00:19:55,227 --> 00:19:57,329 +when you feel a user +is likely to engage + +418 +00:19:57,329 --> 00:20:00,566 +with your App Shortcut +in the near future. + +419 +00:20:00,566 --> 00:20:03,736 +The Siri Tip also +supports dismissal. + +420 +00:20:03,736 --> 00:20:06,472 +The view includes a dismiss +button and will trigger + +421 +00:20:06,472 --> 00:20:09,608 +a custom closure +in your code when tapped. + +422 +00:20:09,608 --> 00:20:11,844 +You'll want to remove +the view from your layout, + +423 +00:20:11,844 --> 00:20:13,779 +and consider +not showing it again + +424 +00:20:13,779 --> 00:20:16,348 +until you feel +it's relevant. + +425 +00:20:16,348 --> 00:20:19,585 +Finally, we've also included +a new ShortcutsLink + +426 +00:20:19,585 --> 00:20:22,988 +that will launch to a list +of Shortcuts from your app. + +427 +00:20:22,988 --> 00:20:24,990 +This new element is great + +428 +00:20:24,990 --> 00:20:27,259 +if your app has a lot +of App Shortcuts + +429 +00:20:27,259 --> 00:20:30,863 +and you want to let users +explore all of them. + +430 +00:20:30,863 --> 00:20:33,365 +Now, the great thing +about App Shortcuts + +431 +00:20:33,365 --> 00:20:36,769 +is they're available as soon +as your app is installed. + +432 +00:20:36,769 --> 00:20:39,038 +Even before the app +is first launched, + +433 +00:20:39,038 --> 00:20:41,273 +users can see and run +your Shortcuts + +434 +00:20:41,273 --> 00:20:44,743 +from Spotlight, Siri, +and the Shortcuts app. + +435 +00:20:44,743 --> 00:20:46,512 +You may need to take this +into account + +436 +00:20:46,512 --> 00:20:48,414 +when building +your App Shortcut. + +437 +00:20:48,414 --> 00:20:51,784 +For example, if your app +requires a log-in flow, + +438 +00:20:51,784 --> 00:20:55,121 +the user may not have logged in +before running your intent. + +439 +00:20:55,121 --> 00:20:58,257 +Your intent should fail +gracefully with an error message + +440 +00:20:58,257 --> 00:21:01,861 +explaining to the user +that they need to log in. + +441 +00:21:01,861 --> 00:21:05,364 +Second, parameterized phrases +for your App Shortcuts + +442 +00:21:05,364 --> 00:21:08,467 +won't be available until +your app has been launched + +443 +00:21:08,467 --> 00:21:10,402 +and notified +the App Intents framework + +444 +00:21:10,402 --> 00:21:12,905 +that you have new +parameter values. + +445 +00:21:12,905 --> 00:21:14,607 +If your App Shortcut +doesn't contain + +446 +00:21:14,607 --> 00:21:17,977 +any non-parameterized +phrases, the user won't see + +447 +00:21:17,977 --> 00:21:21,714 +your App Shortcut at all until +they first launch your app. + +448 +00:21:21,714 --> 00:21:25,084 +You may consider adding a +few non-parameterized phrases + +449 +00:21:25,084 --> 00:21:27,553 +to avoid this issue. + +450 +00:21:27,553 --> 00:21:30,923 +Additionally, Siri has added +support for phrases like, + +451 +00:21:30,923 --> 00:21:35,094 +"What can I do here?" and +"What can I do with Meditation?" + +452 +00:21:35,094 --> 00:21:37,463 +Siri will automatically +gather and recommend + +453 +00:21:37,463 --> 00:21:41,634 +any App Shortcut phrases and +present them on your behalf. + +454 +00:21:41,634 --> 00:21:44,003 +Your app doesn't need +to do anything additional + +455 +00:21:44,003 --> 00:21:46,305 +for this +functionality to work. + +456 +00:21:46,305 --> 00:21:49,275 +Finally, in both Siri +and the Shortcuts app, + +457 +00:21:49,275 --> 00:21:52,011 +the order your App Shortcuts +are displayed is determined + +458 +00:21:52,011 --> 00:21:54,246 +by the order that you list +your App Shortcuts + +459 +00:21:54,246 --> 00:21:55,948 +in your source code. + +460 +00:21:55,948 --> 00:21:58,384 +You'll want to consider putting +your best and most useful + +461 +00:21:58,384 --> 00:22:02,087 +App Shortcuts first, so that +they get the most attention. + +462 +00:22:02,087 --> 00:22:05,591 +Similarly, the first phrase +you list in the phrase array + +463 +00:22:05,591 --> 00:22:09,495 +will be considered the primary +phrase for that App Shortcut. + +464 +00:22:09,495 --> 00:22:13,566 +The primary phrase is used as +the label on the Shortcut tile, + +465 +00:22:13,566 --> 00:22:15,601 +and it's shown +when the user asks Siri + +466 +00:22:15,601 --> 00:22:18,404 +for help with your app. + +467 +00:22:18,404 --> 00:22:22,274 +OK, we covered a lot +about App Intents framework + +468 +00:22:22,274 --> 00:22:24,310 +and App Shortcuts. + +469 +00:22:24,310 --> 00:22:27,246 +I want to leave you +with two key thoughts. + +470 +00:22:27,246 --> 00:22:31,483 +First, App Shortcuts make it +easy for users to use your app + +471 +00:22:31,483 --> 00:22:35,921 +from anywhere in the system, so +think about the best use cases + +472 +00:22:35,921 --> 00:22:40,059 +in your app that fit +this more lightweight model. + +473 +00:22:40,059 --> 00:22:43,395 +Second, once you've +implemented an App Shortcut, + +474 +00:22:43,395 --> 00:22:46,832 +users will not know about it +unless you tell them! + +475 +00:22:46,832 --> 00:22:50,302 +Think hard about how to make +your App Shortcut discoverable. + +476 +00:22:50,302 --> 00:22:52,605 +Consider places in your app +where you can show + +477 +00:22:52,605 --> 00:22:56,008 +the Siri Tip, as well as +off-product locations, + +478 +00:22:56,008 --> 00:22:59,345 +like a website +or a sign in your store. + +479 +00:22:59,345 --> 00:23:02,014 +We can't wait to see +all the great App Shortcuts + +480 +00:23:02,014 --> 00:23:05,217 +that you create with the +new App Intents framework. + +481 +00:23:05,217 --> 00:23:07,319 +To dig deeper into design, + +482 +00:23:07,319 --> 00:23:09,622 +as well as +the App Intents framework, + +483 +00:23:09,622 --> 00:23:12,424 +be sure to check out +other talks this week. + +484 +00:23:12,424 --> 00:23:15,494 +Thanks, and have +a great WWDC. + +485 +00:23:15,494 --> 00:23:19,531 +♪ + diff --git a/eng/2022 Session 102 Platforms State of the Union en.srt b/eng/2022 Session 102 Platforms State of the Union en.srt new file mode 100644 index 0000000..8c3dbe4 --- /dev/null +++ b/eng/2022 Session 102 Platforms State of the Union en.srt @@ -0,0 +1,6383 @@ +1 +00:00:00,334 --> 00:00:07,641 +♪ ♪ + +2 +00:00:09,243 --> 00:00:17,251 +♪ ♪ + +3 +00:00:22,356 --> 00:00:24,858 +Susan Prescott: Welcome +to the Platform State of the Union + +4 +00:00:24,892 --> 00:00:27,895 +for WWDC 2022. + +5 +00:00:27,928 --> 00:00:31,532 +We're always excited for WWDC +because it's an opportunity + +6 +00:00:31,565 --> 00:00:33,634 +for us to connect with all of you, + +7 +00:00:33,667 --> 00:00:35,636 +share some news +about what we've been working on, + +8 +00:00:35,669 --> 00:00:39,973 +and better understand what you need +from our developer platforms. + +9 +00:00:40,007 --> 00:00:43,510 +What you do as developers is amazing. + +10 +00:00:43,544 --> 00:00:48,048 +You transform your ideas, +the stuff of imagination, + +11 +00:00:48,081 --> 00:00:51,618 +to take users' experiences +to another level. + +12 +00:00:51,652 --> 00:00:55,489 +And we want to help you +take your ideas even further. + +13 +00:00:55,522 --> 00:00:58,625 +In the Keynote, we already talked +about some new capabilities + +14 +00:00:58,659 --> 00:01:00,360 +across iPhone, iPad, Mac, + +15 +00:01:00,394 --> 00:01:02,329 +Apple Watch, and Apple TV, + +16 +00:01:02,362 --> 00:01:05,299 +and the incredible power +of Apple silicon + +17 +00:01:05,332 --> 00:01:09,102 +to help bring even the most ambitious +ideas to life. + +18 +00:01:09,136 --> 00:01:13,273 +We have a lot to cover today. +Let's start with some updates. + +19 +00:01:13,307 --> 00:01:17,711 +Earlier this year, we opened +the brand-new Apple Developer Center, + +20 +00:01:17,744 --> 00:01:20,614 +a place designed for you to connect +and collaborate + +21 +00:01:20,647 --> 00:01:24,518 +with Apple engineers and designers +right here at Apple Park. + +22 +00:01:24,551 --> 00:01:28,488 +And last fall, thousands of you +from all over the world + +23 +00:01:28,522 --> 00:01:30,691 +attended our new online Tech Talks, + +24 +00:01:30,724 --> 00:01:34,361 +with hundreds of live sessions +originating in 11 countries + +25 +00:01:34,394 --> 00:01:36,496 +and in five languages. + +26 +00:01:36,530 --> 00:01:38,932 +For us, one of the best parts +of the Tech Talk series + +27 +00:01:38,966 --> 00:01:40,334 +was one to one meetings, + +28 +00:01:40,367 --> 00:01:43,904 +which were a great opportunity +to hear about what you're up to + +29 +00:01:43,937 --> 00:01:46,807 +and share some advice and guidance. + +30 +00:01:46,840 --> 00:01:49,376 +Last fall, Swift Playgrounds 4 shipped + +31 +00:01:49,409 --> 00:01:53,280 +with the power to build apps and submit +them directly to the App Store + +32 +00:01:53,313 --> 00:01:55,182 +and support for SwiftUI, + +33 +00:01:55,215 --> 00:01:59,686 +making it an incredible tool for +experimentation and UI prototyping. + +34 +00:01:59,720 --> 00:02:02,523 +And of course, there's Xcode Cloud. + +35 +00:02:02,556 --> 00:02:04,157 +We built Xcode Cloud to help you + +36 +00:02:04,191 --> 00:02:07,327 +build better apps +faster and more easily. + +37 +00:02:07,361 --> 00:02:09,663 +It's a continuous integration +and delivery service + +38 +00:02:09,696 --> 00:02:14,268 +built right into Xcode +and hosted in the Cloud. + +39 +00:02:14,301 --> 00:02:18,238 +Xcode Cloud supports development +for all Apple platforms. + +40 +00:02:18,272 --> 00:02:21,175 +It integrates with TestFlight +and App Store Connect + +41 +00:02:21,208 --> 00:02:24,578 +as well as every major git based +source control provider. + +42 +00:02:24,611 --> 00:02:28,015 +It even has REST APIs +to help connect to other aspects + +43 +00:02:28,048 --> 00:02:29,483 +of your development workflow. + +44 +00:02:30,217 --> 00:02:34,154 +It's built with advanced security +to protect you and your projects. + +45 +00:02:34,188 --> 00:02:36,557 +And I'm delighted to say + +46 +00:02:36,590 --> 00:02:38,892 +that Xcode Cloud is available + +47 +00:02:38,926 --> 00:02:40,527 +starting today. + +48 +00:02:41,662 --> 00:02:44,031 +We think nearly every development team + +49 +00:02:44,064 --> 00:02:45,732 +can benefit from Xcode Cloud, + +50 +00:02:45,766 --> 00:02:49,937 +and we've priced it to be accessible +to developers of all sizes. + +51 +00:02:49,970 --> 00:02:53,273 +We're offering +the 25 hour per month subscription + +52 +00:02:53,307 --> 00:02:59,179 +free to all Apple Developer Program +members through the end of 2023! + +53 +00:02:59,213 --> 00:03:03,217 +And you'll be able to subscribe to any +of the Xcode Cloud subscription levels + +54 +00:03:03,250 --> 00:03:06,353 +in the Developer app later this Summer. + +55 +00:03:06,386 --> 00:03:10,824 +Today we're gonna talk +about three big topics. + +56 +00:03:10,858 --> 00:03:15,796 +First, we want to share more about our +vision for developing for Apple platforms, + +57 +00:03:15,829 --> 00:03:19,600 +where we are with our platforms, +and where we're headed. + +58 +00:03:19,633 --> 00:03:23,237 +Then, we'll share some compelling +new ways your apps can integrate + +59 +00:03:23,270 --> 00:03:26,340 +with the system experience +on Apple platforms. + +60 +00:03:26,373 --> 00:03:30,244 +And finally, +we'll discuss some powerful new APIs + +61 +00:03:30,277 --> 00:03:33,847 +and show you how they can enable you +to do even more ambitious things + +62 +00:03:33,881 --> 00:03:34,882 +with your apps. + +63 +00:03:34,915 --> 00:03:37,784 +Let's start with the vision +for our developer platform + +64 +00:03:37,818 --> 00:03:39,286 +and how it's been evolving. + +65 +00:03:39,319 --> 00:03:41,822 +Josh is here to tell you all about it., + +66 +00:03:43,524 --> 00:03:46,760 +Josh Shaffer: A great developer platform +provides tight integration + +67 +00:03:46,793 --> 00:03:49,897 +between programming language, +frameworks and tools. + +68 +00:03:49,930 --> 00:03:52,432 +When all three +fully complement one another, + +69 +00:03:52,466 --> 00:03:55,068 +we can ensure that common things are easy, + +70 +00:03:55,102 --> 00:03:57,337 +and even uncommon things are possible. + +71 +00:03:57,938 --> 00:04:01,375 +Now, getting that right shortens the path +to building a great app, + +72 +00:04:01,408 --> 00:04:03,410 +and it benefits everyone. + +73 +00:04:03,443 --> 00:04:06,146 +Customers get a consistent experience, + +74 +00:04:06,180 --> 00:04:08,916 +like scrolling that always feels perfect. + +75 +00:04:08,949 --> 00:04:13,620 +And you're able to focus your time +and effort on what makes your app unique. + +76 +00:04:13,654 --> 00:04:16,657 +But designs evolve, hardware advances, + +77 +00:04:16,690 --> 00:04:20,561 +and what was once cutting edge +becomes the expected baseline. + +78 +00:04:20,594 --> 00:04:24,064 +The Objective-C language, +AppKit & UIKit frameworks, + +79 +00:04:24,097 --> 00:04:28,001 +and Interface Builder +have empowered generations of developers. + +80 +00:04:28,035 --> 00:04:30,571 +These technologies +were built for each other, + +81 +00:04:30,604 --> 00:04:33,907 +and will continue to serve us well +for a long time to come, + +82 +00:04:33,941 --> 00:04:37,611 +but over time +new abstractions become necessary. + +83 +00:04:37,644 --> 00:04:41,648 +For a while now, you've seen us +hard at work defining the next generation + +84 +00:04:41,682 --> 00:04:45,185 +of integrated language, +frameworks, and tools: + +85 +00:04:45,219 --> 00:04:49,089 +Swift, SwiftUI, and Xcode Previews. + +86 +00:04:50,057 --> 00:04:52,659 +Tight integration +in a development platform like this + +87 +00:04:52,693 --> 00:04:56,563 +requires that all three pieces +be designed and evolved together, + +88 +00:04:56,597 --> 00:04:59,666 +both driving and driven by one another. + +89 +00:04:59,700 --> 00:05:04,972 +Swift result builders were inspired +by SwiftUI's compositional structure. + +90 +00:05:05,005 --> 00:05:10,110 +SwiftUI's declarative views +were enabled by Swift value types. + +91 +00:05:10,143 --> 00:05:15,916 +And Xcode Previews was specifically +designed for, and enabled by, both. + +92 +00:05:15,949 --> 00:05:20,120 +Now, the result is the best development +platform that we have ever built. + +93 +00:05:20,153 --> 00:05:25,626 +And this year, Swift, SwiftUI, and Xcode +all have fantastic updates + +94 +00:05:25,659 --> 00:05:27,661 +that take this vision further, + +95 +00:05:27,694 --> 00:05:31,965 +and make it even easier for you to build +great apps for all of our platforms. + +96 +00:05:31,999 --> 00:05:34,134 +And it all starts with Swift. + +97 +00:05:34,168 --> 00:05:37,938 +Now Ben from the Swift team +is gonna tell you all about what's next. + +98 +00:05:40,107 --> 00:05:48,115 +♪ ♪ + +99 +00:05:49,850 --> 00:05:53,120 +Ben Cohen: +Swift is fast, modern, and safe. + +100 +00:05:53,153 --> 00:05:56,156 +It combines the speed +of a strongly-typed language, + +101 +00:05:56,190 --> 00:05:59,193 +with an expressive syntax +that's easy to read and write. + +102 +00:05:59,226 --> 00:06:03,163 +And its design eliminates +entire categories of programming errors. + +103 +00:06:03,197 --> 00:06:07,267 +Swift is the absolute best language +for building apps across our devices. + +104 +00:06:08,101 --> 00:06:09,736 +Swift is also open source, + +105 +00:06:09,770 --> 00:06:11,638 +with an amazing community of contributors + +106 +00:06:11,672 --> 00:06:13,607 +organized at swift.org, + +107 +00:06:13,640 --> 00:06:16,743 +supporting one another through +initiatives like Diversity in Swift + +108 +00:06:16,777 --> 00:06:18,412 +and the Swift Mentorship Program, + +109 +00:06:18,445 --> 00:06:20,147 +and advancing the language + +110 +00:06:20,180 --> 00:06:22,716 +with working groups on topics +like Swift on server, + +111 +00:06:22,749 --> 00:06:24,585 +and C++ interoperability. + +112 +00:06:25,552 --> 00:06:28,121 +Over the last year, +Swift has gotten even better, + +113 +00:06:28,155 --> 00:06:30,424 +with enhancements in concurrency, + +114 +00:06:30,457 --> 00:06:33,460 +upgrades to make Swift code +easier to read and write, + +115 +00:06:33,493 --> 00:06:35,529 +tooling to help you +customize your workflow, + +116 +00:06:35,562 --> 00:06:38,131 +and amazing under-the-hood improvements. + +117 +00:06:38,165 --> 00:06:39,666 +It started last year, + +118 +00:06:39,700 --> 00:06:41,735 +with the introduction +of Swift Concurrency. + +119 +00:06:42,369 --> 00:06:46,073 +Swift Concurrency dramatically simplified +reading and writing code + +120 +00:06:46,106 --> 00:06:49,009 +that runs in parallel, +and has been a huge hit, + +121 +00:06:49,042 --> 00:06:50,978 +with more than 40,000 apps +in the App Store + +122 +00:06:51,011 --> 00:06:52,980 +adopting it in just the first year. + +123 +00:06:53,447 --> 00:06:56,383 +Because it's such a fundamental +and important improvement + +124 +00:06:56,416 --> 00:06:58,118 +to your app's code base, + +125 +00:06:58,151 --> 00:07:00,621 +it's now possible to deploy code +with Swift Concurrency + +126 +00:07:00,654 --> 00:07:04,625 +to all operating systems +released in the last three years. + +127 +00:07:04,658 --> 00:07:08,729 +Swift Concurrency also introduced +async sequences. + +128 +00:07:08,762 --> 00:07:11,431 +This year, there's a new, +open-source package + +129 +00:07:11,465 --> 00:07:15,769 +that brings concurrency to Swift's +rich set of existing sequence algorithms. + +130 +00:07:15,802 --> 00:07:18,138 +It's called async algorithms. + +131 +00:07:18,172 --> 00:07:20,307 +For example, +where Swift's sequence protocol + +132 +00:07:20,340 --> 00:07:23,610 +supports a zip algorithm +to combine two sequences, + +133 +00:07:23,644 --> 00:07:26,446 +async algorithms brings a version +for zipping together + +134 +00:07:26,480 --> 00:07:28,282 +two asynchronous sequences. + +135 +00:07:28,315 --> 00:07:32,085 +Because async sequences are integrated +directly into the Swift language, + +136 +00:07:32,119 --> 00:07:34,955 +they use familiar constructs +like 'for' loops + +137 +00:07:34,988 --> 00:07:37,024 +that, thanks to the async/await syntax, + +138 +00:07:37,057 --> 00:07:39,026 +looks like regular straight-line code. + +139 +00:07:39,059 --> 00:07:42,062 +You're also able to use +the familiar try/catch pattern + +140 +00:07:42,095 --> 00:07:44,031 +to handle things like network failures + +141 +00:07:44,064 --> 00:07:46,834 +from asynchronous data streaming +over the network. + +142 +00:07:46,867 --> 00:07:49,570 +A key thing about async sequences + +143 +00:07:49,603 --> 00:07:52,172 +is how they deliver data values over time. + +144 +00:07:52,940 --> 00:07:57,511 +Swift now includes a new set of +clock types for representing time units, + +145 +00:07:57,544 --> 00:08:01,882 +and async algorithms builds on them +to provide many time-based algorithms, + +146 +00:08:01,915 --> 00:08:03,383 +like throttle here, + +147 +00:08:03,417 --> 00:08:06,987 +which can help slow down updates +from a sequence. + +148 +00:08:07,020 --> 00:08:11,391 +Swift's concurrency model is designed +to make asynchronous code as easy + +149 +00:08:11,425 --> 00:08:14,161 +and safe to write +as your synchronous code. + +150 +00:08:14,194 --> 00:08:17,064 +A big part of that is Swift's actor model. + +151 +00:08:17,097 --> 00:08:20,000 +Actors allow you to isolate your data + +152 +00:08:20,033 --> 00:08:23,537 +using thread-safe, +concurrently-executing code. + +153 +00:08:23,570 --> 00:08:26,440 +Swift prevents you from +accidentally sharing that state + +154 +00:08:26,473 --> 00:08:27,941 +between parallel threads, + +155 +00:08:27,975 --> 00:08:30,511 +defining away a major source of bugs. + +156 +00:08:31,411 --> 00:08:35,849 +Communication between actors is +easy and efficient through async/await. + +157 +00:08:35,883 --> 00:08:39,653 +Now, Swift takes the idea +of actor isolation further + +158 +00:08:39,686 --> 00:08:41,421 +with distributed actors. + +159 +00:08:41,455 --> 00:08:46,059 +Distributed actors can communicate +across multiple processes or devices. + +160 +00:08:46,093 --> 00:08:49,129 +The "distributed" keyword +marks these actors + +161 +00:08:49,162 --> 00:08:51,632 +and methods that can be accessed remotely, + +162 +00:08:51,665 --> 00:08:54,735 +whether that's between +separate processes on your Mac, + +163 +00:08:54,768 --> 00:08:57,104 +peer to peer between different devices, + +164 +00:08:57,137 --> 00:09:00,741 +or from a device talking to your backend +written with Swift on Server. + +165 +00:09:02,109 --> 00:09:05,012 +Just as actors help Swift +protect your state data + +166 +00:09:05,045 --> 00:09:06,280 +from race conditions, + +167 +00:09:06,313 --> 00:09:10,551 +distributed actors help Swift make them +available outside your process, + +168 +00:09:10,584 --> 00:09:12,986 +using a pluggable transport mechanism. + +169 +00:09:13,020 --> 00:09:16,657 +The Swift compiler can then +perform checks that help guarantee + +170 +00:09:16,690 --> 00:09:19,359 +correct behavior +in a distributed environment, + +171 +00:09:19,393 --> 00:09:23,063 +allowing you to get back to working +on the features that you care about. + +172 +00:09:23,497 --> 00:09:26,600 +Distributed actors +and other concurrency features + +173 +00:09:26,633 --> 00:09:30,070 +show just how easy Swift code can be +to read and write + +174 +00:09:30,103 --> 00:09:33,307 +when enhancements +are crafted deep within the syntax. + +175 +00:09:33,340 --> 00:09:36,543 +To tell you more +about usability enhancements in Swift, + +176 +00:09:36,577 --> 00:09:37,878 +here's Ken. + +177 +00:09:38,879 --> 00:09:40,781 +Ken Orr: Strings are one +of the most important features + +178 +00:09:40,814 --> 00:09:42,516 +of any programming language. + +179 +00:09:42,549 --> 00:09:45,853 +But dealing with strings can +be a common source of frustration. + +180 +00:09:46,653 --> 00:09:48,522 +At some point in a developer's journey, + +181 +00:09:48,555 --> 00:09:50,824 +they may find themselves +needing to extract information + +182 +00:09:50,858 --> 00:09:52,359 +from a string like this. + +183 +00:09:53,493 --> 00:09:56,997 +Writing code to parse the string +is easy to get wrong, + +184 +00:09:57,030 --> 00:09:58,999 +with many details to track. + +185 +00:09:59,032 --> 00:10:02,135 +And the resulting code– +it's hard to read and modify. + +186 +00:10:03,003 --> 00:10:06,673 +Regular expressions are a powerful +solution to this challenge. + +187 +00:10:06,707 --> 00:10:10,010 +They allow you to describe the pattern +you expect to see in your string + +188 +00:10:10,043 --> 00:10:14,114 +and specify which pieces of information +you're interested in capturing. + +189 +00:10:14,147 --> 00:10:18,685 +This year, Swift is delivering a huge +improvement to the developer experience + +190 +00:10:18,719 --> 00:10:20,587 +around regular expressions, + +191 +00:10:20,621 --> 00:10:23,590 +starting with +a new regular expression literal. + +192 +00:10:24,758 --> 00:10:26,593 +They're built directly into the language, + +193 +00:10:26,627 --> 00:10:29,062 +allowing the Swift compiler +to check for correctness. + +194 +00:10:29,096 --> 00:10:31,865 +And they unlock the power +of Swift's type system + +195 +00:10:31,899 --> 00:10:34,334 +when you're extracting information +with a regular expression. + +196 +00:10:34,368 --> 00:10:39,406 +And they take full advantage +of Swift's best-in-class Unicode support. + +197 +00:10:39,439 --> 00:10:41,108 +Let's take a look. + +198 +00:10:41,141 --> 00:10:42,809 +I'm working on an app +called Food Truck + +199 +00:10:42,843 --> 00:10:46,613 +that organizes everything +from taking orders to tracking sales. + +200 +00:10:46,647 --> 00:10:49,583 +And some orders come in as strings, +packed with data. + +201 +00:10:49,616 --> 00:10:53,320 +Now, regular expressions are perfect +for extracting the details I want, + +202 +00:10:53,353 --> 00:10:57,057 +and there's no better place to experiment +with them than here in a Playground. + +203 +00:10:57,090 --> 00:10:59,126 +I'll start by creating a regex literal. + +204 +00:11:00,427 --> 00:11:02,429 +Now I'll type the expression, + +205 +00:11:02,462 --> 00:11:04,498 +and pull out who made the order, + +206 +00:11:04,531 --> 00:11:06,066 +the donut type, + +207 +00:11:06,099 --> 00:11:08,168 +and the number of donuts. + +208 +00:11:08,202 --> 00:11:10,971 +Now, as I type, +the regex is syntax highlighted, + +209 +00:11:11,004 --> 00:11:14,241 +which helps me confirm +my expression is correct. + +210 +00:11:14,274 --> 00:11:15,142 +Now I'll try it out. + +211 +00:11:16,310 --> 00:11:18,178 +Use the order string from above + +212 +00:11:18,212 --> 00:11:20,547 +and look for the first match of the regex. + +213 +00:11:21,682 --> 00:11:24,518 +Now, when I run the Playground, +I can see with the inline result + +214 +00:11:24,551 --> 00:11:28,488 +exactly what parts of the order string +the regex matches. + +215 +00:11:28,522 --> 00:11:31,458 +And here, it's finding +just what I was looking for. + +216 +00:11:31,491 --> 00:11:34,862 +Swift's new regex support +doesn't stop here. + +217 +00:11:34,895 --> 00:11:36,597 +As literals become more complex, + +218 +00:11:36,630 --> 00:11:39,833 +like this one +that matches parts of a log file, + +219 +00:11:39,867 --> 00:11:43,871 +Swift offers an even better way +to craft these patterns– + +220 +00:11:43,904 --> 00:11:45,506 +regex builders. + +221 +00:11:46,974 --> 00:11:49,776 +And it's easy +to convert a literal to a builder. + +222 +00:11:50,577 --> 00:11:52,379 +Now I have code, + +223 +00:11:52,412 --> 00:11:55,816 +and that makes it easier +to read and change. + +224 +00:11:55,849 --> 00:11:58,619 +I can simplify this one even more. + +225 +00:11:58,652 --> 00:12:01,054 +Here, where I'm looking for a hex digit, + +226 +00:12:01,088 --> 00:12:03,590 +I'll use the new .hexDigit CharacterClass, + +227 +00:12:03,624 --> 00:12:06,693 +helping making my intent even clearer. + +228 +00:12:06,727 --> 00:12:09,730 +Now, the builder syntax makes it +so much easier for me to create + +229 +00:12:09,763 --> 00:12:12,065 +and extend my expressions, + +230 +00:12:12,099 --> 00:12:14,768 +and get the results I'm looking for. + +231 +00:12:14,801 --> 00:12:17,304 +And that's the powerful, +new developer experience + +232 +00:12:17,337 --> 00:12:19,907 +around regular expressions in Swift. + +233 +00:12:19,940 --> 00:12:23,277 +Ben: Beyond string syntax, +Swift is also getting easier + +234 +00:12:23,310 --> 00:12:28,315 +to read and write through improvements +to a language feature known as generics. + +235 +00:12:28,348 --> 00:12:30,951 +Generics power features of Swift +you use every day, + +236 +00:12:30,984 --> 00:12:32,319 +like the Array type, + +237 +00:12:32,352 --> 00:12:36,023 +which holds any kind of element, +from strings to your own custom types. + +238 +00:12:36,056 --> 00:12:39,293 +Generic code uses the concept +of a placeholder type + +239 +00:12:39,326 --> 00:12:42,796 +to stand in for another type +to be determined later. + +240 +00:12:42,829 --> 00:12:45,666 +By removing assumptions +about specific types, + +241 +00:12:45,699 --> 00:12:48,302 +you can be more clear +about the intent of your code, + +242 +00:12:48,335 --> 00:12:50,838 +and make it easier to re-use. + +243 +00:12:50,871 --> 00:12:53,540 +But this can also make your code +harder to read. + +244 +00:12:54,174 --> 00:12:57,711 +For example, if you wanted to handle +a generic collection of songs + +245 +00:12:57,744 --> 00:12:59,112 +as a function parameter, + +246 +00:12:59,146 --> 00:13:02,516 +you'd have to write quite a bit +of code to express your intent. + +247 +00:13:03,383 --> 00:13:07,287 +Now in Swift, writing a function +that accepts some collection of songs + +248 +00:13:07,321 --> 00:13:12,259 +is as easy as using the 'some' keyword +to tell Swift about the parameter. + +249 +00:13:12,292 --> 00:13:15,796 +You get the same meaning, +but with less code. + +250 +00:13:15,829 --> 00:13:19,399 +In other cases, +you might need more dynamic behavior, + +251 +00:13:19,433 --> 00:13:22,436 +like with this music library's +array of playlists, + +252 +00:13:22,469 --> 00:13:25,973 +which might need to contain different +types of collections of songs– + +253 +00:13:26,006 --> 00:13:28,742 +sets of songs, or arrays of songs. + +254 +00:13:28,775 --> 00:13:31,512 +That's where the new 'any' keyword +can help. + +255 +00:13:31,545 --> 00:13:33,780 +The 'any' keyword +is built right into Swift, + +256 +00:13:33,814 --> 00:13:38,252 +and allows you to express a type +that can hold any collection of songs. + +257 +00:13:38,285 --> 00:13:41,588 +And it works seamlessly +with generic functions too. + +258 +00:13:41,622 --> 00:13:45,559 +By adopting familiar syntax +and using more natural keywords, + +259 +00:13:45,592 --> 00:13:48,929 +writing generic code in Swift +has never been easier. + +260 +00:13:48,962 --> 00:13:52,733 +Just as important as the features +built into the Swift language + +261 +00:13:52,766 --> 00:13:55,135 +are the tools built around it. + +262 +00:13:55,169 --> 00:13:59,039 +The Swift Package Manager makes it easy +to manage your app's dependencies + +263 +00:13:59,072 --> 00:14:01,341 +and take advantage +of the fantastic packages + +264 +00:14:01,375 --> 00:14:03,977 +published by developers around the world. + +265 +00:14:04,011 --> 00:14:08,215 +To date, those developers have +published thousands of Swift packages, + +266 +00:14:08,248 --> 00:14:11,919 +providing code to help with everything +from authentication and web services + +267 +00:14:11,952 --> 00:14:15,222 +to data management +and reusable UI components. + +268 +00:14:15,255 --> 00:14:18,725 +And this year, the Swift Package Manager +is amplifying the ways + +269 +00:14:18,759 --> 00:14:23,163 +you can create and build code +with all-new Package Plugins. + +270 +00:14:23,197 --> 00:14:26,667 +Plugins are Swift packages +you can add to your project + +271 +00:14:26,700 --> 00:14:29,169 +as easily as any other dependency. + +272 +00:14:29,203 --> 00:14:32,606 +They download and build +automatically on a fresh checkout, + +273 +00:14:32,639 --> 00:14:35,275 +except instead of being code in your app, + +274 +00:14:35,309 --> 00:14:37,010 +they're code that helps build your app. + +275 +00:14:37,945 --> 00:14:41,548 +Package Plugins can be invoked +from the command line or within Xcode, + +276 +00:14:41,582 --> 00:14:44,852 +either as part of your build phase +or on-demand. + +277 +00:14:44,885 --> 00:14:46,920 +They run in a sandbox environment + +278 +00:14:46,954 --> 00:14:50,424 +which prompts you for permission +before reading or modifying your code. + +279 +00:14:51,124 --> 00:14:53,660 +There are endless possibilities +for extending your workflow + +280 +00:14:53,694 --> 00:14:54,695 +with Package Plugins. + +281 +00:14:54,728 --> 00:14:57,331 +You could use them to lint +and format your code + +282 +00:14:57,364 --> 00:14:58,765 +to match the team style guide + +283 +00:14:58,799 --> 00:15:01,168 +with packages +like SwiftLint or SwiftFormat + +284 +00:15:01,201 --> 00:15:03,704 +or automatically generate source code +at build-time + +285 +00:15:03,737 --> 00:15:05,205 +with tools like Sourcery. + +286 +00:15:05,239 --> 00:15:07,441 +Anything that helps you get the job done. + +287 +00:15:08,075 --> 00:15:10,944 +Ken: Package Plugins are a great way +to extend Xcode, + +288 +00:15:10,978 --> 00:15:13,180 +just by writing a little Swift. + +289 +00:15:13,213 --> 00:15:14,815 +And you can do that in two ways. + +290 +00:15:14,848 --> 00:15:17,751 +With command plugins +that you use on demand, + +291 +00:15:17,784 --> 00:15:20,921 +and with build plugins +for whenever your project builds. + +292 +00:15:20,954 --> 00:15:22,956 +Now, back here in our Food Truck app, + +293 +00:15:22,990 --> 00:15:25,259 +here's the code for a command plugin +I created. + +294 +00:15:26,894 --> 00:15:30,163 +My team has a unique code aesthetic. + +295 +00:15:30,197 --> 00:15:33,000 +We like our imports +sorted in string length order. + +296 +00:15:33,033 --> 00:15:35,035 +Shortest to longest. + +297 +00:15:35,068 --> 00:15:38,906 +And since Package Plugins are +all about customization and control, + +298 +00:15:38,939 --> 00:15:42,342 +we created a command plugin that uses +SwiftFormat to take care of that. + +299 +00:15:43,377 --> 00:15:46,079 +It finds all the locally modified files, + +300 +00:15:46,113 --> 00:15:48,415 +and then it sorts their imports. + +301 +00:15:48,448 --> 00:15:52,519 +Now, here's a file I've been editing +with some unsorted imports at the top. + +302 +00:15:52,553 --> 00:15:55,255 +I'll use the command +on the entire project. + +303 +00:15:55,289 --> 00:15:58,792 +I can select any number of targets. +I'll run it on everything. + +304 +00:15:59,326 --> 00:16:01,562 +And I can review +the plugin's code if I want. + +305 +00:16:01,595 --> 00:16:03,063 +I'm all set. +I'll run the command. + +306 +00:16:03,830 --> 00:16:06,733 +And then, just like that, +the plugin goes to work on my files. + +307 +00:16:07,367 --> 00:16:10,337 +It finds all of the locally modified +source files, + +308 +00:16:10,370 --> 00:16:12,773 +and then it sorts them in length order. + +309 +00:16:12,806 --> 00:16:15,809 +With plugins, +you can go beyond just formatting. + +310 +00:16:15,843 --> 00:16:18,679 +You can generate source code, +work with git, + +311 +00:16:18,712 --> 00:16:21,715 +even surface your own custom errors +and warnings. + +312 +00:16:21,748 --> 00:16:25,319 +I have another plugin to make sure +my code is well documented. + +313 +00:16:25,352 --> 00:16:29,389 +It's a build plugin, and it's based on +the open source SwiftLint package. + +314 +00:16:30,157 --> 00:16:33,994 +So now when I build, I can easily see +all the places in my code + +315 +00:16:34,027 --> 00:16:36,463 +where I need to add documentation. + +316 +00:16:36,496 --> 00:16:40,267 +And build plugins extend +all the way to Xcode Cloud, + +317 +00:16:40,300 --> 00:16:42,870 +where they run as part of every build. + +318 +00:16:42,903 --> 00:16:44,638 +With Swift package plugins, + +319 +00:16:44,671 --> 00:16:46,974 +my team and I can create our own commands, + +320 +00:16:47,007 --> 00:16:49,943 +customize builds locally +and in Xcode Cloud, + +321 +00:16:49,977 --> 00:16:52,212 +and then share those plugins with others. + +322 +00:16:52,246 --> 00:16:55,449 +All using the power +of a few lines of Swift. + +323 +00:16:55,482 --> 00:16:57,885 +And that's a quick look +at the ways Package Plugins + +324 +00:16:57,918 --> 00:17:00,487 +can level up your development workflow. + +325 +00:17:00,521 --> 00:17:04,591 +Ben: Finally, Swift has some impressive +changes under the hood. + +326 +00:17:04,625 --> 00:17:06,560 +Building Swift projects +is quicker than ever. + +327 +00:17:06,593 --> 00:17:08,795 +Thanks to new parallelization efforts, + +328 +00:17:08,829 --> 00:17:11,131 +link time is up to twice as fast. + +329 +00:17:11,164 --> 00:17:13,000 +And the Swift concurrency runtime + +330 +00:17:13,033 --> 00:17:15,769 +is now more tightly integrated with the OS + +331 +00:17:15,802 --> 00:17:18,939 +to better ensure the priority +of your asynchronous tasks, + +332 +00:17:18,972 --> 00:17:21,441 +helping your apps +stay efficient and responsive. + +333 +00:17:21,475 --> 00:17:26,680 +Lastly, launch time for apps written +in Swift is dramatically faster on iOS 16, + +334 +00:17:26,713 --> 00:17:30,851 +with apps like Lyft or Airbnb +launching almost twice as fast + +335 +00:17:30,884 --> 00:17:33,086 +thanks to improvement +in the dynamic linker. + +336 +00:17:33,120 --> 00:17:35,322 +With these under-the-hood improvements, + +337 +00:17:35,355 --> 00:17:36,990 +new abilities in tooling, + +338 +00:17:37,024 --> 00:17:40,027 +an evolved syntax +that's easier to read and write, + +339 +00:17:40,060 --> 00:17:41,895 +and improvements in concurrency, + +340 +00:17:41,929 --> 00:17:44,831 +there has never been a better time +to develop in Swift. + +341 +00:17:44,865 --> 00:17:49,803 +Swift is the absolute best language +for building apps across our devices. + +342 +00:17:49,837 --> 00:17:53,640 +But a language is just part of what +you need to build your best apps. + +343 +00:17:53,674 --> 00:17:57,644 +You have to pair a language with +a powerful user interface framework. + +344 +00:17:57,678 --> 00:17:59,813 +And Eliza is gonna tell you more. + +345 +00:17:59,847 --> 00:18:02,816 +Eliza Block: A powerful UI framework +provides abstractions + +346 +00:18:02,850 --> 00:18:05,319 +that make it easy +to describe your interface, + +347 +00:18:05,352 --> 00:18:08,355 +to populate it with data, +and to keep it up to date. + +348 +00:18:08,388 --> 00:18:10,657 +It should scale well with complexity. + +349 +00:18:10,691 --> 00:18:13,227 +And it should be designed +for the platform you're targeting, + +350 +00:18:13,260 --> 00:18:15,996 +giving you full access +to the power of the device. + +351 +00:18:16,029 --> 00:18:21,034 +Your UI framework should help you make +your app feel familiar and intuitive. + +352 +00:18:21,068 --> 00:18:23,871 +It should make it easy to create +standard controls + +353 +00:18:23,904 --> 00:18:25,639 +and native interaction patterns, + +354 +00:18:25,672 --> 00:18:28,242 +with options for advanced customization. + +355 +00:18:28,275 --> 00:18:30,644 +And it needs to have an expressive API + +356 +00:18:30,677 --> 00:18:33,580 +that allows you to quickly prototype +your ideas + +357 +00:18:33,614 --> 00:18:36,016 +and see the results +across a range of devices. + +358 +00:18:36,583 --> 00:18:39,152 +SwiftUI offers all this and more. + +359 +00:18:39,186 --> 00:18:42,623 +Like Swift, +Swift UI is designed with strong opinions + +360 +00:18:42,656 --> 00:18:44,658 +about the best way to build apps. + +361 +00:18:44,691 --> 00:18:48,462 +It has a declarative syntax +that's easy to read and write. + +362 +00:18:48,495 --> 00:18:52,933 +You describe what your interface +looks like, instead of how to build it. + +363 +00:18:53,934 --> 00:18:57,504 +And this leaves room for SwiftUI +to provide intelligent defaults + +364 +00:18:57,538 --> 00:18:59,606 +for each platform. + +365 +00:18:59,640 --> 00:19:03,944 +SwiftUI automatically keeps your interface +up-to-date with changes + +366 +00:19:03,977 --> 00:19:05,579 +to the underlying data model, + +367 +00:19:05,612 --> 00:19:08,916 +so your app's UI never ends up +in an inconsistent state. + +368 +00:19:09,750 --> 00:19:13,320 +SwiftUI handles all these details for you, + +369 +00:19:13,353 --> 00:19:17,391 +so you can focus your time and energy +on what makes your app unique. + +370 +00:19:17,424 --> 00:19:21,028 +Writing a new UI framework +is a huge undertaking. + +371 +00:19:21,061 --> 00:19:23,931 +Since its introduction +we've been continually expanding + +372 +00:19:23,964 --> 00:19:27,167 +SwiftUI's API coverage, +guided by your feedback. + +373 +00:19:27,835 --> 00:19:31,772 +This year we've made it even easier +to adopt SwiftUI incrementally + +374 +00:19:31,805 --> 00:19:33,173 +in your existing apps, + +375 +00:19:33,207 --> 00:19:37,377 +and we've made some exciting enhancements +to its power and flexibility, + +376 +00:19:37,411 --> 00:19:39,780 +starting with app navigation. + +377 +00:19:39,813 --> 00:19:42,482 +With SwiftUI, +it has always been easy to create + +378 +00:19:42,516 --> 00:19:46,453 +the common kinds of navigation +hierarchies found in many apps. + +379 +00:19:46,486 --> 00:19:49,690 +And this year, +SwiftUI is expanding that support + +380 +00:19:49,723 --> 00:19:52,292 +with an all-new navigation API. + +381 +00:19:52,860 --> 00:19:56,897 +The new navigation API makes it easy +to express the style of navigation + +382 +00:19:56,930 --> 00:19:59,633 +that best fits the needs of your app. + +383 +00:19:59,666 --> 00:20:03,770 +With robust programmatic control +over the presentation of your app's views, + +384 +00:20:03,804 --> 00:20:06,540 +you can easily save and restore selection, + +385 +00:20:06,573 --> 00:20:10,344 +and even replace the full contents +of a navigation stack. + +386 +00:20:10,377 --> 00:20:13,480 +This is really useful +when handling important behaviors + +387 +00:20:13,514 --> 00:20:15,716 +like setting the launch state of your app, + +388 +00:20:15,749 --> 00:20:18,552 +managing transitions between size classes, + +389 +00:20:18,585 --> 00:20:20,220 +and responding to deep links. + +390 +00:20:21,154 --> 00:20:24,892 +SwiftUI also has huge improvements +when it comes to controlling + +391 +00:20:24,925 --> 00:20:27,094 +the layout of your app's interface. + +392 +00:20:27,828 --> 00:20:32,065 +The layout of many app interfaces +can be described using SwiftUI's model + +393 +00:20:32,099 --> 00:20:34,868 +of horizontal +or vertical stacks of elements. + +394 +00:20:34,902 --> 00:20:37,571 +And while this model works +for many common layouts, + +395 +00:20:37,604 --> 00:20:40,340 +sometimes +you need something more flexible. + +396 +00:20:40,374 --> 00:20:43,243 +This year, we're adding a new Grid API, + +397 +00:20:43,277 --> 00:20:45,779 +which makes it easier +to lay out a set of views + +398 +00:20:45,812 --> 00:20:48,582 +aligned across multiple rows and columns. + +399 +00:20:48,615 --> 00:20:51,118 +And you can take your layouts even further + +400 +00:20:51,151 --> 00:20:54,154 +with the all-new custom layout API. + +401 +00:20:54,188 --> 00:20:57,157 +The custom layout API +gives you the flexibility + +402 +00:20:57,191 --> 00:20:59,760 +to build any type of layout you want. + +403 +00:20:59,793 --> 00:21:02,029 +For example, +you could create a flow layout + +404 +00:21:02,062 --> 00:21:05,465 +where your views are arranged +like the content of a newspaper, + +405 +00:21:05,499 --> 00:21:08,569 +wrapping to the next column +when more space is needed. + +406 +00:21:08,602 --> 00:21:12,573 +Or you could create a radial layout +that draws your views in a circle, + +407 +00:21:12,606 --> 00:21:14,107 +like the numbers on a watch face. + +408 +00:21:14,842 --> 00:21:19,546 +The custom layout API makes it easy +to re-use your layout logic, + +409 +00:21:19,580 --> 00:21:22,349 +making your view code simpler +and easier to read. + +410 +00:21:22,382 --> 00:21:25,352 +SwiftUI continues to grow +to offer + +411 +00:21:25,385 --> 00:21:27,688 +many more types of interface elements. + +412 +00:21:27,721 --> 00:21:30,858 +Like half sheets, +which define secondary views + +413 +00:21:30,891 --> 00:21:32,759 +that slide above a main view. + +414 +00:21:32,793 --> 00:21:37,564 +These are great to provide quick access +to information on smaller screens. + +415 +00:21:37,598 --> 00:21:40,100 +And SwiftUI now supports Share Sheets, + +416 +00:21:40,133 --> 00:21:41,835 +so your app can easily leverage + +417 +00:21:41,869 --> 00:21:45,005 +all of the Share extensions available +on a user's device. + +418 +00:21:45,038 --> 00:21:49,176 +Share Sheet support is powered +by the new Transferable protocol, + +419 +00:21:49,209 --> 00:21:52,913 +which introduces a type-safe API +for transferring app data. + +420 +00:21:53,914 --> 00:21:58,652 +We've also made it easier to adopt SwiftUI +incrementally in your existing apps + +421 +00:21:58,685 --> 00:22:03,557 +with a special collection view cell +that can host SwiftUI views. + +422 +00:22:03,590 --> 00:22:06,660 +If you already have a collection view +in your UIKit app, + +423 +00:22:06,693 --> 00:22:10,764 +you can now write custom cells +using SwiftUI's declarative syntax. + +424 +00:22:10,797 --> 00:22:15,269 +These cells are tightly integrated +with UIKit, supporting swipe actions, + +425 +00:22:15,302 --> 00:22:19,239 +cell backgrounds, and all the other +features of UICollectionView. + +426 +00:22:19,273 --> 00:22:22,109 +Today we're also introducing +a brand-new framework + +427 +00:22:22,142 --> 00:22:23,677 +that complements SwiftUI + +428 +00:22:23,710 --> 00:22:27,047 +and will allow you to express +even more of your interface. + +429 +00:22:27,080 --> 00:22:29,049 +Here's Jo to tell you more. + +430 +00:22:29,082 --> 00:22:31,218 +Jo Arreaza-Taylor: +Today's world is filled with data. + +431 +00:22:31,251 --> 00:22:33,453 +Data to help understand, make decisions, + +432 +00:22:33,487 --> 00:22:35,055 +and see new perspectives. + +433 +00:22:35,088 --> 00:22:37,724 +A well-designed and accessible +data visualization + +434 +00:22:37,758 --> 00:22:40,060 +can communicate complexities to your users + +435 +00:22:40,093 --> 00:22:42,629 +in a way that feels clear and natural, + +436 +00:22:42,663 --> 00:22:44,798 +empowering them +as they move through the day. + +437 +00:22:45,632 --> 00:22:48,702 +Like helping to show +changing trends in their health, + +438 +00:22:48,735 --> 00:22:51,839 +highlighting their progress +towards personal goals, + +439 +00:22:51,872 --> 00:22:53,807 +and preparing them for what's to come. + +440 +00:22:54,474 --> 00:22:58,312 +Today, we're introducing a new framework +to help empower your users + +441 +00:22:58,345 --> 00:23:00,581 +to unlock the data within your apps. + +442 +00:23:00,614 --> 00:23:03,884 +Say hello to Swift Charts. + +443 +00:23:03,917 --> 00:23:07,354 +Swift Charts is a highly customizable +charting framework + +444 +00:23:07,387 --> 00:23:09,056 +built on top of SwiftUI + +445 +00:23:09,089 --> 00:23:12,793 +that makes it easy +to create gorgeous visualizations. + +446 +00:23:12,826 --> 00:23:16,830 +It uses the same declarative syntax +as SwiftUI to make it easy + +447 +00:23:16,864 --> 00:23:19,600 +to read and write code +which conveys visual information. + +448 +00:23:20,567 --> 00:23:23,337 +Swift Chart lets you customize +the presentation of information + +449 +00:23:23,370 --> 00:23:25,606 +to best fit the needs of your app + +450 +00:23:25,639 --> 00:23:28,175 +to create everything +from line and bar charts + +451 +00:23:28,208 --> 00:23:32,446 +to more sophisticated examples +like heat maps and stream graphs, + +452 +00:23:32,479 --> 00:23:34,314 +and many, many more types. + +453 +00:23:35,349 --> 00:23:38,285 +And because Swift Charts +is built on top of SwiftUI, + +454 +00:23:38,318 --> 00:23:40,821 +charts have great support +for accessibility features, + +455 +00:23:40,854 --> 00:23:44,091 +like a terrific, out-of-the-box +VoiceOver experience + +456 +00:23:44,124 --> 00:23:46,226 +that's easy to customize. + +457 +00:23:46,260 --> 00:23:50,163 +Being built on SwiftUI also means +you can animate your charts, + +458 +00:23:50,197 --> 00:23:53,534 +to help you give your app +just the right look and feel. + +459 +00:23:53,567 --> 00:23:57,871 +And of course, Swift Charts works great +across all our devices. + +460 +00:23:57,905 --> 00:23:59,973 +Eliza: Back in our Food Truck app, + +461 +00:24:00,007 --> 00:24:02,042 +here are the beautiful new Swift Charts + +462 +00:24:02,075 --> 00:24:05,479 +in Xcode's fully redesigned preview area. + +463 +00:24:05,512 --> 00:24:09,516 +I'm also using +the new multicolumn SwiftUI table view. + +464 +00:24:09,550 --> 00:24:12,886 +Let me show you how easy it is +to build this chart. + +465 +00:24:12,920 --> 00:24:16,256 +And as I scroll, check out +the awesome new structured headers + +466 +00:24:16,290 --> 00:24:17,524 +in the source editor. + +467 +00:24:17,558 --> 00:24:20,394 +They make it really easy +to see where you are in the file. + +468 +00:24:21,428 --> 00:24:22,863 +Here's the code for the chart. + +469 +00:24:22,896 --> 00:24:26,767 +Now, this is actually a stacked bar chart, +but you can't really tell. + +470 +00:24:26,800 --> 00:24:29,670 +Let's have each donut +use its own color. + +471 +00:24:31,038 --> 00:24:34,508 +Maybe it would be easier to see +how the donuts compare + +472 +00:24:34,541 --> 00:24:36,844 +if we position the bars side by side. + +473 +00:24:38,111 --> 00:24:40,848 +I love how I can make +all these big changes + +474 +00:24:40,881 --> 00:24:43,217 +with just a couple simple modifiers. + +475 +00:24:43,250 --> 00:24:45,719 +We can customize the styling, too. + +476 +00:24:45,752 --> 00:24:48,689 +Let's make the bars +reflect the donut colors. + +477 +00:24:48,722 --> 00:24:52,292 +And we can even add annotations +to the bars with another modifier. + +478 +00:24:53,493 --> 00:24:54,962 +Looking great. + +479 +00:24:54,995 --> 00:24:56,897 +Previews are now live by default, + +480 +00:24:56,930 --> 00:24:59,700 +so I can immediately interact +with my view. + +481 +00:24:59,733 --> 00:25:01,435 +I'm gonna change the sort order. + +482 +00:25:01,468 --> 00:25:04,371 +Watch how the bars animate beautifully, + +483 +00:25:04,404 --> 00:25:07,241 +with Swift Charts +doing all the heavy lifting. + +484 +00:25:07,274 --> 00:25:09,142 +Let's fetch more data. + +485 +00:25:09,176 --> 00:25:11,612 +The chart and table +both automatically update + +486 +00:25:11,645 --> 00:25:13,146 +as my model changes. + +487 +00:25:13,180 --> 00:25:17,351 +The chart even recalculates its Y axis +to reflect the new totals. + +488 +00:25:17,384 --> 00:25:19,186 +Let me show you one more chart. + +489 +00:25:19,219 --> 00:25:22,723 +I've stubbed out a line chart +which we can add to the view. + +490 +00:25:22,756 --> 00:25:25,192 +I'll jump to the implementation. + +491 +00:25:25,225 --> 00:25:29,263 +Line charts with Swift Charts +have some really cool options. + +492 +00:25:29,296 --> 00:25:32,032 +We can add symbols for each donut. + +493 +00:25:32,065 --> 00:25:34,301 +We can annotate the lines. + +494 +00:25:34,334 --> 00:25:37,004 +We can even smooth the curves +with a variety + +495 +00:25:37,037 --> 00:25:38,839 +of interpolation strategies. + +496 +00:25:38,872 --> 00:25:41,108 +Let's use catmullRom. + +497 +00:25:41,141 --> 00:25:43,610 +Finally, I'll override +the chart scale style + +498 +00:25:43,644 --> 00:25:45,746 +by providing my own mapping. + +499 +00:25:45,779 --> 00:25:49,249 +That will make my chart fit in better +with the app's color scheme. + +500 +00:25:49,283 --> 00:25:50,751 +Really nice. + +501 +00:25:50,784 --> 00:25:53,487 +The redesigned preview area +makes it easier than ever + +502 +00:25:53,520 --> 00:25:56,223 +to see how my view looks +in different environments. + +503 +00:25:56,256 --> 00:25:58,458 +By pressing this button +in the canvas, + +504 +00:25:58,492 --> 00:26:01,261 +I can see my view in Dark and Light mode. + +505 +00:26:01,295 --> 00:26:04,798 +I can even look at my layout +in every interface orientation, + +506 +00:26:04,831 --> 00:26:07,534 +all without adding +a single additional preview. + +507 +00:26:07,568 --> 00:26:10,037 +Let's zoom in on landscape. + +508 +00:26:10,070 --> 00:26:13,207 +It looks like my UI +isn't quite fitting here. + +509 +00:26:13,240 --> 00:26:14,608 +A few controls are offscreen, + +510 +00:26:14,641 --> 00:26:17,744 +and the charts have +an awkward aspect ratio. + +511 +00:26:17,778 --> 00:26:21,048 +Let's see where we're describing +this layout. + +512 +00:26:21,081 --> 00:26:24,384 +These views here are +in an implicit Vstack. + +513 +00:26:25,519 --> 00:26:29,089 +This year there are some powerful +new APIs in SwiftUI + +514 +00:26:29,122 --> 00:26:31,558 +that can create more flexible layouts. + +515 +00:26:31,592 --> 00:26:34,194 +Here, I'm going to use a ViewThatFits + +516 +00:26:34,228 --> 00:26:37,431 +to switch between a vertical +and a horizontal stack, + +517 +00:26:37,464 --> 00:26:39,433 +depending on the available space. + +518 +00:26:40,834 --> 00:26:42,469 +That looks much better. + +519 +00:26:42,503 --> 00:26:43,837 +Let's wire this up + +520 +00:26:43,871 --> 00:26:45,806 +so we can navigate to it +from the main screen. + +521 +00:26:46,840 --> 00:26:49,977 +I'm using SwiftUI's +new navigation split view, + +522 +00:26:50,010 --> 00:26:51,778 +which makes this really easy. + +523 +00:26:51,812 --> 00:26:55,249 +The split view has a sidebar +to track the selection + +524 +00:26:55,282 --> 00:26:57,951 +and a NavigationStack +that changes its content + +525 +00:26:57,985 --> 00:27:00,687 +as the sidebar selection changes. + +526 +00:27:00,721 --> 00:27:02,089 +I'll jump into the sidebar + +527 +00:27:02,122 --> 00:27:05,158 +and add a navigation link +for our Donut Champion view. + +528 +00:27:05,192 --> 00:27:07,661 +And then, we can try it out +in the interactive preview. + +529 +00:27:08,428 --> 00:27:10,831 +I'd like to see my split view +in landscape, + +530 +00:27:10,864 --> 00:27:13,100 +so I'll use the new canvas settings + +531 +00:27:13,133 --> 00:27:15,369 +to rotate the live preview. + +532 +00:27:15,402 --> 00:27:17,004 +Works great. + +533 +00:27:17,037 --> 00:27:19,573 +I'm happy with how this is looking +on iPad, + +534 +00:27:19,606 --> 00:27:21,842 +but now I'd love to bring it to Mac, + +535 +00:27:21,875 --> 00:27:25,145 +and it's only a few clicks +to do that. + +536 +00:27:25,179 --> 00:27:27,648 +I want to take full advantage +of the Mac SDK, + +537 +00:27:27,681 --> 00:27:29,116 +so I'm going to use native. + +538 +00:27:31,385 --> 00:27:33,720 +With just a single target backing my app, + +539 +00:27:33,754 --> 00:27:35,756 +I can share almost all my code, + +540 +00:27:35,789 --> 00:27:39,193 +and SwiftUI makes my app look great +on each platform. + +541 +00:27:39,226 --> 00:27:42,596 +I can also easily add +device-specific features. + +542 +00:27:42,629 --> 00:27:45,465 +For my Mac app, +let's add a menu bar extra. + +543 +00:27:45,499 --> 00:27:47,434 +Those are the little useful icons + +544 +00:27:47,467 --> 00:27:49,169 +on the upper right corner of your screen, + +545 +00:27:49,203 --> 00:27:52,239 +like Wi-Fi and Spotlight. + +546 +00:27:52,272 --> 00:27:55,309 +SwiftUI has a new API for this. + +547 +00:27:55,342 --> 00:27:57,411 +I just add it to the body of my app. + +548 +00:27:57,444 --> 00:27:59,313 +Now let's run this for Mac. + +549 +00:28:02,850 --> 00:28:06,720 +Our Donut Champion view looks great +on Mac right out of the box. + +550 +00:28:06,753 --> 00:28:08,755 +And here's that menu bar extra. + +551 +00:28:08,789 --> 00:28:10,157 +That'll be handy. + +552 +00:28:10,190 --> 00:28:12,459 +And that's a quick look at Swift Charts + +553 +00:28:12,492 --> 00:28:15,929 +and just a few of the enhancements +coming to SwiftUI and Xcode. + +554 +00:28:15,963 --> 00:28:17,798 +And now back to Josh. + +555 +00:28:17,831 --> 00:28:21,001 +Josh: We're continuing to expand +our adoption of SwiftUI + +556 +00:28:21,034 --> 00:28:23,570 +across our apps and system interfaces. + +557 +00:28:23,604 --> 00:28:27,808 +For example, iOS's new Lock Screen widgets +were designed from the ground up + +558 +00:28:27,841 --> 00:28:29,409 +using SwiftUI. + +559 +00:28:29,443 --> 00:28:32,346 +The new Font Book app was +completely rewritten with it. + +560 +00:28:32,379 --> 00:28:36,450 +And the modern, forward-looking design +of the new macOS System Settings app + +561 +00:28:36,483 --> 00:28:37,985 +was built using it. + +562 +00:28:38,018 --> 00:28:41,154 +Swift and SwiftUI were designed +from the start + +563 +00:28:41,188 --> 00:28:45,926 +to provide a single, native language +and API for all Apple platforms. + +564 +00:28:45,959 --> 00:28:48,795 +You can learn them once +and apply them everywhere. + +565 +00:28:48,829 --> 00:28:51,965 +Whether your vision is to provide +quick access to information + +566 +00:28:51,999 --> 00:28:54,001 +at a glance on Apple Watch, + +567 +00:28:54,034 --> 00:28:57,271 +productivity tools +on MacBook Pro and iPad, + +568 +00:28:57,304 --> 00:28:59,173 +new experiences on iPhone, + +569 +00:28:59,206 --> 00:29:01,575 +or a new way to relax with Apple TV, + +570 +00:29:01,608 --> 00:29:04,077 +Swift, SwiftUI, and Xcode + +571 +00:29:04,111 --> 00:29:07,781 +provide a next-generation integrated +development platform + +572 +00:29:07,814 --> 00:29:11,051 +to help you build apps +for all of our products. + +573 +00:29:11,084 --> 00:29:12,686 +Now, if you have an existing app, + +574 +00:29:12,719 --> 00:29:16,156 +it's easy to adopt +these new technologies incrementally. + +575 +00:29:16,190 --> 00:29:19,693 +And if you're new to our platforms +or if you're starting a brand-new app, + +576 +00:29:19,726 --> 00:29:23,830 +the best way to build an app +is with Swift and SwiftUI. + +577 +00:29:23,864 --> 00:29:26,066 +Now, of course +that's just the beginning. + +578 +00:29:26,099 --> 00:29:30,037 +We're also continuing to evolve +the user experience of our platforms + +579 +00:29:30,070 --> 00:29:32,873 +to give you more ways +to engage your users. + +580 +00:29:32,906 --> 00:29:35,242 +And to tell you more, here's Sebastien. + +581 +00:29:36,543 --> 00:29:38,579 +Sebastien Marineau-Mes: +Now, apps are about turning ideas, + +582 +00:29:38,612 --> 00:29:41,315 +code, and APIs into user experiences. + +583 +00:29:41,348 --> 00:29:43,650 +And the best apps +are the ones that can meet users + +584 +00:29:43,684 --> 00:29:45,919 +where they are in the moment. + +585 +00:29:45,953 --> 00:29:49,623 +We've created ways to help you take +user experience beyond your apps, + +586 +00:29:49,656 --> 00:29:53,293 +and build it into the system experience +on Apple devices. + +587 +00:29:54,094 --> 00:29:57,831 +This journey started with extensions, +integration with the Share Sheet, + +588 +00:29:57,865 --> 00:29:59,566 +and custom keyboards. + +589 +00:29:59,600 --> 00:30:01,902 +And more recently, +it's included the ability + +590 +00:30:01,935 --> 00:30:06,206 +to have your app display key information +on the Home Screen using widgets. + +591 +00:30:06,240 --> 00:30:08,041 +Now, this year, +there are a number of new ways + +592 +00:30:08,075 --> 00:30:11,144 +for your app to integrate with the system +experience across our platforms, + +593 +00:30:11,178 --> 00:30:15,649 +and it really starts with the Lock Screen, +which gets its biggest update ever. + +594 +00:30:15,682 --> 00:30:19,186 +It re-imagines how +the Lock Screen looks and how it works, + +595 +00:30:19,219 --> 00:30:23,557 +and it gives your ideas and your apps +another place to engage users. + +596 +00:30:23,590 --> 00:30:25,959 +And to tell you more, here's Robert. + +597 +00:30:34,434 --> 00:30:35,969 +Robert Dhaene: +In reimagining the Lock Screen, + +598 +00:30:36,003 --> 00:30:38,972 +we set out to make it +even more personal and beautiful, + +599 +00:30:39,006 --> 00:30:40,841 +while improving everyday utility. + +600 +00:30:40,874 --> 00:30:43,610 +As part of this, +we knew we needed to bring + +601 +00:30:43,644 --> 00:30:46,580 +the power of widgets +to the all-new Lock Screen. + +602 +00:30:46,613 --> 00:30:49,650 +Widgets have been an incredible way +to elevate key information + +603 +00:30:49,683 --> 00:30:53,654 +from your app and display it +where people can view it at a glance. + +604 +00:30:53,687 --> 00:30:57,324 +They make it easy to access +rich, timely information + +605 +00:30:57,357 --> 00:30:58,725 +right from the Home Screen. + +606 +00:30:59,393 --> 00:31:02,963 +The Lock Screen is the first thing you see +every time you pick up your iPhone, + +607 +00:31:02,996 --> 00:31:05,599 +and it's always been a place +to check the date and time + +608 +00:31:05,632 --> 00:31:07,501 +and look out for key messages. + +609 +00:31:07,534 --> 00:31:11,205 +When thinking about the best format +for displaying even more information here, + +610 +00:31:11,238 --> 00:31:13,740 +we didn't have to look far +for design inspiration. + +611 +00:31:14,374 --> 00:31:18,245 +Complications on Apple Watch +already provide glanceable, relevant, + +612 +00:31:18,278 --> 00:31:19,713 +and up-to-date information, + +613 +00:31:19,746 --> 00:31:22,516 +presented beautifully +right when users need it. + +614 +00:31:23,183 --> 00:31:26,887 +The design language naturally extends +to iOS and feels right at home + +615 +00:31:26,920 --> 00:31:28,789 +on the new Lock Screen. + +616 +00:31:28,822 --> 00:31:31,992 +So using WidgetKit, +we brought some of those same designs + +617 +00:31:32,025 --> 00:31:35,128 +to widgets on the Lock Screen, +including Circular, + +618 +00:31:35,162 --> 00:31:39,833 +which displays a small image, +gauge, or a few characters of text. + +619 +00:31:39,867 --> 00:31:43,537 +Circular widgets are great for displaying +whether you've been active enough today + +620 +00:31:43,570 --> 00:31:45,672 +or if you need to go out for a run. + +621 +00:31:45,706 --> 00:31:48,342 +Rectangular provides a large canvas + +622 +00:31:48,375 --> 00:31:51,745 +for displaying things +like the upcoming weather forecast. + +623 +00:31:51,778 --> 00:31:54,681 +Inline provides a powerful way +to convey information + +624 +00:31:54,715 --> 00:31:59,086 +with a tiny amount of text and SF Symbols +above the clock on iPhone, + +625 +00:31:59,119 --> 00:32:03,156 +next to a system-supplied date string, +such as Monday the 6th. + +626 +00:32:03,190 --> 00:32:08,495 +And by the way, all of these widgets +work on both iOS and watchOS + +627 +00:32:08,529 --> 00:32:10,497 +because starting in watchOS 9, + +628 +00:32:10,531 --> 00:32:13,734 +complications +are also powered by WidgetKit. + +629 +00:32:13,767 --> 00:32:16,336 +For the first time, +you can use the same code + +630 +00:32:16,370 --> 00:32:20,274 +to generate glanceable data +on both platforms. + +631 +00:32:20,307 --> 00:32:23,343 +WidgetKit manages platform differences +for you automatically, + +632 +00:32:23,377 --> 00:32:25,646 +using the appropriate system fonts +by default, + +633 +00:32:25,679 --> 00:32:29,449 +and tinting the widgets on +the Lock Screen for maximum legibility. + +634 +00:32:29,483 --> 00:32:32,953 +To show you how to use WidgetKit to create +widgets for the Lock Screen on iPhone + +635 +00:32:32,986 --> 00:32:35,756 +and complications on Apple Watch +using the same code, + +636 +00:32:35,789 --> 00:32:37,691 +I'll hand it over to Michael. + +637 +00:32:39,960 --> 00:32:41,662 +Michael Kent: Building widgets +on the iPhone Lock Screen + +638 +00:32:41,695 --> 00:32:44,965 +and complications on Apple Watch +is really easy with WidgetKit. + +639 +00:32:44,998 --> 00:32:47,568 +If you've made Home Screen widgets, +you're already most of the way there, + +640 +00:32:47,601 --> 00:32:50,037 +including how your data +and timeline are updated. + +641 +00:32:50,070 --> 00:32:52,573 +In our Food Truck app, +we already have a systemSmall widget + +642 +00:32:52,606 --> 00:32:54,274 +that users can add to their Home Screen + +643 +00:32:54,308 --> 00:32:57,144 +to see how many orders they've filled +out of their quota today. + +644 +00:32:57,177 --> 00:32:59,680 +This kind of information would be great +to show on the Lock Screen + +645 +00:32:59,713 --> 00:33:01,915 +or in a complication on the watch face. + +646 +00:33:01,949 --> 00:33:04,151 +Let's start by building out +the Circular family. + +647 +00:33:05,652 --> 00:33:08,422 +We'll first declare support for it +in our Supported Families array. + +648 +00:33:09,556 --> 00:33:12,192 +You'll notice that we're using +some platform conditionals here. + +649 +00:33:12,226 --> 00:33:15,929 +This is because we want this widget +to continue supporting macOS and iOS + +650 +00:33:15,963 --> 00:33:19,533 +with systemSmall, but that family +isn't available on watchOS. + +651 +00:33:20,367 --> 00:33:22,503 +Then, we'll add a case to define its view. + +652 +00:33:24,171 --> 00:33:26,540 +Let's use a gauge +that shows the current number of orders + +653 +00:33:26,573 --> 00:33:28,208 +from 0 to the daily quota + +654 +00:33:28,242 --> 00:33:31,011 +so the users can quickly see +their progress at a glance. + +655 +00:33:31,845 --> 00:33:34,815 +We'll display the current order count +as text in the center, + +656 +00:33:34,848 --> 00:33:37,384 +along with a donut symbol. +There. + +657 +00:33:37,417 --> 00:33:39,319 +Let's take a look at this +in Xcode Previews now. + +658 +00:33:40,888 --> 00:33:42,256 +Awesome! +That feels right at home + +659 +00:33:42,289 --> 00:33:43,123 +on the Lock Screen. + +660 +00:33:44,091 --> 00:33:45,993 +In order to show a bit more detail +at a glance, + +661 +00:33:46,026 --> 00:33:48,495 +we can also add support +for the rectangular family. + +662 +00:33:52,099 --> 00:33:54,902 +For this view, +we'll make a VStack, + +663 +00:33:54,935 --> 00:33:57,271 +starting with a title for the data +that's shown, + +664 +00:33:57,304 --> 00:33:58,972 +and that same donut symbol. + +665 +00:33:59,673 --> 00:34:01,942 +By using the Headline style for the font, + +666 +00:34:01,975 --> 00:34:04,444 +we'll get a treatment +that looks great on both platforms. + +667 +00:34:04,478 --> 00:34:07,581 +And we'll make sure it pops +with the widgetAccentable modifier. + +668 +00:34:07,614 --> 00:34:10,951 +Since the rectangular family gives us +a bit more space, + +669 +00:34:10,984 --> 00:34:13,720 +we'll show a cool custom segmented gauge + +670 +00:34:13,754 --> 00:34:16,557 +and display the current number of orders +out of the daily quota + +671 +00:34:16,590 --> 00:34:18,258 +for the gauge's label. + +672 +00:34:18,792 --> 00:34:20,327 +Looking back at the canvas, + +673 +00:34:20,360 --> 00:34:22,963 +we can see the rectangular widget +in previews as well. + +674 +00:34:22,996 --> 00:34:25,299 +I really love that gauge. + +675 +00:34:25,332 --> 00:34:29,102 +Now let's take a look at how this widget +would appear as a circular complication + +676 +00:34:29,136 --> 00:34:30,604 +on a watch face. + +677 +00:34:32,739 --> 00:34:34,508 +Well, all right, everything is there, + +678 +00:34:34,541 --> 00:34:37,010 +but for complications, +we also need to consider + +679 +00:34:37,044 --> 00:34:38,312 +the full color rendering mode, + +680 +00:34:38,345 --> 00:34:41,415 +which is the default in Xcode Previews. + +681 +00:34:41,448 --> 00:34:45,586 +Let's do that by adding a tint +to each of our gauges. + +682 +00:34:46,854 --> 00:34:49,923 +And a foreground color +to the rectangular headline. + +683 +00:34:51,758 --> 00:34:53,260 +For a bit of a pop in full color, + +684 +00:34:53,293 --> 00:34:56,864 +we can check the rendering mode +with an environment property + +685 +00:34:56,897 --> 00:35:00,300 +to replace the donut symbol +with a donut emoji + +686 +00:35:00,334 --> 00:35:01,969 +on both the circular + +687 +00:35:02,002 --> 00:35:03,770 +and rectangular views. + +688 +00:35:05,072 --> 00:35:06,306 +That looks really great! + +689 +00:35:06,340 --> 00:35:08,208 +With the new variants UI in previews, + +690 +00:35:08,242 --> 00:35:11,245 +we can change the color +we're previewing with no code at all. + +691 +00:35:12,346 --> 00:35:14,348 +Or even look at several at once. + +692 +00:35:15,115 --> 00:35:18,185 +And since we used default spacing, +system font styles, + +693 +00:35:18,218 --> 00:35:19,786 +and adapted to the rendering mode, + +694 +00:35:19,820 --> 00:35:23,290 +the same views look right at home +on both the Lock Screen and watch face. + +695 +00:35:23,323 --> 00:35:26,960 +It's that easy to make a widget +on the brand-new Lock Screen in iOS 16 + +696 +00:35:26,994 --> 00:35:30,731 +and a great complication in watchOS 9, +all with the same code. + +697 +00:35:30,764 --> 00:35:33,967 +But this isn't the only way we've brought +the power of WidgetKit to the Lock Screen. + +698 +00:35:34,001 --> 00:35:35,602 +Here's Matt to tell you more. + +699 +00:35:36,170 --> 00:35:37,237 +Matt Shepherd: With WidgetKit, + +700 +00:35:37,271 --> 00:35:39,973 +you can give people access +to glanceable information. + +701 +00:35:40,007 --> 00:35:42,476 +But what about those moments +when they need live updates, + +702 +00:35:42,509 --> 00:35:44,178 +information tied to an activity, + +703 +00:35:44,211 --> 00:35:46,613 +or an event +that they care about right now? + +704 +00:35:46,647 --> 00:35:51,285 +For that, we are working on something new +we call Live Activities. + +705 +00:35:51,318 --> 00:35:53,487 +Live Activities makes it easier +to stay on top of things + +706 +00:35:53,520 --> 00:35:55,289 +that are happening in real time, + +707 +00:35:55,322 --> 00:35:56,890 +right from the Lock Screen. + +708 +00:35:56,924 --> 00:36:00,194 +Things like the latest score from a game, + +709 +00:36:00,227 --> 00:36:03,664 +the progress of a ride share, +or a workout, + +710 +00:36:03,697 --> 00:36:06,233 +right on the Lock Screen, +and always up-to-date. + +711 +00:36:06,266 --> 00:36:09,870 +Just like with widgets, +you create Live Activities with WidgetKit. + +712 +00:36:09,903 --> 00:36:12,573 +The difference is, you update +your Live Activity's presentation + +713 +00:36:12,606 --> 00:36:14,374 +and state in real time. + +714 +00:36:14,408 --> 00:36:17,611 +Since they're built with SwiftUI, +you can even animate your updates + +715 +00:36:17,644 --> 00:36:19,746 +from one state to the next. + +716 +00:36:19,780 --> 00:36:21,782 +These updates make sure +your Live Activities + +717 +00:36:21,815 --> 00:36:25,619 +has the most current information +when the user chooses to glance at it. + +718 +00:36:25,652 --> 00:36:30,657 +Live Activities will be available starting +in an update to iOS 16 later this year. + +719 +00:36:30,691 --> 00:36:33,160 +So those are the updates +to the all-new Lock Screen. + +720 +00:36:33,193 --> 00:36:35,262 +We think they're gonna be a great way +to help you give people + +721 +00:36:35,295 --> 00:36:38,565 +more information at a glance +in the moments they need it most. + +722 +00:36:39,299 --> 00:36:42,769 +Next, let's talk about a brand-new way +to enhance collaborative experiences. + +723 +00:36:42,803 --> 00:36:45,105 +To tell you more, here's Pierre. + +724 +00:36:45,138 --> 00:36:47,140 +Pierre de Fillipis: +Collaboration is an important part + +725 +00:36:47,174 --> 00:36:50,677 +of what people do +on iOS, iPadOS, and macOS. + +726 +00:36:50,711 --> 00:36:53,547 +And that's due in large part +to the incredible wealth of apps + +727 +00:36:53,580 --> 00:36:55,182 +that many of you have built, + +728 +00:36:55,215 --> 00:36:58,051 +to support teams who are collaborating +across any distance. + +729 +00:36:58,085 --> 00:37:01,755 +There's collaboration for work, +like a product road map in Airtable, + +730 +00:37:01,788 --> 00:37:06,393 +and there's also collaboration for play, +like finding your dream home in Redfin. + +731 +00:37:06,426 --> 00:37:08,262 +Whether it's for work or play, + +732 +00:37:08,295 --> 00:37:11,431 +collaboration often starts +with a conversation. + +733 +00:37:11,465 --> 00:37:13,967 +And with the new +Messages Collaboration API, + +734 +00:37:14,001 --> 00:37:17,137 +you can bring your app's +existing collaboration experiences + +735 +00:37:17,171 --> 00:37:18,939 +into Messages and FaceTime. + +736 +00:37:18,972 --> 00:37:21,875 +When users share a link +to content in your app, + +737 +00:37:21,909 --> 00:37:25,579 +the API makes it easy for you +to mark that link as collaborative, + +738 +00:37:25,612 --> 00:37:27,681 +enabling a seamless experience. + +739 +00:37:27,714 --> 00:37:29,816 +We provide the identifiers you need + +740 +00:37:29,850 --> 00:37:32,319 +so you can give access +to the recipients immediately + +741 +00:37:32,352 --> 00:37:34,154 +when they tap the link to join. + +742 +00:37:34,188 --> 00:37:37,090 +And of course, +this works without compromising privacy. + +743 +00:37:37,124 --> 00:37:41,161 +Messages identities and app identities +remain private and are not shared. + +744 +00:37:41,195 --> 00:37:44,998 +And the best part is, you can do this +with existing technologies + +745 +00:37:45,032 --> 00:37:47,467 +your app is most likely already using. + +746 +00:37:47,501 --> 00:37:51,638 +With one object, +your users can initiate collaboration + +747 +00:37:51,672 --> 00:37:54,741 +in two convenient ways +that they're already familiar with. + +748 +00:37:54,775 --> 00:37:57,444 +One, the Share Sheet, +which has been updated + +749 +00:37:57,477 --> 00:37:59,613 +to put collaboration front and center, + +750 +00:37:59,646 --> 00:38:01,181 +and two, drag & drop, + +751 +00:38:01,215 --> 00:38:03,917 +where you can share content +you want to collaborate on + +752 +00:38:03,951 --> 00:38:07,120 +by dragging it directly +into the Messages conversation. + +753 +00:38:07,154 --> 00:38:10,724 +And once the conversation is started, +you can even post notices + +754 +00:38:10,757 --> 00:38:14,228 +about content updates +right to the Messages conversation. + +755 +00:38:14,261 --> 00:38:17,965 +With a couple lines of code, +your users can get back to collaborating + +756 +00:38:17,998 --> 00:38:21,201 +in your app with a single tap in Messages. + +757 +00:38:21,235 --> 00:38:22,870 +And with the collaboration popover, + +758 +00:38:22,903 --> 00:38:26,740 +your users can get back to the +conversation in Messages or FaceTime + +759 +00:38:26,773 --> 00:38:28,075 +right from your app. + +760 +00:38:28,108 --> 00:38:30,110 +So with the Messages Collaboration API, + +761 +00:38:30,143 --> 00:38:33,947 +your app is truly woven into the fabric +of Messages and FaceTime. + +762 +00:38:33,981 --> 00:38:37,851 +We take care of giving your users +powerful communication tools + +763 +00:38:37,885 --> 00:38:40,921 +so you can focus +on the powerful collaboration tools + +764 +00:38:40,954 --> 00:38:42,589 +you deliver in your app. + +765 +00:38:42,623 --> 00:38:47,027 +So this is going to level up collaboration +on iOS, iPadOS, and macOS, + +766 +00:38:47,060 --> 00:38:50,197 +creating a consistent experience +that's deeply rooted + +767 +00:38:50,230 --> 00:38:52,566 +in the connection +between the people collaborating, + +768 +00:38:52,599 --> 00:38:54,101 +whether for work or play. + +769 +00:38:54,134 --> 00:38:56,637 +Next is Ari, who's going to tell you + +770 +00:38:56,670 --> 00:38:59,239 +about a new framework called App Intents. + +771 +00:38:59,273 --> 00:39:02,776 +Ari Weinstein: I'm excited to tell you +about the App Intents framework, + +772 +00:39:02,809 --> 00:39:05,412 +which makes your app's features +available to the system, + +773 +00:39:05,445 --> 00:39:09,349 +so people can use them automatically +through Siri and Shortcuts. + +774 +00:39:09,383 --> 00:39:11,718 +People love using Shortcuts +with their apps. + +775 +00:39:11,752 --> 00:39:15,189 +They let them get things done so fast, +just by asking Siri, + +776 +00:39:15,222 --> 00:39:18,492 +or by quickly tapping a shortcut +on the Home Screen. + +777 +00:39:18,525 --> 00:39:21,595 +And it's amazing +to see how people remix app capabilities + +778 +00:39:21,628 --> 00:39:23,697 +into totally new pieces of functionality + +779 +00:39:23,730 --> 00:39:25,465 +with custom shortcuts. + +780 +00:39:25,499 --> 00:39:29,870 +Today, people have to add shortcuts +manually before they can use them at all. + +781 +00:39:29,903 --> 00:39:34,208 +We're making this automatic in iOS 16 +with the new App Intents framework. + +782 +00:39:35,242 --> 00:39:39,279 +App Intents works together with Shortcuts +to form App Shortcuts, + +783 +00:39:39,313 --> 00:39:41,448 +which people can use with Siri +right away, + +784 +00:39:41,481 --> 00:39:43,283 +without having to set anything up first, + +785 +00:39:43,317 --> 00:39:46,553 +like, "Hey, Siri, clean the kitchen +with Roomba." + +786 +00:39:46,587 --> 00:39:48,155 +But it's not just Siri. + +787 +00:39:48,188 --> 00:39:50,724 +App shortcuts give your users +a front-row seat + +788 +00:39:50,757 --> 00:39:52,860 +to your app's features +throughout the system, + +789 +00:39:52,893 --> 00:39:56,496 +like in Spotlight, where anytime +people search for your app, + +790 +00:39:56,530 --> 00:39:58,699 +your shortcuts show up, too, + +791 +00:39:58,732 --> 00:40:02,469 +and your shortcuts will be suggested +right below app suggestions + +792 +00:40:02,503 --> 00:40:06,473 +without needing to adopt +any additional APIs, like donations. + +793 +00:40:06,507 --> 00:40:09,576 +Your shortcuts also appear immediately +in the Shortcuts app, + +794 +00:40:09,610 --> 00:40:11,678 +where people can run them with a tap. + +795 +00:40:12,346 --> 00:40:15,516 +App Intents is the next step +for the SiriKit Intents framework + +796 +00:40:15,549 --> 00:40:17,751 +that we introduced in iOS10. + +797 +00:40:17,784 --> 00:40:21,188 +If you adopt Intents to integrate +with Widgets or domains + +798 +00:40:21,221 --> 00:40:22,556 +like media or messaging, + +799 +00:40:22,589 --> 00:40:25,792 +you should keep using +the SiriKit Intents framework, + +800 +00:40:25,826 --> 00:40:29,229 +but for developers who build +custom intents for Siri and Shortcuts, + +801 +00:40:29,263 --> 00:40:31,465 +you should go ahead and upgrade +to App Intents. + +802 +00:40:31,498 --> 00:40:34,668 +You can easily upgrade to App Intents +in Xcode + +803 +00:40:34,701 --> 00:40:37,971 +by pushing the Convert button +in your intent definition file. + +804 +00:40:38,005 --> 00:40:41,008 +Xcode will generate the equivalent +App Intents source code, + +805 +00:40:41,041 --> 00:40:43,477 +and then you fill in the blanks +with your intent handling code. + +806 +00:40:43,510 --> 00:40:46,747 +The App Intents framework is really easy +to develop for + +807 +00:40:46,780 --> 00:40:49,883 +because it's designed +from the ground up for Swift, + +808 +00:40:49,917 --> 00:40:52,252 +and it requires much less code. + +809 +00:40:52,286 --> 00:40:55,622 +The Swift code that you write +is the only source of truth, + +810 +00:40:55,656 --> 00:40:57,925 +There's no separate +intent definition files + +811 +00:40:57,958 --> 00:41:00,227 +or code generation to keep in sync. + +812 +00:41:00,260 --> 00:41:02,863 +And the code is easy to add +to your project. + +813 +00:41:02,896 --> 00:41:05,399 +You don't need to rearchitect +your codebase. + +814 +00:41:05,432 --> 00:41:08,502 +Even if you have Objective-C code, +you can use it with App Intents + +815 +00:41:08,535 --> 00:41:10,137 +by wrapping it with Swift. + +816 +00:41:10,170 --> 00:41:13,540 +An app intent represents something +people can do inside of your app, + +817 +00:41:13,574 --> 00:41:16,777 +and it makes it possible to do it +from outside of your app. + +818 +00:41:16,810 --> 00:41:19,513 +You can define an intent +and add an app shortcut + +819 +00:41:19,546 --> 00:41:21,215 +in just a few lines of code. + +820 +00:41:21,248 --> 00:41:23,417 +Let's give it a try together. + +821 +00:41:23,450 --> 00:41:26,119 +Back in the Food Truck app, +I have this great chart view + +822 +00:41:26,153 --> 00:41:28,722 +that lets me see the top five +best-selling donuts + +823 +00:41:28,755 --> 00:41:32,626 +over a given period of time, +like today or this week. + +824 +00:41:32,659 --> 00:41:34,795 +I want to expose this +to Siri and Shortcuts + +825 +00:41:34,828 --> 00:41:37,564 +so people pull it up super quickly, + +826 +00:41:37,598 --> 00:41:41,001 +so first, in Xcode, +I'll go to a new Swift file. + +827 +00:41:41,034 --> 00:41:44,338 +I'll import the App Intents framework +and SwiftUI. + +828 +00:41:45,839 --> 00:41:48,375 +Then I define the intent +by defining a struct + +829 +00:41:48,408 --> 00:41:51,044 +that conforms to the AppIntent protocol. + +830 +00:41:51,078 --> 00:41:53,413 +I'll give it a title. + +831 +00:41:53,447 --> 00:41:57,417 +And I'll add a parameter +for which time frame of trends to look at. + +832 +00:41:57,451 --> 00:42:01,388 +This uses the time frame enum +that's already defined in my codebase. + +833 +00:42:01,421 --> 00:42:05,092 +I need to extend it to conform +to the AppEnum protocol + +834 +00:42:05,125 --> 00:42:07,394 +so that we can extract +human-readable names + +835 +00:42:07,427 --> 00:42:08,829 +for each enum case, + +836 +00:42:08,862 --> 00:42:11,231 +like "today" and "this week." + +837 +00:42:11,265 --> 00:42:15,736 +Next, on the intent, +I'll implement the perform method. + +838 +00:42:15,769 --> 00:42:19,806 +Here, I return a result that includes +the SwiftUI chart view. + +839 +00:42:19,840 --> 00:42:22,809 +I could also include a dialogue +or output value. + +840 +00:42:22,843 --> 00:42:26,547 +I want people to be able to use +this intent automatically, without setup, + +841 +00:42:26,580 --> 00:42:28,282 +so I'll define an app shortcut. + +842 +00:42:30,184 --> 00:42:32,886 +This includes the phrase +that people can say to Siri + +843 +00:42:32,920 --> 00:42:34,655 +to use this intent. + +844 +00:42:34,688 --> 00:42:38,425 +The phrase has to include the app name +as a variable, + +845 +00:42:38,458 --> 00:42:41,061 +and I've included the time frame parameter + +846 +00:42:41,094 --> 00:42:44,131 +so people can say +"Food truck trends for today," + +847 +00:42:44,164 --> 00:42:47,134 +or "Food truck trends for this week." + +848 +00:42:47,167 --> 00:42:50,637 +The last thing I need to do is make this +discoverable for my users. + +849 +00:42:50,671 --> 00:42:52,840 +People need to see the phrase +at some point, + +850 +00:42:52,873 --> 00:42:54,641 +so they know what to say to Siri, + +851 +00:42:54,675 --> 00:42:57,945 +so I'm going to switch to the file +for the Top 5 Donuts view + +852 +00:42:57,978 --> 00:42:59,713 +that we were looking at a second ago, + +853 +00:42:59,746 --> 00:43:01,815 +and I'll add a Siri tip. + +854 +00:43:03,383 --> 00:43:07,554 +Now I can build and run the app +and hop over to my phone. + +855 +00:43:07,588 --> 00:43:08,622 +Let's give it a try. + +856 +00:43:11,124 --> 00:43:13,760 +I can see the shortcut now appears +in the Shortcuts app, + +857 +00:43:13,794 --> 00:43:16,029 +with variants for each parameter value, + +858 +00:43:16,063 --> 00:43:19,600 +and I can run one +just by tapping on it. + +859 +00:43:19,633 --> 00:43:22,936 +And I can run them from Siri +just by saying the phrase, + +860 +00:43:22,970 --> 00:43:25,005 +"Food truck trends for today." + +861 +00:43:26,807 --> 00:43:30,177 +Or I could say, +"food truck trends for this week." + +862 +00:43:32,446 --> 00:43:34,848 +When people are in +the Top Five view of my app, + +863 +00:43:34,882 --> 00:43:36,950 +they'll see this tip we added +at the bottom, + +864 +00:43:36,984 --> 00:43:40,487 +so they know what to say to Siri +to ask for this feature. + +865 +00:43:40,521 --> 00:43:43,824 +Lastly, people can quickly access these +in Spotlight + +866 +00:43:43,857 --> 00:43:45,926 +when they search for the app, like this. + +867 +00:43:48,161 --> 00:43:50,297 +It's super useful. + +868 +00:43:50,330 --> 00:43:54,268 +App Intents will make it easier than ever +before to make your app's functionality + +869 +00:43:54,301 --> 00:43:58,939 +available throughout the system experience +on all of these platforms. + +870 +00:43:58,972 --> 00:44:01,441 +Next, Ricky will tell us +about some big updates + +871 +00:44:01,475 --> 00:44:03,377 +to authentication technologies. + +872 +00:44:04,044 --> 00:44:05,612 +Ricky Mondello: +For as long as we can remember, + +873 +00:44:05,646 --> 00:44:08,215 +we've been creating and using passwords. + +874 +00:44:08,248 --> 00:44:10,417 +But passwords have serious issues, + +875 +00:44:10,450 --> 00:44:14,588 +like phishing, reuse across accounts, +and website leaks. + +876 +00:44:14,621 --> 00:44:18,258 +The good news is that together +we can solve these issues. + +877 +00:44:18,292 --> 00:44:21,328 +And we can do this today with Passkeys. + +878 +00:44:21,361 --> 00:44:24,298 +Passkeys will streamline +your authentication flows + +879 +00:44:24,331 --> 00:44:27,601 +and address the top security issues +with passwords. + +880 +00:44:27,634 --> 00:44:30,904 +Passkeys were designed +to be incredibly easy to use. + +881 +00:44:30,938 --> 00:44:34,441 +The interface uses familiar +Autofill-style UI + +882 +00:44:34,474 --> 00:44:37,945 +and FaceID and TouchID +for biometric verification. + +883 +00:44:37,978 --> 00:44:41,615 +These elements create a seamless +transition away from passwords, + +884 +00:44:41,648 --> 00:44:45,152 +while delivering +a profound increase in security. + +885 +00:44:45,185 --> 00:44:47,354 +Let's check out Passkeys in action. + +886 +00:44:48,155 --> 00:44:50,257 +When setting up an account with a passkey, + +887 +00:44:50,290 --> 00:44:52,526 +I don't need to create a password. + +888 +00:44:52,559 --> 00:44:57,664 +I'll type a user name and save +the passkey to my iCloud Keychain. + +889 +00:44:57,698 --> 00:45:02,135 +This will securely sync this passkey +to all of my other Apple devices. + +890 +00:45:02,169 --> 00:45:06,273 +And if I sign out, +signing back in is a breeze. + +891 +00:45:06,306 --> 00:45:09,476 +Just Face ID, and I'm in. + +892 +00:45:09,510 --> 00:45:12,713 +Because passkeys are built +on open industry standards + +893 +00:45:12,746 --> 00:45:14,448 +that platforms are adopting, + +894 +00:45:14,481 --> 00:45:17,751 +I can use the passkey +I just created on my iPhone + +895 +00:45:17,784 --> 00:45:21,255 +to sign into the Food Truck website +on my friend's PC. + +896 +00:45:21,288 --> 00:45:25,359 +On the website, +I'll type my username, submit, + +897 +00:45:25,392 --> 00:45:29,062 +and choose the option to sign in +using a phone, + +898 +00:45:29,096 --> 00:45:31,498 +scan the QR code, + +899 +00:45:31,532 --> 00:45:36,737 +let the iPhone and PC securely connect, + +900 +00:45:36,770 --> 00:45:39,306 +and I'm signed in. + +901 +00:45:39,339 --> 00:45:42,709 +In Safari on my Mac, +it's even easier to sign in. + +902 +00:45:42,743 --> 00:45:45,779 +My passkey is already here, +thanks to iCloud Keychain, + +903 +00:45:45,812 --> 00:45:49,449 +and I can sign in directly +from the website's username field. + +904 +00:45:49,483 --> 00:45:53,287 +It's easy to integrate passkeys +into existing sign-in flows. + +905 +00:45:53,320 --> 00:45:57,591 +For example, this website's username field +lets me sign in with a passkey + +906 +00:45:57,624 --> 00:45:59,259 +or a password. + +907 +00:45:59,293 --> 00:46:04,031 +If I type a username +for a password based account, + +908 +00:46:04,064 --> 00:46:05,599 +I can quickly sign in. + +909 +00:46:05,632 --> 00:46:08,402 +With passkeys, +the device does the hard work, + +910 +00:46:08,435 --> 00:46:10,771 +and it's secure every time. + +911 +00:46:10,804 --> 00:46:12,306 +When creating a passkey, + +912 +00:46:12,339 --> 00:46:16,143 +the device generates a unique key +that is specific to the website or app + +913 +00:46:16,176 --> 00:46:19,880 +it was created for +and protects it behind biometrics. + +914 +00:46:19,913 --> 00:46:22,449 +It's impossible to have a weak passkey. + +915 +00:46:22,482 --> 00:46:25,385 +It can't be forgotten, reused, or guessed. + +916 +00:46:25,419 --> 00:46:28,555 +Passkeys are based +on public key cryptography, + +917 +00:46:28,589 --> 00:46:32,259 +which makes credential leaks +from servers a thing of the past. + +918 +00:46:32,292 --> 00:46:35,596 +Instead of storing salted, +hashed passwords, + +919 +00:46:35,629 --> 00:46:40,734 +that can leak and be cracked, +your server keeps only a public key. + +920 +00:46:40,767 --> 00:46:43,270 +Public keys are designed +to be truly public, + +921 +00:46:43,303 --> 00:46:45,572 +and not at all valuable to hackers. + +922 +00:46:45,606 --> 00:46:49,376 +This significantly reduces your risk +as a website owner. + +923 +00:46:49,409 --> 00:46:52,346 +With passkeys– +and this point is huge– + +924 +00:46:52,379 --> 00:46:55,382 +credential phishing as we know it today +is gone, + +925 +00:46:55,415 --> 00:46:59,620 +eliminating the number one +security vulnerability that users face. + +926 +00:46:59,653 --> 00:47:03,690 +Passkeys are intrinsically linked to the +website or app they were set up for, + +927 +00:47:03,724 --> 00:47:07,995 +so users can never be tricked into +using their passkey on the wrong website. + +928 +00:47:08,028 --> 00:47:09,696 +And unlike passwords, + +929 +00:47:09,730 --> 00:47:12,533 +it's not possible to type +or copy a passkey + +930 +00:47:12,566 --> 00:47:14,668 +into a convincing fake website, + +931 +00:47:14,701 --> 00:47:18,405 +or even give anything away to +someone looking over your shoulder. + +932 +00:47:18,438 --> 00:47:21,308 +When you put it all together, +what we're talking about here + +933 +00:47:21,341 --> 00:47:23,810 +is a new era of account security. + +934 +00:47:23,844 --> 00:47:27,915 +Bringing passkeys to your app +and website takes only a few steps. + +935 +00:47:27,948 --> 00:47:31,251 +First, you'll teach your account backend +to store public keys + +936 +00:47:31,285 --> 00:47:33,854 +and issue authentication challenges. + +937 +00:47:33,887 --> 00:47:36,356 +Then, on your website and in your app, + +938 +00:47:36,390 --> 00:47:38,225 +you'll offer passkeys to users + +939 +00:47:38,258 --> 00:47:42,496 +and adopt API to create +a new passkey and sign in with it. + +940 +00:47:42,529 --> 00:47:47,100 +Passkeys are based on the Web +Authentication, or WebAuthn, standard, + +941 +00:47:47,134 --> 00:47:50,037 +which has been a collaborative effort +across the industry + +942 +00:47:50,070 --> 00:47:53,574 +from both platform vendors +and service owners. + +943 +00:47:53,607 --> 00:47:56,777 +The standard itself is mature +and well documented, + +944 +00:47:56,810 --> 00:47:59,279 +and passkeys fit it like a glove. + +945 +00:47:59,313 --> 00:48:02,850 +All of this is ready +for you to build on right now. + +946 +00:48:02,883 --> 00:48:06,620 +Next generation security, +a seamless user experience, + +947 +00:48:06,653 --> 00:48:10,991 +and a design that works beautifully +alongside passwords during the transition. + +948 +00:48:11,024 --> 00:48:12,626 +Back to you, Sebastien. + +949 +00:48:12,659 --> 00:48:15,362 +Sebastien: You've just seen +a few of the newest ways + +950 +00:48:15,395 --> 00:48:19,900 +that you can integrate your apps with the +system experience on all of our platforms. + +951 +00:48:19,933 --> 00:48:23,637 +And beyond those integration points, +there are a ton of new APIs + +952 +00:48:23,670 --> 00:48:25,839 +and frameworks +across all of our platforms + +953 +00:48:25,873 --> 00:48:29,643 +that open up even more possibilities +for you and your apps this year. + +954 +00:48:30,477 --> 00:48:32,479 +And I'd like to walk you through a few + +955 +00:48:32,513 --> 00:48:35,949 +before diving into some others +in more detail. + +956 +00:48:35,983 --> 00:48:38,585 +Let's start with iPadOS. + +957 +00:48:38,619 --> 00:48:43,090 +With iPadOS 16, you'll be able to make +the most powerful iPad apps yet, + +958 +00:48:43,123 --> 00:48:46,493 +with a consistent, +desktop-like experience. + +959 +00:48:46,527 --> 00:48:50,097 +There's a seamless find-and-replace +experience for UI text views + +960 +00:48:50,130 --> 00:48:52,299 +that your apps get automatically, + +961 +00:48:52,332 --> 00:48:54,601 +as well as updates to the navigation bar, + +962 +00:48:54,635 --> 00:48:56,570 +toolbars, the document menu, + +963 +00:48:56,603 --> 00:48:59,506 +that make it easy for your users +to manage documents + +964 +00:48:59,540 --> 00:49:01,742 +and customize their experience. + +965 +00:49:01,775 --> 00:49:05,913 +To enable even more powerful applications +of iPad with connected hardware, + +966 +00:49:05,946 --> 00:49:07,915 +DriverKit comes to iPad, + +967 +00:49:07,948 --> 00:49:11,451 +helping to unlock +the incredible power of the M1 chip. + +968 +00:49:11,485 --> 00:49:14,154 +It's the same API +that's available on Mac today, + +969 +00:49:14,188 --> 00:49:18,525 +enabling you to easily deliver support +for your USB, audio, + +970 +00:49:18,559 --> 00:49:21,428 +and PCI devices +to an even larger audience. + +971 +00:49:22,763 --> 00:49:27,000 +Now, watchOS is creating new opportunities +for apps through deeper integration + +972 +00:49:27,034 --> 00:49:28,869 +with system services. + +973 +00:49:28,902 --> 00:49:31,405 +The CallKit framework in watchOS 9 + +974 +00:49:31,438 --> 00:49:33,907 +includes a new +Voiceover IP background mode + +975 +00:49:33,941 --> 00:49:37,377 +that lets apps make voice calls +directly from Apple Watch, + +976 +00:49:37,411 --> 00:49:42,049 +with the same familiar user experience +as FaceTime audio and phone calls. + +977 +00:49:42,082 --> 00:49:45,452 +And Bluetooth-connected medical devices +get more robust connectivity + +978 +00:49:45,485 --> 00:49:46,854 +and data delivery, + +979 +00:49:46,887 --> 00:49:50,457 +allowing for timely alerts +when a critical condition is detected. + +980 +00:49:51,859 --> 00:49:55,796 +Now, tvOS 16 gives you new ways +to create connected experiences + +981 +00:49:55,829 --> 00:49:57,564 +between your apps on Apple TV + +982 +00:49:57,598 --> 00:50:02,069 +and iPhone, iPad, or Apple Watch apps +on nearby devices. + +983 +00:50:02,102 --> 00:50:05,239 +So a workout could use motion data +from Apple Watch, + +984 +00:50:05,272 --> 00:50:06,907 +or you could use iPhone or iPad + +985 +00:50:06,940 --> 00:50:10,344 +as a custom controller +for your turn-based games, + +986 +00:50:10,377 --> 00:50:14,314 +And tvOS manages device discovery +and connection for you, + +987 +00:50:14,348 --> 00:50:16,617 +so your app doesn't even need +to be running on the other device. + +988 +00:50:16,650 --> 00:50:18,886 +In fact, if your app isn't installed, + +989 +00:50:18,919 --> 00:50:21,421 +the user is automatically prompted +to download it + +990 +00:50:21,455 --> 00:50:23,090 +right from the App Store. + +991 +00:50:23,957 --> 00:50:27,861 +Now, for Phone and iPad, +there are new tools for advertisers. + +992 +00:50:27,895 --> 00:50:31,632 +We know that effective advertising +is important to a lot of your businesses, + +993 +00:50:31,665 --> 00:50:34,568 +which is why we created SKAdNetwork. + +994 +00:50:34,601 --> 00:50:37,371 +It's an API that helps ad networks +and advertisers + +995 +00:50:37,404 --> 00:50:40,741 +measure the performance of campaigns +without tracking users, + +996 +00:50:40,774 --> 00:50:45,112 +and we've been pleased to see +many third-party ad networks adopt it. + +997 +00:50:45,145 --> 00:50:47,848 +Now, we've heard feedback from +ad networks and developers, + +998 +00:50:47,881 --> 00:50:51,318 +and this year, we made a number +of improvements to SKAdNetwork + +999 +00:50:51,351 --> 00:50:53,187 +that reflect some of the biggest requests + +1000 +00:50:53,220 --> 00:50:55,656 +and give you +dramatically more flexibility, + +1001 +00:50:55,689 --> 00:50:58,825 +all without compromising user privacy. + +1002 +00:50:59,960 --> 00:51:02,529 +Now, on iPhone and iPad, +there are new cool features + +1003 +00:51:02,563 --> 00:51:04,798 +that use AR and LiDAR scanning + +1004 +00:51:04,831 --> 00:51:07,201 +with ScanKit and RoomPlan. + +1005 +00:51:07,234 --> 00:51:11,038 +These APIs let your apps create +rich 3D parametric room models + +1006 +00:51:11,071 --> 00:51:13,774 +in USD and USDZ formats. + +1007 +00:51:13,807 --> 00:51:16,910 +So you can create +a variety of workflows and experiences, + +1008 +00:51:16,944 --> 00:51:20,514 +from architecture and design, +to retail and hospitality, + +1009 +00:51:20,547 --> 00:51:24,117 +and the models include furniture +classification for categories + +1010 +00:51:24,151 --> 00:51:29,590 +such as sofas, cabinets, TVs, +and yes, even kitchen sinks. + +1011 +00:51:29,623 --> 00:51:34,094 +Now, last year, we introduced Focus +for iPhone, iPad, Mac, and Apple Watch, + +1012 +00:51:34,127 --> 00:51:39,800 +and with it, ways for your app to manage +notifications based on a user's Focus. + +1013 +00:51:39,833 --> 00:51:43,337 +And this year, +Focus goes further with Focus filters. + +1014 +00:51:43,370 --> 00:51:46,940 +They're built on top of App Intents, +and Focus filters let you adjust + +1015 +00:51:46,974 --> 00:51:50,544 +the content of your app +based on the user's current focus. + +1016 +00:51:50,577 --> 00:51:53,380 +So, for example, +an app could create a Focus filter + +1017 +00:51:53,413 --> 00:51:57,985 +to only show work accounts +when the user is in their Work Focus. + +1018 +00:51:58,018 --> 00:52:01,555 +And those examples are really +just scratching the surface. + +1019 +00:52:01,588 --> 00:52:04,625 +Across the board at every level, +there are new tools and APIs + +1020 +00:52:04,658 --> 00:52:08,529 +with the power that you need +to take your apps further than ever + +1021 +00:52:08,562 --> 00:52:11,265 +and to create entirely new apps +and experiences. + +1022 +00:52:11,298 --> 00:52:13,700 +So next, let's go a little deeper + +1023 +00:52:13,734 --> 00:52:18,205 +starting with Metal, a technology that's +really taking things to the next level. + +1024 +00:52:18,238 --> 00:52:21,108 +And to tell you more, let's go to Sarah. + +1025 +00:52:21,141 --> 00:52:28,148 +♪ ♪ + +1026 +00:52:29,416 --> 00:52:32,452 +Sarah Clawson: Metal is the powerful +graphics and compute API + +1027 +00:52:32,486 --> 00:52:36,590 +that helps you create amazing games +and pro apps for Apple platforms. + +1028 +00:52:36,623 --> 00:52:38,492 +Metal makes it easy to take advantage + +1029 +00:52:38,525 --> 00:52:42,062 +of the groundbreaking Apple GPUs +and unified memory system + +1030 +00:52:42,095 --> 00:52:45,165 +now spanning the latest iPhone, +iPad, and Mac lineups + +1031 +00:52:45,199 --> 00:52:46,867 +shipping with Apple silicon. + +1032 +00:52:46,900 --> 00:52:50,938 +And this year, we're introducing Metal 3, + +1033 +00:52:50,971 --> 00:52:52,673 +with powerful new features that help you + +1034 +00:52:52,706 --> 00:52:53,841 +render immersive graphics + +1035 +00:52:53,874 --> 00:52:55,442 +with even higher frame rates + +1036 +00:52:55,475 --> 00:52:56,643 +and enable new levels + +1037 +00:52:56,677 --> 00:52:58,478 +of computational performance. + +1038 +00:52:58,512 --> 00:53:01,181 +For instance, +you'll get huge performance gains + +1039 +00:53:01,215 --> 00:53:03,350 +for the machine learning framework, +PyTorch, + +1040 +00:53:03,383 --> 00:53:05,252 +which now uses the new Metal backend + +1041 +00:53:05,285 --> 00:53:07,721 +to enable ML training with the GPU. + +1042 +00:53:07,754 --> 00:53:10,424 +And the biggest focus area is on gaming, + +1043 +00:53:10,457 --> 00:53:12,359 +starting with game loading, + +1044 +00:53:12,392 --> 00:53:14,661 +a key element to the gaming experience + +1045 +00:53:14,695 --> 00:53:17,364 +that can affect launch time +and loading new levels. + +1046 +00:53:17,397 --> 00:53:20,067 +Modern games deliver +a rich gaming experience + +1047 +00:53:20,100 --> 00:53:22,436 +by providing high-quality assets, + +1048 +00:53:22,469 --> 00:53:26,440 +and loading these assets quickly from +storage to the GPU can be challenging. + +1049 +00:53:26,473 --> 00:53:30,344 +Often, games will hide asset loading +behind a loading screen, + +1050 +00:53:30,377 --> 00:53:32,846 +and one technique +to launch gameplay faster + +1051 +00:53:32,880 --> 00:53:34,982 +is to load and draw +a lower quality version + +1052 +00:53:35,015 --> 00:53:37,651 +until the high-quality visuals +are available. + +1053 +00:53:37,684 --> 00:53:39,786 +This is not an ideal gaming experience + +1054 +00:53:39,820 --> 00:53:43,323 +since the user sees +lower quality graphics for longer. + +1055 +00:53:43,357 --> 00:53:46,093 +Metal 3 introduces fast resource loading + +1056 +00:53:46,126 --> 00:53:47,794 +with the Metal IO API + +1057 +00:53:47,828 --> 00:53:51,598 +that takes advantage of the Apple GPU's +unified memory architecture + +1058 +00:53:51,632 --> 00:53:53,267 +to minimize loading overhead + +1059 +00:53:53,300 --> 00:53:55,936 +and ensures +that the high-speed SSD storage + +1060 +00:53:55,969 --> 00:53:58,038 +that ships with every Apple silicon Mac + +1061 +00:53:58,071 --> 00:54:01,742 +has enough requests in its queues +to maximize throughput. + +1062 +00:54:01,775 --> 00:54:05,045 +This new API provides faster +and more consistent performance + +1063 +00:54:05,078 --> 00:54:07,981 +so that more time is spent +drawing at the ideal quality. + +1064 +00:54:09,316 --> 00:54:12,319 +In addition to moving resources +from storage to memory, + +1065 +00:54:12,352 --> 00:54:15,122 +game loading +is also about shader compilation. + +1066 +00:54:15,155 --> 00:54:16,924 +Shaders always need to be compiled + +1067 +00:54:16,957 --> 00:54:19,359 +for the user's unique +hardware configuration, + +1068 +00:54:19,393 --> 00:54:22,296 +and with the wide variety +of PC hardware permutations, + +1069 +00:54:22,329 --> 00:54:24,631 +this usually has to be done at runtime. + +1070 +00:54:24,665 --> 00:54:27,901 +This in-game compilation can affect +the gamer's experience + +1071 +00:54:27,935 --> 00:54:31,505 +causing dropped frames, +slower frame rates, and longer loading. + +1072 +00:54:31,538 --> 00:54:35,542 +In contrast, Apple silicon and Metal 3 +are designed together + +1073 +00:54:35,576 --> 00:54:37,311 +to support all Apple devices. + +1074 +00:54:37,344 --> 00:54:39,713 +And now, with offline shader compilation, + +1075 +00:54:39,746 --> 00:54:43,350 +you can generate GPU shader binaries +at project build time, + +1076 +00:54:43,383 --> 00:54:46,320 +enabling you to eliminate +in-game shader compilation + +1077 +00:54:46,353 --> 00:54:49,556 +to reduce load times +and improve rendering performance. + +1078 +00:54:49,590 --> 00:54:54,294 +Another important aspect to gaming +is providing rich, detailed assets, + +1079 +00:54:54,328 --> 00:54:57,497 +and one way to increase the visual +fidelity of your game's graphics + +1080 +00:54:57,531 --> 00:55:01,502 +is by generating much more sophisticated +geometric meshes. + +1081 +00:55:01,535 --> 00:55:04,037 +Traditionally this is done +with a compute pass + +1082 +00:55:04,071 --> 00:55:06,507 +that'll evaluate the surface +and generate geometry + +1083 +00:55:06,540 --> 00:55:08,609 +to be used in a later render pass. + +1084 +00:55:08,642 --> 00:55:11,245 +The challenge is that +this can introduce latency + +1085 +00:55:11,278 --> 00:55:13,614 +and take an unpredictable +amount of memory. + +1086 +00:55:13,647 --> 00:55:16,650 +Metal 3 introduces a new Mesh Shading API, + +1087 +00:55:16,683 --> 00:55:20,821 +which gives you precise control over +an optimized geometry processing pipeline + +1088 +00:55:20,854 --> 00:55:22,456 +from a single render pass. + +1089 +00:55:22,489 --> 00:55:25,359 +The Object shader decides +how many meshes to generate, + +1090 +00:55:25,392 --> 00:55:27,628 +and the Mesh shader +generates the actual geometry + +1091 +00:55:27,661 --> 00:55:30,063 +to be sent directly to the rasterizer, + +1092 +00:55:30,097 --> 00:55:33,433 +avoiding a trip to device memory +and increasing performance. + +1093 +00:55:33,467 --> 00:55:36,236 +Gamers also want to see +these stunning visuals + +1094 +00:55:36,270 --> 00:55:38,005 +at the highest possible frame rate, + +1095 +00:55:38,038 --> 00:55:41,241 +but rendering advanced graphics +at ultra-high resolutions + +1096 +00:55:41,275 --> 00:55:42,976 +can cost precious milliseconds. + +1097 +00:55:43,010 --> 00:55:46,246 +MetalFX upscaling helps +you render immersive graphics + +1098 +00:55:46,280 --> 00:55:47,948 +in less time per frame. + +1099 +00:55:47,981 --> 00:55:49,516 +Here's how it works. + +1100 +00:55:49,550 --> 00:55:52,986 +Previously, you would render +your full frame at native resolution, + +1101 +00:55:53,020 --> 00:55:56,423 +but the GPU render time +might not hit the target frame time. + +1102 +00:55:56,456 --> 00:56:00,260 +Now, you can render the same +complex scene at a lower resolution + +1103 +00:56:00,294 --> 00:56:03,363 +to meet the target frame times, +and use MetalFX framework + +1104 +00:56:03,397 --> 00:56:07,835 +to perform temporal antialiasing +and upscaling to the target resolution. + +1105 +00:56:07,868 --> 00:56:10,804 +With Apple silicon and Metal 3's +optimized features, + +1106 +00:56:10,838 --> 00:56:13,273 +gaming has never looked so good +on the Mac. + +1107 +00:56:13,307 --> 00:56:14,975 +And developers agree. + +1108 +00:56:15,008 --> 00:56:18,412 +Leading game studios have plans to bring +their titles to the Mac, + +1109 +00:56:18,445 --> 00:56:21,448 +like Grid Legends, +taking advantage of Apple silicon + +1110 +00:56:21,481 --> 00:56:23,784 +to help you reach maximum speeds. + +1111 +00:56:23,817 --> 00:56:27,554 +Or Resident Evil Village, +using features like MetalFX upscaling + +1112 +00:56:27,588 --> 00:56:31,525 +to deliver hauntingly beautiful scenes +at the highest resolution. + +1113 +00:56:31,558 --> 00:56:34,127 +And No Man's Sky, +taking advantage of Metal 3 + +1114 +00:56:34,161 --> 00:56:35,963 +to explore rich, expansive worlds + +1115 +00:56:35,996 --> 00:56:37,731 +on both Mac and iPad. + +1116 +00:56:37,764 --> 00:56:41,502 +Metal 3 is incredible, +with features to boost the performance + +1117 +00:56:41,535 --> 00:56:44,771 +of your apps +and provide an amazing gaming experience. + +1118 +00:56:44,805 --> 00:56:49,276 +Now to tell us more about the direction +MapKit is headed, here's Kathy. + +1119 +00:56:49,309 --> 00:56:51,445 +Kathy Lin: Whether you're navigating +to a favorite restaurant, + +1120 +00:56:51,478 --> 00:56:53,146 +planning that next vacation, + +1121 +00:56:53,180 --> 00:56:56,016 +or just checking where your favorite +food truck is parked on a map, + +1122 +00:56:56,049 --> 00:57:00,654 +we rely on our devices more than ever +to help us explore the world around us. + +1123 +00:57:00,687 --> 00:57:04,558 +MapKit is the best way to help users +discover and navigate the world + +1124 +00:57:04,591 --> 00:57:07,528 +with rich and flexible mapping +and location services, + +1125 +00:57:07,561 --> 00:57:11,632 +powered by Apple Maps +and available to developers for free. + +1126 +00:57:11,665 --> 00:57:15,836 +With MapKit, you can display map +or satellite imagery in your app, + +1127 +00:57:15,869 --> 00:57:17,771 +find and call out points of interest, + +1128 +00:57:17,804 --> 00:57:19,406 +add annotations and overlays, + +1129 +00:57:19,439 --> 00:57:21,575 +get directions, and more. + +1130 +00:57:21,608 --> 00:57:24,011 +MapKit is powered by our all-new map, + +1131 +00:57:24,044 --> 00:57:26,413 +built from the ground up by Apple. + +1132 +00:57:26,446 --> 00:57:28,415 +It offers improved detail and accuracy, + +1133 +00:57:28,448 --> 00:57:32,119 +and can bring useful mapping +and location services to your app. + +1134 +00:57:32,152 --> 00:57:34,588 +With iOS 16, +we're building on this map + +1135 +00:57:34,621 --> 00:57:37,357 +to introduce our biggest update ever +for MapKit, + +1136 +00:57:37,391 --> 00:57:42,196 +starting with making the 3D City +Experience available to all developers. + +1137 +00:57:42,229 --> 00:57:45,199 +Users of your apps will be able +to see incredible details, + +1138 +00:57:45,232 --> 00:57:47,968 +like 3D elevation, turn lanes, + +1139 +00:57:48,001 --> 00:57:49,436 +crosswalks and bike lanes, + +1140 +00:57:49,469 --> 00:57:52,206 +and amazing handcrafted 3D landmarks + +1141 +00:57:52,239 --> 00:57:54,675 +like the Golden Gate Bridge, +or the Ferry Building. + +1142 +00:57:54,708 --> 00:57:58,579 +The additional detail of the map allows +you to provide context and precision + +1143 +00:57:58,612 --> 00:58:01,348 +that was never before possible. + +1144 +00:58:01,381 --> 00:58:03,550 +You can, for example, +show that a point of interest + +1145 +00:58:03,584 --> 00:58:07,054 +is between the crosswalk +and where the bike lane starts. + +1146 +00:58:07,087 --> 00:58:09,323 +No other digital map lets you do that, + +1147 +00:58:09,356 --> 00:58:12,526 +and we've made it incredibly easy +to implement. + +1148 +00:58:12,559 --> 00:58:14,428 +To show you more, +let's create an experience + +1149 +00:58:14,461 --> 00:58:17,898 +that makes it easy for a user to find +where their favorite food truck is parked + +1150 +00:58:17,931 --> 00:58:20,567 +using the details of the new map. + +1151 +00:58:20,601 --> 00:58:23,971 +Map views like this one will automatically +get the 3D City Experience + +1152 +00:58:24,004 --> 00:58:25,205 +where it's available. + +1153 +00:58:25,239 --> 00:58:28,408 +Just select iOS 16 +as the deployment target. + +1154 +00:58:28,442 --> 00:58:31,745 +Next, I can utilize +the extraordinary detail of the map + +1155 +00:58:31,778 --> 00:58:35,482 +to illustrate the exact location +of the food truck. + +1156 +00:58:35,516 --> 00:58:39,520 +MapKit has powerful controls that allow us +to position the camera in 3D space + +1157 +00:58:39,553 --> 00:58:42,289 +to create a precise view of the map. + +1158 +00:58:42,322 --> 00:58:44,825 +Here, I can choose how far +we want to be zoomed in + +1159 +00:58:44,858 --> 00:58:48,929 +by setting the distance of the center +coordinate of the camera to 600 meters. + +1160 +00:58:48,962 --> 00:58:52,165 +By adjusting the pitch and heading +and tilting the camera into 3D, + +1161 +00:58:52,199 --> 00:58:55,169 +you can see amazing +and useful details like turn lanes, + +1162 +00:58:55,202 --> 00:58:57,938 +crosswalks, and even trees. + +1163 +00:58:57,971 --> 00:59:00,641 +By default, elevation will be flattened. + +1164 +00:59:00,674 --> 00:59:03,577 +In order to help users understand +the terrain they'll encounter, + +1165 +00:59:03,610 --> 00:59:07,981 +I can specify a preferredConfiguration +with elevationStyle 'realistic' + +1166 +00:59:08,015 --> 00:59:09,950 +to include 3D elevation. + +1167 +00:59:11,018 --> 00:59:15,422 +When adding an annotation or a route line +sourced from MapKit's Directions API, + +1168 +00:59:15,455 --> 00:59:17,624 +MapKit automatically handles elevation + +1169 +00:59:17,658 --> 00:59:20,093 +and will adjust the annotation +or route line + +1170 +00:59:20,127 --> 00:59:23,997 +by placing it on top of the 3D terrain. + +1171 +00:59:24,031 --> 00:59:26,633 +Animating the camera heading +by adding a slow pan + +1172 +00:59:26,667 --> 00:59:29,903 +really brings the map view to life. + +1173 +00:59:29,937 --> 00:59:31,605 +When a user switches into Dark Mode, + +1174 +00:59:31,638 --> 00:59:34,408 +the map will adjust together +with the rest of the UI. + +1175 +00:59:34,441 --> 00:59:37,344 +We're very excited to make +this immersive experience available + +1176 +00:59:37,377 --> 00:59:39,813 +to developers with iOS 16. + +1177 +00:59:39,847 --> 00:59:43,984 +In addition, we're also bringing another +popular Apple Maps feature to MapKit, + +1178 +00:59:44,017 --> 00:59:45,619 +Look Around, + +1179 +00:59:45,652 --> 00:59:49,122 +which is a great way +to explore the world at ground level, + +1180 +00:59:49,156 --> 00:59:52,559 +with high resolution 3D photography +and smooth animations. + +1181 +00:59:52,593 --> 00:59:55,462 +Users can simply tap +to move down the street. + +1182 +00:59:57,164 --> 01:00:00,067 +I can add a static Look Around preview +just below the map + +1183 +01:00:00,100 --> 01:00:03,770 +by dropping in a View Controller +and specifying a MapItem. + +1184 +01:00:03,804 --> 01:00:07,407 +The Look Around view automatically frames +the location correctly. + +1185 +01:00:07,441 --> 01:00:09,776 +When a user taps on the preview, +I can choose to provide + +1186 +01:00:09,810 --> 01:00:11,678 +a full screen Look Around view, + +1187 +01:00:11,712 --> 01:00:13,647 +where users can see the address, + +1188 +01:00:13,680 --> 01:00:15,516 +the date the imagery was collected, + +1189 +01:00:15,549 --> 01:00:17,751 +and they can tap to move around freely + +1190 +01:00:17,784 --> 01:00:20,587 +to get a better understanding +of their surroundings. + +1191 +01:00:20,621 --> 01:00:23,390 +There's one more new, +highly-requested capability + +1192 +01:00:23,423 --> 01:00:26,193 +we're introducing to MapKit in iOS 16– + +1193 +01:00:26,226 --> 01:00:28,462 +Apple Maps Server APIs. + +1194 +01:00:28,495 --> 01:00:30,330 +Maps Server APIs are RESTful + +1195 +01:00:30,364 --> 01:00:33,534 +and support four of the most used +functions of MapKit: + +1196 +01:00:33,567 --> 01:00:36,703 +Geocode, +which turns a lat/long into an address; + +1197 +01:00:36,737 --> 01:00:39,039 +Reverse Geocode, +which does the opposite– + +1198 +01:00:39,072 --> 01:00:41,775 +it turns an address into GPS coordinates; + +1199 +01:00:41,808 --> 01:00:44,278 +Search; +and Estimated Times of Arrival. + +1200 +01:00:44,311 --> 01:00:47,080 +Our new Maps Server APIs +are a great way + +1201 +01:00:47,114 --> 01:00:51,552 +to make your own backend services richer +and more performant. + +1202 +01:00:51,585 --> 01:00:53,887 +Of course, +MapKit is built from the ground up + +1203 +01:00:53,921 --> 01:00:56,156 +on the same foundation of privacy +as Apple Maps, + +1204 +01:00:56,190 --> 01:00:59,393 +and does not associate users' data +with their identity + +1205 +01:00:59,426 --> 01:01:02,062 +or keep a history of where they've been. + +1206 +01:01:02,095 --> 01:01:05,666 +And that's a quick look at +what's new with MapKit in iOS 16. + +1207 +01:01:05,699 --> 01:01:09,102 +Now for the weather, or at least +how you can build it into your app, + +1208 +01:01:09,136 --> 01:01:10,804 +here's Novall. + +1209 +01:01:10,838 --> 01:01:15,142 +Novall Khan: We announced today that we're +bringing the Weather app to iPad and Mac, + +1210 +01:01:15,175 --> 01:01:17,544 +and introducing powerful new features, + +1211 +01:01:17,578 --> 01:01:21,248 +including severe weather notifications, +rich detail views, + +1212 +01:01:21,281 --> 01:01:25,185 +and ten days of hourly temperature +and precipitation forecasts. + +1213 +01:01:25,219 --> 01:01:29,423 +And there are all kinds of +other experiences across Apple devices + +1214 +01:01:29,456 --> 01:01:32,826 +and platforms that get better because +of the weather data we provide– + +1215 +01:01:32,860 --> 01:01:35,462 +from asking Siri for today's forecast, + +1216 +01:01:35,495 --> 01:01:38,398 +to rerouting navigation around flooding. + +1217 +01:01:38,432 --> 01:01:42,202 +All of that is built on +our Apple Weather service. + +1218 +01:01:42,236 --> 01:01:45,539 +Apple Weather delivers +a world-class global weather forecast + +1219 +01:01:45,572 --> 01:01:48,208 +using high-resolution +meteorological models + +1220 +01:01:48,242 --> 01:01:51,078 +combined with machine learning +and prediction algorithms. + +1221 +01:01:51,111 --> 01:01:53,614 +Apple Weather provides current weather, + +1222 +01:01:53,647 --> 01:01:56,683 +10-day hourly forecasts, daily forecasts, + +1223 +01:01:56,717 --> 01:02:00,521 +and historical weather +so you can evaluate trends in data. + +1224 +01:02:00,554 --> 01:02:03,257 +Severe weather alerts +and minute-by-minute precipitation + +1225 +01:02:03,290 --> 01:02:07,027 +are also available +for select countries around the world. + +1226 +01:02:07,060 --> 01:02:10,497 +Forecasts feature 10 days +of hour-by-hour temperature, + +1227 +01:02:10,531 --> 01:02:13,166 +precipitation, UV index forecasts, + +1228 +01:02:13,200 --> 01:02:14,535 +and much more. + +1229 +01:02:15,269 --> 01:02:19,339 +And all of this data is available to you +through WeatherKit. + +1230 +01:02:19,373 --> 01:02:23,377 +WeatherKit is a native Swift API +for all Apple platforms, + +1231 +01:02:23,410 --> 01:02:25,779 +and a REST API you can use from anywhere. + +1232 +01:02:25,812 --> 01:02:29,616 +These APIs deliver accurate, +hyperlocal weather forecasts, + +1233 +01:02:29,650 --> 01:02:33,954 +to help your users stay safe, +informed, and prepared. + +1234 +01:02:33,987 --> 01:02:36,356 +Let me show you how easy it is +to get weather information + +1235 +01:02:36,390 --> 01:02:39,760 +through WeatherKit's great Swift API +in a quick demo. + +1236 +01:02:39,793 --> 01:02:42,029 +Let's revisit our Food Truck app. + +1237 +01:02:42,062 --> 01:02:44,398 +To make sure my customers +don't get caught in the rain, + +1238 +01:02:44,431 --> 01:02:48,135 +my app is set up to recommend +a parking spot with clear skies. + +1239 +01:02:48,168 --> 01:02:50,637 +Let me show you how I can get the weather. + +1240 +01:02:50,671 --> 01:02:53,407 +Here I have a list of safe parking spots. + +1241 +01:02:53,440 --> 01:02:56,276 +I've already added the WeatherKit +capability in Xcode, + +1242 +01:02:56,310 --> 01:02:58,712 +and all it takes is a few lines of code. + +1243 +01:02:58,745 --> 01:03:01,815 +With Swift Concurrency, +requesting weather is simple. + +1244 +01:03:01,849 --> 01:03:06,186 +We call weather(for:) on WeatherService, +and pass in a location. + +1245 +01:03:06,220 --> 01:03:11,558 +Then I can get the relevant data +I need for my app, like condition, + +1246 +01:03:11,592 --> 01:03:14,494 +precipitation, + +1247 +01:03:14,528 --> 01:03:16,363 +and cloud cover. + +1248 +01:03:18,098 --> 01:03:21,235 +Now that I have the data I need +for each of my parking spots, + +1249 +01:03:21,268 --> 01:03:22,536 +when I run my app, + +1250 +01:03:22,569 --> 01:03:26,907 +my custom view has updated to +recommend a location with clear skies. + +1251 +01:03:28,675 --> 01:03:30,944 +There are all kinds of ways +you can use weather data + +1252 +01:03:30,978 --> 01:03:33,881 +to make the experiences +in your apps better. + +1253 +01:03:33,914 --> 01:03:36,783 +You might use weather forecasts +to help you with inventory, + +1254 +01:03:36,817 --> 01:03:38,485 +predicting that +your ice-cream filled donuts + +1255 +01:03:38,519 --> 01:03:40,754 +are going to be a popular order +on a hot day, + +1256 +01:03:40,787 --> 01:03:43,290 +so you should stock up on ice cream. + +1257 +01:03:43,323 --> 01:03:45,459 +That's all it takes +to get the weather for our food truck, + +1258 +01:03:45,492 --> 01:03:48,762 +and there's so much more +to uncover with WeatherKit. + +1259 +01:03:48,795 --> 01:03:51,131 +In keeping with Apple's commitment +to privacy, + +1260 +01:03:51,164 --> 01:03:54,368 +location is used +only to provide weather forecasts, + +1261 +01:03:54,401 --> 01:03:57,671 +is not associated with +any personally identifying information, + +1262 +01:03:57,704 --> 01:04:00,541 +and is never shared or sold. + +1263 +01:04:00,574 --> 01:04:03,677 +Privacy is a shared responsibility, +and through WeatherKit, + +1264 +01:04:03,710 --> 01:04:08,248 +you can get accurate weather data +while protecting user privacy. + +1265 +01:04:08,282 --> 01:04:11,418 +Because we want to make it easy for you +to get started with WeatherKit, + +1266 +01:04:11,451 --> 01:04:15,889 +we're including 500,000 +weather(for:location) API calls per month + +1267 +01:04:15,923 --> 01:04:18,525 +in your Apple Developer Program +membership. + +1268 +01:04:18,559 --> 01:04:22,262 +Those of you who need more will be able +to purchase additional tiers of service + +1269 +01:04:22,296 --> 01:04:24,831 +right in the developer app, +starting this fall. + +1270 +01:04:24,865 --> 01:04:28,969 +So that's WeatherKit, +accurate, hyperlocal weather forecasts + +1271 +01:04:29,002 --> 01:04:31,071 +powered by the Apple Weather service. + +1272 +01:04:31,104 --> 01:04:35,809 +We're starting with a beta, +and it's available now on all platforms. + +1273 +01:04:35,843 --> 01:04:40,080 +There are so many creative ways +that you can use WeatherKit in your apps. + +1274 +01:04:40,113 --> 01:04:42,216 +And now here's Ryan +to give us some perspective + +1275 +01:04:42,249 --> 01:04:44,518 +on what your apps can see with Live Text. + +1276 +01:04:44,551 --> 01:04:47,221 +Ryan Dixon: +Our users are loving Live Text, + +1277 +01:04:47,254 --> 01:04:48,689 +and we have heard from many of you + +1278 +01:04:48,722 --> 01:04:50,924 +that you want to bring it +to your apps too. + +1279 +01:04:50,958 --> 01:04:53,293 +So this year, +we are expanding VisionKit + +1280 +01:04:53,327 --> 01:04:56,496 +with two new APIs +that will allow you to do just that. + +1281 +01:04:57,798 --> 01:05:01,869 +The Live Text API unlocks the ability +to analyze image content, + +1282 +01:05:01,902 --> 01:05:04,538 +allowing users to interact with text +and QR codes + +1283 +01:05:04,571 --> 01:05:07,541 +found in photos and paused video frames, + +1284 +01:05:07,574 --> 01:05:08,942 +and it provides quick actions + +1285 +01:05:08,976 --> 01:05:11,011 +so your users are just a tap away + +1286 +01:05:11,044 --> 01:05:13,547 +from taking action on relevant data. + +1287 +01:05:13,580 --> 01:05:15,916 +It's great for any app +that displays visual media, + +1288 +01:05:15,949 --> 01:05:19,553 +like Apollo for Reddit or Vimeo. + +1289 +01:05:19,586 --> 01:05:23,924 +And the Data Scanner API unlocks +the ability to analyze a live camera feed. + +1290 +01:05:23,957 --> 01:05:27,861 +It dramatically simplifies text +and barcode ingestion for users. + +1291 +01:05:27,895 --> 01:05:31,498 +All you need to do +is add any overlays or custom controls + +1292 +01:05:31,532 --> 01:05:35,335 +that tailor the live camera experience +to the needs of your app. + +1293 +01:05:35,369 --> 01:05:39,306 +This is especially useful for +consumer apps that rely on QR codes + +1294 +01:05:39,339 --> 01:05:42,709 +or enterprise apps built for +back-of-warehouse inventory management, + +1295 +01:05:42,743 --> 01:05:44,378 +pick-and pack delivery services, + +1296 +01:05:44,411 --> 01:05:45,846 +and point of sale kiosks. + +1297 +01:05:46,713 --> 01:05:49,149 +Both the Live Text and Data Scanner APIs + +1298 +01:05:49,183 --> 01:05:51,818 +support automatic detection +of nine languages, + +1299 +01:05:51,852 --> 01:05:54,821 +including this year's additions +of Japanese and Korean. + +1300 +01:05:55,822 --> 01:05:59,459 +These VisionKit APIs will bring years +of computer vision innovation + +1301 +01:05:59,493 --> 01:06:02,563 +to your app with just a few lines of code. + +1302 +01:06:02,596 --> 01:06:04,598 +Here's Jenny to show you how. + +1303 +01:06:04,631 --> 01:06:08,101 +Jenny Chen: To show you a demo, we're back +to our trusty pop-up Food Truck app. + +1304 +01:06:08,135 --> 01:06:11,638 +We're doing a promotion where if +users post a picture to a social channel + +1305 +01:06:11,672 --> 01:06:15,375 +holding a sign up with the hashtag +#freedonut and their address on the app, + +1306 +01:06:15,409 --> 01:06:18,378 +we'll drive to their address +and deliver a free donut to them. + +1307 +01:06:18,412 --> 01:06:20,314 +We'll head to our social donut feed. + +1308 +01:06:20,347 --> 01:06:23,584 +We want to add Live Text to the images +so that drivers can extract the text + +1309 +01:06:23,617 --> 01:06:25,819 +to get the addresses for delivery. + +1310 +01:06:26,620 --> 01:06:29,389 +Here's where the new Live Text APIs +play in. + +1311 +01:06:29,423 --> 01:06:32,492 +I can easily add an ImageInteraction +on top of my view, + +1312 +01:06:32,526 --> 01:06:36,063 +and that will add the Live Text button +with quick action support to it. + +1313 +01:06:36,096 --> 01:06:39,299 +While the Live Text button +normally sits on the bottom right, + +1314 +01:06:39,333 --> 01:06:41,568 +I already have a heart button in my app, + +1315 +01:06:41,602 --> 01:06:44,404 +so I can adjust the placement +using custom insets. + +1316 +01:06:44,438 --> 01:06:48,442 +I can also set the button configuration +to customize the style of the button + +1317 +01:06:48,475 --> 01:06:50,744 +so that it matches my app better. + +1318 +01:06:50,777 --> 01:06:55,015 +Now that I've added it in, +I can tap the Live Text button, + +1319 +01:06:55,048 --> 01:06:56,483 +select the text, + +1320 +01:06:56,517 --> 01:06:59,086 +or use quick actions +to easily grab the address. + +1321 +01:07:00,654 --> 01:07:03,824 +I love that users don't have to learn +a new interaction model, + +1322 +01:07:03,857 --> 01:07:08,462 +as it provides the same ease of use +with the Live Text experience. + +1323 +01:07:08,495 --> 01:07:10,597 +The UI is consistent and familiar. + +1324 +01:07:10,631 --> 01:07:12,666 +It feels integrated with the OS, + +1325 +01:07:12,699 --> 01:07:16,670 +but I can still adjust the placement +even when I have my own custom UI. + +1326 +01:07:17,404 --> 01:07:19,439 +Of course, like any good delivery app, + +1327 +01:07:19,473 --> 01:07:22,643 +we also want to provide +the best service to our customers + +1328 +01:07:22,676 --> 01:07:25,646 +and make sure +we're giving people the right donuts. + +1329 +01:07:25,679 --> 01:07:29,149 +So we track our donut orders via QR code. + +1330 +01:07:29,183 --> 01:07:33,587 +Using the new Data Scanner APIs, +I can easily add that as the first step + +1331 +01:07:33,620 --> 01:07:36,089 +of any customer interaction. + +1332 +01:07:36,123 --> 01:07:38,192 +Right now, +that button doesn't do anything, + +1333 +01:07:38,225 --> 01:07:41,261 +but I can easily instantiate +a new DataScanner object + +1334 +01:07:41,295 --> 01:07:44,398 +that looks for text, QR codes, or barcodes + +1335 +01:07:44,431 --> 01:07:46,633 +that I can then import into my app. + +1336 +01:07:46,667 --> 01:07:49,336 +With just a few lines of code, +I can bring up the camera, + +1337 +01:07:49,369 --> 01:07:53,540 +specify that I want QR codes, +and startScanning! + +1338 +01:07:53,574 --> 01:07:55,709 +When the driver taps on the QR code, + +1339 +01:07:55,742 --> 01:07:58,178 +I want to show that the scan is a success. + +1340 +01:07:58,212 --> 01:08:00,314 +I'll add in the delegate handler +in Xcode... + +1341 +01:08:03,684 --> 01:08:06,019 +...and on tap, show an alert to the user + +1342 +01:08:06,053 --> 01:08:07,521 +that the scan was a success + +1343 +01:08:07,554 --> 01:08:10,424 +so that I can go ahead +and get the donut order started. + +1344 +01:08:10,457 --> 01:08:14,394 +Now when I run the app, +this brings up a view controller + +1345 +01:08:14,428 --> 01:08:17,097 +with the camera view, +and I can see the guidance + +1346 +01:08:17,130 --> 01:08:20,367 +and the reticle view +highlighting the QR code. + +1347 +01:08:20,400 --> 01:08:23,737 +When I tap on the QR code, +I can see my scan was a success + +1348 +01:08:23,770 --> 01:08:26,139 +and the donut order is confirmed. + +1349 +01:08:26,173 --> 01:08:30,611 +And with that, +my #freedonut delivery is on its way. + +1350 +01:08:30,644 --> 01:08:34,615 +With VisionKit, +the new Live Text and Data Scanner APIs + +1351 +01:08:34,648 --> 01:08:39,086 +easily allow you to bring these +powerful vision capabilities to your app. + +1352 +01:08:39,119 --> 01:08:41,455 +And now, back to Susan. + +1353 +01:08:41,488 --> 01:08:44,892 +Susan: This is an exciting time +to be building apps. + +1354 +01:08:44,925 --> 01:08:49,396 +Xcode Cloud is now ready +to help you build better apps faster. + +1355 +01:08:49,429 --> 01:08:52,432 +With Swift and SwiftUI, +it's easier than ever + +1356 +01:08:52,466 --> 01:08:57,738 +to transform your ideas into apps +that work across Apple platforms. + +1357 +01:08:57,771 --> 01:09:00,474 +There are cool new ways for your apps +to bring your ideas + +1358 +01:09:00,507 --> 01:09:02,943 +deeper into the system experience. + +1359 +01:09:02,976 --> 01:09:05,045 +Lock Screen widgets and Live Activities + +1360 +01:09:05,078 --> 01:09:07,648 +bring your app to the Lock Screen. + +1361 +01:09:07,681 --> 01:09:10,417 +Messages Collaboration +makes it incredibly easy + +1362 +01:09:10,450 --> 01:09:13,520 +for your users to connect and collaborate. + +1363 +01:09:13,554 --> 01:09:17,958 +And App Intents +help integrate your app with Siri. + +1364 +01:09:17,991 --> 01:09:19,993 +There are entirely new APIs + +1365 +01:09:20,027 --> 01:09:22,529 +and major updates to existing APIs, + +1366 +01:09:22,563 --> 01:09:27,167 +like WeatherKit, MapKit, +Live Text, and Metal. + +1367 +01:09:27,201 --> 01:09:29,036 +And that's not the end of the story. + +1368 +01:09:29,069 --> 01:09:32,239 +It's another big WWDC this year, + +1369 +01:09:32,272 --> 01:09:34,408 +with 175 sessions, + +1370 +01:09:34,441 --> 01:09:39,179 +hundreds of labs, and Digital Lounge +activities running all week. + +1371 +01:09:39,213 --> 01:09:41,481 +We can't wait to connect with you +this week, + +1372 +01:09:41,515 --> 01:09:44,685 +and more importantly, +this week is for you. + +1373 +01:09:44,718 --> 01:09:47,588 +We're eager to see what you create next. + +1374 +01:09:47,621 --> 01:09:49,256 +Thank you! + +1375 +01:09:49,289 --> 01:09:57,297 +♪ ♪ + +1376 +01:10:06,340 --> 01:10:14,348 +. + diff --git a/eng/2022 Session 103 Apple Design Awards en.srt b/eng/2022 Session 103 Apple Design Awards en.srt new file mode 100644 index 0000000..50c3061 --- /dev/null +++ b/eng/2022 Session 103 Apple Design Awards en.srt @@ -0,0 +1,1840 @@ +1 +00:00:00,000 --> 00:00:04,238 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:04,238 --> 00:00:09,510 +♪ + +3 +00:00:09,510 --> 00:00:13,947 +♪ Sparse upbeat +electronic music ♪ + +4 +00:00:13,947 --> 00:00:23,624 +♪ + +5 +00:00:23,624 --> 00:00:28,629 +- Design, for me, is like a way +to express myself, + +6 +00:00:28,629 --> 00:00:31,465 +just like writing a song +or write a book, + +7 +00:00:31,465 --> 00:00:33,734 +but I don't know music, + +8 +00:00:33,734 --> 00:00:36,670 +and I can't write +good languages. + +9 +00:00:36,670 --> 00:00:39,640 +But I can code. + +10 +00:00:39,640 --> 00:00:42,109 +- Design is like an opportunity + +11 +00:00:42,109 --> 00:00:46,880 +where you can create things +that are not only beautiful, + +12 +00:00:46,880 --> 00:00:49,850 +but that also serve a purpose. + +13 +00:00:49,850 --> 00:00:52,152 +- Design is in +every aspect of life. + +14 +00:00:52,152 --> 00:00:55,689 +- Design influences +people's lives on all levels. + +15 +00:00:55,689 --> 00:00:58,492 +- Design can be +the difference between + +16 +00:00:58,492 --> 00:01:01,461 +having a great experience +or having a bad one. + +17 +00:01:01,461 --> 00:01:05,065 +- If no design, +there will be no surprise. + +18 +00:01:05,065 --> 00:01:08,235 +- It's not just how things look, +it's how things work. + +19 +00:01:08,235 --> 00:01:13,240 +- It's one of the most effective +forms of storytelling. + +20 +00:01:13,240 --> 00:01:15,542 +- To design something, +I think we often have to kind of + +21 +00:01:15,542 --> 00:01:17,811 +reduce it down +to a simplified version of it. + +22 +00:01:17,811 --> 00:01:20,047 +- It's like, yeah, +climbing a mountain, + +23 +00:01:20,047 --> 00:01:21,048 +step by step. + +24 +00:01:21,048 --> 00:01:23,984 +- You get to start with, like, +a blank piece of paper + +25 +00:01:23,984 --> 00:01:26,486 +and then you get to turn it +into something + +26 +00:01:26,486 --> 00:01:27,588 +hopefully really cool. + +27 +00:01:27,588 --> 00:01:29,856 +- When you actually make it work +and then you show it to someone, + +28 +00:01:29,856 --> 00:01:32,259 +and they actually can use it +in a simple way, + +29 +00:01:32,259 --> 00:01:34,695 +when it used to be +this really complex thing, + +30 +00:01:34,695 --> 00:01:36,463 +I think that's just +really cool to do. + +31 +00:01:36,463 --> 00:01:39,733 +- When we first met +on our first date, + +32 +00:01:39,733 --> 00:01:43,236 +he said that he's a UX designer. + +33 +00:01:43,236 --> 00:01:46,673 +And I thought, +will I be stupid if I say, + +34 +00:01:46,673 --> 00:01:48,375 +"What does it mean?" +[LAUGHS] + +35 +00:01:48,375 --> 00:01:50,177 +♪ + +36 +00:01:50,177 --> 00:01:52,512 +- Real design +is far more functional. + +37 +00:01:52,512 --> 00:01:53,513 +- Delightful. + +38 +00:01:53,513 --> 00:01:54,548 +- Cute. + +39 +00:01:54,548 --> 00:01:55,882 +- Soothing. +- It's cozy. + +40 +00:01:55,882 --> 00:01:57,551 +- Powerful. +- Intriguing. + +41 +00:01:57,551 --> 00:01:59,186 +- Flashy. +- Tactile. + +42 +00:01:59,186 --> 00:02:00,287 +- Simple and joyful. + +43 +00:02:00,287 --> 00:02:01,088 +- Playful. + +44 +00:02:01,088 --> 00:02:02,155 +- And pretty! + +45 +00:02:02,155 --> 00:02:05,592 +♪ + +46 +00:02:05,592 --> 00:02:07,828 +- Let me think, +do I love design? + +47 +00:02:07,828 --> 00:02:09,796 +[LAUGHS] + +48 +00:02:09,796 --> 00:02:11,631 +- Sometimes I hate it. + +49 +00:02:11,631 --> 00:02:15,469 +But yeah, it will not last +for a long time. + +50 +00:02:15,469 --> 00:02:18,138 +So basically, +I think I love the design. + +51 +00:02:18,138 --> 00:02:20,807 +- In some ways, +I can't escape designing. + +52 +00:02:20,807 --> 00:02:23,977 +- I can't imagine a life +without designing stuff. + +53 +00:02:23,977 --> 00:02:25,879 +- How could anyone hate design? + +54 +00:02:25,879 --> 00:02:27,714 +- It's kind of like magic. + +55 +00:02:27,714 --> 00:02:29,082 +- I couldn't do anything else. + +56 +00:02:29,082 --> 00:02:33,720 +- I know my user can feel +how I'm thinking in my app + +57 +00:02:33,720 --> 00:02:34,955 +through my design. + +58 +00:02:34,955 --> 00:02:38,925 +- I think design +really does change + +59 +00:02:38,925 --> 00:02:41,695 +the way we approach life itself. + +60 +00:02:41,695 --> 00:02:45,332 +♪ Whimsical music ♪ + +61 +00:02:45,332 --> 00:02:50,904 +- I mainly get inspiration +from walking in nature. + +62 +00:02:50,904 --> 00:02:54,074 +Every time I do that, + +63 +00:02:54,074 --> 00:02:56,043 +I have ideas +that come to my mind. + +64 +00:02:56,043 --> 00:02:57,711 +- The inspiration could be +from anywhere. + +65 +00:02:57,711 --> 00:03:01,248 +It doesn't need to be +in the museum or gallery. + +66 +00:03:01,248 --> 00:03:03,884 +It's in the nature. +It's in the city. + +67 +00:03:03,884 --> 00:03:04,851 +- When it's nighttime, + +68 +00:03:04,851 --> 00:03:06,620 +when I know +everybody's sleeping, + +69 +00:03:06,620 --> 00:03:09,523 +when I know the world +is at rest, + +70 +00:03:09,523 --> 00:03:14,194 +it kind of makes it easier +for me to get into the zone. + +71 +00:03:14,194 --> 00:03:17,164 +- If I just spend time away +from technology, + +72 +00:03:17,164 --> 00:03:20,167 +away from my computer, +away from my phone, + +73 +00:03:20,167 --> 00:03:23,070 +that's the time when I can +really do deep thought. + +74 +00:03:23,070 --> 00:03:27,474 +- I just want to make everything +we cannot see in the real life. + +75 +00:03:27,474 --> 00:03:29,810 +- I was living in Bangkok. +There's a lot of cars. + +76 +00:03:29,810 --> 00:03:31,078 +There's a lot of traffic. + +77 +00:03:31,078 --> 00:03:34,147 +So also creating the game +pretty much designed + +78 +00:03:34,147 --> 00:03:37,017 +a way for you +to escape a little bit. + +79 +00:03:37,017 --> 00:03:39,619 +- Biggest challenges +for designing things + +80 +00:03:39,619 --> 00:03:43,623 +is that your assumptions +are usually wrong. [LAUGHS] + +81 +00:03:43,623 --> 00:03:46,226 +- I kind of had the idea +to focus on witchcraft + +82 +00:03:46,226 --> 00:03:49,162 +when the election was happening +in the United Kingdom + +83 +00:03:49,162 --> 00:03:52,132 +and there was a group +of individuals + +84 +00:03:52,132 --> 00:03:54,901 +who had decided they were +going to hex the government. + +85 +00:03:54,901 --> 00:03:56,503 +I think they were pagans. + +86 +00:03:56,503 --> 00:04:00,874 +♪ Piano lounge music ♪ + +87 +00:04:00,874 --> 00:04:03,410 +- What's my biggest failure? +Just in general? + +88 +00:04:03,410 --> 00:04:04,978 +- Well, that's actually +a great question. + +89 +00:04:04,978 --> 00:04:06,713 +- Oh, greatest failure? +There's a lot of them. + +90 +00:04:06,713 --> 00:04:07,414 +OK, hold on. + +91 +00:04:07,414 --> 00:04:10,016 +- As a person +or in my career life? + +92 +00:04:10,016 --> 00:04:11,384 +- Uh... + +93 +00:04:11,384 --> 00:04:14,054 +- Let me -- +I want to think and really... + +94 +00:04:14,054 --> 00:04:17,524 +- It's kind of hard to answer +without making somebody mad. + +95 +00:04:17,524 --> 00:04:19,059 +- Everything. +[LAUGHS] + +96 +00:04:19,059 --> 00:04:22,662 +- Failing is part of +any creation process. + +97 +00:04:22,662 --> 00:04:26,533 +- We spent, like, months +and months trying to iterate + +98 +00:04:26,533 --> 00:04:28,568 +and to do something great. + +99 +00:04:28,568 --> 00:04:31,171 +- Our biggest fail in the game +was actually at launch + +100 +00:04:31,171 --> 00:04:34,841 +that we didn't have +enough levels in the game. + +101 +00:04:34,841 --> 00:04:37,878 +- We had gotten rid +of the main feature of the app. + +102 +00:04:37,878 --> 00:04:40,180 +You know, overnight we lost, + +103 +00:04:40,180 --> 00:04:42,182 +I think, 20 percent +of our users. + +104 +00:04:42,182 --> 00:04:44,818 +Interesting, you know, +experience. + +105 +00:04:44,818 --> 00:04:47,220 +- We got some good feedback +from some customers, + +106 +00:04:47,220 --> 00:04:50,557 +and none of us really wanted +to admit that it kind of sucked. + +107 +00:04:50,557 --> 00:04:53,059 +- We're always happy +that we did change it. + +108 +00:04:53,059 --> 00:04:55,595 +- My adage was always, +if I came up with 50 ideas + +109 +00:04:55,595 --> 00:04:58,298 +in a day and 49 +were absolutely useless + +110 +00:04:58,298 --> 00:05:01,234 +and one was really good, +that's a very good day. + +111 +00:05:01,234 --> 00:05:04,104 +- I think it was mostly +just staying too long + +112 +00:05:04,104 --> 00:05:07,674 +in situations that were +not where I wanted to be. + +113 +00:05:07,674 --> 00:05:10,010 +Management, economics, +and law seemed like + +114 +00:05:10,010 --> 00:05:12,045 +this super logical way to go. + +115 +00:05:12,045 --> 00:05:16,149 +But as soon as basically the +iPhone was announced in 2007, + +116 +00:05:16,149 --> 00:05:17,984 +that was all I was +just thinking about. + +117 +00:05:17,984 --> 00:05:19,819 +I think in hindsight, +my biggest failure + +118 +00:05:19,819 --> 00:05:21,288 +was actually a really good thing + +119 +00:05:21,288 --> 00:05:24,090 +because it led me +to where I am right now. + +120 +00:05:24,090 --> 00:05:26,893 +♪ + +121 +00:05:26,893 --> 00:05:29,062 +Earlier last year, +I was working on an app + +122 +00:05:29,062 --> 00:05:32,799 +that helped people who are mute +to speak on the phone. + +123 +00:05:32,799 --> 00:05:34,568 +And then when I had made that, + +124 +00:05:34,568 --> 00:05:36,269 +I actually wanted +to make something similar + +125 +00:05:36,269 --> 00:05:38,905 +but the other way around, +so that if you're deaf, + +126 +00:05:38,905 --> 00:05:40,173 +and you're listening +to a phone call + +127 +00:05:40,173 --> 00:05:42,776 +that you can actually see +what's being said. + +128 +00:05:42,776 --> 00:05:46,213 +- It started for me just simply +trying to solve my own problem, + +129 +00:05:46,213 --> 00:05:47,948 +which is not drinking +enough water. + +130 +00:05:47,948 --> 00:05:49,916 +I could sit for hours working + +131 +00:05:49,916 --> 00:05:52,919 +until I feel like +I'm in the middle of the desert. + +132 +00:05:52,919 --> 00:05:57,357 +- Drinking water, it becomes +an exciting moment in your day. + +133 +00:05:57,357 --> 00:05:59,826 +- My kids will just -- +they'll do something + +134 +00:05:59,826 --> 00:06:02,662 +as long as it's on a list and +they can strike it off later. + +135 +00:06:02,662 --> 00:06:06,533 +And so we wanted that moment +when you strike off that habit + +136 +00:06:06,533 --> 00:06:10,237 +for the day to really +just feel gratifying. + +137 +00:06:10,237 --> 00:06:13,873 +- I was walking around and saw +a broken neon sign + +138 +00:06:13,873 --> 00:06:16,209 +and that sparked the idea. + +139 +00:06:16,209 --> 00:06:18,812 +- We want to make +the best clock app in the world. + +140 +00:06:18,812 --> 00:06:22,949 +So to do that, +we visit many furniture shops + +141 +00:06:22,949 --> 00:06:28,054 +to see how the clock feels like +when you hold it in your hand. + +142 +00:06:28,054 --> 00:06:31,057 +- In the very first beginning, +we wanted to help ourselves. + +143 +00:06:31,057 --> 00:06:33,226 +Because we were in a meeting, + +144 +00:06:33,226 --> 00:06:35,895 +and we can't even +take proper notes + +145 +00:06:35,895 --> 00:06:37,330 +and at the same time, + +146 +00:06:37,330 --> 00:06:40,734 +be really engaged in +the meeting; talking to people. + +147 +00:06:40,734 --> 00:06:43,036 +So we want to do this app +to help ourselves. + +148 +00:06:43,036 --> 00:06:47,007 +- After really diving deep into +what inclusivity means, + +149 +00:06:47,007 --> 00:06:49,843 +it really changes +the way we build. + +150 +00:06:49,843 --> 00:06:52,979 +- I was riding up a gondola +and this family -- + +151 +00:06:52,979 --> 00:06:56,049 +they were pointing out that +they really wanted to be able + +152 +00:06:56,049 --> 00:06:58,885 +to compare how they were doing +versus last year. + +153 +00:06:58,885 --> 00:07:00,453 +So I went home and wrote it, + +154 +00:07:00,453 --> 00:07:02,922 +and a week later +it was in the App Store. + +155 +00:07:02,922 --> 00:07:05,525 +- Don't pick up the phone +when you're focused + +156 +00:07:05,525 --> 00:07:10,030 +and don't open the lid off +the pot when cooking noodles. + +157 +00:07:10,030 --> 00:07:13,266 +These concepts run +through the Focus Noodles app. + +158 +00:07:13,266 --> 00:07:16,069 +A user can get +into the headspace + +159 +00:07:16,069 --> 00:07:20,006 +of covering their phones +and start to focus. + +160 +00:07:20,006 --> 00:07:21,574 +- If you love something, + +161 +00:07:21,574 --> 00:07:23,944 +you always find +a way to learn better. + +162 +00:07:23,944 --> 00:07:26,913 +And I want people +to love learning Chinese, + +163 +00:07:26,913 --> 00:07:29,549 +and I want to make it +more playful, more fun, + +164 +00:07:29,549 --> 00:07:31,484 +and people will enjoy learning. + +165 +00:07:31,484 --> 00:07:32,819 +- People see this as a game. + +166 +00:07:32,819 --> 00:07:34,187 +We can be +a lot more intentional + +167 +00:07:34,187 --> 00:07:38,258 +to create a game using our +technology to track your body. + +168 +00:07:38,258 --> 00:07:40,760 +Get you to move +your arms, move your body, + +169 +00:07:40,760 --> 00:07:42,462 +and basically get you +into the rhythm + +170 +00:07:42,462 --> 00:07:44,497 +and usually the game +is always like this. + +171 +00:07:44,497 --> 00:07:46,032 +That's where we took +the inspiration + +172 +00:07:46,032 --> 00:07:48,034 +to create Active Arcade. + +173 +00:07:48,034 --> 00:07:49,235 +Just like choreography; + +174 +00:07:49,235 --> 00:07:51,037 +just like teaching you +how to dance. + +175 +00:07:51,037 --> 00:07:53,707 +- We were in the middle +of quite a long project. + +176 +00:07:53,707 --> 00:07:55,542 +We realized it would be +really good fun + +177 +00:07:55,542 --> 00:07:57,978 +to just do a really fast project + +178 +00:07:57,978 --> 00:08:00,647 +and we could never +lose motivation for it. + +179 +00:08:00,647 --> 00:08:04,117 +- So the concept of the game +is you've murdered your husband, + +180 +00:08:04,117 --> 00:08:08,421 +and your goal is to make sure +other people don't find out. + +181 +00:08:08,421 --> 00:08:09,222 +[LAUGHS] + +182 +00:08:09,222 --> 00:08:11,725 +- The character +of Rebel Girls app + +183 +00:08:11,725 --> 00:08:17,163 +is like your feminist aunt +who is very kind + +184 +00:08:17,163 --> 00:08:19,065 +and who's full of stories. + +185 +00:08:19,065 --> 00:08:23,737 +And her goal is to make sure +that some of the best values + +186 +00:08:23,737 --> 00:08:25,138 +are instilled in you. + +187 +00:08:25,138 --> 00:08:27,007 +Empowering girls to become + +188 +00:08:27,007 --> 00:08:30,877 +the most confident +generation of girls. + +189 +00:08:30,877 --> 00:08:34,481 +- It's an app that is really +the friend you wish you had + +190 +00:08:34,481 --> 00:08:35,849 +when you lose a loved one. + +191 +00:08:35,849 --> 00:08:37,283 +It's the smart checklist + +192 +00:08:37,283 --> 00:08:39,252 +to telling you all the things +you need to do, + +193 +00:08:39,252 --> 00:08:40,253 +but at the same time, + +194 +00:08:40,253 --> 00:08:42,022 +only the things +you need to do right now + +195 +00:08:42,022 --> 00:08:45,625 +and what can wait, but also +allowing us to do it for you. + +196 +00:08:45,625 --> 00:08:46,726 +- For Behind the Frame, + +197 +00:08:46,726 --> 00:08:49,696 +it's basically +an escape room game. + +198 +00:08:49,696 --> 00:08:54,834 +Has that Ghibli kind of feel +to feel like playable anime. + +199 +00:08:54,834 --> 00:08:58,171 +We look at +the color scheme of Ghibli + +200 +00:08:58,171 --> 00:09:00,440 +and also Renaissance paintings. + +201 +00:09:00,440 --> 00:09:05,545 +- The idea behind Townscaper is +you just build beautiful houses. + +202 +00:09:05,545 --> 00:09:07,213 +That's all there is to it. + +203 +00:09:07,213 --> 00:09:09,115 +- The whole concept +is that you're finally + +204 +00:09:09,115 --> 00:09:11,084 +able to touch the artwork, + +205 +00:09:11,084 --> 00:09:14,020 +something that you're not +able to do in real life. + +206 +00:09:14,020 --> 00:09:16,689 +♪ + +207 +00:09:16,689 --> 00:09:19,192 +- One of the biggest challenges +of designing tools + +208 +00:09:19,192 --> 00:09:21,761 +for creative people is that +a lot of the tasks + +209 +00:09:21,761 --> 00:09:24,197 +that artists want to create, +they're quite complex tasks. + +210 +00:09:24,197 --> 00:09:26,866 +So the challenge is +how do we take all that power + +211 +00:09:26,866 --> 00:09:29,836 +and make it really +usable and simple? + +212 +00:09:29,836 --> 00:09:33,072 +- It has to be easy for someone +who's jumping straight in + +213 +00:09:33,072 --> 00:09:35,041 +and hasn't really done +this kind of work before, + +214 +00:09:35,041 --> 00:09:38,478 +but it also has to be effortless +for professionals as well. + +215 +00:09:38,478 --> 00:09:41,614 +Making something simple +is quite complex. + +216 +00:09:41,614 --> 00:09:45,185 +- There are so many tiny details + +217 +00:09:45,185 --> 00:09:48,688 +that go into creating +an experience. + +218 +00:09:48,688 --> 00:09:51,324 +There was no way for it not +to feel really clunky. + +219 +00:09:51,324 --> 00:09:53,460 +The next four and a half years +of development + +220 +00:09:53,460 --> 00:09:56,062 +was a lot of kind of +stripping those things away. + +221 +00:09:56,062 --> 00:09:59,399 +And how do we get rid of the +things that might otherwise + +222 +00:09:59,399 --> 00:10:01,768 +distract people so that +they can actually focus + +223 +00:10:01,768 --> 00:10:05,171 +on this moment +that we're trying to create? + +224 +00:10:05,171 --> 00:10:06,873 +- It was incredibly hard. + +225 +00:10:06,873 --> 00:10:08,842 +Graphic design, +as we know it, + +226 +00:10:08,842 --> 00:10:11,411 +is kind of coming from +the desktop-world era, + +227 +00:10:11,411 --> 00:10:15,615 +and didn't really have a lot of +innovation in the last decades. + +228 +00:10:15,615 --> 00:10:18,785 +- I'd approached it similarly +to designing a building -- + +229 +00:10:18,785 --> 00:10:21,554 +both you need +to navigate through. + +230 +00:10:21,554 --> 00:10:22,922 +With a website, +or even with an app, + +231 +00:10:22,922 --> 00:10:24,491 +you have different views. + +232 +00:10:24,491 --> 00:10:25,425 +You have an entrance. + +233 +00:10:25,425 --> 00:10:26,860 +At a certain point +you have an exit; + +234 +00:10:26,860 --> 00:10:28,361 +you have different rooms, + +235 +00:10:28,361 --> 00:10:30,430 +and the experience +needs to be good. + +236 +00:10:30,430 --> 00:10:33,900 +- My watercolor skills +are horrible. + +237 +00:10:33,900 --> 00:10:37,770 +My code development +is at the HTML levels. + +238 +00:10:37,770 --> 00:10:40,874 +So I also need to convince other +people, through my journey, + +239 +00:10:40,874 --> 00:10:42,876 +to build these things with me. + +240 +00:10:42,876 --> 00:10:47,113 +When we put it out, there was no +other watercolor game like it + +241 +00:10:47,113 --> 00:10:48,681 +or even watercolor app. + +242 +00:10:48,681 --> 00:10:50,083 +So I really worked heavily + +243 +00:10:50,083 --> 00:10:51,784 +with getting in +all the small details, + +244 +00:10:51,784 --> 00:10:53,286 +getting the water +to flow correctly + +245 +00:10:53,286 --> 00:10:56,289 +and getting it to just feel +really nice. + +246 +00:10:56,289 --> 00:10:58,458 +- Pushing the limits +has never been the problem. + +247 +00:10:58,458 --> 00:11:00,693 +The whole team loves to do that, + +248 +00:11:00,693 --> 00:11:02,695 +loves to see how far +they can take a character + +249 +00:11:02,695 --> 00:11:05,798 +or how much story +we can get into this scene. + +250 +00:11:05,798 --> 00:11:07,233 +The challenge +is to keep limits on that + +251 +00:11:07,233 --> 00:11:08,835 +so we can actually get it done. + +252 +00:11:08,835 --> 00:11:11,638 +- If you can create something +that people cannot see + +253 +00:11:11,638 --> 00:11:14,807 +in the real life, then sometimes +they will feel "wow" + +254 +00:11:14,807 --> 00:11:18,478 +at that moment +and we will feel so satisfied. + +255 +00:11:18,478 --> 00:11:19,779 +[LAUGHS] + +256 +00:11:19,779 --> 00:11:23,550 +Yeah, so this is why +we choose optical illusions. + +257 +00:11:23,550 --> 00:11:26,319 +For most people, when you see +some optical illusion, + +258 +00:11:26,319 --> 00:11:27,787 +you are just wowed. + +259 +00:11:27,787 --> 00:11:30,390 +- It was quite +a tricky problem to solve + +260 +00:11:30,390 --> 00:11:32,659 +of just making sure that +you could be playing on a bus, + +261 +00:11:32,659 --> 00:11:34,794 +but you don't notice +that you're on the bus anymore. + +262 +00:11:34,794 --> 00:11:37,597 +It's all of the little things +that build up the immersion, + +263 +00:11:37,597 --> 00:11:39,065 +not the big, obvious ones. + +264 +00:11:39,065 --> 00:11:42,669 +So the scuttling noises, +steam vents, things like that. + +265 +00:11:42,669 --> 00:11:45,371 +- We had huge geometries +and landscapes + +266 +00:11:45,371 --> 00:11:48,007 +and things that we wanted +to make really quick and custom. + +267 +00:11:48,007 --> 00:11:50,510 +We would just use our tool +that we designed, + +268 +00:11:50,510 --> 00:11:53,446 +and it would automatically +Lego-ify it. + +269 +00:11:53,446 --> 00:11:57,116 +- Our goal was to design +a very focused experience + +270 +00:11:57,116 --> 00:11:59,252 +for touch devices. + +271 +00:11:59,252 --> 00:12:00,920 +The player can interact +with this world + +272 +00:12:00,920 --> 00:12:03,223 +without having to think about. + +273 +00:12:03,223 --> 00:12:05,058 +That might sound simple, + +274 +00:12:05,058 --> 00:12:07,927 +but it's actually one of +the hardest things to pull off. + +275 +00:12:07,927 --> 00:12:12,899 +- Marvel Future Revolution +is an open-world, superhero, + +276 +00:12:12,899 --> 00:12:16,135 +action MMO RPG on mobile. + +277 +00:12:16,135 --> 00:12:19,239 +So it's got all your favorite +Marvel superheroes + +278 +00:12:19,239 --> 00:12:22,976 +saving the world with all +your buddies and friends. + +279 +00:12:22,976 --> 00:12:24,477 +The fact that +you could actually have + +280 +00:12:24,477 --> 00:12:26,879 +the true superhero experience + +281 +00:12:26,879 --> 00:12:29,449 +and you could actually take it +everywhere with you. + +282 +00:12:29,449 --> 00:12:31,851 +- I think that from +the very beginning we knew + +283 +00:12:31,851 --> 00:12:33,653 +we wanted to make a game + +284 +00:12:33,653 --> 00:12:37,457 +that was a mix +between music and story. + +285 +00:12:37,457 --> 00:12:40,727 +I mean, everything is in +the name of the game, you know. + +286 +00:12:40,727 --> 00:12:44,263 +So there's this character, +Gabrielle, who's in a coma. + +287 +00:12:44,263 --> 00:12:47,934 +You have to play music +to unlock memories. + +288 +00:12:47,934 --> 00:12:51,371 +To make this whole game, +it was a gigantic process, + +289 +00:12:51,371 --> 00:12:54,907 +but I mean, +it was a fantastic one. + +290 +00:12:54,907 --> 00:12:56,542 +- I thought, +"Cameras are so delightful; + +291 +00:12:56,542 --> 00:13:00,146 +maybe we can bring, like, +a semblance of that delight + +292 +00:13:00,146 --> 00:13:02,015 +to an iPhone camera." + +293 +00:13:02,015 --> 00:13:04,517 +I get emails every month -- +every week, sometimes -- + +294 +00:13:04,517 --> 00:13:06,386 +of people saying, like, +"I really enjoyed that. + +295 +00:13:06,386 --> 00:13:07,553 +It really helped a lot." + +296 +00:13:07,553 --> 00:13:09,922 +And that's so cool. +[LAUGHS] + +297 +00:13:09,922 --> 00:13:12,992 +- This app that we had created +actually convinced people + +298 +00:13:12,992 --> 00:13:15,228 +to get rid of their vehicle + +299 +00:13:15,228 --> 00:13:17,030 +and made riding +public transit fun. + +300 +00:13:17,030 --> 00:13:18,097 +And from that point on, + +301 +00:13:18,097 --> 00:13:20,333 +I think the motivation +became that transit + +302 +00:13:20,333 --> 00:13:23,236 +can have an impact on +people's transportation choices. + +303 +00:13:23,236 --> 00:13:26,739 +- Mental health was one +of those glowing beacons of need + +304 +00:13:26,739 --> 00:13:31,210 +where the skillset of a designer +and a product thinker + +305 +00:13:31,210 --> 00:13:34,881 +are deeply needed, +directing a lot of our attention + +306 +00:13:34,881 --> 00:13:39,018 +toward traditionally +underrepresented populations + +307 +00:13:39,018 --> 00:13:41,921 +and showing up in a way that, +we like to say, + +308 +00:13:41,921 --> 00:13:46,092 +opens a front door to care. + +309 +00:13:46,092 --> 00:13:49,362 +- Apps and games +have to tackle social change. + +310 +00:13:49,362 --> 00:13:51,764 +- An inclusive game +or app is something + +311 +00:13:51,764 --> 00:13:54,033 +that everyone feels at home. + +312 +00:13:54,033 --> 00:13:55,535 +Everyone can... + +313 +00:13:55,535 --> 00:13:58,337 +- ...have fun, +yet still connect with people. + +314 +00:13:58,337 --> 00:14:01,107 +- That authenticity allows us +not to just have + +315 +00:14:01,107 --> 00:14:04,644 +kind of symbolic diversity, +but actual diversity. + +316 +00:14:04,644 --> 00:14:07,647 +- I think if we want +to see social change, + +317 +00:14:07,647 --> 00:14:10,683 +then we need to exhibit it, +especially in entertainment + +318 +00:14:10,683 --> 00:14:14,287 +because it shows people +where we can go. + +319 +00:14:14,287 --> 00:14:16,122 +- I think I really +started to think, + +320 +00:14:16,122 --> 00:14:18,524 +"Hey, I can now make things +that help people + +321 +00:14:18,524 --> 00:14:21,160 +and solve the world +a little bit." + +322 +00:14:21,160 --> 00:14:23,863 +- I think over the last +two years, especially, + +323 +00:14:23,863 --> 00:14:25,998 +like, we're seeing +where we're going, + +324 +00:14:25,998 --> 00:14:28,267 +where we're going to spend more +and more of our lives digitally + +325 +00:14:28,267 --> 00:14:29,769 +in this world. + +326 +00:14:29,769 --> 00:14:37,810 +And how do we, as people, shape +those everyday experiences? + +327 +00:14:37,810 --> 00:14:40,513 +- A tip I could give +to the young developers + +328 +00:14:40,513 --> 00:14:44,083 +is stick to your original idea. + +329 +00:14:44,083 --> 00:14:45,685 +- If you're only +getting inspiration + +330 +00:14:45,685 --> 00:14:48,187 +from all the other games +that you love, + +331 +00:14:48,187 --> 00:14:50,223 +it's just going to be +another game. + +332 +00:14:50,223 --> 00:14:54,093 +- Try, fail, and try again. +And fail. And try again. + +333 +00:14:54,093 --> 00:14:56,863 +- Make as much as you can. + +334 +00:14:56,863 --> 00:14:58,164 +They don't have to be huge. + +335 +00:14:58,164 --> 00:15:00,566 +They don't have to be exciting. + +336 +00:15:00,566 --> 00:15:02,702 +But the more you make, +the more you learn how to make. + +337 +00:15:02,702 --> 00:15:06,072 +- When to leave it alone +and stop tinkering on it. + +338 +00:15:06,072 --> 00:15:07,673 +- Really figure out +what the game is + +339 +00:15:07,673 --> 00:15:09,842 +before bringing people on. + +340 +00:15:09,842 --> 00:15:11,077 +- Don't get frustrated. + +341 +00:15:11,077 --> 00:15:13,112 +- Let go of some +of your assumptions. + +342 +00:15:13,112 --> 00:15:14,580 +- Trying to decide what +the market wants + +343 +00:15:14,580 --> 00:15:15,915 +doesn't really make sense. + +344 +00:15:15,915 --> 00:15:17,917 +It's like, do something that +you're going to be motivated + +345 +00:15:17,917 --> 00:15:18,785 +to keep working on, + +346 +00:15:18,785 --> 00:15:21,154 +that you actually want +to exist in the world. + +347 +00:15:21,154 --> 00:15:24,290 +- You can steal, +but steal from the best + +348 +00:15:24,290 --> 00:15:28,594 +and always add your own +kind of magic fairy dust. + +349 +00:15:28,594 --> 00:15:29,495 +- Get inspired. + +350 +00:15:29,495 --> 00:15:31,831 +Get inspired by what's out there +and what's adjacent. + +351 +00:15:31,831 --> 00:15:33,299 +And look at what techniques +are in there + +352 +00:15:33,299 --> 00:15:35,802 +and how you can approach it +from a totally fresh start + +353 +00:15:35,802 --> 00:15:40,606 +with the things +and the biases that you have. + +354 +00:15:40,606 --> 00:15:47,480 +- The Apple Design Award, +it is like the Oscar. [LAUGHS] + +355 +00:15:47,480 --> 00:15:51,250 +- I couldn't believe it. +I was like, in tears, basically. + +356 +00:15:51,250 --> 00:15:54,520 +- I didn't know who I can tell. +It's like, oh my God. + +357 +00:15:54,520 --> 00:15:57,990 +I think something -- +the most amazing thing happened + +358 +00:15:57,990 --> 00:16:01,494 +and there was no one in the room +I could share it with. + +359 +00:16:01,494 --> 00:16:04,964 +- I remember seeing +some tears in his eyes. + +360 +00:16:04,964 --> 00:16:07,567 +It means a lot to me as well, + +361 +00:16:07,567 --> 00:16:11,003 +because I was the one +that asked him to quit his job. + +362 +00:16:11,003 --> 00:16:13,539 +- Are they still asleep? +I mean, come on. + +363 +00:16:13,539 --> 00:16:17,844 +It's the Apple Design Awards +happening over here. + +364 +00:16:17,844 --> 00:16:21,447 +- Very first reaction +when I received the email. + +365 +00:16:21,447 --> 00:16:23,616 +OK, it's probably a spam. + +366 +00:16:23,616 --> 00:16:27,420 +Then I was like, hmm, +it's a good spam. + +367 +00:16:27,420 --> 00:16:30,022 +And then I started +talking about it with the team, + +368 +00:16:30,022 --> 00:16:34,493 +and, like, everyone was like, +"What?!" [LAUGHS] + +369 +00:16:34,493 --> 00:16:37,363 +- Well, what would I want to say +to my fellow finalists? + +370 +00:16:37,363 --> 00:16:38,731 +Best of luck. + +371 +00:16:38,731 --> 00:16:41,067 +You've built something +that's amazing, + +372 +00:16:41,067 --> 00:16:43,202 +whether it gets an award or not. + +373 +00:16:43,202 --> 00:16:48,608 +- And now we become the +Apple Design Awards finalists. + +374 +00:16:48,608 --> 00:16:53,045 +It's like every day I'm in +the highest point in my life. + +375 +00:16:53,045 --> 00:16:55,348 +- We made it! +[LAUGHS] + +376 +00:16:55,348 --> 00:16:57,250 +- I remember the moment +when I first heard it, + +377 +00:16:57,250 --> 00:16:58,784 +it was quite emotional for me. + +378 +00:16:58,784 --> 00:17:02,488 +So I hope they're also looking +back at their progress + +379 +00:17:02,488 --> 00:17:05,258 +and their work +over the last year or more. + +380 +00:17:05,258 --> 00:17:07,026 +- Great job and congratulations. + +381 +00:17:07,026 --> 00:17:08,227 +- Congratulations to everybody. + +382 +00:17:08,227 --> 00:17:09,362 +- Congratulations. + +383 +00:17:09,362 --> 00:17:10,529 +- Congratulations! + +384 +00:17:10,529 --> 00:17:12,331 +- It's an honor +to be amongst you. + +385 +00:17:12,331 --> 00:17:13,766 +- Great job. + +386 +00:17:13,766 --> 00:17:15,635 +And let's keep on building it. + +387 +00:17:15,635 --> 00:17:20,773 +- Your continuous work +and creation and your effort + +388 +00:17:20,773 --> 00:17:23,276 +will make a difference. + +389 +00:17:23,276 --> 00:17:27,446 +♪ Upbeat electronic music ♪ + +390 +00:17:27,446 --> 00:18:36,182 +♪ + diff --git a/eng/2022 Session 110332 What's new in Create ML en.srt b/eng/2022 Session 110332 What's new in Create ML en.srt new file mode 100644 index 0000000..acc7ef4 --- /dev/null +++ b/eng/2022 Session 110332 What's new in Create ML en.srt @@ -0,0 +1,1474 @@ +1 +00:00:00,167 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,977 +♪ + +3 +00:00:09,977 --> 00:00:13,981 +Namaskar! +Hello and welcome to WWDC22. + +4 +00:00:13,981 --> 00:00:17,651 +My name is Vrushali Mundhe, +engineer on the Create ML team, + +5 +00:00:17,651 --> 00:00:22,456 +and I am excited to share with +you what's new in Create ML. + +6 +00:00:22,456 --> 00:00:24,524 +Create ML allows you +to easily train + +7 +00:00:24,524 --> 00:00:27,628 +powerful machine learning models +with data you have collected + +8 +00:00:27,628 --> 00:00:32,566 +to deliver unique experiences +and enhance your apps. + +9 +00:00:32,566 --> 00:00:36,003 +Create ML ships as an app +bundled with Xcode, + +10 +00:00:36,003 --> 00:00:38,906 +letting you quickly build +and train Core ML models + +11 +00:00:38,906 --> 00:00:41,808 +right on your Mac with no code. + +12 +00:00:41,808 --> 00:00:44,978 +Create ML is also available +as a Swift framework + +13 +00:00:44,978 --> 00:00:47,147 +shipping in SDK. + +14 +00:00:47,147 --> 00:00:51,251 +Using its APIs, you can easily +automate model creation + +15 +00:00:51,251 --> 00:00:54,588 +or create dynamic experiences +powered by training + +16 +00:00:54,588 --> 00:00:57,758 +directly from +within your own app. + +17 +00:00:57,758 --> 00:01:00,427 +To learn more about Create ML's +core capabilities, + +18 +00:01:00,427 --> 00:01:04,031 +you can check out +these previous sessions. + +19 +00:01:04,031 --> 00:01:08,135 +In this session, we'll talk +about what's new in Create ML. + +20 +00:01:08,135 --> 00:01:10,704 +We will start with new features +in the Create ML app + +21 +00:01:10,704 --> 00:01:12,806 +that can help evaluate +the accuracy + +22 +00:01:12,806 --> 00:01:14,942 +and utility of your models. + +23 +00:01:14,942 --> 00:01:18,578 +We will then turn our attention +to the Create ML framework, + +24 +00:01:18,578 --> 00:01:20,681 +its extended capabilities, + +25 +00:01:20,681 --> 00:01:23,550 +and ability to now highly +customize models + +26 +00:01:23,550 --> 00:01:26,119 +for your own app. + +27 +00:01:26,119 --> 00:01:28,188 +Let's start by reviewing +a typical workflow + +28 +00:01:28,188 --> 00:01:30,157 +for model creation. + +29 +00:01:30,157 --> 00:01:33,160 +Given an identified task, +you begin by collecting + +30 +00:01:33,160 --> 00:01:35,629 +and annotating data. + +31 +00:01:35,629 --> 00:01:40,033 +Take, for example, wanting +to visually identify groceries. + +32 +00:01:40,033 --> 00:01:42,002 +For this image +classification task, + +33 +00:01:42,002 --> 00:01:45,839 +your starting point would be +collecting and labeling images; + +34 +00:01:45,839 --> 00:01:47,941 +here, some fruits +and vegetables. + +35 +00:01:50,410 --> 00:01:53,780 +Create ML will then help you +train a model from this data, + +36 +00:01:53,780 --> 00:01:56,283 +which can be used in your app. + +37 +00:01:56,283 --> 00:01:58,719 +However, before using +this model, + +38 +00:01:58,719 --> 00:02:02,756 +an important step is to evaluate +how well it performs; + +39 +00:02:02,756 --> 00:02:05,826 +here, seeing how accurate +the model is on images, + +40 +00:02:05,826 --> 00:02:09,262 +which were not part +of the training set. + +41 +00:02:09,262 --> 00:02:11,431 +Depending on evaluation, + +42 +00:02:11,431 --> 00:02:14,735 +you may iterate on the model +with additional data + +43 +00:02:14,735 --> 00:02:17,170 +and modified training settings + +44 +00:02:17,170 --> 00:02:19,773 +or, once the model is +performing well enough, + +45 +00:02:19,773 --> 00:02:22,976 +you're ready to integrate it +into your app, + +46 +00:02:22,976 --> 00:02:26,947 +I would like to focus +on this evaluation step further. + +47 +00:02:26,947 --> 00:02:29,549 +When performing an evaluation, +we often turn + +48 +00:02:29,549 --> 00:02:33,053 +to a set of metrics measured +by testing your model + +49 +00:02:33,053 --> 00:02:35,922 +on new data held out +from training. + +50 +00:02:35,922 --> 00:02:39,292 +You may start by looking +at a top-level accuracy metric + +51 +00:02:39,292 --> 00:02:41,695 +or dive into +per-class statistics + +52 +00:02:41,695 --> 00:02:44,264 +to get a general sense +of the model's behavior + +53 +00:02:44,264 --> 00:02:48,769 +and ability to generalize +beyond what it was trained on. + +54 +00:02:48,769 --> 00:02:51,972 +Ultimately, the model will be +responsible for empowering + +55 +00:02:51,972 --> 00:02:54,141 +data-driven experience +in your app, + +56 +00:02:54,141 --> 00:02:55,742 +and during evaluation, + +57 +00:02:55,742 --> 00:03:00,313 +you want to identify the model's +main strengths and weaknesses + +58 +00:03:00,313 --> 00:03:03,283 +in terms of categories +of inputs or scenarios; + +59 +00:03:03,283 --> 00:03:07,754 +it works well or may fall short +of expectations. + +60 +00:03:07,754 --> 00:03:10,657 +There are new features in the +Create ML app that can help you + +61 +00:03:10,657 --> 00:03:13,260 +on this part +of your model-creation journey. + +62 +00:03:13,260 --> 00:03:16,296 +Let me show you a project +that I'm working on. + +63 +00:03:16,296 --> 00:03:18,698 +Here, I have a project +set up to create a model + +64 +00:03:18,698 --> 00:03:20,767 +to identify groceries. + +65 +00:03:20,767 --> 00:03:23,070 +I started with collecting +various fruit + +66 +00:03:23,070 --> 00:03:25,839 +and vegetable images +as my training data + +67 +00:03:25,839 --> 00:03:30,143 +and labeled them appropriately. + +68 +00:03:30,143 --> 00:03:32,446 +Here are my different classes +and number of images + +69 +00:03:32,446 --> 00:03:34,281 +for each class. + +70 +00:03:38,285 --> 00:03:43,557 +I have already trained my image +classifier for 25 iterations. + +71 +00:03:43,557 --> 00:03:46,259 +Next, when I click +on the Evaluation tab, + +72 +00:03:46,259 --> 00:03:49,463 +I can add new test data -- +a set of images + +73 +00:03:49,463 --> 00:03:53,433 +apart from training data that +I had set aside for testing. + +74 +00:03:58,371 --> 00:04:03,510 +I will next click Evaluate +to begin testing. + +75 +00:04:03,510 --> 00:04:05,579 +After it finishes evaluation, + +76 +00:04:05,579 --> 00:04:08,615 +the UI provides details +of the results. + +77 +00:04:08,615 --> 00:04:11,318 +On the top, there is +a high-level summary section + +78 +00:04:11,318 --> 00:04:14,721 +which gives a quick sense +of test accuracy. + +79 +00:04:14,721 --> 00:04:19,292 +Here, the accuracy +of this test data is 89 percent. + +80 +00:04:19,292 --> 00:04:21,061 +Below in this Metrics tab, + +81 +00:04:21,061 --> 00:04:24,798 +a table provides a wealth +of metrics for each class. + +82 +00:04:24,798 --> 00:04:28,668 +You can adjust what is shown +here using these drop-down menus + +83 +00:04:28,668 --> 00:04:31,138 +and add additional metrics +like false positive + +84 +00:04:31,138 --> 00:04:32,606 +and false negative. + +85 +00:04:32,606 --> 00:04:34,674 +Let's review one +of these classes. + +86 +00:04:34,674 --> 00:04:36,276 +How about Tomato? + +87 +00:04:36,276 --> 00:04:40,080 +The model classified 29 of +the 32 tomato images correctly. + +88 +00:04:40,080 --> 00:04:45,585 +It also shows the precision +for this class is 91 percent, + +89 +00:04:45,585 --> 00:04:47,154 +which means nine percent +of the time + +90 +00:04:47,154 --> 00:04:50,624 +the model says something +is a tomato, it is incorrect. + +91 +00:04:50,624 --> 00:04:53,226 +While these numbers +and statistics are super useful, + +92 +00:04:53,226 --> 00:04:56,096 +it's sometimes even more +important to look at them + +93 +00:04:56,096 --> 00:04:58,465 +in the context +of the data itself. + +94 +00:04:58,465 --> 00:05:02,235 +When I click the precision, +it takes me to the images + +95 +00:05:02,235 --> 00:05:05,372 +that were incorrectly +classified as tomato. + +96 +00:05:05,372 --> 00:05:08,875 +Here are three of these cases +in the test data. + +97 +00:05:08,875 --> 00:05:12,145 +For each image, +the app displays its thumbnail, + +98 +00:05:12,145 --> 00:05:14,447 +the class the model +has predicted, + +99 +00:05:14,447 --> 00:05:16,550 +and the true label below it. + +100 +00:05:16,550 --> 00:05:19,052 +In this first example, +while the model classified this + +101 +00:05:19,052 --> 00:05:22,589 +as Tomato, +it was labeled as Potato. + +102 +00:05:22,589 --> 00:05:25,058 +But this sure appears +to be a tomato to me. + +103 +00:05:25,058 --> 00:05:27,694 +This seems like a case +of a mislabeled test data. + +104 +00:05:27,694 --> 00:05:31,631 +In fact, all three of these +examples seem to be mislabeled. + +105 +00:05:31,631 --> 00:05:33,567 +This should be easy to address. + +106 +00:05:33,567 --> 00:05:35,135 +I'll make a note to double-check + +107 +00:05:35,135 --> 00:05:37,737 +and revisit +my test-set labeling. + +108 +00:05:37,737 --> 00:05:40,006 +This was clearly +an error on my part, + +109 +00:05:40,006 --> 00:05:42,943 +but is it the only source +of error? + +110 +00:05:42,943 --> 00:05:44,844 +I got here +by exploring the metrics + +111 +00:05:44,844 --> 00:05:47,280 +on a random class of my choice. + +112 +00:05:47,280 --> 00:05:50,317 +You may wonder, +"Where should I start?" + +113 +00:05:50,317 --> 00:05:53,787 +Or, "What should I +explore next?" + +114 +00:05:53,787 --> 00:05:58,225 +The top-level summary section +is here to help you out. + +115 +00:05:58,225 --> 00:05:59,292 +The app has selected + +116 +00:05:59,292 --> 00:06:01,795 +some of the most important +evaluation details + +117 +00:06:01,795 --> 00:06:05,765 +that can serve as a great place +to start your exploration. + +118 +00:06:05,765 --> 00:06:10,604 +Let me restart from the top +and review the successful cases. + +119 +00:06:10,604 --> 00:06:15,742 +Clicking here, +this correct count... + +120 +00:06:15,742 --> 00:06:19,412 +Here, we can get a quick +glance of all the 162 images + +121 +00:06:19,412 --> 00:06:23,550 +the model correctly classified. + +122 +00:06:23,550 --> 00:06:27,187 +Next, let me contrast this with +a review of all the failures + +123 +00:06:27,187 --> 00:06:31,258 +by clicking on the incorrect. + +124 +00:06:31,258 --> 00:06:33,660 +There are 21 failures in total. + +125 +00:06:35,962 --> 00:06:38,765 +Here is that mislabeled +tomato again. + +126 +00:06:38,765 --> 00:06:40,934 +Let me check if there are +any other types of errors + +127 +00:06:40,934 --> 00:06:43,036 +that can stand out. + +128 +00:06:43,036 --> 00:06:46,139 +How about... this one? + +129 +00:06:46,139 --> 00:06:50,343 +This image is labeled +as Carrot, + +130 +00:06:50,343 --> 00:06:53,780 +but the model predicts +as Potato. + +131 +00:06:53,780 --> 00:06:56,483 +It's hard to tell +in this small thumbnail, + +132 +00:06:56,483 --> 00:07:01,154 +but let's confirm by clicking on +this image to get a better view. + +133 +00:07:01,154 --> 00:07:03,556 +Well, this looks like +a foot to me. + +134 +00:07:03,556 --> 00:07:06,893 +This clearly is not a long, +skinny carrot shape I'm used to + +135 +00:07:06,893 --> 00:07:09,562 +but can be easily confused +as a potato. + +136 +00:07:09,562 --> 00:07:12,198 +Perhaps I should consider +adding more shape variations + +137 +00:07:12,198 --> 00:07:14,501 +to my training data for carrots. + +138 +00:07:14,501 --> 00:07:16,603 +Let me make a note +of this as well. + +139 +00:07:16,603 --> 00:07:20,273 +This time, I will click on +the arrow next to the filename + +140 +00:07:20,273 --> 00:07:23,243 +to bring me to this image +in the Finder. + +141 +00:07:25,679 --> 00:07:30,650 +I will right-click +and label this as red, + +142 +00:07:30,650 --> 00:07:33,520 +as reminder of it being +something I want to revisit + +143 +00:07:33,520 --> 00:07:36,456 +in my next round +of data collection. + +144 +00:07:36,456 --> 00:07:40,593 +Let me continue my exploration +from within this expanded view. + +145 +00:07:40,593 --> 00:07:44,297 +Note that this view also shows +full prediction results, + +146 +00:07:44,297 --> 00:07:46,933 +listing the model's confidence +in every class. + +147 +00:07:46,933 --> 00:07:49,669 +It also lets me navigate +through examples + +148 +00:07:49,669 --> 00:07:53,473 +using these left +and right arrows. + +149 +00:07:53,473 --> 00:07:57,243 +I'll move to another example +from here. + +150 +00:07:57,243 --> 00:07:58,912 +This is an interesting case. + +151 +00:07:58,912 --> 00:08:02,882 +It has multiple vegetables +in a single image. + +152 +00:08:02,882 --> 00:08:07,120 +It says it's an eggplant +and it's true + +153 +00:08:07,120 --> 00:08:11,091 +that there are eggplants here +but other things as well. + +154 +00:08:11,091 --> 00:08:13,960 +I need to think about whether +this is an important use case + +155 +00:08:13,960 --> 00:08:16,830 +for me to consider in my app. + +156 +00:08:16,830 --> 00:08:19,599 +Perhaps the UI can guide +my users to make sure + +157 +00:08:19,599 --> 00:08:22,769 +they only point to one type +of grocery at a time, + +158 +00:08:22,769 --> 00:08:24,971 +or if I want to support +multiple types, + +159 +00:08:24,971 --> 00:08:28,141 +I may want to consider +using an object detector -- + +160 +00:08:28,141 --> 00:08:29,776 +another template in the app -- + +161 +00:08:29,776 --> 00:08:32,679 +rather than the whole +image classifier. + +162 +00:08:32,679 --> 00:08:34,481 +Coming back +to the summary section, + +163 +00:08:34,481 --> 00:08:37,884 +let me check out this line +about the top confusion. + +164 +00:08:37,884 --> 00:08:40,620 +Here it says +it's 'Pepper' as 'Bean'. + +165 +00:08:40,620 --> 00:08:44,257 +Let's click +to explore this case. + +166 +00:08:44,257 --> 00:08:46,259 +Four images labeled as peppers + +167 +00:08:46,259 --> 00:08:49,729 +are incorrectly +classified as beans. + +168 +00:08:49,729 --> 00:08:51,598 +These look like +spicy peppers to me, + +169 +00:08:51,598 --> 00:08:55,068 +but I guess they are green +like beans as well. + +170 +00:08:55,068 --> 00:08:58,805 +I wonder if the model is having +trouble with peppers in general. + +171 +00:08:58,805 --> 00:09:05,812 +Let me switch this query option +from Incorrect to Correct... + +172 +00:09:05,812 --> 00:09:09,582 +...to contrast these failures +to correctly classified peppers. + +173 +00:09:09,582 --> 00:09:13,019 +It correctly classified +32 images; + +174 +00:09:13,019 --> 00:09:17,023 +however, I do notice that +most of these are bell peppers. + +175 +00:09:17,023 --> 00:09:19,426 +I should check my training data +to make sure + +176 +00:09:19,426 --> 00:09:24,731 +I have a good representation +of multiple peppers as well. + +177 +00:09:24,731 --> 00:09:26,933 +This quick exploration +was a great reminder + +178 +00:09:26,933 --> 00:09:29,436 +of how important +the quantity, quality, + +179 +00:09:29,436 --> 00:09:34,174 +and variety of training and test +data are for machine learning. + +180 +00:09:34,174 --> 00:09:36,543 +In a matter of a few minutes, +the app helped me + +181 +00:09:36,543 --> 00:09:38,778 +visually identify +some issues with labeling + +182 +00:09:38,778 --> 00:09:40,847 +and representation. + +183 +00:09:40,847 --> 00:09:43,516 +I need to make some tweaks +to my training data + +184 +00:09:43,516 --> 00:09:46,019 +and see if it fixes +the issues we saw. + +185 +00:09:46,019 --> 00:09:49,622 +It also revealed something +I hadn't considered before: + +186 +00:09:49,622 --> 00:09:52,759 +what happens if a user +captures multiple vegetables + +187 +00:09:52,759 --> 00:09:54,494 +in a single photo? + +188 +00:09:54,494 --> 00:09:58,731 +I need to think about +my app design some more. + +189 +00:09:58,731 --> 00:10:00,834 +All of this exploration +was possible + +190 +00:10:00,834 --> 00:10:04,637 +because I had a collection +of labeled data to evaluate. + +191 +00:10:04,637 --> 00:10:08,942 +But what if I have unlabeled +examples I want to quickly test, + +192 +00:10:08,942 --> 00:10:11,277 +or consider whether or not +I should explore + +193 +00:10:11,277 --> 00:10:15,181 +more camera angles +or lighting conditions? + +194 +00:10:15,181 --> 00:10:18,151 +Here is where +the Preview tab can help. + +195 +00:10:18,151 --> 00:10:21,020 +I can drag a few examples my +colleague just sent to me here + +196 +00:10:21,020 --> 00:10:22,856 +and see how well it does. + +197 +00:10:36,603 --> 00:10:38,338 +Or I can even test this live, + +198 +00:10:38,338 --> 00:10:41,374 +using my iPhone +as the Continuity Camera. + +199 +00:10:43,610 --> 00:10:45,745 +As I point +to these actual vegetables, + +200 +00:10:45,745 --> 00:10:49,516 +the model is able +to correctly classify them live. + +201 +00:10:49,516 --> 00:10:54,487 +Here, this is a pepper +and a tomato. + +202 +00:10:54,487 --> 00:10:56,689 +To recap, you can now +dive deeper + +203 +00:10:56,689 --> 00:11:01,060 +into a trained model's behavior +on any labeled dataset. + +204 +00:11:01,060 --> 00:11:04,197 +The Evaluation pane provides +a detailed metric summary + +205 +00:11:04,197 --> 00:11:06,399 +with its extended options. + +206 +00:11:06,399 --> 00:11:09,502 +A new Explore tab provides +options which lets you filter + +207 +00:11:09,502 --> 00:11:12,171 +and visualize +the test evaluation results + +208 +00:11:12,171 --> 00:11:16,543 +along with the associated data +in a new interactive UI, + +209 +00:11:16,543 --> 00:11:18,945 +currently available +for image classifier, + +210 +00:11:18,945 --> 00:11:24,017 +hand pose classifier, +and object detection templates. + +211 +00:11:24,017 --> 00:11:26,986 +Live preview enables +immediate feedback. + +212 +00:11:26,986 --> 00:11:29,455 +It's expanded +to image classifier, + +213 +00:11:29,455 --> 00:11:34,561 +hand action classifier, and body +action classifier templates. + +214 +00:11:34,561 --> 00:11:36,763 +We have also extended +the feature to allow you + +215 +00:11:36,763 --> 00:11:39,332 +to select from +any attached webcam + +216 +00:11:39,332 --> 00:11:42,068 +and we also support +Continuity Cameras + +217 +00:11:42,068 --> 00:11:45,305 +on macOS Ventura. + +218 +00:11:45,305 --> 00:11:48,675 +That's a quick summary of +what's new in the Create ML app. + +219 +00:11:48,675 --> 00:11:50,577 +Let's shift over +to discuss what's new + +220 +00:11:50,577 --> 00:11:52,445 +in the Create ML framework. + +221 +00:11:54,914 --> 00:11:57,517 +The Create ML framework +is available on macOS, + +222 +00:11:57,517 --> 00:11:59,819 +iOS, and iPadOS. + +223 +00:11:59,819 --> 00:12:04,524 +This year, we are expanding +some of its support to tvOS 16. + +224 +00:12:04,524 --> 00:12:07,126 +The programmatic interface +not only lets you + +225 +00:12:07,126 --> 00:12:09,829 +automate model creation +at development time + +226 +00:12:09,829 --> 00:12:12,865 +but also opens up +many opportunities to build + +227 +00:12:12,865 --> 00:12:17,070 +dynamic features that learn +directly from users' input + +228 +00:12:17,070 --> 00:12:20,073 +or on-device behavior, +providing personalized + +229 +00:12:20,073 --> 00:12:24,444 +and adaptive experiences +while preserving user privacy. + +230 +00:12:24,444 --> 00:12:27,780 +Note that task support +does differ per platform. + +231 +00:12:27,780 --> 00:12:31,150 +For example, while tabular +classifiers and regressors + +232 +00:12:31,150 --> 00:12:34,587 +are available everywhere, some +of the tasks with larger data + +233 +00:12:34,587 --> 00:12:38,324 +and computation requirements, +such as those involving video, + +234 +00:12:38,324 --> 00:12:41,060 +require macOS. + +235 +00:12:41,060 --> 00:12:43,096 +One common question +you may have is, + +236 +00:12:43,096 --> 00:12:44,998 +"What if I can't map my idea + +237 +00:12:44,998 --> 00:12:48,801 +into one of these Create ML's +predefined tasks?" + +238 +00:12:48,801 --> 00:12:52,238 +To help answer this question, +we are introducing a new member + +239 +00:12:52,238 --> 00:12:54,440 +to the Create ML family: + +240 +00:12:54,440 --> 00:12:56,609 +Create ML Components. + +241 +00:12:56,609 --> 00:12:59,979 +Create ML Components exposes +the underlying building blocks + +242 +00:12:59,979 --> 00:13:02,482 +of Create ML. + +243 +00:13:02,482 --> 00:13:05,585 +It allows you to combine them +together to create pipelines + +244 +00:13:05,585 --> 00:13:09,555 +and models customized +to your use case. + +245 +00:13:09,555 --> 00:13:11,457 +I highly recommend you +checking out these sessions + +246 +00:13:11,457 --> 00:13:12,925 +to learn more. + +247 +00:13:12,925 --> 00:13:14,894 +In "Get to know +Create ML Components," + +248 +00:13:14,894 --> 00:13:16,963 +you will learn about +the building blocks + +249 +00:13:16,963 --> 00:13:19,599 +and how they can +be composed together. + +250 +00:13:19,599 --> 00:13:22,635 +In "Compose advanced models +with Create ML Components," + +251 +00:13:22,635 --> 00:13:26,239 +you dive deeper into using +async temporal components + +252 +00:13:26,239 --> 00:13:28,608 +and customizing training. + +253 +00:13:28,608 --> 00:13:31,711 +There are endless capabilities; +let me showcase one + +254 +00:13:31,711 --> 00:13:33,680 +that I am personally +excited about: + +255 +00:13:33,680 --> 00:13:35,848 +action repetition counting. + +256 +00:13:35,848 --> 00:13:40,086 +When I am not working, most +likely you'll find me dancing. + +257 +00:13:40,086 --> 00:13:41,354 +I'm a professionally trained + +258 +00:13:41,354 --> 00:13:44,691 +Indian classical dance +Kathak artist. + +259 +00:13:44,691 --> 00:13:45,825 +To improve my form, + +260 +00:13:45,825 --> 00:13:49,562 +I often rely on repeatedly +practicing my routines. + +261 +00:13:49,562 --> 00:13:52,999 +As a choreographer/teacher, +I would like my performers + +262 +00:13:52,999 --> 00:13:56,235 +to practice steps for certain +counts and submit them to me. + +263 +00:13:56,235 --> 00:13:59,005 +The new repetition counting +capabilities in Create ML + +264 +00:13:59,005 --> 00:14:01,474 +can help me actually do that! + +265 +00:14:01,474 --> 00:14:04,377 +Here, this a chakkar -- twirl -- + +266 +00:14:04,377 --> 00:14:06,713 +an integral part +of Kathak dance. + +267 +00:14:09,248 --> 00:14:11,951 +I would like to practice this +for certain counts daily + +268 +00:14:11,951 --> 00:14:14,587 +to build my form and my stamina. + +269 +00:14:14,587 --> 00:14:18,624 +I have an iOS app built using +Create ML that counts my moves. + +270 +00:14:18,624 --> 00:14:20,359 +Let's try it in action. + +271 +00:14:27,867 --> 00:14:30,303 +As I take my chakkars, +the count increases + +272 +00:14:30,303 --> 00:14:31,971 +to correspond to it. + +273 +00:14:31,971 --> 00:14:33,806 +Here, I have done five chakkars, + +274 +00:14:33,806 --> 00:14:36,743 +and the count reflects +exactly that. + +275 +00:14:36,743 --> 00:14:39,145 +Next, let's try a different +small routine + +276 +00:14:39,145 --> 00:14:41,981 +that consist of right- +and left-side movements. + +277 +00:14:41,981 --> 00:14:44,016 +The counter counts them as one. + +278 +00:14:57,029 --> 00:14:59,565 +Here, the count shows three. + +279 +00:14:59,565 --> 00:15:02,902 +Let me try another +quick one-side arm movement. + +280 +00:15:17,283 --> 00:15:18,684 +That's four. + +281 +00:15:18,684 --> 00:15:20,820 +When combined +with Action Classification, + +282 +00:15:20,820 --> 00:15:24,557 +this will allow you to count and +categorize repetitive actions + +283 +00:15:24,557 --> 00:15:26,359 +at the same time. + +284 +00:15:26,359 --> 00:15:30,429 +Repetition counting is available +as a runtime API. + +285 +00:15:30,429 --> 00:15:33,800 +It requires no training data, +and adding this functionality + +286 +00:15:33,800 --> 00:15:37,236 +to your app can be +just a few lines of code. + +287 +00:15:37,236 --> 00:15:40,506 +Its implementation is based +on a pretrained model + +288 +00:15:40,506 --> 00:15:42,642 +designed to be class-agnostic. + +289 +00:15:42,642 --> 00:15:46,179 +Meaning, while it works +on fitness or dance actions, + +290 +00:15:46,179 --> 00:15:49,949 +such as jumping jacks, +squats, twirls, chakkars, + +291 +00:15:49,949 --> 00:15:52,552 +it's also applicable +to a wide variety + +292 +00:15:52,552 --> 00:15:56,088 +of full-body +repetitive actions. + +293 +00:15:56,088 --> 00:15:57,857 +You can find out more +about this model + +294 +00:15:57,857 --> 00:16:00,827 +and potential use case +by checking out the sample code + +295 +00:16:00,827 --> 00:16:04,530 +and the article linked +to this session. + +296 +00:16:04,530 --> 00:16:08,334 +So that's a quick overview +of what's new in Create ML. + +297 +00:16:08,334 --> 00:16:11,103 +Interactive evaluation +and live previews + +298 +00:16:11,103 --> 00:16:13,573 +in the Create ML app +lets you dive deeper + +299 +00:16:13,573 --> 00:16:16,475 +into understanding +the models you train. + +300 +00:16:16,475 --> 00:16:20,079 +The Create ML framework +adds tvOS support, + +301 +00:16:20,079 --> 00:16:23,115 +repetition counting, +and has expanded + +302 +00:16:23,115 --> 00:16:27,019 +to give you access to a rich +set of underlying components + +303 +00:16:27,019 --> 00:16:29,889 +to help you build models +highly customized + +304 +00:16:29,889 --> 00:16:32,058 +to your app needs. + +305 +00:16:32,058 --> 00:16:33,092 +Thank you, + +306 +00:16:33,092 --> 00:16:35,928 +and I hope you enjoyed all +these exciting new features, + +307 +00:16:35,928 --> 00:16:38,464 +and we can't wait to see +what you do with them! + +308 +00:16:38,464 --> 00:16:42,902 +♪ + diff --git a/eng/2022 Session 110335 Explore Apple Business Essentials en.srt b/eng/2022 Session 110335 Explore Apple Business Essentials en.srt new file mode 100644 index 0000000..88a6464 --- /dev/null +++ b/eng/2022 Session 110335 Explore Apple Business Essentials en.srt @@ -0,0 +1,1085 @@ +1 +00:00:00,367 --> 00:00:06,373 +[upbeat music] + +2 +00:00:09,309 --> 00:00:11,678 +- Hello, I'm Josh +and I'm a software engineer + +3 +00:00:11,712 --> 00:00:13,714 +on the Apple Business Essentials team. + +4 +00:00:13,747 --> 00:00:15,983 +As a small business, +you may find yourself managing + +5 +00:00:16,016 --> 00:00:18,785 +an increasing number of devices +as your team expands. + +6 +00:00:18,819 --> 00:00:21,421 +The IT demands that come with this +can be a lot to take on, + +7 +00:00:21,455 --> 00:00:24,391 +taking time away +from doing what you do best. + +8 +00:00:24,424 --> 00:00:26,693 +Apple Business Essentials makes this easy + +9 +00:00:26,727 --> 00:00:29,963 +with a subscription that seamlessly +brings together device management, + +10 +00:00:29,997 --> 00:00:32,733 +24/7 support, and cloud storage + +11 +00:00:32,766 --> 00:00:35,302 +so your small business can manage +every employee's iPhone, + +12 +00:00:35,335 --> 00:00:38,639 +iPad and Mac every step of the way. + +13 +00:00:38,672 --> 00:00:41,175 +And for your employees, it's simple. + +14 +00:00:41,208 --> 00:00:43,777 +All they need to do is sign in +with a Managed Apple ID, + +15 +00:00:43,810 --> 00:00:46,547 +and their devices are set up +with Apple Business Essentials. + +16 +00:00:48,115 --> 00:00:49,349 +Once they're signed in, + +17 +00:00:49,383 --> 00:00:52,052 +devices are automatically configured +with the settings they need, + +18 +00:00:52,085 --> 00:00:56,290 +including critical security settings +like FileVault and passcode policies + +19 +00:00:56,323 --> 00:00:59,960 +as well as all of the apps +they need to be productive. + +20 +00:00:59,993 --> 00:01:02,563 +Plus, the iCloud storage to store, +collaborate on, + +21 +00:01:02,596 --> 00:01:05,132 +and share documents in iCloud Drive. + +22 +00:01:05,165 --> 00:01:08,869 +They can even use iCloud backups +for their iPhones and iPads. + +23 +00:01:08,902 --> 00:01:14,007 +When something goes wrong, AppleCare+ +for Business Essentials is there to help. + +24 +00:01:14,041 --> 00:01:16,743 +Repair credits are shared +across the entire organization + +25 +00:01:16,777 --> 00:01:20,380 +and can be used for any device +under a plan with coverage. + +26 +00:01:20,414 --> 00:01:21,849 +In fact, Apple Business Essentials + +27 +00:01:21,882 --> 00:01:23,283 +helps you with every step + +28 +00:01:23,317 --> 00:01:25,118 +of the device lifecycle. + +29 +00:01:25,152 --> 00:01:26,854 +In this session, we'll explore the ways + +30 +00:01:26,887 --> 00:01:28,255 +you can take advantage of + +31 +00:01:28,288 --> 00:01:31,191 +everything the subscription has to offer. + +32 +00:01:31,225 --> 00:01:35,829 +We'll review the first step: getting +an account in Apple Business Manager. + +33 +00:01:35,863 --> 00:01:39,266 +We'll start a new Apple Business +Essentials subscription, + +34 +00:01:39,299 --> 00:01:41,401 +add settings and apps, + +35 +00:01:41,435 --> 00:01:43,504 +and explore how the experience +looks for employees + +36 +00:01:43,537 --> 00:01:45,339 +as they enroll their devices. + +37 +00:01:45,372 --> 00:01:46,974 +Let's get started. + +38 +00:01:48,809 --> 00:01:51,879 +Apple Business Essentials exists inside +of Apple Business Manager + +39 +00:01:51,912 --> 00:01:55,782 +where you manage devices, +add users, and purchase apps. + +40 +00:01:55,816 --> 00:01:59,453 +To subscribe to Apple Business Essentials +as your device management solution, + +41 +00:01:59,486 --> 00:02:02,055 +you first need an +Apple Business Manager account. + +42 +00:02:04,124 --> 00:02:06,760 +Sign up by visiting business.apple.com + +43 +00:02:06,793 --> 00:02:08,629 +and entering your company name, + +44 +00:02:08,662 --> 00:02:10,731 +DUNS number, and contact information. + +45 +00:02:12,499 --> 00:02:15,702 +Once you have an account, +the next step is to add users. + +46 +00:02:15,736 --> 00:02:19,006 +You add individual users by +manually entering their information + +47 +00:02:19,039 --> 00:02:23,076 +or sync an existing list of users +from a Microsoft Azure Directory + +48 +00:02:23,110 --> 00:02:24,411 +or Google Workspace. + +49 +00:02:25,379 --> 00:02:27,381 +Many of your users share common needs, + +50 +00:02:27,414 --> 00:02:29,616 +like a sales department or field workers, + +51 +00:02:29,650 --> 00:02:32,519 +so you want them to get +the same settings and apps. + +52 +00:02:32,553 --> 00:02:36,223 +To prepare for this, make user groups +with the users you just created. + +53 +00:02:37,724 --> 00:02:39,927 +And save time using 'smart user groups' + +54 +00:02:39,960 --> 00:02:43,230 +which automatically creates groups +based on common attributes + +55 +00:02:43,263 --> 00:02:46,400 +like division, location, and role. + +56 +00:02:46,433 --> 00:02:49,036 +Now that I have +my Apple Business Manager account + +57 +00:02:49,069 --> 00:02:51,672 +and I've set up some users and groups, + +58 +00:02:51,705 --> 00:02:54,308 +I'm ready to enroll +in Apple Business Essentials. + +59 +00:02:54,341 --> 00:02:57,244 +Let's go to the Mac; +here's Apple Business Manager. + +60 +00:02:57,277 --> 00:03:03,150 +I find Subscription in the sidebar +and start a free trial . + +61 +00:03:03,183 --> 00:03:06,820 +I need to choose whether this is +an employee plan or a device plan. + +62 +00:03:06,854 --> 00:03:11,225 +Employee plans are just that-- +plans for devices used by my employees. + +63 +00:03:11,258 --> 00:03:15,062 +Device plans are also available for things +like conference room devices, + +64 +00:03:15,095 --> 00:03:17,497 +kiosks, loaners, or other cases + +65 +00:03:17,531 --> 00:03:20,300 +where the device is not assigned +to a specific employee. + +66 +00:03:20,334 --> 00:03:25,339 +Next is the number of devices +per employee, either just 1 or up to 3. + +67 +00:03:25,372 --> 00:03:27,808 +I'm setting up a plan for my sales team. + +68 +00:03:27,841 --> 00:03:31,078 +I know they're on the road frequently +and usually have a work MacBook + +69 +00:03:31,111 --> 00:03:33,013 +as well as an iPhone and iPad, + +70 +00:03:33,046 --> 00:03:36,383 +so I'll set them up +with a three-device plan. + +71 +00:03:36,416 --> 00:03:38,452 +Next, storage. + +72 +00:03:38,485 --> 00:03:42,456 +With the subscription, my employees get +an iCloud account dedicated for work + +73 +00:03:42,489 --> 00:03:45,526 +so they can access their files +and folders across their devices. + +74 +00:03:45,559 --> 00:03:49,463 +I'll stick with 200 GB for now; +this should be plenty to start with. + +75 +00:03:49,496 --> 00:03:51,665 +But if my team's needs +change in the future, + +76 +00:03:51,698 --> 00:03:53,600 +I can update it at any time. + +77 +00:03:53,634 --> 00:03:57,571 +Last, I select AppleCare+ +for Business Essentials for repair, + +78 +00:03:57,604 --> 00:04:01,742 +service and support; perfect for +my sales staff who are out in the field. + +79 +00:04:03,777 --> 00:04:06,980 +Next, I add user groups +and individual users. + +80 +00:04:07,014 --> 00:04:11,318 +Let's search for the sales team +and add them to this plan. + +81 +00:04:12,653 --> 00:04:14,922 +I've already entered +my payment information. + +82 +00:04:16,723 --> 00:04:18,992 +I review the pricing... + +83 +00:04:19,026 --> 00:04:20,994 +and add the plan. + +84 +00:04:21,028 --> 00:04:23,964 +As my business grows, +having multiple plans lets me cover + +85 +00:04:23,997 --> 00:04:26,366 +all the unique needs +of my employees and devices, + +86 +00:04:26,400 --> 00:04:30,571 +and when I make changes at any time, +I'll only pay for what I use. + +87 +00:04:30,604 --> 00:04:32,806 +My Apple Business Manager view +updates to reflect + +88 +00:04:32,840 --> 00:04:35,576 +that I'm subscribed to +Apple Business Essentials + +89 +00:04:35,609 --> 00:04:38,545 +and I now see Settings +and Collections in the sidebar. + +90 +00:04:38,579 --> 00:04:43,116 +Settings provides a list of configurations +I automatically push to a managed device. + +91 +00:04:43,150 --> 00:04:47,287 +Apps provides a list of apps to purchase +for my users to download on their devices + +92 +00:04:47,321 --> 00:04:49,189 +with the Essentials app. + +93 +00:04:49,223 --> 00:04:53,894 +Collections are a group of Settings +and Apps that I assign to users or groups. + +94 +00:04:53,927 --> 00:04:56,363 +We'll get started +by exploring the settings. + +95 +00:04:56,396 --> 00:05:00,267 +The Essentials category brings together +the settings I'm most likely to use. + +96 +00:05:00,300 --> 00:05:03,270 +But I have a number of options here, +so let's understand what they are + +97 +00:05:03,303 --> 00:05:05,506 +and then add the ones I need. + +98 +00:05:07,541 --> 00:05:10,377 +The security tab brings together +all your critical security settings + +99 +00:05:10,410 --> 00:05:13,113 +to one place so they're easy to configure. + +100 +00:05:13,146 --> 00:05:15,916 +For my iOS devices, +I want a password setting. + +101 +00:05:15,949 --> 00:05:17,451 +I'll do that shortly. + +102 +00:05:17,484 --> 00:05:20,554 +For my macOS devices, +I add the FileVault setting + +103 +00:05:20,587 --> 00:05:23,190 +to ensure my employee's Macs +encrypt their data. + +104 +00:05:23,223 --> 00:05:26,059 +I turn on Application Layer Firewall +to protect against threats + +105 +00:05:26,093 --> 00:05:29,530 +from incoming network traffic +to untrusted apps. + +106 +00:05:29,563 --> 00:05:33,567 +I turn on Gatekeeper to prevent +untrusted apps from running altogether. + +107 +00:05:34,801 --> 00:05:38,405 +Moving on to the network category, +I configure my devices with AirPrint + +108 +00:05:38,438 --> 00:05:41,308 +to automatically connect to my printers +and use the Wi-Fi setting + +109 +00:05:41,341 --> 00:05:44,511 +to configure trusted networks +so they're available automatically + +110 +00:05:44,545 --> 00:05:46,613 +when the user signs in to their device. + +111 +00:05:47,881 --> 00:05:50,250 +Personalization provides +additional settings that I can use + +112 +00:05:50,284 --> 00:05:52,986 +to customize the experience +for the devices in my fleet. + +113 +00:05:53,020 --> 00:05:58,592 +For example, I use conference room display +to restrict my apple TVs to airplay only. + +114 +00:05:58,625 --> 00:06:00,894 +For my macOS devices, +the login Window setting + +115 +00:06:00,928 --> 00:06:03,163 +allows me to display a message +on the login window, + +116 +00:06:03,197 --> 00:06:06,066 +which is great for showing things +like support email addresses + +117 +00:06:06,099 --> 00:06:08,502 +or phone numbers. + +118 +00:06:08,535 --> 00:06:12,506 +Let's go back to the essentials category +to add a few of these settings. + +119 +00:06:13,974 --> 00:06:17,244 +We'll create a new configured setting +and name it Password Policy. + +120 +00:06:23,283 --> 00:06:26,520 +The check boxes are on +for both macOS and iOS, + +121 +00:06:26,553 --> 00:06:29,556 +and let's leave on the switch on +for "password and security options" + +122 +00:06:29,590 --> 00:06:32,693 +to require my users +to create a strong password. + +123 +00:06:32,726 --> 00:06:35,596 +I select the "no" option +for "allow simple passwords" + +124 +00:06:35,629 --> 00:06:38,398 +to require my employees to choose +at least one special character + +125 +00:06:38,432 --> 00:06:40,200 +as part of their password, + +126 +00:06:40,234 --> 00:06:43,237 +and 'yes' for +"require alpha numeric password" + +127 +00:06:43,270 --> 00:06:46,740 +requires at least one letter and number +for their password. + +128 +00:06:46,773 --> 00:06:48,408 +Let's save this setting. + +129 +00:06:50,344 --> 00:06:53,146 +And here it is, added to my settings list. + +130 +00:06:53,180 --> 00:06:56,517 +Next, I configure Wi-Fi passwords +so my employees' devices + +131 +00:06:56,550 --> 00:06:58,552 +automatically join a known Wi-Fi network. + +132 +00:06:58,585 --> 00:07:01,555 +I'll name this setting +BetterBag Secure Wi-Fi. + +133 +00:07:03,590 --> 00:07:08,495 +I enter my company's Wi-Fi name... +and password. + +134 +00:07:11,532 --> 00:07:14,101 +I leave the "network visibility" +as visible, + +135 +00:07:14,134 --> 00:07:17,304 +leave "auto join" as "yes" and save. + +136 +00:07:18,539 --> 00:07:21,008 +Let's move on to apps. + +137 +00:07:24,211 --> 00:07:27,014 +The Apps page is where I get +apps, both paid and free, + +138 +00:07:27,047 --> 00:07:30,951 +that my users then install with +the Essentials app on their device. + +139 +00:07:30,984 --> 00:07:34,321 +Here I have the apps and books available +from the Apple App Store + +140 +00:07:34,354 --> 00:07:35,889 +as well as any Custom Apps + +141 +00:07:35,923 --> 00:07:38,892 +that have been made available +specifically for me to purchase. + +142 +00:07:38,926 --> 00:07:42,596 +I'd like all my users to have the Pages +app for iOS available to install, + +143 +00:07:42,629 --> 00:07:45,499 +so let's search for and get Pages. + +144 +00:07:50,771 --> 00:07:54,308 +When my users install Pages from the +Essentials app, the app is managed, + +145 +00:07:54,341 --> 00:07:56,643 +which means updates happen automatically, + +146 +00:07:56,677 --> 00:08:00,214 +and the app will be uninstalled +if the user signs out of the device. + +147 +00:08:00,247 --> 00:08:02,950 +I assign the app to my office. + +148 +00:08:05,152 --> 00:08:10,157 +I need 4 licenses, and I confirm +the price--free, in this case. + +149 +00:08:10,190 --> 00:08:12,593 +Let's get the licenses for the app. + +150 +00:08:15,162 --> 00:08:18,265 +Now that I have settings configured +and apps purchased, + +151 +00:08:18,298 --> 00:08:21,368 +let's group them together +into a Collection. + +152 +00:08:21,401 --> 00:08:25,572 +A Collection applies a combination of +settings and apps to users and groups. + +153 +00:08:25,606 --> 00:08:28,242 +This ensures that employees' devices +are configured correctly + +154 +00:08:28,275 --> 00:08:30,143 +and they have access +to the apps that they need + +155 +00:08:30,177 --> 00:08:32,079 +as soon as they sign in to their devices. + +156 +00:08:32,112 --> 00:08:36,717 +I create a new Collection +for my sales team and name it Sales. + +157 +00:08:38,418 --> 00:08:40,988 +I add apps to my collection +in the apps tab. + +158 +00:08:41,021 --> 00:08:42,589 +Let's add Pages. + +159 +00:08:46,193 --> 00:08:49,129 +Similarly, I add settings to +my collection in the settings tab. + +160 +00:08:50,831 --> 00:08:55,235 +Let's search for and add the Wi-Fi + +161 +00:08:55,269 --> 00:08:56,870 +and password policy settings. + +162 +00:08:58,372 --> 00:09:02,442 +Finally, I add my Sales team to +this collection in the User Groups tab. + +163 +00:09:05,546 --> 00:09:08,815 +I just search for the Sales User group +and add it. + +164 +00:09:10,584 --> 00:09:15,422 +Let's save, and I can now see +my newly created collection. + +165 +00:09:15,989 --> 00:09:20,294 +Okay. I have apps and settings, +and I've assigned them to my employees. + +166 +00:09:20,327 --> 00:09:23,864 +Now, I need to give the employees +a way to sign in to their devices. + +167 +00:09:23,897 --> 00:09:28,602 +In the User list, +let's find one of my employees, + +168 +00:09:28,635 --> 00:09:30,003 +Eliza... + +169 +00:09:31,572 --> 00:09:33,941 +And create a Managed Apple ID for her. + +170 +00:09:35,275 --> 00:09:39,746 +Either I download a file with +the user name and temporary password + +171 +00:09:39,780 --> 00:09:43,884 +or send Eliza an email with her +Managed Apple ID credentials directly. + +172 +00:09:47,321 --> 00:09:50,858 +Let's explore what this looks like +from Eliza's perspective. + +173 +00:09:50,891 --> 00:09:53,794 +She has a company iPad +and a personal iPhone. + +174 +00:09:53,827 --> 00:09:57,831 +She uses her Managed Apple ID +we created to enroll both devices. + +175 +00:09:57,865 --> 00:10:00,100 +Let's start with the company-owned iPad. + +176 +00:10:02,035 --> 00:10:04,238 +Because this iPad +was purchased specifically + +177 +00:10:04,271 --> 00:10:07,374 +for my organization through Apple +or an authorized reseller, + +178 +00:10:07,407 --> 00:10:11,645 +Eliza gets the Work sign-in +right when she sets up her device. + +179 +00:10:11,678 --> 00:10:14,948 +She enters the username and +password from the email I sent her. + +180 +00:10:16,250 --> 00:10:17,784 +Upon completing setup, + +181 +00:10:17,818 --> 00:10:21,154 +the device automatically joins the office +Wi-Fi network that I've set up + +182 +00:10:21,188 --> 00:10:22,923 +and the Essentials app is installed. + +183 +00:10:24,925 --> 00:10:27,694 +Next, she sets up her personal iPhone. + +184 +00:10:27,728 --> 00:10:30,631 +This device is already signed in +with her personal Apple ID + +185 +00:10:30,664 --> 00:10:33,567 +and has all of her personal apps and data. + +186 +00:10:33,600 --> 00:10:35,135 +With Apple Business Essentials, + +187 +00:10:35,169 --> 00:10:38,138 +Eliza can sign in to +her work account separately. + +188 +00:10:38,172 --> 00:10:41,041 +She goes to the settings app, +selects general, + +189 +00:10:41,074 --> 00:10:43,710 +VPN and Device Management, + +190 +00:10:43,744 --> 00:10:46,246 +and sign in with a work or school account. + +191 +00:10:46,280 --> 00:10:49,650 +Then she enters her username and password. + +192 +00:10:49,683 --> 00:10:52,152 +Now her iPhone is signed into +both her personal account + +193 +00:10:52,186 --> 00:10:54,721 +and her Apple Business Essentials account. + +194 +00:10:54,755 --> 00:10:57,724 +The best part is that these are +crytographically separated, + +195 +00:10:57,758 --> 00:11:00,294 +meaning her personal data stays private + +196 +00:11:00,327 --> 00:11:03,397 +while her work data +remains managed and secure. + +197 +00:11:05,399 --> 00:11:07,568 +Once enrolled, +Eliza has all the settings + +198 +00:11:07,601 --> 00:11:11,939 +automatically deployed to her device, +and the Essentials app is downloaded. + +199 +00:11:13,407 --> 00:11:16,076 +She uses it to install all the apps +I've assigned to her. + +200 +00:11:17,110 --> 00:11:20,047 +On the devices tab, +she has all her devices + +201 +00:11:20,080 --> 00:11:21,682 +and the Apple Support button. + +202 +00:11:21,715 --> 00:11:23,951 +If something goes wrong +with one of her devices, + +203 +00:11:23,984 --> 00:11:26,854 +she initiates a repair request +from inside the Essentials app + +204 +00:11:26,887 --> 00:11:29,356 +with the "Visit Apple Support" button. + +205 +00:11:29,389 --> 00:11:32,492 +Once approved, she requests +a repair technician to come to her + +206 +00:11:32,526 --> 00:11:36,697 +or she brings the device to an +Apple Authorized Service Provider. + +207 +00:11:36,730 --> 00:11:41,502 +I see the status in Service and Support, +where I approve or deny the request. + +208 +00:11:43,804 --> 00:11:46,273 +With Apple Business Essentials, +as your needs change, + +209 +00:11:46,306 --> 00:11:49,643 +you go back and revisit +your apps and settings. + +210 +00:11:49,676 --> 00:11:53,313 +The changes you make are automatically +pushed to your employees' devices. + +211 +00:11:54,114 --> 00:11:58,285 +If a device is lost or needs to be +re-provisioned, you select the device, + +212 +00:11:58,318 --> 00:12:02,289 +and either "sign out user", +"lock device" or "erase," + +213 +00:12:02,322 --> 00:12:05,325 +and the action is taken on the device. + +214 +00:12:05,359 --> 00:12:08,095 +And that employee replacing +or upgrading their device + +215 +00:12:08,128 --> 00:12:11,198 +just needs to sign in to a new device +with their Managed Apple ID + +216 +00:12:11,231 --> 00:12:14,201 +to access their apps, +settings, and work data in iCloud. + +217 +00:12:16,069 --> 00:12:17,771 +Let's wrap up. + +218 +00:12:17,804 --> 00:12:20,541 +We reviewed how to get set up +in Apple Business Manager. + +219 +00:12:20,574 --> 00:12:22,676 +We subscribed +to Apple Business Essentials, + +220 +00:12:22,709 --> 00:12:25,445 +making a few key decisions along the way. + +221 +00:12:25,479 --> 00:12:27,314 +We added settings and apps +to collections + +222 +00:12:27,347 --> 00:12:29,917 +which we assigned +to a user group of employees + +223 +00:12:29,950 --> 00:12:34,488 +and explored how the experience +looks for employees on their devices. + +224 +00:12:34,521 --> 00:12:38,792 +Apple Business Essentials, currently +available to U.S.-based small businesses, + +225 +00:12:38,825 --> 00:12:42,763 +lets you get back to doing +what you do best, faster. + +226 +00:12:42,796 --> 00:12:46,066 +It's simple to manage the entire life of +your employees' devices + +227 +00:12:46,099 --> 00:12:49,436 +with easy set up and onboarding, +security and privacy, + +228 +00:12:49,469 --> 00:12:52,339 +storage and backup, support and repair, + +229 +00:12:52,372 --> 00:12:56,009 +and updates +with one flexible subscription. + +230 +00:12:56,043 --> 00:12:58,645 +To learn more about +Apple Business Essentials, + +231 +00:12:58,679 --> 00:13:03,250 +visit apple.com/business/essentials + +232 +00:13:03,283 --> 00:13:05,519 +Have a great WWDC. + diff --git a/eng/2022 Session 110336 What's new in Screen Time API en.srt b/eng/2022 Session 110336 What's new in Screen Time API en.srt new file mode 100644 index 0000000..f126e73 --- /dev/null +++ b/eng/2022 Session 110336 What's new in Screen Time API en.srt @@ -0,0 +1,828 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,743 --> 00:00:12,546 +Hello, everyone, +my name is MaryAshley Etefia, + +3 +00:00:12,579 --> 00:00:15,349 +and I'm a software engineer +for Screen Time. + +4 +00:00:15,382 --> 00:00:18,886 +The COVID-19 pandemic put +a lot of pressure on our industry + +5 +00:00:18,919 --> 00:00:23,624 +to respond to the growing demand for +digital health and productivity resources. + +6 +00:00:23,657 --> 00:00:26,527 +The pandemic reinforced +society's need for balance + +7 +00:00:26,560 --> 00:00:29,796 +and has put everyone's ability +to successfully manage their own space + +8 +00:00:29,830 --> 00:00:31,565 +to the test. + +9 +00:00:31,598 --> 00:00:34,935 +Last year, we introduced to you all +the Screen Time API, + +10 +00:00:34,968 --> 00:00:37,538 +which allowed you to build +new apps to help your users + +11 +00:00:37,571 --> 00:00:40,607 +manage their child's relationship +with their device. + +12 +00:00:40,641 --> 00:00:43,410 +We were very pleased to see +the creative and productive ways + +13 +00:00:43,443 --> 00:00:45,579 +that you all have made use of our product. + +14 +00:00:45,612 --> 00:00:49,583 +It excites us to see you embracing +our culture of thinking differently. + +15 +00:00:49,616 --> 00:00:53,487 +We are also very thankful for the feedback +we have received in the past year. + +16 +00:00:53,520 --> 00:00:57,558 +Before I introduce to you all +what's new in iOS 16's ScreenTime API, + +17 +00:00:57,591 --> 00:01:01,828 +I would first like to recap +some highlights from our iOS 15 release. + +18 +00:01:01,862 --> 00:01:05,299 +Last year's Screen Time API +introduced three new frameworks: + +19 +00:01:05,332 --> 00:01:09,570 +Family Controls, +Managed Settings, and Device Activity. + +20 +00:01:09,603 --> 00:01:12,806 +In iOS 15, these frameworks +brought new capabilities + +21 +00:01:12,840 --> 00:01:14,975 +to your parental control apps. + +22 +00:01:15,008 --> 00:01:18,145 +To jog your memory, +let's take quick look at all three. + +23 +00:01:18,178 --> 00:01:20,447 +Family Controls is essentially a gateway + +24 +00:01:20,480 --> 00:01:23,350 +since it authorizes access +to the Screen Time API. + +25 +00:01:23,383 --> 00:01:25,719 +This framework allowed you +to prevent removal + +26 +00:01:25,752 --> 00:01:29,022 +of your parental control app, +and it protected user privacy + +27 +00:01:29,056 --> 00:01:33,694 +by providing opaque tokens +used to identify apps and websites. + +28 +00:01:33,727 --> 00:01:36,997 +With ManagedSettings, +your app could apply restrictions, + +29 +00:01:37,030 --> 00:01:40,567 +filter web traffic, and shield activity, +much like Screen Time does, + +30 +00:01:40,601 --> 00:01:43,837 +but customized with your app's +branding and functionality. + +31 +00:01:43,871 --> 00:01:47,241 +The Device Activity framework +allowed you to execute code on the start + +32 +00:01:47,274 --> 00:01:49,943 +and end of timing windows, +as well as whenever usage + +33 +00:01:49,977 --> 00:01:52,145 +of an app or website exceeded a threshold. + +34 +00:01:52,179 --> 00:01:56,950 +Each of these three frameworks have had +some exciting new updates in iOS 16. + +35 +00:01:56,984 --> 00:02:00,754 +These updates are not only going to make +our API easier for you to use + +36 +00:02:00,787 --> 00:02:04,858 +but are also going to enhance +the experience of our users. + +37 +00:02:04,892 --> 00:02:08,962 +I'll walk you through the details +using our demo application, Worklog. + +38 +00:02:08,996 --> 00:02:12,633 +Worklog encourages good habits +for new professionals by restricting + +39 +00:02:12,666 --> 00:02:16,737 +access to certain apps +until a certain usage metric is met. + +40 +00:02:16,770 --> 00:02:20,107 +I would like to show you how Aniyah, +the owner of this iPhone, + +41 +00:02:20,140 --> 00:02:24,978 +can impose these restrictions on herself +using iOS 16's ScreenTime API. + +42 +00:02:25,012 --> 00:02:27,614 +Lets start with Family Controls. + +43 +00:02:27,648 --> 00:02:31,485 +If you remember from iOS 15, +Family Controls was only capable + +44 +00:02:31,518 --> 00:02:35,455 +of authorizing child devices +via iCloud authentication. + +45 +00:02:35,489 --> 00:02:39,693 +We are happy to inform you that in iOS 16, +Family Controls is now capable + +46 +00:02:39,726 --> 00:02:43,330 +of authorizing independent users +from their own device. + +47 +00:02:43,363 --> 00:02:47,167 +This new authorization method +means the Screen Time API can be + +48 +00:02:47,201 --> 00:02:50,070 +used to build more than +just parental controls apps. + +49 +00:02:50,103 --> 00:02:52,873 +Unlike the existing +parental control authorization, + +50 +00:02:52,906 --> 00:02:57,344 +individual authorization can be +used by any number of apps per device. + +51 +00:02:57,377 --> 00:03:02,182 +Also, since individual authorization +is not for parental control use cases, + +52 +00:03:02,216 --> 00:03:06,854 +implicit restrictions for iCloud sign-out +and deletion of an app do not apply. + +53 +00:03:06,887 --> 00:03:09,389 +Let's look at how to use +the new authorization. + +54 +00:03:09,423 --> 00:03:13,160 +The first thing your app needs to do +on launch is request authorization + +55 +00:03:13,193 --> 00:03:14,862 +for FamilyControls. + +56 +00:03:14,895 --> 00:03:17,130 +I will use a shared Authorization Center + +57 +00:03:17,164 --> 00:03:20,434 +to make this request +when Worklog first launches. + +58 +00:03:20,467 --> 00:03:23,937 +The request can either result +in updating the authorization status + +59 +00:03:23,971 --> 00:03:26,139 +or throwing an error. + +60 +00:03:26,173 --> 00:03:28,876 +Since this app has never been +run on this iPhone before, + +61 +00:03:28,909 --> 00:03:32,913 +requestAuthorization will ask +for Aniyah's approval with an alert. + +62 +00:03:32,946 --> 00:03:36,149 +Tapping on Allow will then prompt +the user to authenticate + +63 +00:03:36,183 --> 00:03:40,287 +with Face ID, Touch ID, +or device passcode to continue. + +64 +00:03:40,320 --> 00:03:42,656 +Once the user +has successfully authenticated, + +65 +00:03:42,689 --> 00:03:46,426 +calling requestAuthorization again +will not prompt with an alert again, + +66 +00:03:46,460 --> 00:03:48,529 +but instead silently succeed. + +67 +00:03:48,562 --> 00:03:51,265 +Once an app has +authorized with Family Controls, + +68 +00:03:51,298 --> 00:03:54,468 +two switches are added +in Settings for the app. + +69 +00:03:54,501 --> 00:03:57,804 +One in Screen Time, under +the Apps with Screen Time Access List + +70 +00:03:57,838 --> 00:04:01,675 +and one in per app settings, +labeled as Screen Time Restrictions. + +71 +00:04:01,708 --> 00:04:04,845 +Parents and individual users can +choose to deauthorize the app + +72 +00:04:04,878 --> 00:04:07,581 +from Family Controls +via either of these switches. + +73 +00:04:07,614 --> 00:04:10,751 +As you can see, +using the new individual authorization + +74 +00:04:10,784 --> 00:04:13,887 +is just as simple as using +the parental controls authorization, + +75 +00:04:13,921 --> 00:04:17,558 +and we've made some nice improvements +to both with our use of async. + +76 +00:04:17,591 --> 00:04:20,527 +Now, lets look at the new changes +to Managed Settings. + +77 +00:04:20,561 --> 00:04:24,364 +Managed Settings has been revamped +to make developer use a lot more easy, + +78 +00:04:24,398 --> 00:04:27,401 +particularly in the use +of Managed Settings Store. + +79 +00:04:27,434 --> 00:04:31,471 +For those of you who are unfamiliar, +a Managed Settings Store is a data store + +80 +00:04:31,505 --> 00:04:34,474 +that applies settings +to the current user or device. + +81 +00:04:34,508 --> 00:04:39,580 +In iOS 15, you could only have +one Managed Settings Store per process. + +82 +00:04:39,613 --> 00:04:42,115 +Also, your app +and device activity extensions + +83 +00:04:42,149 --> 00:04:44,818 +had to have +different Managed Settings Stores. + +84 +00:04:44,852 --> 00:04:48,822 +This made it difficult to change settings +in response to device activity. + +85 +00:04:48,856 --> 00:04:53,493 +Now, in iOS 16, you can create +up to 50 Managed Settings Stores + +86 +00:04:53,527 --> 00:04:56,964 +per process, +each with their own unique name. + +87 +00:04:56,997 --> 00:04:59,800 +These named stores are also +automatically shared between + +88 +00:04:59,833 --> 00:05:02,636 +your app and all your app extensions. + +89 +00:05:02,669 --> 00:05:07,341 +Also, you can now remove all the settings +in any given named store all at once. + +90 +00:05:07,374 --> 00:05:10,844 +I'd like to show you +how Worklog uses named stores. + +91 +00:05:10,878 --> 00:05:14,348 +When Worklog launches for the first time +and Aniyah's device has + +92 +00:05:14,381 --> 00:05:18,852 +been successfully authorized, +we create a Gaming ManagedSettingsStore. + +93 +00:05:18,886 --> 00:05:21,255 +This store will contain +our gaming restrictions, + +94 +00:05:21,288 --> 00:05:25,792 +which, in Worklog's case, includes +shielding for all gaming websites. + +95 +00:05:25,826 --> 00:05:29,596 +Worklog also has a store named "Social" +which shields social media + +96 +00:05:29,630 --> 00:05:33,734 +apps and websites as soon as +the app is launched for the first time. + +97 +00:05:33,767 --> 00:05:35,402 +However, when using Worklog, + +98 +00:05:35,435 --> 00:05:38,138 +Aniyah can click +the Allow for Evenings button, + +99 +00:05:38,172 --> 00:05:40,340 +which will create +a Device Activity Schedule + +100 +00:05:40,374 --> 00:05:44,545 +that only allows social media +between 5:00 and 8:00 p.m. + +101 +00:05:44,578 --> 00:05:47,948 +Once it's 5:00 p.m., +our Device Activity Monitor un-restricts + +102 +00:05:47,981 --> 00:05:49,883 +the corresponding named store. + +103 +00:05:49,917 --> 00:05:53,153 +When the timing window ends at 8:00 p.m., +we re-apply restrictions + +104 +00:05:53,187 --> 00:05:56,190 +to social media apps and websites +on Aniyah's device. + +105 +00:05:56,223 --> 00:05:58,225 +Now, some of you may be wondering, + +106 +00:05:58,258 --> 00:06:00,827 +"Didn't our Gaming store +restrict gaming websites?" + +107 +00:06:00,861 --> 00:06:03,197 +"Won't clearing all the settings +in our Social store + +108 +00:06:03,230 --> 00:06:05,799 +conflict with our settings +from our Gaming store?" + +109 +00:06:05,832 --> 00:06:07,134 +The answer is no. + +110 +00:06:07,167 --> 00:06:11,104 +The most restrictive setting always wins, +therefore Gaming websites will + +111 +00:06:11,138 --> 00:06:13,774 +still remain restricted +on Aniyah's device. + +112 +00:06:13,807 --> 00:06:16,743 +We feel that these new named stores +are extremely powerful + +113 +00:06:16,777 --> 00:06:20,814 +and will make it a lot simpler for you +to develop apps using Managed Settings. + +114 +00:06:20,848 --> 00:06:22,449 +But we're not done yet. + +115 +00:06:22,482 --> 00:06:26,386 +We've also added a very exciting feature +to the Device Activity framework. + +116 +00:06:26,420 --> 00:06:30,057 +In iOS 15, +Device Activity allowed your app + +117 +00:06:30,090 --> 00:06:32,726 +to respond to the start and end +of timing windows, + +118 +00:06:32,759 --> 00:06:35,596 +as well as usage thresholds +for apps and websites. + +119 +00:06:35,629 --> 00:06:40,067 +In iOS 16, Device Activity has +a new reporting service which enables + +120 +00:06:40,100 --> 00:06:45,005 +your app to create completely custom +usage reports using SwiftUI. + +121 +00:06:45,038 --> 00:06:49,076 +The usage data is provided to a new +extension point where you can customize + +122 +00:06:49,109 --> 00:06:53,080 +which data is shown to the user, +as well as how it's rendered on screen. + +123 +00:06:53,113 --> 00:06:56,116 +These device activity reports +allow for you to completely + +124 +00:06:56,149 --> 00:06:58,285 +customize your user's experience, + +125 +00:06:58,318 --> 00:07:02,222 +whilst providing the end user +with complete privacy. + +126 +00:07:02,256 --> 00:07:06,059 +In Worklog, you can see us establish +a DeviceActivityReport.Context + +127 +00:07:06,093 --> 00:07:08,762 +and a DeviceActivityFilter. + +128 +00:07:08,795 --> 00:07:11,064 +You can think +of a DeviceActivityReport.Context + +129 +00:07:11,098 --> 00:07:13,500 +as a customizable type +that tells your report + +130 +00:07:13,534 --> 00:07:17,604 +what type of view to draw +based on DeviceActivity Data. + +131 +00:07:17,638 --> 00:07:20,841 +You can customize the timing windows +of whichever report context + +132 +00:07:20,874 --> 00:07:23,177 +by specifying a DeviceActivityFilter. + +133 +00:07:23,210 --> 00:07:27,047 +We will then set the definition +of our device activity report context + +134 +00:07:27,080 --> 00:07:29,516 +inside of our DeviceActivityReportScene + +135 +00:07:29,550 --> 00:07:32,286 +in order to tell the scene +what to represent. + +136 +00:07:32,319 --> 00:07:37,191 +Our content defines our custom +configuration, PieChartView.Configuration, + +137 +00:07:37,224 --> 00:07:40,661 +as well as the resulting +SwiftUI view for this report. + +138 +00:07:40,694 --> 00:07:45,432 +Then, inside of makeConfiguration, we will +map over Aniyah's DeviceActivity Data, + +139 +00:07:45,465 --> 00:07:48,969 +in order to hydrate +our pie chart view's configuration. + +140 +00:07:49,002 --> 00:07:50,938 +The framework will invoke +makeConfiguration + +141 +00:07:50,971 --> 00:07:56,176 +whenever new usage data is fetched +so you do not need to invoke it yourself. + +142 +00:07:56,210 --> 00:07:59,546 +Here, you can see +how PieChartView.Configuration + +143 +00:07:59,580 --> 00:08:01,782 +serves as a view model +for our pie chart view + +144 +00:08:01,815 --> 00:08:05,219 +by providing the view +with Aniyah's device activity data. + +145 +00:08:05,252 --> 00:08:08,455 +Finally, we are able to render +our custom SwiftUI report + +146 +00:08:08,488 --> 00:08:12,259 +by defining it in the body +of our DeviceActivityReport extension. + +147 +00:08:12,292 --> 00:08:15,429 +The pie chart report that we created +for Worklog is just one + +148 +00:08:15,462 --> 00:08:19,132 +of the exciting ways that you can +customize device activity reports. + +149 +00:08:19,166 --> 00:08:24,104 +That concludes our briefing +of what's new to iOS 16's Screen Time API. + +150 +00:08:24,137 --> 00:08:27,407 +As you can see, +the Screen Time API continues to support + +151 +00:08:27,441 --> 00:08:30,410 +features like core restrictions +and device activity monitoring + +152 +00:08:30,444 --> 00:08:34,114 +in the same secure yet privacy-preserving +way that we first launched it. + +153 +00:08:34,147 --> 00:08:35,315 +To recap, + +154 +00:08:35,349 --> 00:08:38,819 +independent users can now authorize +with Family Controls with the option + +155 +00:08:38,852 --> 00:08:42,623 +of authorizing multiple third party +applications per device, + +156 +00:08:42,656 --> 00:08:46,159 +Managed Settings Stores now +locally share settings between apps + +157 +00:08:46,193 --> 00:08:47,461 +and their app extensions, + +158 +00:08:47,494 --> 00:08:49,563 +and as a result, +you are now allowed to manage + +159 +00:08:49,596 --> 00:08:51,999 +multiple named stores in your application, + +160 +00:08:52,032 --> 00:08:57,004 +and finally, the Device Activity framework +is introducing a new privacy-preserving UI + +161 +00:08:57,037 --> 00:09:01,074 +that reveals usage data to users +and is customizable. + +162 +00:09:01,108 --> 00:09:03,944 +We believe that these new enhancements +and additions will broaden + +163 +00:09:03,977 --> 00:09:07,047 +the amount of users capable +of engaging with your application. + +164 +00:09:07,080 --> 00:09:09,516 +Thank you for inspiring this new release + +165 +00:09:09,550 --> 00:09:11,385 +and thank you +for your future contributions + +166 +00:09:11,418 --> 00:09:13,921 +to the digital health +and productivity space. + +167 +00:09:13,954 --> 00:09:16,757 +We cannot wait to see the new +and exciting ways that you make use + +168 +00:09:16,790 --> 00:09:18,325 +of the Screen Time API. + +169 +00:09:18,358 --> 00:09:22,362 +We welcome any feedback that you have +and we will continue working to make this + +170 +00:09:22,396 --> 00:09:25,566 +technology more accessible +for you as well as our users. + +171 +00:09:25,599 --> 00:09:26,700 +Thank you. + diff --git a/eng/2022 Session 110338 Explore media metadata publishing and playback interactions en.srt b/eng/2022 Session 110338 Explore media metadata publishing and playback interactions en.srt new file mode 100644 index 0000000..87e4537 --- /dev/null +++ b/eng/2022 Session 110338 Explore media metadata publishing and playback interactions en.srt @@ -0,0 +1,1356 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:12,646 +Hi, my name is Nik, +and I'm an engineer on the Video team. + +3 +00:00:12,679 --> 00:00:16,049 +Today I'm excited to talk to you +about media metadata publishing + +4 +00:00:16,083 --> 00:00:17,684 +and playback interactions. + +5 +00:00:17,718 --> 00:00:19,887 +So what exactly does that mean? + +6 +00:00:19,920 --> 00:00:21,788 +There are a number of places +on Apple devices + +7 +00:00:21,822 --> 00:00:25,192 +where playback information is displayed, +and where playback can be controlled. + +8 +00:00:25,225 --> 00:00:28,562 +For example, the Now Playing section +of Control Center displays the artwork, + +9 +00:00:28,595 --> 00:00:32,165 +title, and progress for media +that is currently playing on the device. + +10 +00:00:32,199 --> 00:00:36,737 +It also lets you play, pause, +or even skip forward or backward. + +11 +00:00:36,770 --> 00:00:40,741 +Expanding the Now Playing tile shows more +details, like the artwork and progress. + +12 +00:00:40,774 --> 00:00:45,812 +it also allows you to scrub +and increase or decrease the volume. + +13 +00:00:45,846 --> 00:00:48,315 +Lock Screen also displays +the same information and controls, + +14 +00:00:48,348 --> 00:00:51,685 +giving users a convenient place +to check in on progress, pause, + +15 +00:00:51,718 --> 00:00:54,755 +or even AirPlay to another device +without needing to unlock. + +16 +00:00:57,424 --> 00:01:00,527 +No matter what device is playing, +the Now Playing app on Apple Watch + +17 +00:01:00,561 --> 00:01:01,895 +provides the same experience. + +18 +00:01:01,929 --> 00:01:04,398 +It even has an Apple TV remote built in. + +19 +00:01:05,866 --> 00:01:08,702 +On tvOS when using AVKit, +the info overlay + +20 +00:01:08,735 --> 00:01:11,905 +when controls are presented +will show title and chapter information. + +21 +00:01:11,939 --> 00:01:13,373 +When you swipe down to the info pane, + +22 +00:01:13,407 --> 00:01:16,176 +more details like the artwork +and description are shown. + +23 +00:01:17,878 --> 00:01:21,114 +Holding the TV button on +your Apple TV remote shows Control Center, + +24 +00:01:21,148 --> 00:01:25,586 +which like iOS has a Now Playing tile +that also can be expanded. + +25 +00:01:25,619 --> 00:01:28,722 +When audio content starts playing +from the background on tvOS, + +26 +00:01:28,755 --> 00:01:30,657 +be it from pressing the play button +on the remote, + +27 +00:01:30,691 --> 00:01:33,660 +or from selecting a track +in the Music app from another device, + +28 +00:01:33,694 --> 00:01:38,365 +a notification with the Now Playing +information is presented. + +29 +00:01:38,398 --> 00:01:42,936 +Additionally, after a brief period of +inactivity on tvOS when playing audio, + +30 +00:01:42,970 --> 00:01:46,607 +a full screen overlay showing +what's currently playing is presented. + +31 +00:01:48,308 --> 00:01:52,112 +Lastly, on iOS, +the Control Other Speakers and TVs button + +32 +00:01:52,145 --> 00:01:54,815 +lets you view the Now Playing information +on all of your devices, + +33 +00:01:54,848 --> 00:01:56,950 +as well as control playback. + +34 +00:01:58,352 --> 00:02:01,989 +With the growing number of devices and UIs +where Now Playing information is presented + +35 +00:02:02,022 --> 00:02:03,690 +and where playback can be controlled from, + +36 +00:02:03,724 --> 00:02:05,759 +properly publishing +Now Playing information + +37 +00:02:05,792 --> 00:02:08,896 +and responding to remote commands +is more important than ever. + +38 +00:02:08,929 --> 00:02:10,898 +Over the course +of the rest of this session, + +39 +00:02:10,931 --> 00:02:12,900 +we will cover responding +to playback interactions + +40 +00:02:12,933 --> 00:02:15,936 +in the form of remote commands, +automatic metadata publishing, + +41 +00:02:15,969 --> 00:02:18,872 +publishing with AVKit, +and manual publishing. + +42 +00:02:18,906 --> 00:02:21,008 +When using AVFoundation +for media playback, + +43 +00:02:21,041 --> 00:02:22,876 +the best way +to publish Now Playing metadata + +44 +00:02:22,910 --> 00:02:27,347 +and respond to playback interactions +is using the MPNowPlayingSession class. + +45 +00:02:28,749 --> 00:02:31,852 +Historically, this class has only +been available on tvOS, + +46 +00:02:31,885 --> 00:02:34,621 +but is now available on iOS 16. + +47 +00:02:36,523 --> 00:02:38,825 +It is used to represent +a distinct playback session, + +48 +00:02:38,859 --> 00:02:40,727 +and offers control over Now Playing status + +49 +00:02:40,761 --> 00:02:44,398 +if your app contains +multiple active sessions. + +50 +00:02:44,431 --> 00:02:46,633 +It supports both +manual metadata publishing, + +51 +00:02:46,667 --> 00:02:50,537 +as well as the new automatic publishing +available in iOS and tvOS 16. + +52 +00:02:52,206 --> 00:02:55,475 +MPNowPlayingSession shouldn't be used +on tvOS when using AVKit, + +53 +00:02:55,509 --> 00:02:57,678 +which has its own +automatic publishing mechanisms + +54 +00:02:57,711 --> 00:02:59,613 +we'll cover later in the session. + +55 +00:03:00,514 --> 00:03:03,016 +Being the "Now Playing" app means +that your app is what will populate + +56 +00:03:03,050 --> 00:03:05,219 +Control Center, Lock Screen, etcetera, + +57 +00:03:05,252 --> 00:03:06,620 +and receive the playback controls + +58 +00:03:06,653 --> 00:03:10,123 +when the user, for example, presses +pause from one of those interfaces. + +59 +00:03:10,157 --> 00:03:12,559 +With MPNowPlayingSession, +you can represent multiple + +60 +00:03:12,593 --> 00:03:15,229 +concurrent playback sessions +within a single app. + +61 +00:03:15,262 --> 00:03:17,064 +However, when using multiple sessions, + +62 +00:03:17,097 --> 00:03:19,032 +your app must promote one +as the active session + +63 +00:03:19,066 --> 00:03:22,035 +that will appear throughout the system +when remote controlling your app. + +64 +00:03:22,069 --> 00:03:23,737 +For example, with Picture in Picture + +65 +00:03:23,770 --> 00:03:25,772 +you may have two +concurrent playback sessions, + +66 +00:03:25,806 --> 00:03:27,574 +where the full screen playback +should be considered + +67 +00:03:27,608 --> 00:03:30,244 +the active Now Playing session. + +68 +00:03:30,277 --> 00:03:34,548 +The system also has a few heuristics +to qualify apps as Now Playing eligible. + +69 +00:03:34,581 --> 00:03:38,151 +First, you must register a handler +for at least one remote command. + +70 +00:03:38,185 --> 00:03:41,655 +As you can imagine, an app that won't +respond to any playback interactions + +71 +00:03:41,688 --> 00:03:45,559 +is most likely not an ideal candidate +to show up as the Now Playing app. + +72 +00:03:45,592 --> 00:03:48,695 +Second, your apps AVAudioSession +must be configured + +73 +00:03:48,729 --> 00:03:52,032 +with a non-mixable category +and category option. + +74 +00:03:52,065 --> 00:03:54,668 +Mixable playback categories +and options are generally used + +75 +00:03:54,701 --> 00:03:57,471 +when playing back notifications, +and therefore this is a good indication + +76 +00:03:57,504 --> 00:04:01,808 +to the system that whatever is playing is +also not a good candidate for Now Playing. + +77 +00:04:01,842 --> 00:04:04,811 +Here's a few examples +to help understand playback sessions. + +78 +00:04:04,845 --> 00:04:07,281 +In this example there is a single piece +of content playing, + +79 +00:04:07,314 --> 00:04:10,951 +so this would be represented +using a single MPNowPlayingSession. + +80 +00:04:10,984 --> 00:04:14,188 +If your app supports PiP, +you would have two MPNowPlayingSessions: + +81 +00:04:14,221 --> 00:04:17,824 +one for the main player, +and one for the PiP playback. + +82 +00:04:17,858 --> 00:04:20,961 +A more complex scenario would be +a single MPNowPlayingSession + +83 +00:04:20,994 --> 00:04:22,362 +that has several players. + +84 +00:04:22,396 --> 00:04:25,132 +In this example, we have four players, one +in each quadrant, + +85 +00:04:25,165 --> 00:04:27,801 +showing different points of view +for the same race. + +86 +00:04:27,835 --> 00:04:30,337 +Players added to the same +MPNowPlayingSession + +87 +00:04:30,370 --> 00:04:33,173 +should always be part of the same content. + +88 +00:04:33,207 --> 00:04:36,476 +And here's how each of these +example sessions would be instantiated. + +89 +00:04:36,510 --> 00:04:38,879 +The first, we're just playing +a single piece of content, + +90 +00:04:38,912 --> 00:04:41,715 +so we have the single session +with the single player. + +91 +00:04:41,748 --> 00:04:44,051 +The second example +is using Picture-in-Picture, + +92 +00:04:44,084 --> 00:04:46,520 +so we have two sessions, +each with a single player. + +93 +00:04:46,553 --> 00:04:50,958 +The first being the full screen content, +and the second being the content in PiP. + +94 +00:04:50,991 --> 00:04:53,794 +The last example, the multi-view race, +is represented + +95 +00:04:53,827 --> 00:04:56,997 +with a single session with four players. + +96 +00:04:57,030 --> 00:04:58,732 +When an app does have multiple sessions, + +97 +00:04:58,765 --> 00:05:02,936 +it's the apps responsibility for promoting +a given session as active when applicable. + +98 +00:05:02,970 --> 00:05:05,606 +For example, +if media is playing in Picture-in-Picture, + +99 +00:05:05,639 --> 00:05:07,508 +if the user expands it to be full screen, + +100 +00:05:07,541 --> 00:05:10,010 +the previously full screen session +should no longer be active, + +101 +00:05:10,043 --> 00:05:12,679 +or Now Playing, and the PiP session +that is now full screen + +102 +00:05:12,713 --> 00:05:13,947 +should become active. + +103 +00:05:13,981 --> 00:05:17,184 +This transition can be done +by calling becomeActiveIfPossible + +104 +00:05:17,217 --> 00:05:20,787 +on MPNowPlayingSession. + +105 +00:05:20,821 --> 00:05:23,090 +Now that we've covered the basics +of setting up instances + +106 +00:05:23,123 --> 00:05:26,260 +of MPNowPlayingSession +and controlling the Now Playing session, + +107 +00:05:26,293 --> 00:05:28,529 +let's talk about receiving +and responding to remote commands, + +108 +00:05:28,562 --> 00:05:31,298 +be it from Lock Screen, +or from a HomePod in another room. + +109 +00:05:31,331 --> 00:05:33,667 +Let's start off with a basic example +of registering + +110 +00:05:33,700 --> 00:05:35,402 +for the play and pause command. + +111 +00:05:35,435 --> 00:05:37,771 +Doing so will enable your app +to receive callbacks + +112 +00:05:37,804 --> 00:05:40,674 +when the user presses play or pause +from another device, + +113 +00:05:40,707 --> 00:05:43,410 +or issues that command using Siri. + +114 +00:05:43,443 --> 00:05:46,713 +First, we instantiate +our MPNowPlayingSession. + +115 +00:05:46,747 --> 00:05:48,081 +Since we only have one session, + +116 +00:05:48,115 --> 00:05:50,918 +we don't need to invoke +the 'becomeActiveIfPossible' method. + +117 +00:05:50,951 --> 00:05:52,386 +When you only have one session, + +118 +00:05:52,419 --> 00:05:56,256 +it will be the default session +when your app is the Now Playing app. + +119 +00:05:56,290 --> 00:06:00,594 +Each MPNowPlayingSession instance has +its own MPRemoteCommandCenter instance, + +120 +00:06:00,627 --> 00:06:02,563 +which is used to declare +which remote commands + +121 +00:06:02,596 --> 00:06:04,464 +your playback session can respond to. + +122 +00:06:04,498 --> 00:06:07,167 +Next we add a handler for the playCommand + +123 +00:06:07,201 --> 00:06:11,738 +where we invoke the play method +on our player, and return success. + +124 +00:06:11,772 --> 00:06:14,141 +Then we do the same for the pauseCommand. + +125 +00:06:14,174 --> 00:06:17,010 +You should add handlers +for every command that your app supports + +126 +00:06:17,044 --> 00:06:20,280 +and that is applicable +for the currently playing content. + +127 +00:06:20,314 --> 00:06:23,350 +Another example is the skip forward +and skip backward command. + +128 +00:06:23,383 --> 00:06:25,886 +This command should be used +for most content, + +129 +00:06:25,919 --> 00:06:27,287 +and wouldn't be applicable, for example, + +130 +00:06:27,321 --> 00:06:30,357 +live streams +where jumping forward isn't possible. + +131 +00:06:30,390 --> 00:06:32,860 +First we have to indicate +what our preferred intervals are, + +132 +00:06:32,893 --> 00:06:35,996 +or the number of seconds we prefer +to jump in either direction. + +133 +00:06:36,029 --> 00:06:38,365 +In this case, we use 15 seconds. + +134 +00:06:38,398 --> 00:06:40,968 +Then similar to what we did +for the play and pause commands, + +135 +00:06:41,001 --> 00:06:43,504 +we add a handler that will be invoked +when the user presses + +136 +00:06:43,537 --> 00:06:47,174 +the skip forward button +or asks Siri to skip forward. + +137 +00:06:47,207 --> 00:06:51,078 +In our handler, we will be receiving +an MPSkipIntervalCommandEvent, + +138 +00:06:51,111 --> 00:06:54,481 +so first we will cast the event +to that type. + +139 +00:06:54,515 --> 00:06:57,584 +We then calculate the new elapsed time +by taking the current time, + +140 +00:06:57,618 --> 00:07:00,754 +and the interval provided to us +in the MPSkipIntervalCommandEvent, + +141 +00:07:00,787 --> 00:07:05,058 +seek to it, and return success to indicate +that we jumped to the new position. + +142 +00:07:05,092 --> 00:07:07,294 +It's also possible +that your app has situations + +143 +00:07:07,327 --> 00:07:09,096 +where a command +is temporarily not allowed, + +144 +00:07:09,129 --> 00:07:11,265 +for example skipping forward +while in an advertisement. + +145 +00:07:11,298 --> 00:07:14,568 +In that case, +the skipForwardCommand can be disabled. + +146 +00:07:14,601 --> 00:07:16,537 +Now that we're responding +to remote commands, + +147 +00:07:16,570 --> 00:07:18,972 +we will cover +automatic metadata publishing. + +148 +00:07:19,006 --> 00:07:22,376 +Automatic publishing takes the hard work +out of keeping metadata accurate + +149 +00:07:22,409 --> 00:07:25,312 +by automatically maintaining +metadata properties it can observe + +150 +00:07:25,345 --> 00:07:28,882 +directly from the player such as duration, +the current elapsed time, + +151 +00:07:28,916 --> 00:07:31,585 +the playback state, and playback progress. + +152 +00:07:31,618 --> 00:07:34,388 +If the content has ads baked into it +that shouldn't contribute + +153 +00:07:34,421 --> 00:07:36,156 +to the total duration and elapsed time, + +154 +00:07:36,190 --> 00:07:39,726 +it can also take care of calculating +the net time and report that instead. + +155 +00:07:39,760 --> 00:07:43,330 +Other metadata such as the title, +description, and artwork can be added + +156 +00:07:43,363 --> 00:07:47,434 +to the AVPlayerItems directly +using the nowPlayingInfo property. + +157 +00:07:47,467 --> 00:07:49,503 +In this example, +we will use automatic publishing + +158 +00:07:49,536 --> 00:07:52,639 +to do the bulk of the work +and set the title and artwork ourselves. + +159 +00:07:52,673 --> 00:07:55,776 +First, we create +a new MPMediaItemArtwork instance, + +160 +00:07:55,809 --> 00:07:57,144 +passing in the artwork image. + +161 +00:07:57,177 --> 00:08:00,180 +Most apps will perform a network request +to fetch this. + +162 +00:08:00,214 --> 00:08:03,150 +Then we set the string title +of the content. + +163 +00:08:03,183 --> 00:08:04,985 +Then we take our artwork and title + +164 +00:08:05,018 --> 00:08:06,987 +and set them +as the nowPlayingInfo dictionary + +165 +00:08:07,020 --> 00:08:10,457 +on the current player item using +MPMediaItemPropertyTitle + +166 +00:08:10,490 --> 00:08:12,893 +and MPMediaItemPropertyArtwork. + +167 +00:08:12,926 --> 00:08:16,396 +Now Playing metadata can consist of +both MPMediaItemProperty's + +168 +00:08:16,430 --> 00:08:19,399 +and MPNowPlayingInfoProperty's. + +169 +00:08:19,433 --> 00:08:22,102 +Lastly, we create +our MPNowPlayingSession instance + +170 +00:08:22,135 --> 00:08:23,270 +passing in our player, + +171 +00:08:23,303 --> 00:08:26,740 +and set automaticallyPublishNowPlayingInfo +to true. + +172 +00:08:26,773 --> 00:08:29,376 +Once automaticallyPublishNowPlayingInfo +is set to true, + +173 +00:08:29,409 --> 00:08:32,279 +the MPNowPlayingSession instance +will begin observing the player + +174 +00:08:32,312 --> 00:08:33,814 +for state changes such as scrubbing, + +175 +00:08:33,847 --> 00:08:37,050 +play/pause events, +or the current player item changing. + +176 +00:08:37,084 --> 00:08:40,120 +Here's another example where we will show +how to use automatic publishing + +177 +00:08:40,153 --> 00:08:42,789 +for instances where +ads are baked into the asset + +178 +00:08:42,823 --> 00:08:47,327 +and you don't want the total duration or +current elapsed time to include ad time. + +179 +00:08:47,361 --> 00:08:50,898 +To do this, we'll create instances +of MPAdTimeRange + +180 +00:08:50,931 --> 00:08:52,766 +for every ad that we have baked in. + +181 +00:08:52,799 --> 00:08:55,502 +In this example, +we have a single 30-second ad + +182 +00:08:55,536 --> 00:08:56,837 +that starts at the very beginning. + +183 +00:08:56,870 --> 00:09:01,909 +So we create it with a starting point +of zero, and a duration of 30 seconds. + +184 +00:09:01,942 --> 00:09:04,811 +Similar to how we did the title +and artwork earlier, + +185 +00:09:04,845 --> 00:09:09,082 +we simply add an array of MPAdTimeRange's +to the nowPlayingInfo dictionary + +186 +00:09:09,116 --> 00:09:14,154 +on the player item using +the MPNowPlayingInfoPropertyAdTimeRanges. + +187 +00:09:14,188 --> 00:09:17,724 +Then just as we did before, +create the MPNowPlayingSession + +188 +00:09:17,758 --> 00:09:19,993 +and enable automatic publishing. + +189 +00:09:20,027 --> 00:09:22,729 +Next is metadata publishing with AVKit. + +190 +00:09:22,763 --> 00:09:26,400 +Publishing Now Playing metadata with AVKit +on tvOS works very similar + +191 +00:09:26,433 --> 00:09:27,968 +to MPNowPlayingSession: + +192 +00:09:28,001 --> 00:09:30,137 +metadata is added +directly to the AVPlayerItem, + +193 +00:09:30,170 --> 00:09:33,207 +and values like elapsed time, duration, +and playback state are published + +194 +00:09:33,240 --> 00:09:34,842 +and kept up to date for you. + +195 +00:09:34,875 --> 00:09:37,377 +The metadata gathered from the player +and asset directly, + +196 +00:09:37,411 --> 00:09:40,480 +combined with the metadata provided +by your app on the AVPlayerItem + +197 +00:09:40,514 --> 00:09:43,417 +are also used to populate the info pane +in the player UI. + +198 +00:09:43,450 --> 00:09:47,554 +AVKit also takes care of registering for +and responding to remote commands. + +199 +00:09:47,588 --> 00:09:50,858 +Using AVKit is the best and easiest way to +integrate with the platform features + +200 +00:09:50,891 --> 00:09:55,562 +we've discussed so far, as well as others +such as AirPlay and Picture-in-Picture. + +201 +00:09:55,596 --> 00:09:59,466 +Setting the metadata when using AVKit +is done using the externalMetadata array + +202 +00:09:59,499 --> 00:10:03,637 +on the AVPlayerItem, which consists of +the AVMetadataItem instances + +203 +00:10:03,670 --> 00:10:05,138 +to describe your content. + +204 +00:10:05,172 --> 00:10:08,342 +You usually up setting three values +on each AVMetadataItem. + +205 +00:10:08,375 --> 00:10:10,711 +First, the identifier, which is the key + +206 +00:10:10,744 --> 00:10:14,381 +to indicate what metadata +the AVMetadataItem represents. + +207 +00:10:14,414 --> 00:10:18,285 +For example, +AVMetadataCommonIdentifierTitle + +208 +00:10:18,318 --> 00:10:21,788 +for the content title, +or AVMetadataCommonIdentifierArtwork + +209 +00:10:21,822 --> 00:10:23,357 +for the artwork. + +210 +00:10:23,390 --> 00:10:24,992 +Second is the value. + +211 +00:10:25,025 --> 00:10:27,160 +For title, this would +be a string containing the title. + +212 +00:10:27,194 --> 00:10:31,198 +For artwork, this would be an NSData +instance containing image data. + +213 +00:10:31,231 --> 00:10:34,735 +The dataType is used to indicate +the format of the artwork provided. + +214 +00:10:34,768 --> 00:10:36,537 +If it contained JPEG data, + +215 +00:10:36,570 --> 00:10:40,274 +kCMMetadatabaseDataType_JPEG +would be used. + +216 +00:10:40,307 --> 00:10:44,378 +Lastly, the extendedLanguageTag +is used to indicate the language used + +217 +00:10:44,411 --> 00:10:47,014 +for strings +such as the title and description. + +218 +00:10:47,047 --> 00:10:49,917 +Most of the time, the value +"und" should be used here + +219 +00:10:49,950 --> 00:10:52,653 +to ensure all audiences +see the same values. + +220 +00:10:52,686 --> 00:10:55,923 +You may be tempted to use +"en-us" if the values are in English, + +221 +00:10:55,956 --> 00:10:58,992 +but doing so would cause devices with +the language set to any other language + +222 +00:10:59,026 --> 00:11:01,261 +such as Spanish to not show the metadata. + +223 +00:11:02,262 --> 00:11:05,899 +Here we have an example where +we are setting the artwork and title. + +224 +00:11:05,933 --> 00:11:08,402 +First, we grab the artwork image data +from our bundle. + +225 +00:11:08,435 --> 00:11:11,471 +Most apps will fetch this +from a network resource. + +226 +00:11:11,505 --> 00:11:15,442 +Then we instantiate +a new mutable AVMetadataItem. + +227 +00:11:15,475 --> 00:11:18,712 +We set the identifier +to .commonIdentifierArtwork. + +228 +00:11:18,745 --> 00:11:23,183 +Then we set the value as the raw +artwork image data as NSData. + +229 +00:11:23,217 --> 00:11:24,651 +Since the image data is JPEG, + +230 +00:11:24,685 --> 00:11:28,021 +we set the dataType +to kCMMetadataBaseDataType_JPEG. + +231 +00:11:28,055 --> 00:11:29,890 +If your artwork was instead a PNG, + +232 +00:11:29,923 --> 00:11:33,193 +you would use +kCMMetadataBaseDataType_PNG. + +233 +00:11:33,227 --> 00:11:35,128 +Because we want this metadata +to be visible + +234 +00:11:35,162 --> 00:11:37,097 +to users with devices set to any language, + +235 +00:11:37,130 --> 00:11:40,734 +we set the extendedLanguageTag +to "und," or "undefined." + +236 +00:11:40,767 --> 00:11:44,137 +We then repeat the same steps for +the title, using .commonIdentifierTitle, + +237 +00:11:44,171 --> 00:11:46,073 +and the string title for the value, + +238 +00:11:46,106 --> 00:11:49,543 +and "und" once again +for the extendedLanguageTag. + +239 +00:11:49,576 --> 00:11:51,545 +Once we've set up +all of our metadata items, + +240 +00:11:51,578 --> 00:11:54,114 +we add them to an array and +set it to the AVPlayerItem's + +241 +00:11:54,147 --> 00:11:57,718 +externalMetadata property. + +242 +00:11:57,751 --> 00:12:00,320 +Now that we have the artwork and title +added to the player item, + +243 +00:12:00,354 --> 00:12:05,425 +you can see how this maps to what is shown +in Control Center and Lock Screen on iOS. + +244 +00:12:05,459 --> 00:12:07,995 +Like artwork, there are other +metadata types that can be set + +245 +00:12:08,028 --> 00:12:11,532 +such as the description, +subtitle information, and content rating. + +246 +00:12:11,565 --> 00:12:13,634 +Your app should set +as many of these as possible + +247 +00:12:13,667 --> 00:12:17,604 +to provide the user with +as rich of an experience as possible. + +248 +00:12:17,638 --> 00:12:20,807 +So far we've covered automatic publishing +with MPNowPlayingSession + +249 +00:12:20,841 --> 00:12:22,276 +and publishing with AVKit. + +250 +00:12:22,309 --> 00:12:24,811 +But MPNowPlayingSession +and its automatic publishing feature + +251 +00:12:24,845 --> 00:12:27,314 +require passing +an AVPlayer instance to it. + +252 +00:12:27,347 --> 00:12:31,785 +That may not be an option for all apps, +and manual publishing is still possible. + +253 +00:12:31,818 --> 00:12:35,055 +Publishing manually requires +that you provide values for all metadata. + +254 +00:12:35,088 --> 00:12:37,824 +Unlike automatic publishing, +information such as elapsed time + +255 +00:12:37,858 --> 00:12:40,360 +and playback rate can't be determined +by the system for you. + +256 +00:12:40,394 --> 00:12:43,764 +This means that you have manual fine grain +control over low level playback state, + +257 +00:12:43,797 --> 00:12:48,302 +and your app is responsible for keeping +it accurate over time as playback changes. + +258 +00:12:48,335 --> 00:12:51,071 +Note that registering for +and responding to remote commands + +259 +00:12:51,104 --> 00:12:52,406 +is still required as well, + +260 +00:12:52,439 --> 00:12:54,975 +and because we are not using +MPNowPlayingSession, + +261 +00:12:55,008 --> 00:12:58,645 +the shared instance +of MPRemoteCommandCenter must be used. + +262 +00:12:58,679 --> 00:13:02,082 +Here's a basic example showing how +to update the Now Playing Info dictionary. + +263 +00:13:02,115 --> 00:13:06,019 +First, we create an MPMediaItemArtwork +instance containing the image, + +264 +00:13:06,053 --> 00:13:08,055 +similar to what we did +for automatic publishing. + +265 +00:13:08,088 --> 00:13:12,125 +Then, we create a dictionary containing +the metadata that we have available. + +266 +00:13:12,159 --> 00:13:17,064 +In this case, we set the title, artwork, +and the player values duration, + +267 +00:13:17,097 --> 00:13:19,399 +elapsed time, and playback rate. + +268 +00:13:19,433 --> 00:13:21,768 +We then set it on +the MPNowPlayingInfoCenter + +269 +00:13:21,802 --> 00:13:23,670 +default instance. + +270 +00:13:23,704 --> 00:13:27,074 +Updates to this metadata should be made +any time significant changes happen + +271 +00:13:27,107 --> 00:13:29,076 +during playback, +such as a play or pause, + +272 +00:13:29,109 --> 00:13:32,779 +the user scrubs forwards or backwards, +or a new piece of content begins playing. + +273 +00:13:32,813 --> 00:13:34,948 +You do not need to update +elapsed time periodically. + +274 +00:13:34,982 --> 00:13:37,484 +The system will always infer +the correct elapsed time + +275 +00:13:37,518 --> 00:13:41,054 +based on how much time has passed +since the last update. + +276 +00:13:41,088 --> 00:13:43,857 +Now that you are familiar with all of +the different ways to publish Now Playing + +277 +00:13:43,891 --> 00:13:47,494 +metadata and respond to remote commands +from other devices and interfaces, + +278 +00:13:47,528 --> 00:13:50,130 +you should integrate to maximize +the user experience. + +279 +00:13:50,163 --> 00:13:51,632 +It's easier than ever. + +280 +00:13:51,665 --> 00:13:53,834 +Existing integrations can benefit too– + +281 +00:13:53,867 --> 00:13:57,070 +switching to automatic publishing is an +easy way to prevent future regressions + +282 +00:13:57,104 --> 00:13:59,506 +and minimize the amount of code +you must maintain. + +283 +00:13:59,540 --> 00:14:03,410 +For more information, +see MediaPlayer on developer.apple.com. + +284 +00:14:03,443 --> 00:14:04,811 +Thanks for watching. + diff --git a/eng/2022 Session 110339 Build device-to-device interactions with Network Framework en.srt b/eng/2022 Session 110339 Build device-to-device interactions with Network Framework en.srt new file mode 100644 index 0000000..513b79d --- /dev/null +++ b/eng/2022 Session 110339 Build device-to-device interactions with Network Framework en.srt @@ -0,0 +1,966 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:12,045 +Hello, everyone. +I'm Elliot Garner. + +3 +00:00:12,079 --> 00:00:14,848 +I'm an engineer +on the Internet Technologies team, + +4 +00:00:14,882 --> 00:00:18,852 +and today I'm going to be discussing +how to build device-to-device experiences + +5 +00:00:18,886 --> 00:00:20,420 +using Network framework, + +6 +00:00:20,454 --> 00:00:23,924 +and its new companion framework, +DeviceDiscoveryUI. + +7 +00:00:23,957 --> 00:00:26,960 +We'll start off by learning +about cross-device connectivity, + +8 +00:00:26,994 --> 00:00:30,197 +and how it can improve your apps. + +9 +00:00:30,230 --> 00:00:33,600 +We'll explore how a new framework, +DeviceDiscoveryUI, + +10 +00:00:33,634 --> 00:00:39,306 +which pairs with Network framework to make +that connectivity as simple as possible. + +11 +00:00:39,339 --> 00:00:42,075 +We'll walk through a code example +of how to update your apps + +12 +00:00:42,109 --> 00:00:44,311 +to use DeviceDiscoveryUI, + +13 +00:00:44,344 --> 00:00:47,381 +including an in-depth look +at how to discover devices + +14 +00:00:47,414 --> 00:00:50,117 +using the new system device picker, + +15 +00:00:50,150 --> 00:00:55,255 +And how to connect to those devices +using Network framework. + +16 +00:00:55,289 --> 00:00:58,258 +So let's start +with cross-device connectivity. + +17 +00:00:58,292 --> 00:01:00,894 +Connecting to, +and exchanging data between, + +18 +00:01:00,928 --> 00:01:04,064 +nearby devices is often essential +for building seamless, + +19 +00:01:04,097 --> 00:01:07,067 +integrated experiences within your apps. + +20 +00:01:07,100 --> 00:01:10,737 +Fitness and meditation apps +often work best on a large screen + +21 +00:01:10,771 --> 00:01:13,307 +where a coach can demonstrate moves. + +22 +00:01:13,340 --> 00:01:15,576 +People benefit +from seeing their heart rate, + +23 +00:01:15,609 --> 00:01:20,848 +calorie burn, and movement data +collected from their Apple Watch. + +24 +00:01:20,881 --> 00:01:24,685 +Likewise, gaming experiences +can be made more immersive + +25 +00:01:24,718 --> 00:01:27,855 +with input and actions +from a connected iPhone, + +26 +00:01:27,888 --> 00:01:33,126 +or augmented by extending a second-screen +experience to a nearby iPad. + +27 +00:01:33,160 --> 00:01:38,332 +All of these experiences– +games, fitness, and wellness apps– + +28 +00:01:38,365 --> 00:01:43,270 +all share a need for reliable and +easy-to-use cross-device connectivity. + +29 +00:01:43,303 --> 00:01:46,039 +And that all starts +with discovering a device. + +30 +00:01:46,073 --> 00:01:50,944 +To help kickstart that discovery, +we're introducing DeviceDiscoveryUI– + +31 +00:01:50,978 --> 00:01:53,647 +a new framework that provides secure, + +32 +00:01:53,680 --> 00:01:57,885 +privacy-preserving discovery +of nearby devices. + +33 +00:01:57,918 --> 00:02:02,756 +New in iOS 16, DeviceDiscoveryUI +pairs with Network framework + +34 +00:02:02,789 --> 00:02:05,092 +to enable device-to-device connectivity + +35 +00:02:05,125 --> 00:02:10,631 +between your Apple TV apps and nearby +iPhones, iPads, and Apple Watches. + +36 +00:02:12,432 --> 00:02:19,139 +DeviceDiscoveryUI provides system UI +for easy discovery of nearby devices. + +37 +00:02:19,173 --> 00:02:21,074 +Once a device has been discovered, + +38 +00:02:21,108 --> 00:02:24,545 +your app can easily open a connection +to that device. + +39 +00:02:24,578 --> 00:02:27,848 +Because this connection was established +using system UI, + +40 +00:02:27,881 --> 00:02:32,386 +you don't need to worry about managing +permissions for local network access. + +41 +00:02:32,419 --> 00:02:36,723 +And because the system handles +securely establishing this connection, + +42 +00:02:36,757 --> 00:02:39,459 +you no longer need to implement +your own key exchange, + +43 +00:02:39,493 --> 00:02:42,963 +because the platform encrypts +transmitted data for you. + +44 +00:02:42,996 --> 00:02:45,899 +Here we can see that system UI in action. + +45 +00:02:45,933 --> 00:02:48,936 +On the left, your app's icon is displayed, + +46 +00:02:48,969 --> 00:02:51,839 +along with a required +usage description string + +47 +00:02:51,872 --> 00:02:55,542 +explaining what your app does +with cross-device connectivity. + +48 +00:02:55,576 --> 00:02:59,379 +On the right +is the list of discovered devices. + +49 +00:02:59,413 --> 00:03:02,950 +Because some apps may only be available +on specific platforms, + +50 +00:03:02,983 --> 00:03:05,986 +you can filter discovered devices +by platform. + +51 +00:03:08,488 --> 00:03:10,924 +Upon selection of "Adam's iPhone", + +52 +00:03:10,958 --> 00:03:13,393 +the system prompts for permission +to create a connection + +53 +00:03:13,427 --> 00:03:15,562 +between those devices. + +54 +00:03:15,596 --> 00:03:19,566 +With this explicit user consent, +you no longer need to request access + +55 +00:03:19,600 --> 00:03:21,668 +to the entire local network. + +56 +00:03:21,702 --> 00:03:23,904 +And once permission has been granted, + +57 +00:03:23,937 --> 00:03:26,039 +your app will be launched +on the selected device + +58 +00:03:26,073 --> 00:03:28,375 +to handle incoming connections. + +59 +00:03:29,977 --> 00:03:33,213 +This means your app no longer needs +to be running on both devices + +60 +00:03:33,247 --> 00:03:35,382 +before a connection can be established. + +61 +00:03:35,415 --> 00:03:38,285 +When permission is granted on +"Joe's Apple Watch", + +62 +00:03:38,318 --> 00:03:42,823 +the system immediately launches your app +so the two devices can connect. + +63 +00:03:44,358 --> 00:03:46,760 +And if your app isn't installed +on that device, + +64 +00:03:46,793 --> 00:03:49,630 +the system offers to take people +to the App Store. + +65 +00:03:49,663 --> 00:03:51,899 +That way they can immediately +download your app + +66 +00:03:51,932 --> 00:03:54,635 +and begin using your new features. + +67 +00:03:54,668 --> 00:03:57,204 +Here's how that looks on watchOS. + +68 +00:03:57,237 --> 00:04:01,808 +Tapping the button will immediately launch +your app's page in the App Store, + +69 +00:04:01,842 --> 00:04:03,977 +for a quick and easy download. + +70 +00:04:04,011 --> 00:04:08,682 +So now that we've discussed what +DeviceDiscoveryUI can do for your apps, + +71 +00:04:08,715 --> 00:04:11,952 +let's look at how to adopt +this new framework. + +72 +00:04:13,187 --> 00:04:16,323 +In previous sessions, +we built the Tic-Tac-Toe app + +73 +00:04:16,356 --> 00:04:18,058 +using Network framework. + +74 +00:04:18,091 --> 00:04:21,995 +Today we're gonna be updating it +to use DeviceDiscoveryUI + +75 +00:04:22,029 --> 00:04:26,099 +for discovery of +and connections to nearby devices. + +76 +00:04:26,133 --> 00:04:29,069 +In earlier iterations, +our app only supported + +77 +00:04:29,102 --> 00:04:31,538 +playing games between iOS devices. + +78 +00:04:31,572 --> 00:04:35,709 +So to start, we've updated our project +for Universal Purchase + +79 +00:04:35,742 --> 00:04:40,981 +and added a tvOS and watchOS target +that all share the same bundle ID. + +80 +00:04:41,014 --> 00:04:45,285 +And we've changed the app so that instead +of competing against another player, + +81 +00:04:45,319 --> 00:04:49,356 +you're using your device +to play against an AI on the TV. + +82 +00:04:49,389 --> 00:04:52,292 +Next, we need to make additions +to the Info.plist + +83 +00:04:52,326 --> 00:04:56,163 +on both our tvOS application +as well as the other platforms + +84 +00:04:56,196 --> 00:05:00,000 +to declare our new Tic-Tac-Toe +application service. + +85 +00:05:00,033 --> 00:05:04,071 +Finally, we'll present the new device +picker and use the resulting endpoints + +86 +00:05:04,104 --> 00:05:05,706 +to make our connection. + +87 +00:05:05,739 --> 00:05:07,841 +Now I'm gonna shift over to Xcode + +88 +00:05:07,875 --> 00:05:10,644 +and show you +what those new Info.plist keys are, + +89 +00:05:10,677 --> 00:05:14,014 +and how they need to be added to the app. + +90 +00:05:14,047 --> 00:05:19,019 +On tvOS, our app needs to tell the system +what application services to discover, + +91 +00:05:19,052 --> 00:05:22,022 +and what the platforms +those services support. + +92 +00:05:22,055 --> 00:05:26,894 +To do that, we need to add our new +"Application Services" Info.plist. + +93 +00:05:28,595 --> 00:05:32,266 +This dictionary will map to +one of two different arrays. + +94 +00:05:32,299 --> 00:05:36,303 +On tvOS, we need to declare +the "Browses" array, + +95 +00:05:36,336 --> 00:05:40,541 +which contains all of the application +services that our app discovers. + +96 +00:05:40,574 --> 00:05:44,411 +Each entry represents +a different application service. + +97 +00:05:44,444 --> 00:05:48,682 +Our first item represents +our Tic-Tac-Toe application service. + +98 +00:05:48,715 --> 00:05:51,552 +It is a dictionary +containing our service identifier, + +99 +00:05:51,585 --> 00:05:55,789 +usage description, +and platforms this service supports. + +100 +00:05:55,822 --> 00:06:00,227 +The service identifier is the name +of our service, "TicTacToe". + +101 +00:06:00,260 --> 00:06:04,431 +The usage description is a string that +will be displayed in the device picker + +102 +00:06:04,464 --> 00:06:08,869 +to explain why our app needs access +to other local devices. + +103 +00:06:08,902 --> 00:06:11,772 +Finally, we have our platform support. + +104 +00:06:11,805 --> 00:06:15,209 +This array contains the platforms +supported by the service, + +105 +00:06:15,242 --> 00:06:18,145 +and the system will filter +discovered devices accordingly. + +106 +00:06:18,178 --> 00:06:24,017 +You can see here that our service supports +iOS, iPadOS, and watchOS devices. + +107 +00:06:24,051 --> 00:06:28,222 +We've already updated the Info.plist +used for iOS and iPadOS + +108 +00:06:28,255 --> 00:06:31,491 +with the corresponding "Application +Services" dictionary. + +109 +00:06:31,525 --> 00:06:35,062 +Now we're gonna update +the plist for watchOS. + +110 +00:06:35,095 --> 00:06:38,532 +This time, +we declare the "Advertises" array. + +111 +00:06:38,565 --> 00:06:42,636 +Because DeviceDiscoveryUI will launch +our app when it's not running, + +112 +00:06:42,669 --> 00:06:47,174 +the system uses this array +to know which services to advertise. + +113 +00:06:47,207 --> 00:06:52,846 +Our entry in the "Advertises" array only +needs the "TicTacToe" service identifier. + +114 +00:06:52,880 --> 00:06:56,250 +Make sure that the service identifier +is the same for every platform + +115 +00:06:56,283 --> 00:06:59,620 +that was declared in the tvOS Info.plist. + +116 +00:06:59,653 --> 00:07:02,656 +Now that Tic-Tac-Toe is configured +to browse for + +117 +00:07:02,689 --> 00:07:05,092 +and advertise our application service, + +118 +00:07:05,125 --> 00:07:08,262 +let's have it actually display +the device picker UI. + +119 +00:07:08,295 --> 00:07:10,931 +Our usage description appears on the left. + +120 +00:07:10,964 --> 00:07:14,601 +We made sure this string is active +and that it informs people + +121 +00:07:14,635 --> 00:07:18,305 +of why they want to connect a device +and what data they're sharing. + +122 +00:07:18,338 --> 00:07:23,477 +For Tic-Tac-Toe, we're using the connected +device as a controller to play the game. + +123 +00:07:23,510 --> 00:07:27,581 +Back in Xcode, because DeviceDiscoveryUI +replaces the need + +124 +00:07:27,614 --> 00:07:32,286 +to manually browse for nearby devices +and secures the connection for us, + +125 +00:07:32,319 --> 00:07:36,223 +we can remove the PeerBrowser file +and our passcode extension + +126 +00:07:36,256 --> 00:07:40,227 +to NWParameters +since they're no longer necessary. + +127 +00:07:40,260 --> 00:07:45,032 +Now, we need parameters to describe how +we'd like to connect to nearby devices. + +128 +00:07:48,802 --> 00:07:51,872 +First, we use the new convenience initializer, + +129 +00:07:51,905 --> 00:07:54,975 +applicationService, on NWParameters, + +130 +00:07:55,008 --> 00:07:58,679 +which gives us everything we need +for this kind of local connectivity. + +131 +00:08:00,547 --> 00:08:04,952 +Next, we can use our existing framer +for communicating gameplay actions, + +132 +00:08:04,985 --> 00:08:10,390 +without any changes, and simply add it to +the protocol stack in those parameters. + +133 +00:08:10,424 --> 00:08:13,760 +Now, we're ready to create +and show the device picker. + +134 +00:08:13,794 --> 00:08:17,664 +The device picker is how +our application discovers nearby iPhones, + +135 +00:08:17,698 --> 00:08:21,268 +iPads, and Apple Watches. + +136 +00:08:21,301 --> 00:08:24,738 +First, we need to check to see +if the device picker is supported + +137 +00:08:24,771 --> 00:08:26,473 +on the current device. + +138 +00:08:26,507 --> 00:08:29,209 +To do that, we call the +isSupported function + +139 +00:08:29,243 --> 00:08:33,981 +with the browse descriptor and parameters +that we'll be using with the picker. + +140 +00:08:34,014 --> 00:08:38,085 +Assuming it returns true, we can go ahead +and create the device picker. + +141 +00:08:40,053 --> 00:08:43,991 +We instantiate the device picker +with the parameters we created earlier, + +142 +00:08:44,024 --> 00:08:47,294 +and a browse descriptor +of type .applicationService, + +143 +00:08:47,327 --> 00:08:52,399 +specifying the name of the application +service that we defined in the Info.plist. + +144 +00:08:55,402 --> 00:08:57,771 +Now that the device picker +has been created, + +145 +00:08:57,804 --> 00:09:00,073 +we need to present the view controller. + +146 +00:09:00,107 --> 00:09:05,012 +The device picker needs to always be +presented as a full screen modal view. + +147 +00:09:07,581 --> 00:09:12,519 +Next, we need to access +the picker's async endpoint property. + +148 +00:09:12,553 --> 00:09:15,856 +Once the connection has been confirmed +and our application has been launched + +149 +00:09:15,889 --> 00:09:17,357 +on the remote device, + +150 +00:09:17,391 --> 00:09:21,094 +we'll receive an NWEndpoint, +and execution will continue. + +151 +00:09:21,128 --> 00:09:24,965 +Now that our application +has received an NWEndpoint, + +152 +00:09:24,998 --> 00:09:27,868 +we can use it to connect +to the selected device, + +153 +00:09:27,901 --> 00:09:31,572 +just like we did +in prior Tic-Tac-Toe versions. + +154 +00:09:31,605 --> 00:09:34,608 +We'll use the same parameters +when opening the connection + +155 +00:09:34,641 --> 00:09:37,244 +as provided to the device picker. + +156 +00:09:37,277 --> 00:09:40,781 +Aside from the parameters, +opening a connection to this device + +157 +00:09:40,814 --> 00:09:43,617 +looks exactly the same as it did before. + +158 +00:09:44,384 --> 00:09:48,088 +Moving to the selected device, +once our application has been launched, + +159 +00:09:48,121 --> 00:09:52,426 +we need to immediately fulfill the promise +our application made to the system + +160 +00:09:52,459 --> 00:09:54,928 +by creating an NWListener. + +161 +00:09:54,962 --> 00:09:58,899 +The NWListener needs to be created +as soon as the app is launched + +162 +00:09:58,932 --> 00:10:03,070 +to accept any incoming connections +for that application service. + +163 +00:10:03,103 --> 00:10:07,975 +The NWListener needs to be created +with the exact same parameters as before, + +164 +00:10:08,008 --> 00:10:10,911 +and we need to set the application service +on the listener + +165 +00:10:10,944 --> 00:10:14,081 +using the identifier from the Info.plist. + +166 +00:10:14,114 --> 00:10:16,783 +When the TV opens +a connection to this device, + +167 +00:10:16,817 --> 00:10:20,354 +the listener that we established +will receive that connection here, + +168 +00:10:20,387 --> 00:10:23,357 +in the new connection handler, +same as before. + +169 +00:10:23,390 --> 00:10:26,159 +So now that the connection +has been properly established, + +170 +00:10:26,193 --> 00:10:29,096 +we need to handle +application state transitions. + +171 +00:10:29,129 --> 00:10:32,866 +When our application is backgrounded, +the connection will transition + +172 +00:10:32,900 --> 00:10:37,371 +to the failed state with the associated +error ECONNABORTED. + +173 +00:10:37,404 --> 00:10:40,607 +If we want to continue +communication between the devices, + +174 +00:10:40,641 --> 00:10:45,078 +we establish a new connection +from the TV to the same endpoint. + +175 +00:10:45,112 --> 00:10:48,982 +Once started, this new connection stays +in the preparing state + +176 +00:10:49,016 --> 00:10:54,087 +and moves to the ready state once +the app is resumed on the selected device. + +177 +00:10:54,121 --> 00:10:57,057 +On that device, +the new connection will be delivered + +178 +00:10:57,090 --> 00:11:01,195 +to the same NWListener +and can be used to resume our activity. + +179 +00:11:01,228 --> 00:11:02,796 +And that's it. + +180 +00:11:02,829 --> 00:11:07,401 +Everything we need to do to migrate +to DeviceDiscoveryUI is now complete. + +181 +00:11:07,434 --> 00:11:09,503 +Let's see our game in action. + +182 +00:11:09,536 --> 00:11:11,271 +The app launches on the TV, + +183 +00:11:11,305 --> 00:11:15,142 +and we press Find Opponent +to present the device picker. + +184 +00:11:19,046 --> 00:11:22,082 +When we choose a device, +it prompts for permission. + +185 +00:11:22,115 --> 00:11:26,620 +When granted, the app is launched +immediately into our game session. + +186 +00:11:32,860 --> 00:11:37,731 +Now, we can play against the TV +by placing emojis on the board. + +187 +00:11:43,470 --> 00:11:45,439 +Great. + +188 +00:11:45,472 --> 00:11:49,810 +And that's how easy it is +to connect devices to Apple TV. + +189 +00:11:49,843 --> 00:11:52,813 +Tic-Tac-Toe is available +on the developer website, + +190 +00:11:52,846 --> 00:11:57,150 +so you can download it and explore +all of the code we discussed today. + +191 +00:11:57,184 --> 00:12:01,021 +If you have any questions, +post them on the Developer Forums. + +192 +00:12:01,054 --> 00:12:04,057 +Consider adopting DeviceDiscoveryUI +in your apps + +193 +00:12:04,091 --> 00:12:07,227 +to provide seamless device-to-device +experiences. + +194 +00:12:07,261 --> 00:12:10,130 +And please provide us feedback. + +195 +00:12:10,163 --> 00:12:13,967 +We know that device-to-device +communication is an essential experience. + +196 +00:12:14,001 --> 00:12:17,804 +DeviceDiscoveryUI was created +in response to developer feedback. + +197 +00:12:17,838 --> 00:12:19,840 +We want to build these features with you, + +198 +00:12:19,873 --> 00:12:22,543 +so please file reports +in Feedback Assistant + +199 +00:12:22,576 --> 00:12:25,512 +for anything that you'd like to see +in DeviceDiscoveryUI. + +200 +00:12:25,546 --> 00:12:27,881 +We're excited +to improve this technology together + +201 +00:12:27,915 --> 00:12:30,984 +to help make your apps even better. + +202 +00:12:31,018 --> 00:12:32,753 +Thank you so much for joining me, + +203 +00:12:32,786 --> 00:12:35,956 +and have a great WWDC 2022. + diff --git a/eng/2022 Session 110340 Design an effective chart en.srt b/eng/2022 Session 110340 Design an effective chart en.srt new file mode 100644 index 0000000..8068152 --- /dev/null +++ b/eng/2022 Session 110340 Design an effective chart en.srt @@ -0,0 +1,2107 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,610 --> 00:00:11,044 +Halden: Hello, I'm Halden. + +3 +00:00:11,078 --> 00:00:12,479 +Lilian: And I'm Lilian. + +4 +00:00:12,513 --> 00:00:16,250 +Halden: Today, we're going to show you +how to design an effective chart. + +5 +00:00:16,283 --> 00:00:19,720 +Lilian: Charts are powerful tools +for making your apps more engaging + +6 +00:00:19,753 --> 00:00:21,555 +and informative. + +7 +00:00:21,588 --> 00:00:25,192 +People can use charts of +weather forecasts to make plans, + +8 +00:00:25,225 --> 00:00:28,028 +stock prices to make financial decisions, + +9 +00:00:28,061 --> 00:00:32,766 +and fitness data to reflect +on their activities and set new goals. + +10 +00:00:32,799 --> 00:00:34,701 +Halden: When you design a chart in an app, + +11 +00:00:34,735 --> 00:00:37,471 +you first need to design +the app's experience. + +12 +00:00:37,504 --> 00:00:41,041 +By identifying your app's needs, +you can decide when to use charts, + +13 +00:00:41,074 --> 00:00:44,645 +how to use charts, +and what design system will unify them. + +14 +00:00:46,380 --> 00:00:49,683 +To learn about this important first step +of the design process, + +15 +00:00:49,716 --> 00:00:54,054 +see this year's talk called +"Design app experiences with charts". + +16 +00:00:54,087 --> 00:00:58,292 +Lilian: In this talk, we'll dive into +the next part of the design process: + +17 +00:00:58,325 --> 00:01:00,527 +how to design a chart. + +18 +00:01:00,561 --> 00:01:04,398 +In particular, +designing an effective chart, + +19 +00:01:04,431 --> 00:01:08,435 +which means a chart that's focused on +what you want to communicate, + +20 +00:01:08,468 --> 00:01:12,339 +approachable, and accessible +for people with disabilities. + +21 +00:01:12,372 --> 00:01:16,476 +To do that, we'll walk through +the design of a chart in an app for + +22 +00:01:16,510 --> 00:01:19,213 +a food truck +selling international pancakes + +23 +00:01:19,246 --> 00:01:22,282 +in San Francisco and Cupertino. + +24 +00:01:22,316 --> 00:01:25,652 +This finished app has +an entire system of charts + +25 +00:01:25,686 --> 00:01:28,188 +to help plan and manage pancake sales. + +26 +00:01:28,222 --> 00:01:31,658 +Today, we'll focus on how to design +one of these charts, + +27 +00:01:31,692 --> 00:01:35,462 +which visualizes pancake sales +over the last 30 days. + +28 +00:01:35,495 --> 00:01:38,832 +Halden: Good chart design begins +with identifying the chart's goal. + +29 +00:01:38,866 --> 00:01:41,635 +For this page in our app, +our goal was communicating: + +30 +00:01:41,668 --> 00:01:44,304 +"how were pancake sales +in the last 30 days?" + +31 +00:01:44,338 --> 00:01:47,441 +Goals like this can go in many directions. + +32 +00:01:47,474 --> 00:01:51,278 +It might be useful for our food truck +owners to see the pattern of recent sales + +33 +00:01:51,311 --> 00:01:54,081 +to understand how sales have fluctuated +or their range, + +34 +00:01:54,114 --> 00:01:57,017 +to get an idea of how big +these fluctuations were. + +35 +00:01:57,050 --> 00:02:01,522 +Identifying values could be useful +to examine specific days. + +36 +00:02:01,555 --> 00:02:06,126 +Other insights of interest +could be maximum sales, outlier days, + +37 +00:02:06,159 --> 00:02:09,429 +or comparisons between days of the week +or location. + +38 +00:02:09,463 --> 00:02:11,598 +The list can go on and on. + +39 +00:02:11,632 --> 00:02:15,269 +Which of these insights are most important +for our food truck owners? + +40 +00:02:15,302 --> 00:02:18,739 +An effective chart focuses on +a few key pieces of information. + +41 +00:02:18,772 --> 00:02:21,308 +Design charts with intention. + +42 +00:02:21,341 --> 00:02:23,177 +With regards to pancake sales, + +43 +00:02:23,210 --> 00:02:27,014 +our food truck owners primarily want to +examine how their sales fared over time + +44 +00:02:27,047 --> 00:02:29,416 +and on specific days. + +45 +00:02:29,449 --> 00:02:32,719 +That means we'll want to focus +on communicating pattern, + +46 +00:02:32,753 --> 00:02:35,722 +range, and values. + +47 +00:02:35,756 --> 00:02:39,326 +How did we get from +these three priorities to this chart? + +48 +00:02:39,359 --> 00:02:43,063 +Let's walk through the design process +step by step. + +49 +00:02:43,096 --> 00:02:47,134 +Lilian: +Our design process involves five topics. + +50 +00:02:47,167 --> 00:02:50,003 +Marks, which are a chart's +visual building blocks, + +51 +00:02:50,037 --> 00:02:52,272 +like the bars in a bar chart. + +52 +00:02:52,306 --> 00:02:55,509 +Axes, to provide framing for these marks. + +53 +00:02:55,542 --> 00:03:00,280 +Descriptions, which make charts more +approachable and easy to interpret. + +54 +00:03:00,314 --> 00:03:03,684 +Interaction, +which empowers deeper exploration of data + +55 +00:03:03,717 --> 00:03:06,086 +and is critical to accessibility. + +56 +00:03:06,119 --> 00:03:08,622 +And color, which adds personality, + +57 +00:03:08,655 --> 00:03:10,824 +and, as we'll show with a second chart, + +58 +00:03:10,858 --> 00:03:13,527 +can also enhance clarity. + +59 +00:03:13,560 --> 00:03:17,097 +Halden: Our first step is to figure out +what kind of form we want for our chart, + +60 +00:03:17,130 --> 00:03:20,868 +using the visual building block +of charts called marks. + +61 +00:03:20,901 --> 00:03:23,036 +A mark is the bar in a bar chart, + +62 +00:03:23,070 --> 00:03:26,740 +the line in a line chart, +and the point in a scatterplot. + +63 +00:03:26,773 --> 00:03:30,444 +They're the visual elements +that represent items in data. + +64 +00:03:30,477 --> 00:03:32,246 +There are many kinds of marks, + +65 +00:03:32,279 --> 00:03:33,580 +and with any one mark, + +66 +00:03:33,614 --> 00:03:37,651 +you have a rich variety of ways +to express data. + +67 +00:03:37,684 --> 00:03:39,953 +Take bars, for example. + +68 +00:03:39,987 --> 00:03:44,925 +You can line them up +to represent data's change over time. + +69 +00:03:44,958 --> 00:03:47,394 +You can stack them to show proportions: + +70 +00:03:47,427 --> 00:03:51,064 +how different categories add up +to a total. + +71 +00:03:51,098 --> 00:03:54,001 +Or you can put them side by side +to compare values + +72 +00:03:54,034 --> 00:03:56,870 +between different categories. + +73 +00:03:56,904 --> 00:04:01,975 +These are just a few examples of the many +rich ways you can express data with marks. + +74 +00:04:02,009 --> 00:04:05,913 +So what options make sense for our chart? + +75 +00:04:05,946 --> 00:04:10,384 +When it comes to marks, you need +to design for your goals and data. + +76 +00:04:10,417 --> 00:04:13,954 +As we decided earlier, +our chart should focus on three insights + +77 +00:04:13,987 --> 00:04:16,857 +about pancake sales in the last 30 days: + +78 +00:04:16,890 --> 00:04:20,394 +pattern, range, and individual values. + +79 +00:04:20,427 --> 00:04:24,431 +As we choose our mark, +we'll pay close attention to pattern. + +80 +00:04:24,464 --> 00:04:27,668 +It would be useful for our food truck +owners to see any fluctuations + +81 +00:04:27,701 --> 00:04:30,003 +or trajectories in their sales. + +82 +00:04:30,037 --> 00:04:32,472 +We'll start with time +on the horizontal axis + +83 +00:04:32,506 --> 00:04:35,175 +and pancake sales on the vertical axis. + +84 +00:04:35,209 --> 00:04:38,979 +These axes are incomplete; +we'll fill them in later. + +85 +00:04:39,012 --> 00:04:44,418 +One option for a mark is to use points +to represent each day of pancake sales. + +86 +00:04:44,451 --> 00:04:48,856 +When we envision nice smooth data for +the chart like this, points look great! + +87 +00:04:48,889 --> 00:04:52,526 +But real data is rarely this neat. + +88 +00:04:52,559 --> 00:04:55,195 +With realistic data, +we can see that using points + +89 +00:04:55,229 --> 00:04:57,698 +makes it difficult +to make out any pattern. + +90 +00:04:57,731 --> 00:05:01,668 +It's important to test your designs +with real data early. + +91 +00:05:01,702 --> 00:05:04,638 +While point marks are a great +at revealing some insights, + +92 +00:05:04,671 --> 00:05:07,641 +such as identifying outliers +or clusters in data, + +93 +00:05:07,674 --> 00:05:11,211 +our needs call for something else. + +94 +00:05:11,245 --> 00:05:13,514 +To make the pattern of sales +easier to see, + +95 +00:05:13,547 --> 00:05:16,850 +we can connect sales counts +by using a line mark. + +96 +00:05:16,884 --> 00:05:19,720 +Lines are great +at representing rates of change. + +97 +00:05:19,753 --> 00:05:23,090 +Here, patterns and trajectories are clear. + +98 +00:05:23,123 --> 00:05:24,691 +But what if, in one month, + +99 +00:05:24,725 --> 00:05:28,562 +our food truck needed to close +for five alternating days? + +100 +00:05:28,595 --> 00:05:32,132 +In this situation, +the pattern of sales becomes less clear + +101 +00:05:32,165 --> 00:05:34,701 +because the segments connecting +far-apart values + +102 +00:05:34,735 --> 00:05:37,738 +become more prominent +than the values themselves. + +103 +00:05:37,771 --> 00:05:42,376 +Remember to design for a variety +of scenarios in your data. + +104 +00:05:42,409 --> 00:05:45,445 +Bar marks are a more flexible option +for our chart. + +105 +00:05:45,479 --> 00:05:49,049 +Here, zeros are visible +without creating a distraction. + +106 +00:05:49,082 --> 00:05:51,418 +This chart is also intuitive to read: + +107 +00:05:51,451 --> 00:05:53,687 +more white means more sales. + +108 +00:05:53,720 --> 00:05:55,522 +Since sales are cumulative, + +109 +00:05:55,556 --> 00:05:58,125 +the visual weight of all the bars +corresponds directly + +110 +00:05:58,158 --> 00:06:01,228 +to the number of sales made +in the last 30 days. + +111 +00:06:02,996 --> 00:06:04,131 +Lilian: Great! + +112 +00:06:04,164 --> 00:06:08,235 +We've chosen a mark that makes the pattern +of pancake sales visually apparent. + +113 +00:06:08,268 --> 00:06:12,306 +But we also need to follow +an important tenant of accessible design: + +114 +00:06:12,339 --> 00:06:14,675 +for all information you show visually, + +115 +00:06:14,708 --> 00:06:18,779 +you also need to design +how to represent it non-visually. + +116 +00:06:18,812 --> 00:06:22,349 +In other words, +you need to make sure these marks + +117 +00:06:22,382 --> 00:06:27,120 +and the information they represent +are also accessible with VoiceOver. + +118 +00:06:27,154 --> 00:06:29,223 +VoiceOver is a screen reader. + +119 +00:06:29,256 --> 00:06:33,460 +It lets people read information on +their screen through Braille or speech + +120 +00:06:33,493 --> 00:06:37,564 +so people who are blind and others +can use apps without needing to see. + +121 +00:06:38,799 --> 00:06:42,102 +VoiceOver: 1,234 pancakes. + +122 +00:06:42,135 --> 00:06:44,605 +Lilian: You can use VoiceOver +to navigate content, + +123 +00:06:44,638 --> 00:06:46,206 +like the elements on a chart. + +124 +00:06:47,007 --> 00:06:49,676 +VoiceOver: May 8th. +54 pancakes. + +125 +00:06:49,710 --> 00:06:52,713 +May 9th. 36 pancakes. + +126 +00:06:52,746 --> 00:06:55,749 +One year, button two of two. + +127 +00:06:55,782 --> 00:06:58,852 +Lilian: You can also use it to interact. + +128 +00:06:58,886 --> 00:07:01,955 +VoiceOver: +Selected: one year, two of two. + +129 +00:07:01,989 --> 00:07:05,592 +Lilian: And through a feature +called audio graphs... + +130 +00:07:05,626 --> 00:07:07,794 +VoiceOver: Audio graph. + +131 +00:07:07,828 --> 00:07:10,797 +Lilian: Play a sonification of the chart + +132 +00:07:10,831 --> 00:07:13,100 +VoiceOver: Play audio graph. + +133 +00:07:13,133 --> 00:07:18,138 +[phone chiming] + +134 +00:07:19,673 --> 00:07:22,075 +Complete. + +135 +00:07:22,109 --> 00:07:25,412 +Lilian: To make your chart's marks +non-visually accessible, + +136 +00:07:25,445 --> 00:07:30,384 +you need to 1. design how VoiceOver +will navigate over data values, + +137 +00:07:30,417 --> 00:07:33,587 +and 2. use audio graphs. + +138 +00:07:33,620 --> 00:07:37,591 +Conveniently, charts implemented +with the Swift Charts API + +139 +00:07:37,624 --> 00:07:41,962 +automatically include customizable +accessibility labels for marks + +140 +00:07:41,995 --> 00:07:45,199 +and an implementation of audio graphs. + +141 +00:07:45,232 --> 00:07:47,968 +To learn more about audio graphs +and Swift Charts, + +142 +00:07:48,001 --> 00:07:50,270 +check out developer documentation, + +143 +00:07:50,304 --> 00:07:53,707 +last year's talk "Bring accessibility +to charts in your app", + +144 +00:07:53,740 --> 00:07:57,511 +and this year's talk, +"Hello Swift Charts". + +145 +00:07:57,544 --> 00:07:58,312 +Nice! + +146 +00:07:58,345 --> 00:08:00,647 +We've designed our marks +for our goals and data, + +147 +00:08:00,681 --> 00:08:03,650 +and made them accessible in VoiceOver. + +148 +00:08:03,684 --> 00:08:07,354 +With our decisions, +we addressed one part of the question, + +149 +00:08:07,387 --> 00:08:09,923 +"how were pancake sales +over the last 30 days?" + +150 +00:08:09,957 --> 00:08:12,326 +Specifically, their pattern. + +151 +00:08:12,359 --> 00:08:17,231 +Now our food truck owners can see +recent fluctuations in their sales. + +152 +00:08:17,264 --> 00:08:20,200 +But what about the range +of these fluctuations, + +153 +00:08:20,234 --> 00:08:23,103 +and values on specific days? + +154 +00:08:23,136 --> 00:08:25,339 +Halden: For that, we'll design axes. + +155 +00:08:25,372 --> 00:08:29,409 +Axes frame marks to provide references +for their values. + +156 +00:08:29,443 --> 00:08:32,713 +For example, +in our chart of pancake sales, + +157 +00:08:32,746 --> 00:08:35,849 +when we label the horizontal axis +with the start and end dates, + +158 +00:08:35,883 --> 00:08:41,522 +it becomes clear that our chart shows +the 30 days from May 8th to June 6th. + +159 +00:08:41,555 --> 00:08:43,357 +What about the vertical axis? + +160 +00:08:43,390 --> 00:08:47,327 +The values here are entirely dependent +on the sales of the food truck. + +161 +00:08:47,361 --> 00:08:50,898 +With axes like this, +it's important to consider the range. + +162 +00:08:50,931 --> 00:08:53,867 +A range can be either fixed or dynamic. + +163 +00:08:53,901 --> 00:08:56,703 +An example of a fixed range +is the vertical axis + +164 +00:08:56,737 --> 00:08:58,872 +of the battery chart in the Settings app. + +165 +00:08:58,906 --> 00:09:03,410 +We know that the battery level +will always go from 0 to 100%. + +166 +00:09:03,443 --> 00:09:07,214 +Fixing the vertical axis to this range +helps us see, at a glance, + +167 +00:09:07,247 --> 00:09:11,151 +when the battery is full, empty, +or somewhere in between. + +168 +00:09:11,185 --> 00:09:14,488 +While it'd be confusing to change +the vertical axis range for this chart, + +169 +00:09:14,521 --> 00:09:16,857 +it can be necessary for others. + +170 +00:09:16,890 --> 00:09:19,426 +Consider the step count chart +in the Health app, + +171 +00:09:19,459 --> 00:09:21,562 +which uses a dynamic range. + +172 +00:09:21,595 --> 00:09:23,830 +There is no fixed maximum step count, + +173 +00:09:23,864 --> 00:09:28,402 +so it makes sense to dynamically adapt +the vertical axis range to fit the data. + +174 +00:09:28,435 --> 00:09:31,171 +This way, +even when the step counts are low, + +175 +00:09:31,205 --> 00:09:34,241 +the bars can make full use of +the vertical space available, + +176 +00:09:34,274 --> 00:09:37,978 +making fluctuations easier to see. + +177 +00:09:38,011 --> 00:09:41,114 +Let's return to our chart +on pancake sales. + +178 +00:09:41,148 --> 00:09:45,819 +As with steps, there is no limit to how +many pancakes the food trucks will sell. + +179 +00:09:45,853 --> 00:09:49,189 +So let's use a dynamic range +to automatically adapt + +180 +00:09:49,223 --> 00:09:52,359 +the upper bound +of our vertical axis to our data. + +181 +00:09:52,392 --> 00:09:55,696 +Notice how we still fix +the lower bound to 0. + +182 +00:09:55,729 --> 00:09:59,066 +Doing so is generally a good idea +when using bar marks + +183 +00:09:59,099 --> 00:10:01,835 +as it keeps the heights +of the bars meaningful. + +184 +00:10:01,869 --> 00:10:06,740 +This way, a bar that's twice as tall +as another has twice as many sales. + +185 +00:10:06,773 --> 00:10:10,444 +While labels for the lower +and upper bounds of each axis are helpful, + +186 +00:10:10,477 --> 00:10:15,115 +we still need more structure to interpret +sales in the middle of the chart. + +187 +00:10:15,148 --> 00:10:17,084 +That leads to our next concept. + +188 +00:10:17,117 --> 00:10:21,655 +We need to tailor the density +of our axis grid lines and labels. + +189 +00:10:21,688 --> 00:10:25,259 +Grid lines give you reference points +to estimate the values of marks. + +190 +00:10:25,292 --> 00:10:30,230 +The more grid lines you have, +the easier it is to estimate these values. + +191 +00:10:30,264 --> 00:10:33,166 +Some charts don't need grid lines +and labels at all, + +192 +00:10:33,200 --> 00:10:35,636 +such as this trend platter +in the Health app. + +193 +00:10:35,669 --> 00:10:39,573 +These charts tend to be sneak peeks +of larger charts in another view, + +194 +00:10:39,606 --> 00:10:42,776 +so an idea of the data's pattern +is all you need. + +195 +00:10:42,809 --> 00:10:45,579 +Grid lines and labels +would add distraction. + +196 +00:10:45,612 --> 00:10:48,415 +Grid lines and labels appear +in the follow-up detailed chart, + +197 +00:10:48,448 --> 00:10:51,919 +where you may want to analyze values +more precisely. + +198 +00:10:51,952 --> 00:10:54,288 +Earlier, we pointed out +that our food truck owners + +199 +00:10:54,321 --> 00:10:58,325 +want to examine the range +and values of their sales. + +200 +00:10:58,358 --> 00:11:01,328 +Right now, +we have two horizontal grid lines: + +201 +00:11:01,361 --> 00:11:06,233 +one at zero and another at roughly the +maximum number of sales for the month, + +202 +00:11:06,266 --> 00:11:10,838 +which is too few for estimating the range +of sales in the middle of the chart. + +203 +00:11:10,871 --> 00:11:15,108 +At the same time, too many grid lines +and labels can be distracting. + +204 +00:11:15,142 --> 00:11:19,913 +Here, having seven horizontal grid lines +might be overwhelming. + +205 +00:11:19,947 --> 00:11:23,016 +Balance these factors to choose +the appropriate density. + +206 +00:11:23,050 --> 00:11:25,586 +For our chart's context and intended use, + +207 +00:11:25,619 --> 00:11:28,689 +it's more effective to use around +four horizontal grid lines + +208 +00:11:28,722 --> 00:11:32,559 +and adjust +as the range of the axis changes. + +209 +00:11:32,593 --> 00:11:35,195 +Note that as we place +these grid lines and labels, + +210 +00:11:35,229 --> 00:11:38,298 +we use intuitive values +to make the chart approachable– + +211 +00:11:38,332 --> 00:11:41,535 +in this case, multiples of 20. + +212 +00:11:41,568 --> 00:11:46,607 +In the same way, it's intuitive for people +to read time in steps of seven days, + +213 +00:11:46,640 --> 00:11:50,944 +which gives us +five grid lines for a 30-day period. + +214 +00:11:50,978 --> 00:11:54,114 +So we've designed our axes +by considering their range + +215 +00:11:54,147 --> 00:11:56,717 +and tailoring the density of grid lines +and labels. + +216 +00:11:56,750 --> 00:11:59,853 +We've gone a long way towards +making our chart effective, + +217 +00:11:59,887 --> 00:12:02,022 +but charts are complex visual elements, + +218 +00:12:02,055 --> 00:12:05,192 +and our example still needs work +before it's approachable. + +219 +00:12:05,225 --> 00:12:07,828 +How can we convey the meaning +behind the marks and axes + +220 +00:12:07,861 --> 00:12:10,831 +in a way that's quick and intuitive? + +221 +00:12:10,864 --> 00:12:14,301 +Lilian: That brings us +to our next topic: descriptions. + +222 +00:12:14,334 --> 00:12:17,471 +Descriptions are important +for framing the intent of our chart + +223 +00:12:17,504 --> 00:12:20,207 +and making it approachable and accessible. + +224 +00:12:20,240 --> 00:12:23,410 +Use descriptions, +or text preceding the chart, + +225 +00:12:23,443 --> 00:12:26,947 +to provide context to make +the chart more approachable. + +226 +00:12:26,980 --> 00:12:32,319 +They can make it clear that our chart is +about pancake sales in the last 30 days. + +227 +00:12:32,352 --> 00:12:34,688 +This text can be brief +and should be a part of + +228 +00:12:34,721 --> 00:12:36,790 +the user interface around a chart. + +229 +00:12:36,823 --> 00:12:39,593 +When we look at this chart +in the UI of our app, + +230 +00:12:39,626 --> 00:12:43,864 +the screen's title "Total Sales" +already gives some context, + +231 +00:12:43,897 --> 00:12:47,134 +and the label on the segmented control +that says "30 Days" + +232 +00:12:47,167 --> 00:12:50,137 +quickly establishes our time range. + +233 +00:12:50,170 --> 00:12:51,772 +That's a start. + +234 +00:12:51,805 --> 00:12:56,176 +Something else we need to clarify +is what the vertical axis is showing. + +235 +00:12:56,210 --> 00:12:58,345 +Are we looking at sales in terms of money? + +236 +00:12:58,378 --> 00:13:00,814 +In number of pancakes? + +237 +00:13:00,848 --> 00:13:03,884 +One solution could be adding +an axis label. + +238 +00:13:03,917 --> 00:13:08,488 +We can write "Pancakes sold" +above the vertical axis. + +239 +00:13:08,522 --> 00:13:11,358 +But here it's small and off to the side. + +240 +00:13:11,391 --> 00:13:14,094 +We want the meaning of this chart +to be obvious. + +241 +00:13:14,127 --> 00:13:18,065 +An alternative is to contextualize +the data with a title. + +242 +00:13:18,098 --> 00:13:21,935 +By using "Pancakes sold" +as a heading for the chart, + +243 +00:13:21,969 --> 00:13:25,606 +the chart's meaning is front and center. + +244 +00:13:25,639 --> 00:13:29,610 +Providing this context is important +for framing the chart. + +245 +00:13:29,643 --> 00:13:32,246 +We can make these descriptions +even better + +246 +00:13:32,279 --> 00:13:35,015 +by summarizing the main take-away +of the chart. + +247 +00:13:35,048 --> 00:13:37,451 +A lot of charts use this approach. + +248 +00:13:37,484 --> 00:13:41,488 +For example, the text above +a precipitation chart in Weather + +249 +00:13:41,522 --> 00:13:43,290 +describes its main take-away. + +250 +00:13:43,323 --> 00:13:47,027 +In this example, +it's: "Light Rain Forecasted– + +251 +00:13:47,060 --> 00:13:51,164 +Light rain is expected to start +in 9 minutes and last for 36 minutes" + +252 +00:13:52,466 --> 00:13:54,868 +There are many ways to present takeaways, + +253 +00:13:54,902 --> 00:13:59,106 +from sentence descriptions +to glyphs comparing historical data. + +254 +00:13:59,139 --> 00:14:01,375 +We'll take a simple approach +for our chart. + +255 +00:14:01,408 --> 00:14:03,777 +Let's bring out a main take-away +for our chart + +256 +00:14:03,810 --> 00:14:05,779 +by rewriting our chart title + +257 +00:14:05,812 --> 00:14:10,951 +to say "Total Sales: 1,234 Pancakes". + +258 +00:14:10,984 --> 00:14:14,188 +This description grounds our chart +with a concrete value + +259 +00:14:14,221 --> 00:14:17,424 +and summarizes +the most critical information. + +260 +00:14:17,457 --> 00:14:20,027 +Providing descriptions +that contextualize the data + +261 +00:14:20,060 --> 00:14:24,131 +and summarize key take-aways +eases readers into your charts. + +262 +00:14:24,164 --> 00:14:27,968 +It makes a chart more approachable +and accessible for everyone. + +263 +00:14:28,001 --> 00:14:30,237 +It sets up what to expect or look for, + +264 +00:14:30,270 --> 00:14:33,106 +which can be especially helpful +for people with certain disabilities + +265 +00:14:33,140 --> 00:14:36,577 +that make examining the details +of the chart particularly time consuming + +266 +00:14:36,610 --> 00:14:38,445 +or challenging. + +267 +00:14:38,478 --> 00:14:40,514 +Speaking of accessibility, + +268 +00:14:40,547 --> 00:14:45,419 +using audio graphs for our chart adds +important descriptions for VoiceOver. + +269 +00:14:45,452 --> 00:14:48,188 +With audio graphs... + +270 +00:14:48,222 --> 00:14:49,890 +VoiceOver: Audio graph. + +271 +00:14:49,923 --> 00:14:52,059 +Lilian: +VoiceOver can non-visually describe + +272 +00:14:52,092 --> 00:14:53,927 +what the axes are. + +273 +00:14:53,961 --> 00:14:55,896 +VoiceOver: Describe chart. + +274 +00:14:55,929 --> 00:14:59,600 +The x-axis is time. The y-axis is sales. + +275 +00:14:59,633 --> 00:15:02,269 +There is one data series. + +276 +00:15:02,302 --> 00:15:05,272 +Lilian: Audio graphs also provide +several summaries about the data, + +277 +00:15:05,305 --> 00:15:07,274 +including one you can customize. + +278 +00:15:07,307 --> 00:15:11,478 +These descriptions, +especially of the x and y axes, + +279 +00:15:11,512 --> 00:15:14,615 +are critical for non-visually +communicating the chart. + +280 +00:15:14,648 --> 00:15:16,683 +If audio graphs isn't an option, + +281 +00:15:16,717 --> 00:15:20,554 +make sure VoiceOver still has immediate +access to these types of descriptions + +282 +00:15:20,587 --> 00:15:24,191 +some other way, +like through accessibility text labels. + +283 +00:15:24,224 --> 00:15:27,728 +Halden: Now that we provide context, +summarized the main take-away, + +284 +00:15:27,761 --> 00:15:30,931 +and use audio graphs or an equivalent +source of descriptions, + +285 +00:15:30,964 --> 00:15:33,267 +our chart has all its essential parts. + +286 +00:15:33,300 --> 00:15:37,571 +But we could make it +even more effective with interaction. + +287 +00:15:37,604 --> 00:15:40,541 +You can design interaction +to empower people to explore + +288 +00:15:40,574 --> 00:15:43,043 +and understand their data +at a deeper level. + +289 +00:15:43,076 --> 00:15:47,381 +For example, you can use interactions +to highlight sections of a chart + +290 +00:15:47,414 --> 00:15:50,851 +to explore how their data fits +in a broader context, + +291 +00:15:50,884 --> 00:15:55,956 +or toggle between days, +weeks, months, and years. + +292 +00:15:55,989 --> 00:16:00,627 +We have an opportunity to enhance +our pancake sales chart with interaction. + +293 +00:16:00,661 --> 00:16:03,430 +Our food truck owners would find it +useful to see exactly + +294 +00:16:03,463 --> 00:16:06,900 +how many pancakes were sold +on a particular day. + +295 +00:16:06,934 --> 00:16:09,503 +We can offer that +with an interactive tooltip, + +296 +00:16:09,536 --> 00:16:13,941 +which lets us touch the chart to highlight +and read these specific values. + +297 +00:16:13,974 --> 00:16:16,844 +To make these touch-based interactions +easy to use, + +298 +00:16:16,877 --> 00:16:19,546 +make sure to use large touch targets. + +299 +00:16:19,580 --> 00:16:23,784 +So instead of making touch targets +on our chart the same size as our marks, + +300 +00:16:23,817 --> 00:16:27,521 +adding padding and stretching targets +to the full height of the chart, + +301 +00:16:27,554 --> 00:16:30,657 +makes it easy to use our tooltip because +you can touch any part of the chart + +302 +00:16:30,691 --> 00:16:33,293 +including the white space above the bars. + +303 +00:16:35,662 --> 00:16:38,165 +Lilian: +Interaction isn't only about touch. + +304 +00:16:38,198 --> 00:16:41,301 +People use a variety of other ways +to interact with devices, + +305 +00:16:41,335 --> 00:16:44,805 +depending on factors +like situation or disability. + +306 +00:16:44,838 --> 00:16:47,774 +So you need to design +for multiple types of input + +307 +00:16:47,808 --> 00:16:50,777 +to make chart interaction approachable +and accessible. + +308 +00:16:50,811 --> 00:16:55,749 +In other words, for every interaction +you design for touch or mouse clicks, + +309 +00:16:55,782 --> 00:16:59,853 +you need to design the same experience +for inputs like keyboard, + +310 +00:16:59,887 --> 00:17:04,591 +Voice Control, +Switch Control, and VoiceOver. + +311 +00:17:04,625 --> 00:17:07,794 +As you design these interactions, +it's important to make changes + +312 +00:17:07,828 --> 00:17:09,396 +on the screen visible, + +313 +00:17:09,429 --> 00:17:11,865 +like sizing the focus indicator +for Switch Control + +314 +00:17:11,899 --> 00:17:13,700 +or VoiceOver to be large, + +315 +00:17:13,734 --> 00:17:16,703 +the same way +we designed our touch targets. + +316 +00:17:18,238 --> 00:17:22,109 +Part of providing support for VoiceOver +is that you also need to design + +317 +00:17:22,142 --> 00:17:25,312 +good accessibility labels. + +318 +00:17:25,345 --> 00:17:27,281 +Recall how when we designed our marks, + +319 +00:17:27,314 --> 00:17:31,852 +we made sure VoiceOver can navigate +the chart's data values. + +320 +00:17:31,885 --> 00:17:35,556 +We made it so that when VoiceOver +navigates onto one of the bars, + +321 +00:17:35,589 --> 00:17:40,661 +it reads out values like +"June 1, 36 pancakes". + +322 +00:17:40,694 --> 00:17:44,598 +Let's examine what makes this label +a good design. + +323 +00:17:44,631 --> 00:17:46,233 +It's succinct. + +324 +00:17:46,266 --> 00:17:51,505 +We don't repeat unnecessary information, +like the axis names "Time" or "Sales" + +325 +00:17:51,538 --> 00:17:54,875 +which audio graphs already describes. + +326 +00:17:54,908 --> 00:17:58,512 +We spell out entire words +instead of using abbreviations. + +327 +00:17:58,545 --> 00:18:04,151 +That way, VoiceOver reads out +"June" instead of "Jun" or 6-dash-1, + +328 +00:18:04,184 --> 00:18:08,455 +and we make it clear +that 36 is the number of pancakes. + +329 +00:18:08,488 --> 00:18:11,558 +We also order the contextualizing +data value, + +330 +00:18:11,592 --> 00:18:14,828 +in this case, the date, +to come first. + +331 +00:18:14,862 --> 00:18:19,466 +This ordering makes it easier +to quickly look for a specific value. + +332 +00:18:20,968 --> 00:18:23,136 +VoiceOver: May 27th. +May 28. + +333 +00:18:23,170 --> 00:18:26,907 +May 29. +May 30th, 41 pancakes. + +334 +00:18:26,940 --> 00:18:29,543 +Lilian: It also makes the data +easier to interpret, + +335 +00:18:29,576 --> 00:18:32,412 +especially since you can jump +to different parts of the chart + +336 +00:18:32,446 --> 00:18:35,782 +and getting the context first +lets you know where you are. + +337 +00:18:38,318 --> 00:18:42,856 +The kind of labels you design +depend a lot on the goals of the chart. + +338 +00:18:42,890 --> 00:18:47,661 +For example, the charts on elevation +changes in cycling routes from Maps + +339 +00:18:47,694 --> 00:18:49,663 +don't show individual values, + +340 +00:18:49,696 --> 00:18:52,633 +but a route's pattern of elevation +gains and losses, + +341 +00:18:52,666 --> 00:18:57,571 +and use many vertical bars– +too many to navigate over individually. + +342 +00:18:57,604 --> 00:19:03,710 +A well-designed accessibility label +could then label a section of bars with: + +343 +00:19:03,744 --> 00:19:07,014 +"From 3.6 miles to 4.4 miles: + +344 +00:19:07,047 --> 00:19:11,118 +Climb 100 feet, descend 5 feet". + +345 +00:19:11,151 --> 00:19:14,988 +In contrast, if a chart is a tiny preview +inside a button + +346 +00:19:15,022 --> 00:19:17,424 +for opening the same chart in more detail, + +347 +00:19:17,457 --> 00:19:20,327 +it might make sense +to summarize the entire button, + +348 +00:19:20,360 --> 00:19:23,764 +chart and all, with one label. + +349 +00:19:23,797 --> 00:19:27,835 +Now that we've designed our interactions +to serve the focus, approachability, + +350 +00:19:27,868 --> 00:19:29,670 +and accessibility of our chart, + +351 +00:19:29,703 --> 00:19:32,439 +there's one more topic +we'd like to talk about. + +352 +00:19:32,472 --> 00:19:33,874 +Halden: Color. + +353 +00:19:33,907 --> 00:19:37,411 +Color can add personality +and enhance clarity in a chart. + +354 +00:19:37,444 --> 00:19:40,547 +So far, we've been designing +in black and white. + +355 +00:19:40,581 --> 00:19:43,417 +Color can give our chart +more pop in our app. + +356 +00:19:43,450 --> 00:19:46,453 +But it can also convey so much more. + +357 +00:19:46,486 --> 00:19:48,922 +You can use color +to distinguish categories, + +358 +00:19:48,956 --> 00:19:52,259 +as with the three activity rings +in the Fitness app. + +359 +00:19:52,292 --> 00:19:54,895 +You can also use it +to communicate intensity, + +360 +00:19:54,928 --> 00:19:57,764 +such as heat in a weather forecast. + +361 +00:19:57,798 --> 00:20:01,401 +You can even remove color +to draw attention to features of a chart. + +362 +00:20:01,435 --> 00:20:03,370 +Here the health app does so + +363 +00:20:03,403 --> 00:20:06,673 +to highlight your minimum and maximum +heart rate within a day. + +364 +00:20:08,175 --> 00:20:10,677 +Consider using color to enhance. + +365 +00:20:10,711 --> 00:20:14,381 +You ideally use color as an addition +to make the chart easier to understand + +366 +00:20:14,414 --> 00:20:18,485 +and not the only means +to convey critical information. + +367 +00:20:18,519 --> 00:20:23,390 +To help explain, let's use a scenario +of tracking two food trucks in our app: + +368 +00:20:23,423 --> 00:20:27,427 +one in San Francisco +and the other in Cupertino. + +369 +00:20:27,461 --> 00:20:30,697 +We want to modify our total sales chart +to allow us to compare sales + +370 +00:20:30,731 --> 00:20:32,366 +in these two locations. + +371 +00:20:32,399 --> 00:20:34,034 +How do we do this? + +372 +00:20:34,067 --> 00:20:36,370 +We could use a line for each city's sales, + +373 +00:20:36,403 --> 00:20:39,006 +but we can't tell which line +belongs to which city. + +374 +00:20:39,039 --> 00:20:42,142 +No label or layout distinguishes the two. + +375 +00:20:42,176 --> 00:20:45,846 +We can clarify the association +by placing a symbols on each line + +376 +00:20:45,879 --> 00:20:48,248 +to mark individual days. + +377 +00:20:48,282 --> 00:20:52,152 +Here we use circles for San Francisco +and squares for Cupertino. + +378 +00:20:52,186 --> 00:20:54,855 +Notice that we need a legend now. + +379 +00:20:54,888 --> 00:20:57,491 +We can add color +to enhance this difference. + +380 +00:20:57,524 --> 00:21:00,894 +It's important that color is added +on top of the symbols here. + +381 +00:21:00,928 --> 00:21:05,299 +Some people with colorblindness may not be +able to distinguish the lines otherwise. + +382 +00:21:05,332 --> 00:21:07,968 +This practice is especially important +for system settings + +383 +00:21:08,001 --> 00:21:10,971 +like Differentiate Without Color. + +384 +00:21:11,004 --> 00:21:13,273 +When choosing colors to represent data, + +385 +00:21:13,307 --> 00:21:15,475 +there are a few things to consider. + +386 +00:21:15,509 --> 00:21:19,046 +First, +consider how color can carry meaning. + +387 +00:21:19,079 --> 00:21:23,617 +For example, red and green +for low versus charged battery levels. + +388 +00:21:23,650 --> 00:21:26,854 +Culture can inform +the meanings of colors too. + +389 +00:21:26,887 --> 00:21:29,990 +In the US, +it makes sense to color code Stocks + +390 +00:21:30,023 --> 00:21:33,961 +with green for gains and red for losses. + +391 +00:21:33,994 --> 00:21:37,598 +In some countries like China, however, +people expect the opposite, + +392 +00:21:37,631 --> 00:21:40,934 +with red indicating gains +and green indicating losses. + +393 +00:21:43,170 --> 00:21:46,340 +For our chart, +system blue and green work well. + +394 +00:21:46,373 --> 00:21:50,677 +There is no obvious meaning tied to +these colors in the context of cities. + +395 +00:21:50,711 --> 00:21:52,813 +But what if we wanted to customize +the colors + +396 +00:21:52,846 --> 00:21:55,115 +to match the look and feel of our app? + +397 +00:21:57,084 --> 00:22:00,754 +When choosing colors for categories, +balance visual weight. + +398 +00:22:00,787 --> 00:22:05,659 +If one color overpowers the other, +a hierarchy can be implied. + +399 +00:22:05,692 --> 00:22:08,328 +Say we started with a deep purple +for San Francisco + +400 +00:22:08,362 --> 00:22:11,365 +and this vibrant pink for Cupertino. + +401 +00:22:11,398 --> 00:22:15,269 +Notice how in this case +the prominence of Cupertino's pink + +402 +00:22:15,302 --> 00:22:18,272 +compared to the darkness +of San Francisco's purple + +403 +00:22:18,305 --> 00:22:20,974 +makes Cupertino look more important. + +404 +00:22:21,008 --> 00:22:25,913 +We can fix that by evening out +the saturation and luminosity. + +405 +00:22:25,946 --> 00:22:26,980 +That's better. + +406 +00:22:27,014 --> 00:22:29,216 +And while these color choices +still need some work, + +407 +00:22:29,249 --> 00:22:32,586 +now they have equal weight +and don't suggest one city as primary + +408 +00:22:32,619 --> 00:22:35,122 +and the other, secondary. + +409 +00:22:35,155 --> 00:22:39,359 +Note that adjusting color +for visual weight is a tool. + +410 +00:22:39,393 --> 00:22:42,062 +Sometimes it's useful to use a color +with more visual weight + +411 +00:22:42,095 --> 00:22:46,366 +when we want to draw attention +to specific values or sections in a chart. + +412 +00:22:46,400 --> 00:22:48,802 +Trend charts in the Fitness app +use this strategy + +413 +00:22:48,836 --> 00:22:51,104 +to highlight your recent activity. + +414 +00:22:51,138 --> 00:22:53,540 +When choosing a palette, +it's also important to choose + +415 +00:22:53,574 --> 00:22:58,212 +visually distinct colors +to improve accessibility and readability. + +416 +00:22:58,245 --> 00:23:01,682 +A good rule is to pick colors +that are easy to differentiate by name + +417 +00:23:01,715 --> 00:23:03,884 +and contrast well from each other. + +418 +00:23:03,917 --> 00:23:08,355 +So instead of purple for San Francisco +and purpley-pink for Cupertino, + +419 +00:23:08,388 --> 00:23:11,258 +let's use purple and green. + +420 +00:23:11,291 --> 00:23:13,727 +Let's also make sure +they're not just distinct from each other, + +421 +00:23:13,760 --> 00:23:15,863 +but also from the background. + +422 +00:23:15,896 --> 00:23:18,765 +Ensuring that colors have high contrast +both from each other + +423 +00:23:18,799 --> 00:23:21,435 +and the rest of the chart +makes the chart more accessible + +424 +00:23:21,468 --> 00:23:26,507 +for people with visual impairments and +is a better overall design for everyone. + +425 +00:23:26,540 --> 00:23:31,278 +Remember to also try making your colors +distinct for people with color blindness. + +426 +00:23:31,311 --> 00:23:35,215 +You can check your choices +with color blindness filters. + +427 +00:23:35,249 --> 00:23:39,620 +Finally, make sure your chart respects +system settings by designing colors + +428 +00:23:39,653 --> 00:23:42,022 +to adapt for both Dark Mode +and Light Mode, + +429 +00:23:42,055 --> 00:23:44,424 +as well as Increase Contrast. + +430 +00:23:44,458 --> 00:23:47,494 +All right, let's recap. +What did we do today? + +431 +00:23:47,528 --> 00:23:51,365 +Lilian: Well, we designed a chart +for our pancake food truck app. + +432 +00:23:51,398 --> 00:23:55,369 +To do so, we chose marks that most +effectively communicate our goal, + +433 +00:23:55,402 --> 00:23:57,171 +and represented them in VoiceOver, + +434 +00:23:57,204 --> 00:24:01,508 +set our axis range and gridlines +to better communicate values at a glance, + +435 +00:24:01,542 --> 00:24:06,180 +added descriptions to ease people in +with context and overviews, + +436 +00:24:06,213 --> 00:24:09,850 +incorporated interaction +to offer details on demand, + +437 +00:24:09,883 --> 00:24:13,954 +added a splash of color as the butter +on top of our pancake chart, + +438 +00:24:13,987 --> 00:24:16,924 +and further used color to enhance +our second chart + +439 +00:24:16,957 --> 00:24:21,228 +for comparing how our pancake sales +between cities stack up. + +440 +00:24:21,261 --> 00:24:22,863 +At every step of the way, + +441 +00:24:22,896 --> 00:24:26,834 +we made design decisions to ensure +that our chart was focused, + +442 +00:24:26,867 --> 00:24:29,570 +approachable, and accessible– + +443 +00:24:29,603 --> 00:24:33,607 +in other words, +that we made an effective chart. + +444 +00:24:33,640 --> 00:24:35,475 +Halden: +Remember, the chart we designed + +445 +00:24:35,509 --> 00:24:39,179 +is part of an entire system of charts +within the rich context of an app. + +446 +00:24:39,213 --> 00:24:42,449 +You can use the same design process +to make these other charts on pancakes + +447 +00:24:42,482 --> 00:24:44,885 +flippin' fantastic. + +448 +00:24:44,918 --> 00:24:46,854 +Now I want some pancakes. + +449 +00:24:46,887 --> 00:24:49,256 +Lilian: Yeah, me too. + +450 +00:24:52,359 --> 00:24:53,894 +Halden: Thanks for watching. + diff --git a/eng/2022 Session 110341 Explore SMS message filters en.srt b/eng/2022 Session 110341 Explore SMS message filters en.srt new file mode 100644 index 0000000..9fabbeb --- /dev/null +++ b/eng/2022 Session 110341 Explore SMS message filters en.srt @@ -0,0 +1,808 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,776 --> 00:00:11,979 +Hello and welcome to WWDC. + +3 +00:00:12,012 --> 00:00:14,014 +My name is Ajay Singh, + +4 +00:00:14,047 --> 00:00:17,184 +and today I am excited to take you +on a journey + +5 +00:00:17,217 --> 00:00:20,487 +to explore SMS message filters. + +6 +00:00:20,521 --> 00:00:24,391 +These allow you to create +message filter extensions + +7 +00:00:24,424 --> 00:00:29,196 +which help people categorize incoming +SMS messages + +8 +00:00:29,229 --> 00:00:31,798 +from unknown senders. + +9 +00:00:31,832 --> 00:00:37,504 +First, I will describe how +SMS message filters work. + +10 +00:00:37,538 --> 00:00:43,477 +Next, I will talk about +what's new in iOS 16. + +11 +00:00:43,510 --> 00:00:49,883 +I will walk through an example of how +to build a message filter extension + +12 +00:00:49,917 --> 00:00:52,920 +using iOS 16 APIs, + +13 +00:00:52,953 --> 00:00:57,157 +then show you what people will see +in Messages + +14 +00:00:57,191 --> 00:01:00,527 +when they use your extension. + +15 +00:01:00,561 --> 00:01:04,031 +Finally, I will talk about enhancements + +16 +00:01:04,064 --> 00:01:07,167 +to Apple's SMS filter for India. + +17 +00:01:08,302 --> 00:01:11,505 +Let's talk about SMS message filters. + +18 +00:01:11,538 --> 00:01:17,811 +In many countries, +SMS messages are now used by businesses + +19 +00:01:17,845 --> 00:01:21,315 +to notify customers about transactions, + +20 +00:01:21,348 --> 00:01:25,452 +marketing campaigns, +alerts, and reminders. + +21 +00:01:25,485 --> 00:01:30,858 +In this example you +can see a mix of SMS messages + +22 +00:01:30,891 --> 00:01:33,160 +including short codes, + +23 +00:01:33,193 --> 00:01:37,431 +alphanumeric codes, +and standard phone numbers. + +24 +00:01:37,464 --> 00:01:40,901 +This results in a very cluttered inbox, + +25 +00:01:40,934 --> 00:01:44,571 +and it's really difficult +to find personal messages + +26 +00:01:44,605 --> 00:01:46,306 +from your loved ones. + +27 +00:01:47,808 --> 00:01:54,414 +iOS does provide an option +to filter messages from unknown senders, + +28 +00:01:54,448 --> 00:01:58,018 +but if you receive several messages +each day, + +29 +00:01:58,051 --> 00:02:02,589 +even the unknown sender folder +will quickly get filled + +30 +00:02:02,623 --> 00:02:04,625 +with unread messages. + +31 +00:02:04,658 --> 00:02:10,731 +Automated filtering of SMS messages +is important to many people, + +32 +00:02:10,764 --> 00:02:13,433 +and iOS provides a secure + +33 +00:02:13,467 --> 00:02:16,503 +sandbox-based extension model + +34 +00:02:16,537 --> 00:02:21,675 +that allow you to further classify +messages from unknown senders. + +35 +00:02:21,708 --> 00:02:28,215 +People can find and install +SMS filter apps from the App Store. + +36 +00:02:28,248 --> 00:02:32,819 +Once installed, +you can turn on message filtering + +37 +00:02:32,853 --> 00:02:36,723 +by going to Settings, Messages, + +38 +00:02:36,757 --> 00:02:42,663 +Unknown & Spam, +and turning on Filter Unknown Senders. + +39 +00:02:42,696 --> 00:02:47,601 +Then, you can select +your SMS filter of choice. + +40 +00:02:47,634 --> 00:02:51,205 +Here we have installed two SMS filters. + +41 +00:02:51,238 --> 00:02:56,376 +Note that only one filter +can be active at a time. + +42 +00:02:57,377 --> 00:03:00,781 +In iOS 14 and later, + +43 +00:03:00,814 --> 00:03:04,484 +new folders will appear in Messages + +44 +00:03:04,518 --> 00:03:09,122 +for Transactions, Promotions, and Junk. + +45 +00:03:09,156 --> 00:03:14,661 +These folders help people organize +and find messages + +46 +00:03:14,695 --> 00:03:17,397 +that are most relevant to them. + +47 +00:03:17,431 --> 00:03:20,534 +Regardless of which filter is chosen, + +48 +00:03:20,567 --> 00:03:25,205 +Messages provide +the same classification structure + +49 +00:03:25,239 --> 00:03:29,810 +consisting of Transactions, +Promotions, and Junk. + +50 +00:03:29,843 --> 00:03:32,913 +We have heard from you loud and clear, + +51 +00:03:32,946 --> 00:03:37,217 +that you would like to provide +more detailed classifications. + +52 +00:03:37,251 --> 00:03:42,523 +So in iOS 16 +we are introducing enhancements + +53 +00:03:42,556 --> 00:03:46,159 +to the filter classification API. + +54 +00:03:46,193 --> 00:03:49,696 +Now your SMS message filter extension + +55 +00:03:49,730 --> 00:03:54,701 +can classify messages +into 12 additional sub-categories. + +56 +00:03:55,736 --> 00:04:00,140 +Here are the new sub-categories +that you can specify + +57 +00:04:00,174 --> 00:04:03,911 +in your SMS filter extension. + +58 +00:04:03,944 --> 00:04:09,516 +The new sub-categories fall under +the existing top level categories– + +59 +00:04:09,550 --> 00:04:12,819 +Transactions and Promotions. + +60 +00:04:12,853 --> 00:04:16,223 +Now you can further refine +incoming messages + +61 +00:04:16,256 --> 00:04:19,593 +and provide an even better experience. + +62 +00:04:19,626 --> 00:04:22,796 +For example, in markets like India, + +63 +00:04:22,829 --> 00:04:26,500 +it's common to receive +a large number of messages + +64 +00:04:26,533 --> 00:04:29,536 +related to financial transactions. + +65 +00:04:29,570 --> 00:04:33,140 +These include activities +in their bank account + +66 +00:04:33,173 --> 00:04:36,443 +and alerts for credit card spending. + +67 +00:04:36,476 --> 00:04:40,514 +These messages can now +be nicely organized + +68 +00:04:40,547 --> 00:04:44,952 +in the Finance sub-folder +under Transactions. + +69 +00:04:44,985 --> 00:04:50,557 +Let's see how the flow works +for SMS message filters. + +70 +00:04:50,591 --> 00:04:53,994 +We can divide the flow into two phases; + +71 +00:04:54,027 --> 00:04:58,332 +configuration and runtime classification. + +72 +00:04:58,365 --> 00:05:00,734 +In the configuration phase, + +73 +00:05:00,767 --> 00:05:05,072 +users select your message filter +in Settings. + +74 +00:05:05,105 --> 00:05:10,644 +This triggers a new API +introduced in iOS 16 + +75 +00:05:10,677 --> 00:05:15,382 +to request the capabilities +supported by your filter. + +76 +00:05:15,415 --> 00:05:20,454 +You can now respond with a list +of supported categories + +77 +00:05:20,487 --> 00:05:22,489 +and sub-categories. + +78 +00:05:22,523 --> 00:05:26,159 +In this example, +the filter reports + +79 +00:05:26,193 --> 00:05:29,296 +that it supports the sub-categories + +80 +00:05:29,329 --> 00:05:33,634 +Finance, Orders, and Coupons. + +81 +00:05:33,667 --> 00:05:36,904 +iOS validates these capabilities + +82 +00:05:36,937 --> 00:05:40,073 +and updates the inbox in Messages + +83 +00:05:40,107 --> 00:05:43,310 +with the appropriate folders. + +84 +00:05:43,343 --> 00:05:46,413 +In the runtime classification phase, + +85 +00:05:46,446 --> 00:05:52,286 +every time an SMS message is received +from an unknown sender, + +86 +00:05:52,319 --> 00:05:54,621 +iOS queries your filter + +87 +00:05:54,655 --> 00:06:00,327 +to determine which category +and sub-category it belongs to. + +88 +00:06:00,360 --> 00:06:05,732 +You can also see the terms +action and sub-action used here. + +89 +00:06:05,766 --> 00:06:10,037 +Filters must respond with +one of the capabilities declared + +90 +00:06:10,070 --> 00:06:12,472 +in the configuration phase. + +91 +00:06:12,506 --> 00:06:15,709 +The SMS message will then appear + +92 +00:06:15,742 --> 00:06:20,047 +in the corresponding sub-folder +in Messages. + +93 +00:06:20,080 --> 00:06:24,251 +Let's build +a simple message filter extension + +94 +00:06:24,284 --> 00:06:28,055 +to see how the APIs are used + +95 +00:06:28,088 --> 00:06:31,658 +and what people will see in Messages. + +96 +00:06:32,726 --> 00:06:35,829 +In Xcode, you start by creating + +97 +00:06:35,863 --> 00:06:39,933 +a new Message Filter Extension target. + +98 +00:06:39,967 --> 00:06:45,772 +The Message Filter Extension target +appears as one of the options + +99 +00:06:45,806 --> 00:06:48,509 +when you create a new target + +100 +00:06:48,542 --> 00:06:51,311 +and go to template selection. + +101 +00:06:51,345 --> 00:06:56,316 +Select the Message Filter Extension +and click Next. + +102 +00:06:56,350 --> 00:07:01,054 +Now give your filter a name +and click Finish. + +103 +00:07:02,289 --> 00:07:04,591 +When the target is created, + +104 +00:07:04,625 --> 00:07:09,630 +MessageFilterExtension.swift +will be auto-populated + +105 +00:07:09,663 --> 00:07:12,065 +with all the required functions. + +106 +00:07:12,099 --> 00:07:16,203 +In iOS 16, we have a new API handle + +107 +00:07:16,236 --> 00:07:19,573 +called capabilitiesRequest. + +108 +00:07:19,606 --> 00:07:24,578 +Fill in the +ILMessageFilterCapabilitiesQueryResponse + +109 +00:07:24,611 --> 00:07:29,816 +structure with the transactional +or promotional sub-actions + +110 +00:07:29,850 --> 00:07:31,518 +that you support. + +111 +00:07:31,552 --> 00:07:35,689 +You can specify up to five sub-actions. + +112 +00:07:35,722 --> 00:07:39,860 +Here we are indicating support +for Finance, + +113 +00:07:39,893 --> 00:07:44,498 +Orders, and Health sub-actions +under Transactions, + +114 +00:07:44,531 --> 00:07:49,670 +and Coupons and Offers sub-actions +under Promotions. + +115 +00:07:49,703 --> 00:07:53,307 +Now build and install +your filter extension. + +116 +00:07:53,340 --> 00:07:56,844 +When you choose your filter in Settings, + +117 +00:07:56,877 --> 00:08:00,047 +the Message inbox will be updated + +118 +00:08:00,080 --> 00:08:03,383 +with the sub-actions we declared; + +119 +00:08:03,417 --> 00:08:09,523 +Finance, Orders, and Health appear +under Transactions, + +120 +00:08:09,556 --> 00:08:15,495 +and Coupons and Offers +appear under Promotions. + +121 +00:08:16,330 --> 00:08:20,634 +Now, we have confirmed +that our configuration works. + +122 +00:08:20,667 --> 00:08:23,370 +Let's define some simple logic + +123 +00:08:23,403 --> 00:08:26,974 +to return suitable categories to iOS + +124 +00:08:27,007 --> 00:08:31,445 +when an incoming SMS message is received. + +125 +00:08:31,478 --> 00:08:38,485 +First, we extract the message body text +from the queryRequest object. + +126 +00:08:38,519 --> 00:08:42,356 +If the message contains specific keywords, + +127 +00:08:42,389 --> 00:08:47,628 +we return a suitable matching action +and sub-action. + +128 +00:08:47,661 --> 00:08:53,166 +Here, we have added the code +where if the keyword is debited + +129 +00:08:53,200 --> 00:08:57,171 +then we will return filterAction +as Transaction + +130 +00:08:57,204 --> 00:09:02,075 +and filterSubaction +as transactionalfinance. + +131 +00:09:02,109 --> 00:09:06,713 +Similarly, when the message has +the keyword coupon, + +132 +00:09:06,747 --> 00:09:12,252 +then Promotion and PromotionalCoupons +will be returned. + +133 +00:09:12,286 --> 00:09:15,088 +You can also update the business logic + +134 +00:09:15,122 --> 00:09:21,361 +for other sub classifications within +Transactions and Promotions. + +135 +00:09:21,395 --> 00:09:25,566 +Note that if you return +an incorrect combination + +136 +00:09:25,599 --> 00:09:29,336 +for filterAction and filterSubAction, + +137 +00:09:29,369 --> 00:09:32,072 +iOS will discard the sub-action + +138 +00:09:32,105 --> 00:09:34,808 +and only honor the action. + +139 +00:09:35,576 --> 00:09:39,680 +For example, +if we return the action Transaction + +140 +00:09:39,713 --> 00:09:41,982 +and sub-action Coupons, + +141 +00:09:42,015 --> 00:09:47,321 +then the message will only go +to the All Transaction folder. + +142 +00:09:48,422 --> 00:09:53,560 +Here, we have an example +of SMS received from bank + +143 +00:09:53,594 --> 00:09:55,963 +with the keyword "debited" + +144 +00:09:55,996 --> 00:10:00,400 +and it has been classified +under Finance sub-folder by Messages. + +145 +00:10:02,936 --> 00:10:04,738 +Here is another example + +146 +00:10:04,771 --> 00:10:10,511 +where we have received a Black Friday Deal +with the keyword "coupon." + +147 +00:10:10,544 --> 00:10:15,716 +In Messages it has been classified +under the Coupons sub-folder. + +148 +00:10:17,050 --> 00:10:21,255 +In iOS 16, +you can choose sub-categories + +149 +00:10:21,288 --> 00:10:25,492 +that are the best fit +for your user demographics. + +150 +00:10:25,526 --> 00:10:31,031 +In the first example, the SMS message +filter shows folders + +151 +00:10:31,064 --> 00:10:34,735 +for Orders, Reminders, Health, + +152 +00:10:34,768 --> 00:10:40,007 +Public Services, and Weather +under Transactions, + +153 +00:10:40,040 --> 00:10:45,279 +while the second example shows +folders for Finance, + +154 +00:10:45,312 --> 00:10:48,615 +Reminders, Health, and Rewards + +155 +00:10:48,649 --> 00:10:50,517 +under Transactions + +156 +00:10:50,551 --> 00:10:53,954 +and Offers under Promotions. + +157 +00:10:53,987 --> 00:10:58,458 +Your filter extension can use +these sub-categories + +158 +00:10:58,492 --> 00:11:03,630 +to provide a differentiated experience +for your users. + +159 +00:11:03,664 --> 00:11:08,035 +Apple provides an SMS filter in India, + +160 +00:11:08,068 --> 00:11:13,841 +and we have updated it +using the enhancements in iOS 16. + +161 +00:11:13,874 --> 00:11:17,277 +The Apple SMS filter in India + +162 +00:11:17,311 --> 00:11:20,814 +now supports additional sub-folders + +163 +00:11:20,848 --> 00:11:24,084 +including Finance, Orders, + +164 +00:11:24,117 --> 00:11:27,321 +and Reminders under Transactions. + +165 +00:11:27,354 --> 00:11:31,825 +Your bank transactions appear in Finance, + +166 +00:11:31,859 --> 00:11:35,495 +your food +or commercial delivery messages + +167 +00:11:35,529 --> 00:11:38,065 +are organized under Orders, + +168 +00:11:38,098 --> 00:11:43,504 +while important events and to-do's +appear in the Reminders folder. + +169 +00:11:44,238 --> 00:11:50,043 +To wrap up, in this video +we talked about SMS message filters + +170 +00:11:50,077 --> 00:11:54,581 +and how they can classify messages +into Transactions, + +171 +00:11:54,615 --> 00:11:57,351 +Promotions, and Junk. + +172 +00:11:57,384 --> 00:12:01,722 +In iOS 16, +your message filter extension + +173 +00:12:01,755 --> 00:12:07,561 +can now classify messages +into 12 additional sub-categories. + +174 +00:12:07,594 --> 00:12:11,164 +To learn more, see the API documentation + +175 +00:12:11,198 --> 00:12:14,101 +in the link accompanying this video. + +176 +00:12:14,134 --> 00:12:18,639 +I look forward to seeing your new +and creative ideas + +177 +00:12:18,672 --> 00:12:22,442 +to help people organize SMS messages. + +178 +00:12:22,476 --> 00:12:25,612 +And as always, +we appreciate your feedback + +179 +00:12:25,646 --> 00:12:30,384 +to help us improve +SMS message filtering in future. + diff --git a/eng/2022 Session 110342 Design app experiences with charts en.srt b/eng/2022 Session 110342 Design app experiences with charts en.srt new file mode 100644 index 0000000..6d92fd0 --- /dev/null +++ b/eng/2022 Session 110342 Design app experiences with charts en.srt @@ -0,0 +1,1325 @@ +1 +00:00:01,335 --> 00:00:07,341 +[spacey music] + +2 +00:00:09,776 --> 00:00:11,578 +Nicholas: Hi. +I’m Nicholas Felton, + +3 +00:00:11,612 --> 00:00:14,047 +a designer +on the Human Interface team. + +4 +00:00:14,081 --> 00:00:17,718 +Today I’ll be talking about how to build +great app experiences using charts. + +5 +00:00:18,285 --> 00:00:21,321 +Charts are a great way to communicate +complex information. + +6 +00:00:21,355 --> 00:00:24,057 +We use them throughout Apple +to enhance our products. + +7 +00:00:25,225 --> 00:00:28,962 +Health uses charts to help us understand +our bodies, + +8 +00:00:28,996 --> 00:00:31,965 +Fitness inspires us +to stay active with charts, + +9 +00:00:31,999 --> 00:00:34,401 +and Weather displays charts +to help us plan our day. + +10 +00:00:36,069 --> 00:00:38,105 +We know that developers love them as well, + +11 +00:00:38,138 --> 00:00:41,341 +and we see inspiring examples +in every category. + +12 +00:00:41,375 --> 00:00:44,545 +From the detailed workout analysis +in Strava + +13 +00:00:44,578 --> 00:00:47,014 +to the playful progress charts +in Duolingo. + +14 +00:00:47,714 --> 00:00:50,350 +Charts can be found everywhere, and when +well-designed, + +15 +00:00:50,384 --> 00:00:52,019 +can reveal subtleties in data + +16 +00:00:52,052 --> 00:00:54,555 +that you cannot communicate easily +through text. + +17 +00:00:54,588 --> 00:00:58,091 +In addition to their utility, charts +are also an opportunity + +18 +00:00:58,125 --> 00:01:01,828 +to support the personality of your app +and add visual interest to your UI. + +19 +00:01:03,397 --> 00:01:06,166 +To demonstrate how an app can be enhanced +with charts, + +20 +00:01:06,200 --> 00:01:08,435 +let’s look at one made +for the owner of a food truck + +21 +00:01:08,468 --> 00:01:09,736 +selling pancakes. + +22 +00:01:09,770 --> 00:01:13,340 +The app currently has a tab +for entering orders, + +23 +00:01:13,373 --> 00:01:16,543 +and another for viewing +recent transactions. + +24 +00:01:16,577 --> 00:01:18,879 +This works fine for keeping track +of sales, + +25 +00:01:18,912 --> 00:01:22,149 +but this information could be +much more useful. + +26 +00:01:22,182 --> 00:01:24,718 +This year, we’re introducing Swift Charts. + +27 +00:01:24,751 --> 00:01:29,156 +With this framework, making charts +for Apple devices has never been easier. + +28 +00:01:29,189 --> 00:01:32,426 +In the following sections, I will share +the principles we follow + +29 +00:01:32,459 --> 00:01:34,962 +when designing experiences +with charts at Apple. + +30 +00:01:34,995 --> 00:01:38,065 +We will apply these concepts to create +wireframes + +31 +00:01:38,098 --> 00:01:41,068 +for a more informative version +of the food truck app. + +32 +00:01:41,101 --> 00:01:43,971 +In order to build a great experience +with charts, + +33 +00:01:44,004 --> 00:01:45,906 +we should consider three things: + +34 +00:01:45,939 --> 00:01:48,008 +when to use charts, + +35 +00:01:48,041 --> 00:01:49,743 +how to use them, + +36 +00:01:49,776 --> 00:01:53,614 +and how they relate to each other +in a chart design system. + +37 +00:01:53,647 --> 00:01:56,683 +Let’s begin by discussing +when to use charts in an app. + +38 +00:01:56,717 --> 00:01:59,586 +Here are some common cases +where a chart can elevate + +39 +00:01:59,620 --> 00:02:01,522 +the information being presented: + +40 +00:02:01,555 --> 00:02:04,691 +When showing historical +or predicted values, + +41 +00:02:04,725 --> 00:02:07,394 +a chart can vividly demonstrate +changes in data. + +42 +00:02:08,529 --> 00:02:12,466 +By visualizing a portion of a whole, +we can use charts to show the state + +43 +00:02:12,499 --> 00:02:16,737 +of something that is completing, +progressing towards a goal, or emptying. + +44 +00:02:17,871 --> 00:02:22,676 +When comparing items or categories, we can +easily evaluate their values with charts. + +45 +00:02:24,478 --> 00:02:26,713 +To decide whether any +of these approaches + +46 +00:02:26,747 --> 00:02:30,684 +are appropriate for your app, +consider what the experience needs first. + +47 +00:02:30,717 --> 00:02:33,954 +How will a chart support +the core goals of your app? + +48 +00:02:33,987 --> 00:02:37,224 +When used correctly, charts provide focus. + +49 +00:02:37,257 --> 00:02:39,993 +As an app creator, +there are many things you could visualize, + +50 +00:02:40,027 --> 00:02:42,496 +but only the most important information +should become a chart. + +51 +00:02:43,397 --> 00:02:46,366 +For someone using an app, +charts are a strong signal. + +52 +00:02:46,400 --> 00:02:50,003 +They direct attention to the information +you want them to understand. + +53 +00:02:50,037 --> 00:02:53,273 +The goal of the food truck app +is to improve the operation + +54 +00:02:53,307 --> 00:02:55,175 +of the businesses using it. + +55 +00:02:55,209 --> 00:02:58,412 +If we can use charts to turn +a list of transactions + +56 +00:02:58,445 --> 00:03:00,180 +into actionable information, + +57 +00:03:00,214 --> 00:03:04,051 +food truck owners +will welcome the improvement. + +58 +00:03:04,084 --> 00:03:08,155 +Here is some key information that we know +will be useful to food truck owners: + +59 +00:03:08,188 --> 00:03:09,957 +viewing recent sales, + +60 +00:03:09,990 --> 00:03:11,825 +understanding what items are popular, + +61 +00:03:11,859 --> 00:03:14,862 +and knowing the best location +to visit each day. + +62 +00:03:15,963 --> 00:03:19,433 +As you consider your app, remember +that charts should direct attention + +63 +00:03:19,466 --> 00:03:23,203 +and provide focus to the most +important information in your experience. + +64 +00:03:23,237 --> 00:03:26,273 +Now that we’ve established +what we want to communicate, + +65 +00:03:26,306 --> 00:03:28,809 +let’s look +at how to achieve this using charts. + +66 +00:03:29,810 --> 00:03:32,613 +To illustrate recent sales, +a chart showing change over time + +67 +00:03:32,646 --> 00:03:33,981 +will be appropriate. + +68 +00:03:34,681 --> 00:03:37,951 +One way of doing this is to use +a bar chart showing sales totals + +69 +00:03:37,985 --> 00:03:40,354 +for each of the last 30 days. + +70 +00:03:40,387 --> 00:03:43,790 +We will add axis labels soon, +but first, it needs a description + +71 +00:03:43,824 --> 00:03:45,626 +to tell someone what the chart represents. + +72 +00:03:47,261 --> 00:03:51,365 +A title like "Sales in the Past 30 Days" +labels the elements of the chart + +73 +00:03:51,398 --> 00:03:53,534 +but doesn’t communicate anything more. + +74 +00:03:53,567 --> 00:03:55,736 +How can we summarize +the information being shown? + +75 +00:03:57,004 --> 00:04:00,741 +A chart should be accompanied by text +that describes the chart contents. + +76 +00:04:01,275 --> 00:04:04,044 +If read in isolation, +it should be informative. + +77 +00:04:04,711 --> 00:04:07,848 +Adding the total number of pancakes sold +transforms the title + +78 +00:04:07,881 --> 00:04:10,217 +into a self-contained description. + +79 +00:04:10,250 --> 00:04:13,554 +Now someone can quickly gather +key information from the text, + +80 +00:04:13,587 --> 00:04:15,689 +while the chart +provides supporting details. + +81 +00:04:17,324 --> 00:04:20,327 +When a chart is not straightforward, +using a complete sentence + +82 +00:04:20,360 --> 00:04:22,696 +for the description can make +it easier to understand. + +83 +00:04:23,330 --> 00:04:29,803 +For example: "Sales in the past 30 days +totaled 1,234 pancakes." + +84 +00:04:31,004 --> 00:04:34,708 +Another way of framing the chart +is to interpret the data. + +85 +00:04:34,741 --> 00:04:38,645 +For example: "Sales for the past 30 +days are up 12%, + +86 +00:04:38,679 --> 00:04:41,815 +totaling 1,234 pancakes." + +87 +00:04:41,849 --> 00:04:43,684 +This will help someone understand + +88 +00:04:43,717 --> 00:04:46,954 +if the level of recent sales +is high or low + +89 +00:04:46,987 --> 00:04:49,223 +or if it is increasing or decreasing. + +90 +00:04:49,256 --> 00:04:52,226 +This technique can make +a description more meaningful, + +91 +00:04:52,259 --> 00:04:54,194 +particularly when the data is unfamiliar. + +92 +00:04:55,762 --> 00:04:58,765 +Each of these approaches is +a good way of describing a chart, + +93 +00:04:58,799 --> 00:05:02,369 +but this overview is just one way +of explaining our sales data. + +94 +00:05:02,402 --> 00:05:05,405 +What else can we do +to illuminate these transactions? + +95 +00:05:06,573 --> 00:05:09,910 +Try to incorporate details +from other perspectives. + +96 +00:05:09,943 --> 00:05:13,280 +It’s important to summarize your data, +but when we find ways + +97 +00:05:13,313 --> 00:05:16,450 +to elevate categories or highlight +individual records, + +98 +00:05:16,483 --> 00:05:18,485 +a chart becomes multi-dimensional. + +99 +00:05:18,519 --> 00:05:21,321 +Here are some additional perspectives +to consider: + +100 +00:05:21,355 --> 00:05:25,192 +At a macro level, we look for ways to +describe the entire data set, + +101 +00:05:25,225 --> 00:05:28,795 +like a total, or average value. + +102 +00:05:28,829 --> 00:05:32,533 +At a medium scale, +we look at sub-sets of data. + +103 +00:05:32,566 --> 00:05:36,403 +These might be time-based, +like comparing weekdays to weekends + +104 +00:05:36,436 --> 00:05:40,607 +or investigating changes +related to the time of day. + +105 +00:05:40,641 --> 00:05:43,777 +Other approaches might categorize +sales by the style of pancake + +106 +00:05:43,810 --> 00:05:45,746 +or the city where they were sold. + +107 +00:05:47,147 --> 00:05:50,651 +At the micro level, +we focus on individual data points. + +108 +00:05:50,684 --> 00:05:55,055 +The last transaction or largest sale +are important small-scale details + +109 +00:05:55,088 --> 00:05:56,957 +you may want to call out in your charts. + +110 +00:05:58,258 --> 00:06:02,629 +The more carefully you observe a data set, +the more detail will emerge. + +111 +00:06:02,663 --> 00:06:05,766 +Some of the perspectives +we’ve identified could be useful + +112 +00:06:05,799 --> 00:06:09,503 +to food truck owners planning +their inventory or sales locations. + +113 +00:06:09,536 --> 00:06:12,940 +It would be useful to augment +the recent sales chart + +114 +00:06:12,973 --> 00:06:14,842 +with some of these details. + +115 +00:06:14,875 --> 00:06:19,146 +To do this, we can use a set +of tappable rows under the chart. + +116 +00:06:19,179 --> 00:06:22,416 +Each row provides a summary statistic, +and when tapped, + +117 +00:06:22,449 --> 00:06:24,384 +the chart is updated to match. + +118 +00:06:24,418 --> 00:06:27,855 +We can show the daily average value + +119 +00:06:27,888 --> 00:06:33,160 +or the difference between weekday +and weekend sales + +120 +00:06:33,193 --> 00:06:35,195 +or the best sales day. + +121 +00:06:35,229 --> 00:06:38,665 +These overlays can be extremely useful, +but this amount of information + +122 +00:06:38,699 --> 00:06:41,068 +requires a large surface +to work effectively. + +123 +00:06:42,102 --> 00:06:46,206 +As the functionality of a chart increases, +so will its size. + +124 +00:06:46,907 --> 00:06:49,276 +Smaller charts tend to be static. + +125 +00:06:49,309 --> 00:06:51,945 +Examples include Watch complications, + +126 +00:06:51,979 --> 00:06:55,015 +the repeating thumbnail charts +in Stocks, + +127 +00:06:55,048 --> 00:06:56,817 +and the trend platters in Health. + +128 +00:06:57,384 --> 00:06:59,953 +Static charts rarely exist in isolation. + +129 +00:06:59,987 --> 00:07:04,491 +They tend to provide a preview +of a larger chart in another view. + +130 +00:07:04,525 --> 00:07:07,861 +As they are generally small, +static charts don’t require grid lines, + +131 +00:07:07,895 --> 00:07:10,163 +labels, or interactivity, + +132 +00:07:10,197 --> 00:07:14,334 +since they create the expectation +that additional detail is just a tap away. + +133 +00:07:14,368 --> 00:07:16,803 +Interactive charts tend to be larger, + +134 +00:07:16,837 --> 00:07:18,572 +and include much more detail, + +135 +00:07:18,605 --> 00:07:21,542 +like the charts in Stocks and in Health. + +136 +00:07:22,109 --> 00:07:25,479 +An interactive chart will typically be +the width of your view, + +137 +00:07:25,512 --> 00:07:26,880 +but not full-height. + +138 +00:07:26,914 --> 00:07:31,451 +You should include axis lines and labels +so that values can be estimated. + +139 +00:07:32,586 --> 00:07:36,990 +Interactivity is recommended at this size +for accessing precise values + +140 +00:07:37,024 --> 00:07:38,492 +in the chart + +141 +00:07:38,525 --> 00:07:43,730 +and the ability to change the time range +or time scope will aid exploration. + +142 +00:07:44,865 --> 00:07:49,770 +The largest and most interactive charts +allow for deep investigation of data + +143 +00:07:49,803 --> 00:07:52,339 +and require the most vertical space. + +144 +00:07:52,372 --> 00:07:54,875 +As charts become more powerful, +it’s important + +145 +00:07:54,908 --> 00:07:57,845 +to introduce +additional functionality gradually. + +146 +00:07:57,878 --> 00:08:00,781 +You should progressively reveal +chart complexity + +147 +00:08:00,814 --> 00:08:03,083 +so that someone can choose +the level of information + +148 +00:08:03,116 --> 00:08:04,718 +that matches their interest. + +149 +00:08:06,320 --> 00:08:10,691 +Use a small static chart higher +in the navigation hierarchy + +150 +00:08:10,724 --> 00:08:13,493 +to offer a path to expanded versions +of the chart. + +151 +00:08:15,028 --> 00:08:17,898 +When you create a link +between two versions of the same chart, + +152 +00:08:17,931 --> 00:08:20,234 +that progression should maintain +continuity + +153 +00:08:20,267 --> 00:08:23,403 +by preserving values, context and state. + +154 +00:08:25,305 --> 00:08:28,542 +Keep in mind that when someone +expresses interest in a chart, + +155 +00:08:28,575 --> 00:08:32,045 +they want to see more of what +they have already seen. + +156 +00:08:32,079 --> 00:08:35,148 +This means that a chart +should retain its shape + +157 +00:08:35,182 --> 00:08:38,252 +and any numbers apparent +in an earlier view should be preserved. + +158 +00:08:40,621 --> 00:08:43,056 +You can add information, +but showing something different + +159 +00:08:43,090 --> 00:08:45,158 +can be frustrating or disorienting. + +160 +00:08:46,493 --> 00:08:50,097 +While we’re considering the functionality +of different sized charts, + +161 +00:08:50,130 --> 00:08:51,899 +let’s revisit our food truck app + +162 +00:08:51,932 --> 00:08:54,568 +to determine where our planned charts +will go. + +163 +00:08:54,601 --> 00:08:58,805 +We currently have two tabs, +one dedicated to placing orders, + +164 +00:08:58,839 --> 00:09:01,241 +and a second that shows the sales history. + +165 +00:09:01,275 --> 00:09:03,610 +These records +are what we will be visualizing, + +166 +00:09:03,644 --> 00:09:06,413 +so it makes sense to adapt +this view to include our charts. + +167 +00:09:07,981 --> 00:09:11,084 +Let’s clear the tab by moving +the sales to a separate view + +168 +00:09:11,118 --> 00:09:14,221 +behind a "View all sales" +navigational element. + +169 +00:09:14,254 --> 00:09:16,657 +We now have a prominent canvas +for our charts. + +170 +00:09:16,690 --> 00:09:19,560 +As a result, +it’s appropriate to use a static chart + +171 +00:09:19,593 --> 00:09:22,930 +that lets someone scan the data +and decide whether they want to see more. + +172 +00:09:24,565 --> 00:09:28,368 +Here’s the static chart +showing recent sales. + +173 +00:09:28,402 --> 00:09:32,606 +Tapping the platter +leads to an expanded interactive chart. + +174 +00:09:32,639 --> 00:09:35,576 +We now have +a complete recent sales experience. + +175 +00:09:35,609 --> 00:09:38,679 +This interactive chart +will support detailed analysis + +176 +00:09:38,712 --> 00:09:42,216 +with 30-day and 1-year views, +touch states, + +177 +00:09:42,249 --> 00:09:43,951 +and tappable summary statistics. + +178 +00:09:46,053 --> 00:09:49,857 +As you’re working on your app, +remember that charts need descriptions + +179 +00:09:49,890 --> 00:09:52,459 +to inform the contents +and provide a key takeaway, + +180 +00:09:53,227 --> 00:09:56,763 +the details in your data can add richness +to your charts, + +181 +00:09:56,797 --> 00:10:00,000 +and that you should progressively reveal +chart complexity within your app. + +182 +00:10:01,001 --> 00:10:03,537 +Now let’s look at chart design systems. + +183 +00:10:03,570 --> 00:10:05,839 +When your app includes +more than one chart, + +184 +00:10:05,873 --> 00:10:08,008 +you’ve created a chart design system. + +185 +00:10:08,041 --> 00:10:11,245 +Here are some things to remember +when planning multiple charts: + +186 +00:10:13,080 --> 00:10:14,915 +Use familiar forms. + +187 +00:10:14,948 --> 00:10:18,452 +Start with common chart styles +to aid comprehension. + +188 +00:10:18,485 --> 00:10:20,988 +If someone has +already used a similar chart, + +189 +00:10:21,021 --> 00:10:23,991 +they will be more likely +to understand yours. + +190 +00:10:24,024 --> 00:10:29,596 +Bar charts and line charts are forms +that many people encounter and use daily, + +191 +00:10:29,630 --> 00:10:32,499 +while a scatter plot +is less common and may require + +192 +00:10:32,533 --> 00:10:35,936 +extra guidance to ensure that +it is interpreted correctly. + +193 +00:10:36,637 --> 00:10:40,474 +If you want to do something unique, +it should be introduced clearly, + +194 +00:10:40,507 --> 00:10:44,411 +as demonstrated here +in the onboarding flow for Activity. + +195 +00:10:44,444 --> 00:10:47,181 +After the activity rings are introduced, + +196 +00:10:47,214 --> 00:10:54,121 +they are split apart to show the move, +exercise, and stand components. + +197 +00:10:56,290 --> 00:10:59,826 +Ideally, a new form is central +to your app, not supplementary. + +198 +00:10:59,860 --> 00:11:02,763 +The prominence you give a novel chart +will encourage people + +199 +00:11:02,796 --> 00:11:04,398 +to explore and understand it. + +200 +00:11:06,433 --> 00:11:08,035 +When creating supporting charts, + +201 +00:11:08,068 --> 00:11:09,803 +familiar forms are more important, + +202 +00:11:09,837 --> 00:11:11,872 +as they will not be given +as much prominence. + +203 +00:11:13,340 --> 00:11:16,443 +Differences matter, +and the variations between charts + +204 +00:11:16,476 --> 00:11:18,378 +are a signal that something has changed. + +205 +00:11:19,246 --> 00:11:24,051 +To demonstrate this, let’s start +with two copies of our recent sales chart. + +206 +00:11:24,084 --> 00:11:28,288 +I’ll slowly update the chart on the right +to communicate different information, + +207 +00:11:28,322 --> 00:11:32,893 +and we'll show how the design evolves +to signal these distinctions. + +208 +00:11:32,926 --> 00:11:36,663 +First, I’ll change the time scope +of the chart on the right. + +209 +00:11:36,697 --> 00:11:41,602 +Rather than showing recent sales, +it now shows sales for the last 12 months. + +210 +00:11:41,635 --> 00:11:47,140 +I have changed the description and updated +the chart to show 12 months of data. + +211 +00:11:47,174 --> 00:11:50,844 +Minimal changes +are required for this modification. + +212 +00:11:50,878 --> 00:11:54,515 +If I want to change the type of data +being displayed in the right-hand chart, + +213 +00:11:54,548 --> 00:11:57,451 +it may not be sufficient +to only change the description. + +214 +00:11:58,352 --> 00:12:00,721 +Noticing this change in text +could be easily missed, + +215 +00:12:00,754 --> 00:12:03,757 +so an additional change is necessary. + +216 +00:12:03,790 --> 00:12:07,094 +Giving each of these charts +a distinct color helps. + +217 +00:12:07,127 --> 00:12:10,631 +Now it is easier to notice +that these charts are unique. + +218 +00:12:10,664 --> 00:12:13,133 +Creating this difference +makes it more likely + +219 +00:12:13,166 --> 00:12:15,469 +that someone will now read +the description. + +220 +00:12:17,604 --> 00:12:20,474 +Finally, I want to update the chart +on the right + +221 +00:12:20,507 --> 00:12:24,545 +to show the range of daily sales +for each of past 12 months. + +222 +00:12:24,578 --> 00:12:27,214 +This requires both a change +to the description + +223 +00:12:27,247 --> 00:12:29,183 +and the way that the data is represented. + +224 +00:12:30,751 --> 00:12:33,220 +To underscore +the significance of this change, + +225 +00:12:33,253 --> 00:12:35,889 +modifying the styling +of the bars is appropriate. + +226 +00:12:36,657 --> 00:12:39,860 +The chart on the right +now conveys a different subject, + +227 +00:12:39,893 --> 00:12:42,429 +different time range, +and different metrics. + +228 +00:12:42,462 --> 00:12:44,631 +The design is purposefully distinct + +229 +00:12:44,665 --> 00:12:47,234 +to ensure that these differences +will be noticed. + +230 +00:12:47,267 --> 00:12:50,370 +We can apply these principles +to the two other charts + +231 +00:12:50,404 --> 00:12:51,939 +we need for our food truck app. + +232 +00:12:53,740 --> 00:12:56,643 +To compliment the recent sales, +I’d like to add another chart + +233 +00:12:56,677 --> 00:12:59,213 +that shows +the most popular style of pancake. + +234 +00:13:00,147 --> 00:13:04,051 +One way of showing the most popular style +is to compare the relative sales level + +235 +00:13:04,084 --> 00:13:05,786 +for each pancake. + +236 +00:13:06,420 --> 00:13:09,056 +Since we are interested +in the last 30 days as a whole, + +237 +00:13:09,089 --> 00:13:12,259 +we only need one bar to compare +the popularity of styles. + +238 +00:13:13,727 --> 00:13:17,197 +If I separate the bars, +I can compare their sizes more clearly, + +239 +00:13:17,231 --> 00:13:19,900 +but this starts to look +like a time series chart again. + +240 +00:13:21,268 --> 00:13:24,605 +By making the bars horizontal, +I can accentuate the difference + +241 +00:13:24,638 --> 00:13:26,940 +between this chart +and the recent sales chart. + +242 +00:13:28,342 --> 00:13:32,012 +The horizontal orientation also +lets me make the bars longer + +243 +00:13:32,045 --> 00:13:34,848 +without needing to make the platter +on the sales tab taller. + +244 +00:13:35,782 --> 00:13:40,320 +In this preview platter, I’ve omitted +labels to focus only on the top style. + +245 +00:13:41,955 --> 00:13:46,560 +In the detail view, each bar is labeled, +and the relative values are visible. + +246 +00:13:47,628 --> 00:13:50,197 +For our final chart, +we would like to show sales + +247 +00:13:50,230 --> 00:13:53,066 +in the two cities where the truck operates +for each day of the week. + +248 +00:13:53,901 --> 00:13:56,703 +For this, we will need to do +something a bit specialized. + +249 +00:13:56,737 --> 00:14:01,008 +The context for this chart +is sales by day of the week. + +250 +00:14:01,041 --> 00:14:03,977 +In this case, we want to look +at each day independently. + +251 +00:14:04,011 --> 00:14:07,548 +Here are the average daily sales +for each day of the week + +252 +00:14:07,581 --> 00:14:09,149 +over the past 30 days. + +253 +00:14:10,017 --> 00:14:13,453 +As the food truck operates +in both Cupertino and San Francisco, + +254 +00:14:13,487 --> 00:14:17,457 +we need to split these bars +to represent each location. + +255 +00:14:17,491 --> 00:14:22,596 +Finally, let’s convert these bars +into lines to focus on the daily changes. + +256 +00:14:22,629 --> 00:14:24,898 +I’ve added this chart to the sales tab + +257 +00:14:24,932 --> 00:14:28,035 +and included a description +to summarize the data. + +258 +00:14:28,068 --> 00:14:31,038 +In this case, the best sales day +over the past 30 days + +259 +00:14:31,071 --> 00:14:33,674 +was Sunday in San Francisco. + +260 +00:14:33,707 --> 00:14:38,412 +The subsequent detail page +is the last chart we will add to the app. + +261 +00:14:38,445 --> 00:14:40,948 +It provides additional interactivity +and details, + +262 +00:14:40,981 --> 00:14:43,183 +including keys for the two lines. + +263 +00:14:44,351 --> 00:14:47,187 +We’ve now sketched out the set of charts +we plan to add + +264 +00:14:47,221 --> 00:14:49,289 +to the pancake food truck app. + +265 +00:14:49,323 --> 00:14:52,626 +Using charts to show recent sales, +popular items, + +266 +00:14:52,659 --> 00:14:54,294 +and top days and locations + +267 +00:14:54,328 --> 00:14:57,030 +will dramatically improve +the utility of this app. + +268 +00:14:58,866 --> 00:15:02,736 +As you work on your app, remember +to use familiar chart forms + +269 +00:15:02,769 --> 00:15:04,738 +to assist comprehension + +270 +00:15:04,771 --> 00:15:07,674 +and intentionally create differences +between your charts + +271 +00:15:07,708 --> 00:15:09,877 +to improve their interpretation. + +272 +00:15:09,910 --> 00:15:13,180 +In this talk, +we discussed when to use charts, + +273 +00:15:13,213 --> 00:15:17,684 +how to use them, and how they relate +to each other in a chart design system. + +274 +00:15:17,718 --> 00:15:20,954 +Applying these principles will help you +communicate data in your app + +275 +00:15:20,988 --> 00:15:23,690 +with more clarity and appeal. + +276 +00:15:23,724 --> 00:15:25,993 +To continue learning about chart design, + +277 +00:15:26,026 --> 00:15:28,929 +you can watch "Design an Effective Chart," + +278 +00:15:28,962 --> 00:15:30,731 +or for an introduction to Swift Charts, + +279 +00:15:30,764 --> 00:15:32,165 +watch "Hello Swift Charts." + +280 +00:15:34,902 --> 00:15:38,105 +[spacey music] + diff --git a/eng/2022 Session 110343 SwiftUI on iPad - Add toolbars, titles, and more en.srt b/eng/2022 Session 110343 SwiftUI on iPad - Add toolbars, titles, and more en.srt new file mode 100644 index 0000000..861173c --- /dev/null +++ b/eng/2022 Session 110343 SwiftUI on iPad - Add toolbars, titles, and more en.srt @@ -0,0 +1,1061 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:12,746 --> 00:00:16,550 +Harry: Hi, I'm Harry, +an engineer on the SwiftUI team. + +3 +00:00:16,583 --> 00:00:21,221 +Welcome to the second part +of the SwiftUI on iPad series. + +4 +00:00:21,255 --> 00:00:24,992 +In the first part of this series, +my colleague Raj dove deep + +5 +00:00:25,025 --> 00:00:27,294 +into how you can use lists, tables, + +6 +00:00:27,327 --> 00:00:30,330 +selection, and split views +to really make your apps shine + +7 +00:00:30,364 --> 00:00:34,935 +using the iPad's larger screens +and various input devices. + +8 +00:00:34,968 --> 00:00:36,470 +If you haven't seen that session, + +9 +00:00:36,503 --> 00:00:38,839 +I highly recommend starting with that. + +10 +00:00:38,872 --> 00:00:42,109 +I really enjoyed seeing Raj build out +the Places app + +11 +00:00:42,142 --> 00:00:44,811 +and I wanted to add some features +of my own. + +12 +00:00:44,845 --> 00:00:46,947 +So in this session +I'll be looking at something + +13 +00:00:46,980 --> 00:00:50,150 +near and dear to my heart, toolbars! + +14 +00:00:50,184 --> 00:00:53,787 +In SwiftUI, the toolbar API +configures many system bars + +15 +00:00:53,820 --> 00:00:56,790 +like the navigation bar +or bottom bar on iOS + +16 +00:00:56,823 --> 00:00:59,493 +or the window toolbar on macOS. + +17 +00:00:59,526 --> 00:01:03,830 +Toolbars provide quick actions +to a lot of your most common features. + +18 +00:01:03,864 --> 00:01:09,036 +Creating a good toolbar can really improve +the productivity of people using your app. + +19 +00:01:10,103 --> 00:01:12,673 +I've been spending a lot of time +thinking about toolbars + +20 +00:01:12,706 --> 00:01:17,411 +and how Places could use some of +the new toolbar features in iOS 16. + +21 +00:01:17,444 --> 00:01:20,080 +I'd like to start by briefly showing you +what I've built + +22 +00:01:20,113 --> 00:01:23,450 +to give you a taste +of what is now possible on iPad. + +23 +00:01:24,885 --> 00:01:28,021 +After all my changes, +here is the updated Places app. + +24 +00:01:29,823 --> 00:01:33,694 +You might notice some new features, +like leading aligned navigation titles, + +25 +00:01:33,727 --> 00:01:36,930 +title menus, title menu headers, + +26 +00:01:36,964 --> 00:01:39,867 +and centered aligned toolbar items. + +27 +00:01:39,900 --> 00:01:43,036 +If you've used a Mac, you might +also be familiar with features + +28 +00:01:43,070 --> 00:01:44,872 +like Toolbar customization, + +29 +00:01:44,905 --> 00:01:48,575 +which allows people to make toolbars +uniquely their own. + +30 +00:01:48,609 --> 00:01:52,412 +These powerful Mac features +are making their debut on the iPad. + +31 +00:01:53,514 --> 00:01:54,715 +To start this session, + +32 +00:01:54,748 --> 00:01:58,185 +I'll show some enhancements +to the toolbar API. + +33 +00:01:58,218 --> 00:02:02,589 +Then I'll walk you through +some new APIs for titles and documents. + +34 +00:02:02,623 --> 00:02:05,259 +So let's dive right in with toolbars. + +35 +00:02:05,292 --> 00:02:09,630 +Now, many of you have already configured +toolbars in your own iOS app + +36 +00:02:09,663 --> 00:02:12,132 +and to optimize for a smaller screen, + +37 +00:02:12,165 --> 00:02:16,236 +you might've added a menu like I did +when I first looked at Places. + +38 +00:02:17,471 --> 00:02:20,073 +My menu looked like this in code. + +39 +00:02:21,141 --> 00:02:25,946 +You can see I have a toolbar item +with a primary action placement. + +40 +00:02:25,979 --> 00:02:27,948 +Inside, I have a more menu, + +41 +00:02:27,981 --> 00:02:31,985 +with a few controls as the menu's content. + +42 +00:02:32,019 --> 00:02:34,788 +Let's see what this looks like on iPad. + +43 +00:02:35,756 --> 00:02:38,125 +As you might have guessed, +it doesn't really take advantage + +44 +00:02:38,158 --> 00:02:40,093 +of the space available there. + +45 +00:02:40,127 --> 00:02:42,963 +What's great, though, is that in iOS 16, + +46 +00:02:42,996 --> 00:02:46,900 +toolbars can implement these kind of menus +on your behalf. + +47 +00:02:46,934 --> 00:02:49,002 +We call these overflow menus + +48 +00:02:49,036 --> 00:02:50,671 +and to make best use of them, + +49 +00:02:50,704 --> 00:02:53,841 +I'll want to restructure +the content of my toolbar. + +50 +00:02:55,008 --> 00:03:00,347 +I'll start by converting the toolbar item +into a toolbar item group. + +51 +00:03:00,380 --> 00:03:03,083 +I'll then remove the menu +and make its content + +52 +00:03:03,116 --> 00:03:06,320 +the content of the toolbar item group. + +53 +00:03:06,353 --> 00:03:11,425 +This group inserts individual toolbar +items for each view in the group. + +54 +00:03:11,458 --> 00:03:13,360 +On iPad and Mac, +this is all that's needed + +55 +00:03:13,393 --> 00:03:17,364 +to automatically move items into +an overflow menu when needed. + +56 +00:03:17,397 --> 00:03:20,501 +There's more I can do here, +but before I can get to that, + +57 +00:03:20,534 --> 00:03:23,604 +I'll need to think about the placement +of my toolbar items. + +58 +00:03:24,505 --> 00:03:27,708 +Placements define the area +in which a toolbar item is rendered. + +59 +00:03:27,741 --> 00:03:31,512 +Different placements +can resolve to the same area. + +60 +00:03:31,545 --> 00:03:35,682 +You can think of a navigation bar +as having three distinct areas: + +61 +00:03:35,716 --> 00:03:40,053 +the leading, the center, +and the trailing area. + +62 +00:03:40,087 --> 00:03:44,291 +The leading and trailing areas +typically contain controls. + +63 +00:03:44,324 --> 00:03:47,661 +While the center contains +your app's navigation title. + +64 +00:03:47,694 --> 00:03:50,264 +Let's look at this +in the context of Places. + +65 +00:03:51,765 --> 00:03:54,668 +In Places +my primary action toolbar item group + +66 +00:03:54,701 --> 00:03:58,872 +resolves to the trailing area +on iPad or Mac. + +67 +00:03:58,906 --> 00:04:02,376 +Primary actions represent +the most common action available + +68 +00:04:02,409 --> 00:04:04,878 +to the user for a particular screen. + +69 +00:04:05,779 --> 00:04:10,317 +In iOS 16, there's a new placement +called secondary action. + +70 +00:04:10,350 --> 00:04:14,221 +These items represent controls +that are not the most used controls, + +71 +00:04:14,254 --> 00:04:16,957 +but still warrant their own toolbar items. + +72 +00:04:16,990 --> 00:04:21,495 +Actions like favoriting and editing +aren't the most important in Places, + +73 +00:04:21,528 --> 00:04:23,430 +so I'll make them secondary actions. + +74 +00:04:24,498 --> 00:04:29,403 +By default, secondary actions +will not be visible in the toolbar. + +75 +00:04:29,436 --> 00:04:32,973 +Instead, they live in the overflow menu. + +76 +00:04:33,006 --> 00:04:37,477 +You can change that behavior +by using the new toolbarRole modifier. + +77 +00:04:39,012 --> 00:04:44,885 +This modifier influences the behavior of +a toolbar by assigning it a semantic role. + +78 +00:04:44,918 --> 00:04:48,188 +Here I pass an editor role +which tells the navigation bar + +79 +00:04:48,222 --> 00:04:52,192 +that it should be optimized +for editing content. + +80 +00:04:52,226 --> 00:04:55,562 +The navigation bar interprets this +as a desire to have more space + +81 +00:04:55,596 --> 00:04:59,266 +to render toolbar items +and so it moves the navigation title + +82 +00:04:59,299 --> 00:05:02,236 +from the center area to the leading area. + +83 +00:05:02,269 --> 00:05:05,639 +This makes room for secondary actions +to be placed in the center + +84 +00:05:05,672 --> 00:05:08,842 +before moving to the overflow menu. + +85 +00:05:08,876 --> 00:05:12,379 +In compact size classes, +the navigation bar doesn't change + +86 +00:05:12,412 --> 00:05:15,649 +and continues to render the title +in the center. + +87 +00:05:16,884 --> 00:05:19,953 +Using the secondary action +and toolbar role API + +88 +00:05:19,987 --> 00:05:23,957 +really lets Places start to take advantage +of the iPad's size. + +89 +00:05:23,991 --> 00:05:27,661 +With the center available to me, +I can add more items to my toolbar, + +90 +00:05:27,694 --> 00:05:29,997 +like a noise button, +or a comfort level button, + +91 +00:05:30,030 --> 00:05:31,265 +or a trash button. + +92 +00:05:31,298 --> 00:05:33,433 +But if I start adding a lot of items, + +93 +00:05:33,467 --> 00:05:37,104 +my toolbar might become unwieldy +for some users. + +94 +00:05:37,137 --> 00:05:39,973 +MacOS has long supported +customizable toolbars + +95 +00:05:40,007 --> 00:05:43,177 +which lets each person choose the toolbar +best for them + +96 +00:05:43,210 --> 00:05:48,682 +and I'm excited to say that now iPadOS +also supports user customization. + +97 +00:05:48,715 --> 00:05:51,919 +You can use the existing toolbar +customization API + +98 +00:05:51,952 --> 00:05:54,521 +that's worked on macOS +to adopt this feature. + +99 +00:05:54,555 --> 00:05:56,590 +Let's look at that now. + +100 +00:05:57,391 --> 00:05:59,760 +Only toolbar items are customizable + +101 +00:05:59,793 --> 00:06:05,365 +so I'll first split up my toolbar item +group into individual toolbar items. + +102 +00:06:05,399 --> 00:06:08,602 +Note that there is no functional +difference after this change. + +103 +00:06:08,635 --> 00:06:11,905 +Customization also requires +every item in the toolbar + +104 +00:06:11,939 --> 00:06:14,808 +to be associated with a unique identifier + +105 +00:06:14,842 --> 00:06:18,011 +so I'll add IDs to each of my items. + +106 +00:06:18,045 --> 00:06:22,783 +It's important for these IDs to be unique +and consistent across app launches. + +107 +00:06:22,816 --> 00:06:25,052 +When a user customizes their toolbar, + +108 +00:06:25,085 --> 00:06:27,287 +SwiftUI persists these IDs + +109 +00:06:27,321 --> 00:06:31,225 +and uses them to look up +the associated view to render. + +110 +00:06:31,258 --> 00:06:34,962 +Finally, I'll add an ID +to the toolbar modifier as a whole. + +111 +00:06:34,995 --> 00:06:38,465 +Altogether, this opts the toolbar +into supporting customization. + +112 +00:06:39,833 --> 00:06:41,935 +Unique to customizable toolbars, + +113 +00:06:41,969 --> 00:06:46,874 +toolbar items have the ability to not be +initially present in the toolbar. + +114 +00:06:46,907 --> 00:06:50,210 +These items start their life +in the customization popover + +115 +00:06:50,244 --> 00:06:52,880 +and can be added to the toolbar later. + +116 +00:06:52,913 --> 00:06:55,082 +Because these items +aren't initially present, + +117 +00:06:55,115 --> 00:06:59,620 +this is a good option for actions that are +more useful to specific workflows. + +118 +00:06:59,653 --> 00:07:01,021 +Let's check them out. + +119 +00:07:02,222 --> 00:07:06,760 +I want to hide some of my current toolbar +items to make my new items easier to see. + +120 +00:07:07,928 --> 00:07:11,131 +Now let's add a toolbar item +that contains a share link. + +121 +00:07:11,164 --> 00:07:16,503 +Share links are new in SwiftUI and rely on +a new protocol called transferable. + +122 +00:07:16,537 --> 00:07:19,473 +For more information +on Transferable and ShareLink, + +123 +00:07:19,506 --> 00:07:22,743 +be sure to check out +the Meet Transferable session. + +124 +00:07:22,776 --> 00:07:24,411 +With my toolbar item in place, + +125 +00:07:24,444 --> 00:07:28,048 +I'll provide a value of false to the shows +by default parameter + +126 +00:07:28,081 --> 00:07:31,418 +which makes this item +not be initially present in the bar. + +127 +00:07:32,920 --> 00:07:35,255 +Now you can see my share link +living its best life + +128 +00:07:35,289 --> 00:07:38,392 +in the customization popover. + +129 +00:07:38,425 --> 00:07:42,596 +And I'm able to drag it from +the customization popover into the bar. + +130 +00:07:42,629 --> 00:07:45,465 +People are going to love that. + +131 +00:07:45,499 --> 00:07:47,868 +With my share link in place, +I started to think about + +132 +00:07:47,901 --> 00:07:51,471 +the relationship between my toolbar items. + +133 +00:07:51,505 --> 00:07:53,607 +After moving the share link into the bar, + +134 +00:07:53,640 --> 00:07:57,477 +I noticed that my image +and map adjustment items are separated, + +135 +00:07:57,511 --> 00:08:00,814 +but I think of these items +as a group of quick editing control, + +136 +00:08:00,848 --> 00:08:04,418 +and I'd like to model that relationship +in the toolbar itself. + +137 +00:08:05,786 --> 00:08:09,890 +iOS 16 and macOS Ventura support +modeling this relationship + +138 +00:08:09,923 --> 00:08:11,692 +by using ControlGroup. + +139 +00:08:11,725 --> 00:08:13,894 +I'll show you how. + +140 +00:08:13,927 --> 00:08:16,430 +You can see +I have two individual toolbar items + +141 +00:08:16,463 --> 00:08:19,166 +for my image and map adjustment actions. + +142 +00:08:19,199 --> 00:08:23,904 +To group them together, +I'll first move them to the same item. + +143 +00:08:23,937 --> 00:08:26,240 +Then I'll wrap them in a control group. + +144 +00:08:27,975 --> 00:08:32,613 +A user can now add or remove them +from the toolbar as a unit. + +145 +00:08:32,646 --> 00:08:35,682 +That's pretty cool, +but I can take this even further + +146 +00:08:35,716 --> 00:08:39,720 +using a new API available on ControlGroup. + +147 +00:08:39,753 --> 00:08:42,356 +By providing a label to the control group, + +148 +00:08:42,389 --> 00:08:45,859 +this group of items can collapse +into its own smaller menu + +149 +00:08:45,893 --> 00:08:49,162 +before moving into the overflow menu. + +150 +00:08:50,063 --> 00:08:52,266 +The toolbar is really +starting to come together. + +151 +00:08:52,299 --> 00:08:54,701 +There's one last change I'd like to make. + +152 +00:08:54,735 --> 00:08:58,372 +Adding a new place +is a common and important action, + +153 +00:08:58,405 --> 00:09:00,774 +so I'd like to add a toolbar item +for that. + +154 +00:09:02,142 --> 00:09:05,546 +To do that, +I'll add a new button to my toolbar, + +155 +00:09:05,579 --> 00:09:08,949 +but this time +I'll use the primary action placement + +156 +00:09:08,982 --> 00:09:12,486 +as I consider the new button +to be the most common action. + +157 +00:09:14,788 --> 00:09:19,593 +This placement highlights an important +distinction between iOS and macOS. + +158 +00:09:19,626 --> 00:09:25,465 +All items within a customizable toolbar +modifier support customization on macOS, + +159 +00:09:25,499 --> 00:09:29,903 +but on iPadOS, +only secondary actions do. + +160 +00:09:29,937 --> 00:09:33,540 +So my new button here renders +in the trailing area of the bar + +161 +00:09:33,574 --> 00:09:37,010 +and is not customizable by the user. + +162 +00:09:37,044 --> 00:09:38,378 +Wow! + +163 +00:09:38,412 --> 00:09:39,780 +That was a lot about toolbars + +164 +00:09:39,813 --> 00:09:41,748 +and those aren't the only improvements. + +165 +00:09:41,782 --> 00:09:44,117 +Navigation titles gained +some new features as well + +166 +00:09:44,151 --> 00:09:46,854 +around menus, documents, and more. + +167 +00:09:46,887 --> 00:09:49,223 +Let's take documents as an example. + +168 +00:09:49,256 --> 00:09:51,191 +There are lot of kinds of documents. + +169 +00:09:51,225 --> 00:09:53,493 +You might be familiar with documents +that are managed + +170 +00:09:53,527 --> 00:09:55,996 +by the document group API. + +171 +00:09:56,029 --> 00:09:59,233 +Document groups come with +a lot of built-in functionality + +172 +00:09:59,266 --> 00:10:02,236 +for representing +and managing their documents. + +173 +00:10:02,269 --> 00:10:06,673 +All of what I'm about to talk about +comes for free when using document groups. + +174 +00:10:07,975 --> 00:10:12,779 +In Places, though, an individual place +can be considered a document + +175 +00:10:12,813 --> 00:10:16,717 +even though Places +isn't using a document group. + +176 +00:10:16,750 --> 00:10:18,485 +It has properties that you can edit, + +177 +00:10:18,519 --> 00:10:21,021 +you can add or remove them +to the Places app, + +178 +00:10:21,054 --> 00:10:23,357 +and you can share +a place with your friends. + +179 +00:10:23,390 --> 00:10:25,726 +Let's look at how +we can show off this relationship + +180 +00:10:25,759 --> 00:10:28,562 +in a non-document group based app. + +181 +00:10:28,595 --> 00:10:33,400 +I already associate the name of the place +as the navigation title of the view. + +182 +00:10:33,433 --> 00:10:38,105 +So I'm already associating a piece +of the place to the title of my toolbar. + +183 +00:10:38,138 --> 00:10:41,074 +In iOS 16, I can take that even further + +184 +00:10:41,108 --> 00:10:44,144 +by using new navigation title modifiers. + +185 +00:10:44,178 --> 00:10:47,481 +Navigation titles now support +presenting a menu. + +186 +00:10:47,514 --> 00:10:51,185 +You can kind of think of this +like the File menu on macOS. + +187 +00:10:51,218 --> 00:10:52,886 +To create one of these menus, + +188 +00:10:52,920 --> 00:10:55,722 +provide a set of actions +to the navigation title, + +189 +00:10:55,756 --> 00:10:57,925 +just like you would a normal menu. + +190 +00:10:57,958 --> 00:11:01,528 +Notice that the title now has +a menu indicator attached to it + +191 +00:11:01,562 --> 00:11:04,765 +that presents a menu +populated with your actions. + +192 +00:11:04,798 --> 00:11:07,801 +And that's not all +navigation titles can do. + +193 +00:11:07,835 --> 00:11:12,573 +One of my favorite parts is its +new ability to support editable titles. + +194 +00:11:12,606 --> 00:11:15,209 +You can pass a binding +to your navigation title + +195 +00:11:15,242 --> 00:11:19,513 +and this tells the toolbar +that you support editing the title. + +196 +00:11:19,546 --> 00:11:23,317 +All you're missing is a way +to actually start editing. + +197 +00:11:23,350 --> 00:11:26,987 +You can use the new RenameButton +inside your titles menus actions + +198 +00:11:27,020 --> 00:11:28,889 +to accomplish this. + +199 +00:11:30,424 --> 00:11:34,595 +Tapping on the rename button +allows you to start renaming the title. + +200 +00:11:34,628 --> 00:11:37,831 +Just like you can associate +a navigation title to your view, + +201 +00:11:37,865 --> 00:11:42,135 +you can now also associate a document, +like my place. + +202 +00:11:42,169 --> 00:11:44,671 +When provided a document, +the title menu renders + +203 +00:11:44,705 --> 00:11:48,408 +a specialized header that shows +a preview of that document. + +204 +00:11:48,442 --> 00:11:53,080 +The preview can be dragged and there's +quick access to share the document. + +205 +00:11:53,113 --> 00:11:56,517 +To get one of these headers, +associate your navigation document + +206 +00:11:56,550 --> 00:12:00,654 +to your view using the new +navigation document modifier. + +207 +00:12:00,687 --> 00:12:02,523 +You can do this by providing a type + +208 +00:12:02,556 --> 00:12:06,960 +that conforms to transferable, +or by providing a URL directly. + +209 +00:12:06,994 --> 00:12:12,299 +Here I'll provide a URL that will open +the Maps app to the place I'm viewing. + +210 +00:12:12,332 --> 00:12:16,570 +The navigation document modifier +will also configure the proxy icon + +211 +00:12:16,603 --> 00:12:20,174 +of the window toolbar on macOS +when providing a URL. + +212 +00:12:21,441 --> 00:12:25,279 +After all that, I think I'll take a break +from updating my app's toolbars. + +213 +00:12:25,312 --> 00:12:27,614 +Can you believe all the features +I've added in this time? + +214 +00:12:27,648 --> 00:12:30,150 +I can't wait to start using them. + +215 +00:12:30,184 --> 00:12:35,255 +I covered a lot in my journey +to improve the Places experience on iPad. + +216 +00:12:35,289 --> 00:12:37,491 +Toolbars on iPad have gained +a lot of new features + +217 +00:12:37,524 --> 00:12:41,061 +like overflow menus +and user customization. + +218 +00:12:41,094 --> 00:12:44,698 +Use the secondary action placement +and customization APIs + +219 +00:12:44,731 --> 00:12:48,535 +to really make the most of the larger +space available on iPad and Mac. + +220 +00:12:50,003 --> 00:12:53,640 +Titles have gained some new ways +to represent themselves in the toolbar. + +221 +00:12:53,674 --> 00:12:59,546 +Use a navigation title or create +a title menu or support title renaming. + +222 +00:12:59,580 --> 00:13:04,351 +And remember to use the navigation +document modifier when appropriate. + +223 +00:13:04,384 --> 00:13:07,221 +I hope you've enjoyed +the SwiftUI on iPad series. + +224 +00:13:07,254 --> 00:13:10,424 +With all the improvements in tables, +selection, toolbars, and more, + +225 +00:13:10,457 --> 00:13:13,260 +go out and take your iPad apps +to the next level. + +226 +00:13:13,293 --> 00:13:15,662 +Thank you, and have a great WW. + diff --git a/eng/2022 Session 110344 Get to know Developer Mode en.srt b/eng/2022 Session 110344 Get to know Developer Mode en.srt new file mode 100644 index 0000000..f62b6e0 --- /dev/null +++ b/eng/2022 Session 110344 Get to know Developer Mode en.srt @@ -0,0 +1,619 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,376 +♪ + +3 +00:00:09,376 --> 00:00:11,545 +Hi, folks. My name is Pavlo. + +4 +00:00:11,545 --> 00:00:12,913 +I work on security technologies + +5 +00:00:12,913 --> 00:00:15,682 +that help keep you +and your users safe. + +6 +00:00:15,682 --> 00:00:17,684 +Today I'm going to tell you +about some upcoming changes + +7 +00:00:17,684 --> 00:00:19,920 +that may impact the way +you develop, test, + +8 +00:00:19,920 --> 00:00:22,756 +and deploy your applications. + +9 +00:00:22,756 --> 00:00:25,292 +First, I want to tell you +what Developer Mode is, + +10 +00:00:25,292 --> 00:00:27,661 +the reasoning behind +why it was created, + +11 +00:00:27,661 --> 00:00:29,363 +the security benefit it brings, + +12 +00:00:29,363 --> 00:00:32,766 +and the workflows +that it will affect. + +13 +00:00:32,766 --> 00:00:34,835 +Then, we will discuss +when and how + +14 +00:00:34,835 --> 00:00:37,337 +to turn on Developer Mode. + +15 +00:00:37,337 --> 00:00:40,107 +And then we will finish off +this session with a walk-through + +16 +00:00:40,107 --> 00:00:42,442 +of the tools we built +to support automation flows + +17 +00:00:42,442 --> 00:00:44,144 +for when you're working +with many devices, + +18 +00:00:44,144 --> 00:00:47,214 +like in a testing lab +environment. + +19 +00:00:47,214 --> 00:00:49,783 +OK, so what is Developer Mode? + +20 +00:00:49,783 --> 00:00:52,886 +It's a new mode in iOS 16 +and watchOS 9 + +21 +00:00:52,886 --> 00:00:55,956 +that enables common +developer workflows. + +22 +00:00:55,956 --> 00:00:58,125 +Developer Mode +is disabled by default + +23 +00:00:58,125 --> 00:01:00,460 +and requires you +to explicitly enroll the device + +24 +00:01:00,460 --> 00:01:01,862 +into Developer Mode. + +25 +00:01:01,862 --> 00:01:05,732 +Your enrollment persists across +reboots and system updates. + +26 +00:01:05,732 --> 00:01:07,434 +And of course, +we have built tools + +27 +00:01:07,434 --> 00:01:09,369 +that enable you to automate +Developer Mode setup + +28 +00:01:09,369 --> 00:01:11,171 +if you want. + +29 +00:01:11,171 --> 00:01:12,806 +A natural question +you will have is, + +30 +00:01:12,806 --> 00:01:15,909 +"Why are we introducing +Developer Mode?" + +31 +00:01:15,909 --> 00:01:18,011 +The reason is that +powerful developer features + +32 +00:01:18,011 --> 00:01:20,280 +are being abused +in targeted attacks. + +33 +00:01:20,280 --> 00:01:21,882 +Meanwhile, +the vast majority of users + +34 +00:01:21,882 --> 00:01:23,817 +do not need such functionality + +35 +00:01:23,817 --> 00:01:26,353 +and so they shouldn't +be enabled by default. + +36 +00:01:26,353 --> 00:01:28,655 +This way we can retain +the development capabilities + +37 +00:01:28,655 --> 00:01:31,425 +that you rely on and increase +the security for users + +38 +00:01:31,425 --> 00:01:34,895 +with mitigations that otherwise +would get in your way. + +39 +00:01:34,895 --> 00:01:36,063 +Now having said that, + +40 +00:01:36,063 --> 00:01:39,132 +most common distribution flows +will not require Developer Mode. + +41 +00:01:39,132 --> 00:01:41,635 +For example, deploying your +application through TestFlight + +42 +00:01:41,635 --> 00:01:43,570 +or using Enterprise +in-house distribution + +43 +00:01:43,570 --> 00:01:46,606 +does not require Developer Mode. + +44 +00:01:46,606 --> 00:01:48,775 +And of course, +distributing your applications + +45 +00:01:48,775 --> 00:01:52,145 +through the App Store +does not require Developer Mode. + +46 +00:01:52,145 --> 00:01:54,281 +It's only required for when you, +the developer, + +47 +00:01:54,281 --> 00:01:57,884 +are actively developing +your application on your device. + +48 +00:01:57,884 --> 00:01:59,920 +All right, by this point +I'm sure you can't wait + +49 +00:01:59,920 --> 00:02:01,688 +until I tell you +how you would get started + +50 +00:02:01,688 --> 00:02:03,223 +with using Developer Mode. + +51 +00:02:03,223 --> 00:02:05,659 +So let's go through when you +need to turn on Developer Mode + +52 +00:02:05,659 --> 00:02:07,794 +and how to do it. + +53 +00:02:07,794 --> 00:02:10,597 +You should turn Developer Mode +on if you need to + +54 +00:02:10,597 --> 00:02:12,866 +run and install development +signed applications, + +55 +00:02:12,866 --> 00:02:16,069 +including applications signed +by using a Personal Team; + +56 +00:02:16,069 --> 00:02:18,438 +debug and instrument +your applications; + +57 +00:02:18,438 --> 00:02:23,043 +or you want to use testing +automation on your device. + +58 +00:02:23,043 --> 00:02:24,878 +Turning on Developer Mode +is straightforward, + +59 +00:02:24,878 --> 00:02:27,381 +but first you need to connect +your device to Xcode + +60 +00:02:27,381 --> 00:02:30,617 +for the Developer Mode +menu item to appear. + +61 +00:02:30,617 --> 00:02:32,119 +The beta releases +that you have downloaded + +62 +00:02:32,119 --> 00:02:36,390 +will have the menu item always +visible for the time being. + +63 +00:02:36,390 --> 00:02:38,992 +Installing a development-signed +application without Xcode, + +64 +00:02:38,992 --> 00:02:40,327 +like through Apple Configurator, + +65 +00:02:40,327 --> 00:02:43,397 +will also make +the menu item visible. + +66 +00:02:43,397 --> 00:02:45,732 +Once you've done that, you can +find the Developer Mode controls + +67 +00:02:45,732 --> 00:02:49,803 +under Privacy & Security +in Settings. + +68 +00:02:49,803 --> 00:02:52,105 +And for automation, +you can use the new devmodectl + +69 +00:02:52,105 --> 00:02:54,408 +that ships by default +on macOS Ventura, + +70 +00:02:54,408 --> 00:02:56,943 +but more on that later. + +71 +00:02:56,943 --> 00:02:58,445 +OK, so let's walk through + +72 +00:02:58,445 --> 00:03:00,280 +with how you would turn on +Developer Mode. + +73 +00:03:00,280 --> 00:03:02,049 +In front of me, +I have an iPhone 13 Pro + +74 +00:03:02,049 --> 00:03:03,984 +that I just picked up +to run my code on. + +75 +00:03:03,984 --> 00:03:05,652 +First, I'm going +to plug it in to my Mac + +76 +00:03:05,652 --> 00:03:08,555 +that has Xcode already running. + +77 +00:03:08,555 --> 00:03:11,024 +As you can see, Xcode recognizes +that this device + +78 +00:03:11,024 --> 00:03:13,126 +does not have +Developer Mode enabled + +79 +00:03:13,126 --> 00:03:17,564 +and will prevent me +from running this application. + +80 +00:03:17,564 --> 00:03:19,433 +But now that +I have plugged it in, + +81 +00:03:19,433 --> 00:03:25,505 +I can go into Settings, +Privacy & Security, + +82 +00:03:25,505 --> 00:03:28,008 +Developer Mode. + +83 +00:03:30,010 --> 00:03:30,911 +Turning on Developer Mode + +84 +00:03:30,911 --> 00:03:32,612 +requires that you reboot +your device, + +85 +00:03:32,612 --> 00:03:35,215 +so let's go ahead and do that. + +86 +00:03:37,451 --> 00:03:38,985 +Once the device has rebooted, + +87 +00:03:38,985 --> 00:03:42,222 +you will be prompted once again +to confirm your decision. + +88 +00:03:42,222 --> 00:03:46,893 +Once you tap Turn On, +you will be good to go. + +89 +00:03:46,893 --> 00:03:52,466 +Now, Xcode sees that the device +has Developer Mode enabled, + +90 +00:03:52,466 --> 00:03:55,168 +and I can run my application. + +91 +00:03:59,005 --> 00:04:00,774 +While this flow works +when you're working + +92 +00:04:00,774 --> 00:04:01,842 +with a single device, + +93 +00:04:01,842 --> 00:04:03,210 +this process +can be time consuming + +94 +00:04:03,210 --> 00:04:05,145 +if you're dealing +with multiple devices. + +95 +00:04:05,145 --> 00:04:06,379 +For this reason, +we've built tools + +96 +00:04:06,379 --> 00:04:09,382 +that help you automate +this process. + +97 +00:04:09,382 --> 00:04:11,451 +Automation flows +have one limitation: + +98 +00:04:11,451 --> 00:04:12,886 +only devices without a passcode + +99 +00:04:12,886 --> 00:04:15,789 +can have Developer Mode +automatically enabled. + +100 +00:04:15,789 --> 00:04:17,424 +This is because +when you restart your iPhone + +101 +00:04:17,424 --> 00:04:18,892 +you need to unlock your device + +102 +00:04:18,892 --> 00:04:21,761 +before your device +can be interacted with. + +103 +00:04:21,761 --> 00:04:23,063 +To support automation, + +104 +00:04:23,063 --> 00:04:25,632 +macOS Ventura +ships with devmodectl + +105 +00:04:25,632 --> 00:04:27,634 +that you can use to either +enable Developer Mode + +106 +00:04:27,634 --> 00:04:29,669 +on a single device +that you have already connected, + +107 +00:04:29,669 --> 00:04:31,805 +or in Streaming Mode +that will automatically turn on + +108 +00:04:31,805 --> 00:04:34,708 +Developer Mode +on all devices that you plug in. + +109 +00:04:36,977 --> 00:04:39,880 +Here I have two devices +plugged into my Mac. + +110 +00:04:43,049 --> 00:04:44,351 +They don't have passcodes, + +111 +00:04:44,351 --> 00:04:46,853 +and I don't want to manually +set up Developer Mode on them. + +112 +00:04:46,853 --> 00:04:50,991 +So I'm going to run devmodectl +with the streaming subcommand. + +113 +00:04:50,991 --> 00:04:53,093 +This will automatically reboot +the connected devices + +114 +00:04:53,093 --> 00:04:56,963 +and set up Developer Mode. + +115 +00:04:56,963 --> 00:04:58,465 +Once Developer Mode +has been set up, + +116 +00:04:58,465 --> 00:05:01,368 +you will get a notification +on the device. + +117 +00:05:05,372 --> 00:05:08,041 +And now these devices +are good to go. + +118 +00:05:08,041 --> 00:05:10,477 +All right, that's all I have +for you today. + +119 +00:05:10,477 --> 00:05:13,013 +To wrap it up, +in iOS 16 and watchOS 9 + +120 +00:05:13,013 --> 00:05:14,481 +you will have to enable +Developer Mode + +121 +00:05:14,481 --> 00:05:16,082 +to perform common +development activities + +122 +00:05:16,082 --> 00:05:19,419 +like deploying and debugging +your applications. + +123 +00:05:19,419 --> 00:05:21,321 +And if you need to automate +Developer Mode setup, + +124 +00:05:21,321 --> 00:05:25,392 +you can use devmodectl, +which ships in macOS Ventura. + +125 +00:05:25,392 --> 00:05:27,360 +If you want to learn more +about security changes + +126 +00:05:27,360 --> 00:05:29,696 +that may impact your +macOS distribution workflows, + +127 +00:05:29,696 --> 00:05:33,033 +check out the "What's new in +notarization for Mac apps" talk. + +128 +00:05:33,033 --> 00:05:34,734 +I hope you have +a great rest of your day + +129 +00:05:34,734 --> 00:05:35,902 +and happy coding! + +130 +00:05:35,902 --> 00:05:40,173 +♪ + diff --git "a/eng/2022 Session 110345 What\342\200\231s new in Endpoint Security en.srt" "b/eng/2022 Session 110345 What\342\200\231s new in Endpoint Security en.srt" new file mode 100644 index 0000000..791b8b9 --- /dev/null +++ "b/eng/2022 Session 110345 What\342\200\231s new in Endpoint Security en.srt" @@ -0,0 +1,942 @@ +1 +00:00:00,133 --> 00:00:03,136 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,136 --> 00:00:09,643 +♪ + +3 +00:00:09,643 --> 00:00:12,880 +Hey, my name is Daniel, and I'm +on the Security Engineering + +4 +00:00:12,880 --> 00:00:14,314 +and Architecture team. + +5 +00:00:14,314 --> 00:00:18,218 +I'm going to give you +an update on Endpoint Security. + +6 +00:00:18,218 --> 00:00:20,621 +Endpoint Security +is an API that we provide, + +7 +00:00:20,621 --> 00:00:23,490 +so you can build +security products for the Mac. + +8 +00:00:23,490 --> 00:00:26,593 +It is a C API, providing a rich +security event stream + +9 +00:00:26,593 --> 00:00:28,295 +to third-party anti-virus, + +10 +00:00:28,295 --> 00:00:30,163 +Endpoint Detection +and Response, + +11 +00:00:30,163 --> 00:00:33,100 +and data-leakage +prevention solutions. + +12 +00:00:33,100 --> 00:00:36,136 +We first introduced Endpoint +Security in macOS Catalina + +13 +00:00:36,136 --> 00:00:39,640 +as a replacement for +the deprecated KAuth KPI, + +14 +00:00:39,640 --> 00:00:41,975 +the unsupported MAC +Kernel Framework, + +15 +00:00:41,975 --> 00:00:45,412 +and the also deprecated +OpenBSM audit trail. + +16 +00:00:45,412 --> 00:00:47,514 +By making this functionality +available + +17 +00:00:47,514 --> 00:00:49,950 +in the Endpoint Security API, +we removed the need + +18 +00:00:49,950 --> 00:00:53,120 +for you to have to develop +kernel extensions. + +19 +00:00:53,120 --> 00:00:54,922 +The introduction +of Endpoint Security + +20 +00:00:54,922 --> 00:00:56,723 +has been +a resounding success. + +21 +00:00:56,723 --> 00:00:59,192 +We are thrilled with +the positive feedback we receive + +22 +00:00:59,192 --> 00:01:02,562 +and with the security products +that it is enabling. + +23 +00:01:02,562 --> 00:01:04,698 +For an introduction +to Endpoint Security, + +24 +00:01:04,698 --> 00:01:07,901 +refer to +the WWDC 2020 session + +25 +00:01:07,901 --> 00:01:10,103 +"Build an +Endpoint Security app." + +26 +00:01:10,103 --> 00:01:11,305 +In this session, + +27 +00:01:11,305 --> 00:01:14,641 +we'll be covering new events +added in macOS Ventura, + +28 +00:01:14,641 --> 00:01:17,144 +improvements +to muting functionality, + +29 +00:01:17,144 --> 00:01:20,948 +as well as a new utility +called eslogger. + +30 +00:01:20,948 --> 00:01:23,784 +Let's start with the new events. + +31 +00:01:23,784 --> 00:01:25,385 +As of macOS Monterey, + +32 +00:01:25,385 --> 00:01:28,989 +Endpoint Security supports +over a hundred event types. + +33 +00:01:28,989 --> 00:01:31,992 +Up until now, these events +focused on critical events + +34 +00:01:31,992 --> 00:01:35,228 +that happen within the kernel, +such as forking a process + +35 +00:01:35,228 --> 00:01:36,964 +or opening a file. + +36 +00:01:36,964 --> 00:01:39,733 +In macOS Ventura, +we're expanding the set + +37 +00:01:39,733 --> 00:01:42,469 +of observable events to include +security-relevant events + +38 +00:01:42,469 --> 00:01:44,504 +happening in user space. + +39 +00:01:44,504 --> 00:01:48,208 +Specifically, we are adding +visibility into authentication, + +40 +00:01:48,208 --> 00:01:52,846 +login and logout, +and Gatekeeper's XProtect. + +41 +00:01:52,846 --> 00:01:55,782 +The new authentication event +covers the situation + +42 +00:01:55,782 --> 00:01:59,252 +where a user authenticates +to the operating system. + +43 +00:01:59,252 --> 00:02:02,189 +This includes logging +into local user accounts, + +44 +00:02:02,189 --> 00:02:04,791 +but also authenticating +in other places, + +45 +00:02:04,791 --> 00:02:08,662 +such as to authorize an +operation as an administrator. + +46 +00:02:08,662 --> 00:02:11,164 +These events are typically +used by security products + +47 +00:02:11,164 --> 00:02:14,701 +that wish to observe +suspicious access patterns. + +48 +00:02:14,701 --> 00:02:18,972 +Previously, if you wished to +observe authentication events, + +49 +00:02:18,972 --> 00:02:22,476 +you still had to rely on the +deprecated OpenBSM audit trail + +50 +00:02:22,476 --> 00:02:26,079 +to do so; this will now +no longer be necessary. + +51 +00:02:26,079 --> 00:02:28,482 +And compared +to their audit counterparts, + +52 +00:02:28,482 --> 00:02:31,184 +these new events are +much richer in information, + +53 +00:02:31,184 --> 00:02:35,255 +and also provide visibility into +Auto Unlock using Apple Watch, + +54 +00:02:35,255 --> 00:02:38,825 +which wasn't available in audit. + +55 +00:02:38,825 --> 00:02:43,663 +In a similar vein, we're adding +visibility into user sessions. + +56 +00:02:43,663 --> 00:02:46,633 +Login events cover situations +where someone or something + +57 +00:02:46,633 --> 00:02:49,936 +logs on to the system, +either locally at the console + +58 +00:02:49,936 --> 00:02:52,472 +or remotely over one +of the supported methods + +59 +00:02:52,472 --> 00:02:54,808 +of remote access. + +60 +00:02:54,808 --> 00:02:57,611 +These events go substantially +beyond what was available + +61 +00:02:57,611 --> 00:03:00,013 +with the OpenBSM audit trail. + +62 +00:03:00,013 --> 00:03:02,783 +They allow you to gain +more comprehensive visibility + +63 +00:03:02,783 --> 00:03:04,684 +into access to systems, + +64 +00:03:04,684 --> 00:03:09,089 +including lateral movement +across enterprise fleets. + +65 +00:03:09,089 --> 00:03:11,992 +Part of Gatekeeper's mission +is to detect and block + +66 +00:03:11,992 --> 00:03:14,127 +known malicious software. + +67 +00:03:14,127 --> 00:03:16,029 +Starting with macOS Ventura, + +68 +00:03:16,029 --> 00:03:18,265 +Endpoint Security +will provide visibility + +69 +00:03:18,265 --> 00:03:20,934 +into both the detection +of malicious software + +70 +00:03:20,934 --> 00:03:25,439 +and into actions taken to stop +and remove malicious software. + +71 +00:03:25,439 --> 00:03:29,176 +This information was previously +unavailable in a structured way. + +72 +00:03:29,176 --> 00:03:33,780 +It is now available through +the Endpoint Security API. + +73 +00:03:33,780 --> 00:03:35,816 +We expect that +with these additions, + +74 +00:03:35,816 --> 00:03:38,018 +most Endpoint Security clients +no longer need + +75 +00:03:38,018 --> 00:03:42,656 +to rely onto the deprecated +OpenBSM audit trail at all. + +76 +00:03:42,656 --> 00:03:45,926 +The audit trail has been +deprecated since macOS Big Sur + +77 +00:03:45,926 --> 00:03:49,563 +and will be removed +in a future version of macOS. + +78 +00:03:49,563 --> 00:03:52,032 +Let's move on to muting. + +79 +00:03:52,032 --> 00:03:55,402 +Since macOS Catalina, we have +supported muting processes + +80 +00:03:55,402 --> 00:03:59,072 +by audit token +or executable image path. + +81 +00:03:59,072 --> 00:04:01,408 +Muting is an important tool +we give you + +82 +00:04:01,408 --> 00:04:05,078 +to prevent deadlocks, hangs, +and watchdog timeouts, + +83 +00:04:05,078 --> 00:04:07,948 +and to manage the performance +impact of your solutions + +84 +00:04:07,948 --> 00:04:10,383 +based on Endpoint Security. + +85 +00:04:10,383 --> 00:04:13,653 +In macOS Monterey last year, +we started to address + +86 +00:04:13,653 --> 00:04:16,289 +frequent causes +of system instability + +87 +00:04:16,289 --> 00:04:17,858 +by muting some event types + +88 +00:04:17,858 --> 00:04:21,294 +for a small set +of executables by default. + +89 +00:04:21,294 --> 00:04:24,131 +You are free to unmute those, +but we do recommend + +90 +00:04:24,131 --> 00:04:28,001 +you keep them muted in the +interest of system stability. + +91 +00:04:28,001 --> 00:04:31,605 +In macOS Ventura, we are +taking muting one step further + +92 +00:04:31,605 --> 00:04:35,175 +by introducing muting +based on target path. + +93 +00:04:35,175 --> 00:04:37,110 +For many real-world use cases, + +94 +00:04:37,110 --> 00:04:40,614 +this allows for scalpel-like +precision over muting. + +95 +00:04:40,614 --> 00:04:43,950 +You can now mute file events +pertaining to certain paths + +96 +00:04:43,950 --> 00:04:46,353 +or path prefixes +that your application + +97 +00:04:46,353 --> 00:04:49,656 +is not interested in receiving. + +98 +00:04:49,656 --> 00:04:51,758 +This shows how +you might mute all events + +99 +00:04:51,758 --> 00:04:54,427 +having a target path +under /var/log + +100 +00:04:54,427 --> 00:04:59,432 +when you're not interested +in events for log files. + +101 +00:04:59,432 --> 00:05:01,735 +The second example shows +how you might use + +102 +00:05:01,735 --> 00:05:05,205 +es_mute_path_events +to mute only write events + +103 +00:05:05,205 --> 00:05:09,309 +to a single file, +such as /dev/null. + +104 +00:05:09,309 --> 00:05:12,179 +And what's more, we're even +introducing the possibility + +105 +00:05:12,179 --> 00:05:14,347 +to invert muting logic. + +106 +00:05:14,347 --> 00:05:17,751 +Instead of suppressing events +based on specified process, + +107 +00:05:17,751 --> 00:05:21,188 +executable path, or target path, +we now allow you to invert + +108 +00:05:21,188 --> 00:05:24,157 +any of these +three types of muting. + +109 +00:05:24,157 --> 00:05:28,295 +Effectively, you can now select +processes, executable paths, + +110 +00:05:28,295 --> 00:05:30,730 +or target paths +you are interested in + +111 +00:05:30,730 --> 00:05:32,499 +to only receive events +that match -- + +112 +00:05:32,499 --> 00:05:35,769 +instead of don't match -- +anything you've muted. + +113 +00:05:35,769 --> 00:05:39,439 +For example, this now allows you +to select file events + +114 +00:05:39,439 --> 00:05:42,909 +only for certain +persistence locations. + +115 +00:05:42,909 --> 00:05:45,312 +Here's how you would use +es_invert_muting + +116 +00:05:45,312 --> 00:05:48,615 +to invert muting +for target paths. + +117 +00:05:48,615 --> 00:05:52,385 +You would then proceed to remove +previously existing target paths + +118 +00:05:52,385 --> 00:05:55,488 +from the mute set, +and then select only events + +119 +00:05:55,488 --> 00:05:58,858 +with the target-path prefix +that you are interested in. + +120 +00:05:58,858 --> 00:06:02,562 +We expect this to allow for much +improved muting strategies, + +121 +00:06:02,562 --> 00:06:04,364 +and ultimately, +to make it easier + +122 +00:06:04,364 --> 00:06:06,266 +to provide a great +user experience + +123 +00:06:06,266 --> 00:06:09,436 +with products +based on Endpoint Security. + +124 +00:06:09,436 --> 00:06:12,973 +And finally, +we're introducing eslogger. + +125 +00:06:12,973 --> 00:06:15,875 +We have had many requests +for a capability + +126 +00:06:15,875 --> 00:06:17,877 +to provide +Endpoint Security events + +127 +00:06:17,877 --> 00:06:20,680 +without the need +to write a native client. + +128 +00:06:20,680 --> 00:06:24,017 +Starting in macOS Ventura, +you will be able to harness + +129 +00:06:24,017 --> 00:06:27,587 +the power of Endpoint Security +via a command-line utility + +130 +00:06:27,587 --> 00:06:31,625 +that brings deep introspection +into the OS. + +131 +00:06:31,625 --> 00:06:34,527 +eslogger taps into the +Endpoint Security event stream + +132 +00:06:34,527 --> 00:06:37,931 +for specific events and emits +JSON-formatted event data + +133 +00:06:37,931 --> 00:06:42,469 +to standard output or +to the unified logging system. + +134 +00:06:42,469 --> 00:06:45,071 +Data is structured just +like the C representation + +135 +00:06:45,071 --> 00:06:47,474 +native clients use. + +136 +00:06:47,474 --> 00:06:50,043 +eslogger supports all +80 NOTIFY events + +137 +00:06:50,043 --> 00:06:54,114 +that Endpoint Security +supports in macOS Ventura. + +138 +00:06:54,114 --> 00:06:57,450 +We hope eslogger will be useful +not only for engineers + +139 +00:06:57,450 --> 00:06:59,686 +working on +Endpoint Security clients, + +140 +00:06:59,686 --> 00:07:03,590 +but also for security analysts +and other security practitioners + +141 +00:07:03,590 --> 00:07:06,493 +who need visibility into +security-relevant events + +142 +00:07:06,493 --> 00:07:08,161 +on macOS. + +143 +00:07:09,929 --> 00:07:13,266 +eslogger ships with the OS +and is already entitled + +144 +00:07:13,266 --> 00:07:15,368 +for Endpoint Security. + +145 +00:07:15,368 --> 00:07:17,771 +Like all Endpoint Security +clients, + +146 +00:07:17,771 --> 00:07:19,906 +eslogger must be run +as superuser + +147 +00:07:19,906 --> 00:07:22,008 +and requires the user +to have authorized + +148 +00:07:22,008 --> 00:07:25,378 +the responsible process +for Full Disk Access, + +149 +00:07:25,378 --> 00:07:29,582 +such as Terminal.app or SSH. + +150 +00:07:29,582 --> 00:07:33,586 +eslogger is not intended +to be used by applications. + +151 +00:07:33,586 --> 00:07:37,157 +Its output is subject to change +in software updates. + +152 +00:07:37,157 --> 00:07:39,092 +It is neither meant +to provide the same + +153 +00:07:39,092 --> 00:07:42,295 +performance characteristics, +nor the same feature set, + +154 +00:07:42,295 --> 00:07:46,766 +as natively interfacing with +the Endpoint Security API does. + +155 +00:07:46,766 --> 00:07:51,171 +Applications will continue +to interface natively. + +156 +00:07:51,171 --> 00:07:53,540 +With that out of the way, +let's use eslogger + +157 +00:07:53,540 --> 00:07:56,943 +to explore some +of the new events. + +158 +00:07:56,943 --> 00:07:59,713 +I'm going to use eslogger +to log the start + +159 +00:07:59,713 --> 00:08:02,549 +and end of an SSH session +by subscribing + +160 +00:08:02,549 --> 00:08:05,719 +to the openssh_login +and _logout events + +161 +00:08:05,719 --> 00:08:07,854 +and redirecting output +to a file. + +162 +00:08:10,857 --> 00:08:13,293 +Now in this other tab, +I'm using SSH + +163 +00:08:13,293 --> 00:08:16,363 +to locally log in +and log out again. + +164 +00:08:18,164 --> 00:08:20,734 +[KEYSTROKES] + +165 +00:08:21,701 --> 00:08:25,372 +Back in the first tab, +I'm interrupting eslogger. + +166 +00:08:28,808 --> 00:08:31,411 +Let's have a look at the +Endpoint Security event data + +167 +00:08:31,411 --> 00:08:34,247 +in its raw JSON form. + +168 +00:08:34,247 --> 00:08:37,450 +[KEYSTROKES] + +169 +00:08:37,450 --> 00:08:40,253 +It's very likely that you will +have your own favorite way + +170 +00:08:40,253 --> 00:08:42,155 +to work with JSON data. + +171 +00:08:42,155 --> 00:08:46,226 +Here, I will use jq +to examine the events further. + +172 +00:08:46,226 --> 00:08:49,295 +[KEYSTROKES] + +173 +00:08:50,830 --> 00:08:52,799 +This is a lot of information. + +174 +00:08:52,799 --> 00:08:55,668 +I'll just drill down +to some interesting fields. + +175 +00:08:55,668 --> 00:08:57,804 +Like all Endpoint Security +events, + +176 +00:08:57,804 --> 00:09:01,007 +we have information on +the process that emitted them. + +177 +00:09:01,007 --> 00:09:08,681 +[KEYSTROKES] + +178 +00:09:11,050 --> 00:09:15,021 +We can see that both events +were emitted by the SSH daemon. + +179 +00:09:15,021 --> 00:09:16,990 +We can also inspect, +for instance, + +180 +00:09:16,990 --> 00:09:19,626 +its audit token to glean +the PID of the process + +181 +00:09:19,626 --> 00:09:22,462 +that emitted the events. + +182 +00:09:22,462 --> 00:09:27,333 +[KEYSTROKES] + +183 +00:09:28,902 --> 00:09:31,538 +And finally, let's have a look +at the event-specific fields + +184 +00:09:31,538 --> 00:09:34,007 +of these events. + +185 +00:09:34,007 --> 00:09:38,077 +[KEYSTROKES] + +186 +00:09:38,077 --> 00:09:40,847 +We can see that this was +a successful SSH login + +187 +00:09:40,847 --> 00:09:44,584 +into my user account, +followed by a logout. + +188 +00:09:44,584 --> 00:09:46,820 +I love how I can get visibility + +189 +00:09:46,820 --> 00:09:49,088 +into Endpoint Security +events this way, + +190 +00:09:49,088 --> 00:09:52,058 +whether I want to observe +malicious software behavior, + +191 +00:09:52,058 --> 00:09:55,995 +or to just quickly prototype +a detection approach. + +192 +00:09:55,995 --> 00:09:58,264 +We look forward to seeing +your security solutions + +193 +00:09:58,264 --> 00:09:59,933 +take advantage +of the new events + +194 +00:09:59,933 --> 00:10:03,837 +as well as of the improved +muting capabilities. + +195 +00:10:03,837 --> 00:10:06,773 +Thanks for joining us at WWDC. + +196 +00:10:06,773 --> 00:10:11,077 +♪ + diff --git a/eng/2022 Session 110347 Explore more content with MusicKit en.srt b/eng/2022 Session 110347 Explore more content with MusicKit en.srt new file mode 100644 index 0000000..9d6f0f1 --- /dev/null +++ b/eng/2022 Session 110347 Explore more content with MusicKit en.srt @@ -0,0 +1,2162 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:10,177 --> 00:00:13,180 +Hi, and welcome to WWDC. + +3 +00:00:13,213 --> 00:00:15,415 +My name is David, +and I'd like to tell you about + +4 +00:00:15,449 --> 00:00:18,685 +how you can explore +more content with MusicKit. + +5 +00:00:18,719 --> 00:00:21,288 +The MusicKit framework +was launched in 2021, + +6 +00:00:21,321 --> 00:00:25,659 +providing a set of APIs to access +and play music natively in Swift. + +7 +00:00:25,692 --> 00:00:28,996 +This framework makes it easy for +your app to integrate with Apple Music, + +8 +00:00:29,029 --> 00:00:32,299 +providing access +to the entire Apple Music catalog. + +9 +00:00:32,332 --> 00:00:34,968 +Today, I'd like to talk about +some of the major enhancements + +10 +00:00:35,002 --> 00:00:36,937 +we've made to MusicKit. + +11 +00:00:36,970 --> 00:00:40,641 +I'll start off with some additions to get +even more out of the Apple Music catalog, + +12 +00:00:40,674 --> 00:00:45,012 +with new music item types, +new requests, and new metadata. + +13 +00:00:46,046 --> 00:00:49,283 +After that, I'll discuss how +you can fetch personalized content + +14 +00:00:49,316 --> 00:00:53,187 +to provide a tailored experience +for each of your users. + +15 +00:00:53,220 --> 00:00:56,223 +Next up, I'll go +beyond the Apple Music catalog. + +16 +00:00:56,256 --> 00:00:59,092 +This year, you can take your app +to a whole new level + +17 +00:00:59,126 --> 00:01:02,596 +by including music +from your user's library. + +18 +00:01:02,629 --> 00:01:06,133 +Finally, I'll discuss +how to actively interact with the library, + +19 +00:01:06,166 --> 00:01:09,036 +such as adding items to the library +or a playlist, + +20 +00:01:09,069 --> 00:01:12,072 +as well as creating and editing playlists. + +21 +00:01:12,105 --> 00:01:14,842 +Let's dive into +the catalog content additions. + +22 +00:01:14,875 --> 00:01:18,579 +The initial release of MusicKit +introduced a new model layer for music, + +23 +00:01:18,612 --> 00:01:22,616 +including core types like Songs, +Albums, and Playlists. + +24 +00:01:22,649 --> 00:01:25,252 +This year, we're making it easier +to discover new music + +25 +00:01:25,285 --> 00:01:28,722 +with MusicKit +with the addition of two new types: + +26 +00:01:28,755 --> 00:01:30,824 +Curator and Radio Show. + +27 +00:01:32,092 --> 00:01:34,561 +Beyond that, MusicKit now also allows you + +28 +00:01:34,595 --> 00:01:37,831 +to build great UI +for searching through the catalog, + +29 +00:01:37,865 --> 00:01:41,134 +access top charts to get +the most popular songs, albums, + +30 +00:01:41,168 --> 00:01:44,204 +and more, and fetch new attributes + +31 +00:01:44,238 --> 00:01:48,809 +such as higher quality audio metadata +like Spatial Audio with Dolby Atmos. + +32 +00:01:49,610 --> 00:01:53,213 +Let's start with curators and radio shows, +which are fantastic resources + +33 +00:01:53,247 --> 00:01:54,882 +for music discovery. + +34 +00:01:54,915 --> 00:01:58,385 +Here, we're taking a look +at an example Curator, Nike. + +35 +00:01:58,418 --> 00:02:02,756 +Other examples of curators +are Shazam and Beats by Dr. Dre. + +36 +00:02:02,789 --> 00:02:07,127 +Here, we can easily find all of +the playlists generated by this curator. + +37 +00:02:07,160 --> 00:02:11,231 +This functionality allows people to get +quick access to playlists they may love, + +38 +00:02:11,265 --> 00:02:14,434 +finding new songs +or revisiting old favorites. + +39 +00:02:14,468 --> 00:02:17,304 +Now let's dive +into the more technical details. + +40 +00:02:18,238 --> 00:02:20,641 +Curators host a variety of attributes. + +41 +00:02:20,674 --> 00:02:24,244 +Some of the primary attributes +of this new Curator type are name, + +42 +00:02:24,278 --> 00:02:28,348 +url, artwork, and kind. + +43 +00:02:28,382 --> 00:02:32,719 +The kind property is an enum that can +either be "editorial" or "external" + +44 +00:02:32,753 --> 00:02:37,291 +indicating whether a given curator is +an Apple curator or a third party curator. + +45 +00:02:38,258 --> 00:02:40,761 +Curators also have +a playlists relationship + +46 +00:02:40,794 --> 00:02:42,896 +showing playlists made by that curator, + +47 +00:02:42,930 --> 00:02:46,133 +truly servicing +the music discovery notion we just saw. + +48 +00:02:47,835 --> 00:02:50,304 +Next, we have the Radio Show type. + +49 +00:02:50,337 --> 00:02:52,940 +Radio Shows like +"New Music Daily by Zane Lowe" + +50 +00:02:52,973 --> 00:02:56,343 +and "Pop Hits Radio by Brooke Reese" +are another way to discover + +51 +00:02:56,376 --> 00:02:59,046 +new music through seasoned professionals. + +52 +00:02:59,079 --> 00:03:00,647 +Much like the Curator type, + +53 +00:03:00,681 --> 00:03:03,317 +radio shows also have +a playlists relationship + +54 +00:03:03,350 --> 00:03:06,320 +to find the music a radio show features. + +55 +00:03:06,353 --> 00:03:09,623 +Just as these two new types +hold relationships to playlists, + +56 +00:03:09,656 --> 00:03:13,260 +we are also exposing two new +relationships on the Playlist type, + +57 +00:03:13,293 --> 00:03:17,698 +"Curator" and "RadioShow" for +the reversed logic: that given a playlist, + +58 +00:03:17,731 --> 00:03:21,702 +we can easily get the structure +of which entity generated said playlist. + +59 +00:03:23,203 --> 00:03:26,840 +MusicKit allows searching the catalog +for content from a plethora of types, + +60 +00:03:26,874 --> 00:03:31,645 +and now we're adding support for our new +item types, like curators and radio shows. + +61 +00:03:31,678 --> 00:03:34,481 +The list just continues to grow, +and as a result, + +62 +00:03:34,515 --> 00:03:37,651 +building good UI becomes +more and more challenging. + +63 +00:03:37,684 --> 00:03:40,621 +That's why this year, +we're making it much easier for you + +64 +00:03:40,654 --> 00:03:43,857 +through top results and suggestions. + +65 +00:03:43,891 --> 00:03:47,694 +Let's take a look at the utility +these enhancements bring to a UI. + +66 +00:03:47,728 --> 00:03:51,164 +When typing to search for content, +you may want to provide strong, + +67 +00:03:51,198 --> 00:03:53,834 +music-related auto-complete support. + +68 +00:03:53,867 --> 00:03:56,170 +That's where suggestions come into play, + +69 +00:03:56,203 --> 00:03:59,239 +providing terms +that people may be trying to reach. + +70 +00:03:59,273 --> 00:04:02,309 +You can even take it a step further +and display top results + +71 +00:04:02,342 --> 00:04:06,380 +for quick access +to what people may be searching for. + +72 +00:04:06,413 --> 00:04:09,650 +For the best results, you may not care +about the type of the item, + +73 +00:04:09,683 --> 00:04:12,653 +but instead want to focus on relevancy. + +74 +00:04:12,686 --> 00:04:15,355 +This is the power of top results. + +75 +00:04:15,389 --> 00:04:18,659 +Now, let's take a look at what +implementing this would look like, + +76 +00:04:18,692 --> 00:04:20,227 +starting with Top Results. + +77 +00:04:20,260 --> 00:04:23,864 +Here we have the existing way to create +a catalog search request, + +78 +00:04:23,897 --> 00:04:25,499 +requiring a search term + +79 +00:04:25,532 --> 00:04:28,001 +and the types of items +that you want represented. + +80 +00:04:28,035 --> 00:04:31,805 +The response includes collections +categorized by the requested types, + +81 +00:04:31,839 --> 00:04:35,175 +meaning multiple lists +of type specific results. + +82 +00:04:35,209 --> 00:04:38,078 +Although this is great, +we want to expose a single list + +83 +00:04:38,111 --> 00:04:41,081 +of the most relevant results +that is type agnostic. + +84 +00:04:41,114 --> 00:04:45,652 +And requesting this information +is as simple as adding one line. + +85 +00:04:45,686 --> 00:04:49,790 +Here we're setting the includeTopResults +property on the request to true, + +86 +00:04:49,823 --> 00:04:53,060 +and a new property +is filled in the response. + +87 +00:04:53,093 --> 00:04:55,796 +This new property is named topResults + +88 +00:04:55,829 --> 00:04:58,866 +which contains items +of any of the requested types. + +89 +00:04:58,899 --> 00:05:02,202 +Here's what the output +of the print statement looks like. + +90 +00:05:02,236 --> 00:05:06,073 +We can see that the top results +returned are a mix of songs, artists, + +91 +00:05:06,106 --> 00:05:11,011 +and albums in a single collection +and is ordered by relevancy. + +92 +00:05:11,044 --> 00:05:14,047 +Now I'll show you how to help +people get to their search destination + +93 +00:05:14,081 --> 00:05:16,683 +faster with Suggestions. + +94 +00:05:16,717 --> 00:05:20,687 +Simply create a suggestions request +with a string term. + +95 +00:05:20,721 --> 00:05:25,192 +Upon calling response, +you'll get back a suggestions response. + +96 +00:05:25,225 --> 00:05:27,961 +The response contains +an array of Suggestions. + +97 +00:05:27,995 --> 00:05:32,232 +And each suggestion includes a display +term which is suitable for your UI, + +98 +00:05:32,266 --> 00:05:34,067 +as well as a search term. + +99 +00:05:34,101 --> 00:05:37,538 +When people select a suggestion, +you can fetch the corresponding results + +100 +00:05:37,571 --> 00:05:40,240 +by performing a search request +with the search term. + +101 +00:05:41,508 --> 00:05:46,280 +Catalog charts are a great way to stay +up-to-date with the most popular songs. + +102 +00:05:46,313 --> 00:05:50,584 +MusicKit offers various types of charts +to see what's trending. + +103 +00:05:50,617 --> 00:05:53,053 +The types of charts +being offered are top charts, + +104 +00:05:53,086 --> 00:05:57,824 +such as Top Songs or Top Albums, +which correspond to the most played music, + +105 +00:05:57,858 --> 00:06:01,528 +city charts, and daily top 100. + +106 +00:06:01,562 --> 00:06:06,633 +You may also specify the requested charts +to be filtered by a specific genre. + +107 +00:06:06,667 --> 00:06:09,870 +Retrieving these charts +through code is extremely simple. + +108 +00:06:09,903 --> 00:06:12,539 +The catalog charts request +follows established patterns + +109 +00:06:12,573 --> 00:06:15,709 +already used +in the catalog search request. + +110 +00:06:15,742 --> 00:06:18,879 +First, initialize a charts request. + +111 +00:06:18,912 --> 00:06:22,182 +You can then specify +the kinds of charts you'd like. + +112 +00:06:22,216 --> 00:06:25,252 +By default, +this will fetch the most played content, + +113 +00:06:25,285 --> 00:06:30,157 +but you can also include daily +global top charts and city top charts. + +114 +00:06:30,190 --> 00:06:33,560 +Lastly, just specify the types +you want your charts to contain. + +115 +00:06:33,594 --> 00:06:35,896 +And that's it. + +116 +00:06:35,929 --> 00:06:38,699 +When we access the first playlist chart +in the response, + +117 +00:06:38,732 --> 00:06:43,270 +we get a MusicCatalogChart +representing the daily global top charts. + +118 +00:06:43,303 --> 00:06:48,775 +Its items are playlists like "Top 100: +Global" and "Top 100: USA". + +119 +00:06:48,809 --> 00:06:52,279 +If you've been fetching catalog charts +using MusicDataRequest in the past, + +120 +00:06:52,312 --> 00:06:55,482 +you no longer need to, +as MusicKit will do the work for you, + +121 +00:06:55,516 --> 00:06:58,752 +with pagination support +built into the collection of items. + +122 +00:06:58,785 --> 00:07:00,320 +In 2021, + +123 +00:07:00,354 --> 00:07:02,789 +we introduced +groundbreaking audio experiences + +124 +00:07:02,823 --> 00:07:05,959 +with true multidimensional sound +and clarity: + +125 +00:07:05,993 --> 00:07:09,396 +Spatial Audio with support +for Dolby Atmos. + +126 +00:07:09,429 --> 00:07:13,166 +This immersive experience is +already available for thousands of songs, + +127 +00:07:13,200 --> 00:07:15,702 +and now you can access this data. + +128 +00:07:15,736 --> 00:07:20,674 +MusicKit exposes which audio resources are +available for any song or album + +129 +00:07:20,707 --> 00:07:25,646 +through Audio Variants, so now, +you can relay this information to others. + +130 +00:07:25,679 --> 00:07:28,315 +Examples of audio variants +are the previously mentioned + +131 +00:07:28,348 --> 00:07:33,387 +Spatial Audio with Dolby Atmos, +Lossless Audio, and much more. + +132 +00:07:34,755 --> 00:07:38,492 +We are also exposing a new +boolean property alongside audio variants, + +133 +00:07:38,525 --> 00:07:43,297 +is Apple Digital Master, which is +the highest quality master supported. + +134 +00:07:43,330 --> 00:07:46,099 +Since this metadata is +exposed at the item level, + +135 +00:07:46,133 --> 00:07:48,368 +audio variants +are perfect for a detail view, + +136 +00:07:48,402 --> 00:07:51,471 +allowing you to achieve UI like this. + +137 +00:07:51,505 --> 00:07:54,341 +Here we have a detail view of an album. + +138 +00:07:54,374 --> 00:07:56,743 +And here, +we can see the appropriate badges + +139 +00:07:56,777 --> 00:07:59,580 +based on the audio variants +property mentioned earlier, + +140 +00:07:59,613 --> 00:08:02,950 +letting users know +what audio quality they can expect. + +141 +00:08:02,983 --> 00:08:05,819 +In this case, +spatial audio and lossless audio + +142 +00:08:05,853 --> 00:08:08,088 +are available for this specific album. + +143 +00:08:08,121 --> 00:08:11,325 +Now let's see how we can write +code to achieve this. + +144 +00:08:11,358 --> 00:08:14,928 +Loading audio variants are like loading +any other extended attribute. + +145 +00:08:14,962 --> 00:08:18,732 +Take an existing album or song, +in this case an album, + +146 +00:08:18,765 --> 00:08:22,803 +and use the with method to load +the audioVariants extended attribute. + +147 +00:08:22,836 --> 00:08:27,441 +Now your detailedAlbum has +the audioVariants property populated. + +148 +00:08:27,474 --> 00:08:29,510 +Here we can see +the audio variants property, + +149 +00:08:29,543 --> 00:08:32,913 +which is an array +whose element is an AudioVariant. + +150 +00:08:32,946 --> 00:08:36,884 +With these values, you can indicate +in your UI the available audio resources + +151 +00:08:36,917 --> 00:08:40,153 +for that particular element, +just like we saw earlier. + +152 +00:08:40,187 --> 00:08:42,256 +Now, this is great, +but you may want to show these + +153 +00:08:42,289 --> 00:08:45,959 +audio badges on more than +just top level or detail views. + +154 +00:08:45,993 --> 00:08:48,562 +That's why +we're also taking it one step further + +155 +00:08:48,595 --> 00:08:52,099 +and exposing the active audio variant +for the music player. + +156 +00:08:52,132 --> 00:08:55,469 +Accessing the active audio variant allows +for a visual indication + +157 +00:08:55,502 --> 00:08:58,772 +of the quality of audio +for the currently playing item, + +158 +00:08:58,805 --> 00:09:01,542 +such as Dolby Atmos in this view. + +159 +00:09:01,575 --> 00:09:04,511 +And the MusicKit player API +automatically chooses + +160 +00:09:04,545 --> 00:09:08,382 +the correct audio quality based on +user settings and network conditions. + +161 +00:09:09,216 --> 00:09:11,518 +To access the active +trait from the player, + +162 +00:09:11,552 --> 00:09:15,122 +first, we access the +ApplicationMusicPlayer's playback state + +163 +00:09:15,155 --> 00:09:17,157 +in an observed object. + +164 +00:09:17,191 --> 00:09:21,728 +We can then access the active audioVariant +directly from the playback state + +165 +00:09:21,762 --> 00:09:24,031 +Now, +we simply check the audioVariant property + +166 +00:09:24,064 --> 00:09:27,734 +to see if it's dolbyAtmos, +and add additional UI if it is. + +167 +00:09:27,768 --> 00:09:30,204 +Because the playback state +is an observed object, + +168 +00:09:30,237 --> 00:09:32,272 +this view will automatically update + +169 +00:09:32,306 --> 00:09:34,274 +whenever the currently +playing item changes, + +170 +00:09:34,308 --> 00:09:37,311 +making sure your view is +always up-to-date. + +171 +00:09:37,344 --> 00:09:39,680 +Now that we've gone over +some catalog additions, + +172 +00:09:39,713 --> 00:09:42,282 +let's dive into +fetching personalized content. + +173 +00:09:42,316 --> 00:09:45,285 +Personalized content is data +specific to a subscriber, + +174 +00:09:45,319 --> 00:09:49,423 +providing a unique and tailored +experience for every user in your app. + +175 +00:09:49,456 --> 00:09:52,526 +Normally, personalized content +requires special authentications + +176 +00:09:52,559 --> 00:09:55,395 +and user tokens, +but in the MusicKit framework, + +177 +00:09:55,429 --> 00:09:59,132 +we've made this all automatic so you don't +have to deal with any of the hassle. + +178 +00:09:59,166 --> 00:10:01,502 +The personalized content +we're bringing to developers + +179 +00:10:01,535 --> 00:10:03,770 +is access to recently played items + +180 +00:10:03,804 --> 00:10:06,907 +and personal recommendations. + +181 +00:10:06,940 --> 00:10:09,443 +Recently played content is +a valuable piece of data + +182 +00:10:09,476 --> 00:10:12,279 +for a person's +music consumption experience. + +183 +00:10:12,312 --> 00:10:16,917 +It allows for quick and easy access +to music items you know they enjoy. + +184 +00:10:16,950 --> 00:10:20,354 +And when listening to new music, +it allows people to later go back + +185 +00:10:20,387 --> 00:10:22,489 +and refer to their history. + +186 +00:10:22,523 --> 00:10:25,526 +To fetch recently played +containers like albums, playlists, + +187 +00:10:25,559 --> 00:10:29,730 +and stations, you can create +a recently played container request. + +188 +00:10:29,763 --> 00:10:32,432 +Note that if you play a song +from a playlist or album, + +189 +00:10:32,466 --> 00:10:34,635 +the container type will be retrieved. + +190 +00:10:34,668 --> 00:10:38,138 +In the response, +you will find recently played music items, + +191 +00:10:38,172 --> 00:10:43,443 +which offer convenience accessors +for the title, subtitle, and artwork. + +192 +00:10:43,477 --> 00:10:46,947 +You can also fetch recently played items +of more specific types, + +193 +00:10:46,980 --> 00:10:48,882 +like songs or stations. + +194 +00:10:48,916 --> 00:10:52,653 +Here, we create a recently played request, +specifying the Song type + +195 +00:10:52,686 --> 00:10:56,490 +through the generic parameter, +indicated by the angle brackets. + +196 +00:10:56,523 --> 00:10:59,693 +Our response now only contains +the songs that we have played. + +197 +00:10:59,726 --> 00:11:02,596 +Now, onto personal recommendations. + +198 +00:11:02,629 --> 00:11:07,067 +Personal recommendations let your app +experience feel more intimate and engaging + +199 +00:11:07,100 --> 00:11:11,905 +as they are generated based off +the user's library and listening history. + +200 +00:11:11,939 --> 00:11:14,575 +Recommendations are +nicely organized by themes, + +201 +00:11:14,608 --> 00:11:20,147 +resulting in groupings by genres, artists, +collections like "Made for You," and more. + +202 +00:11:20,180 --> 00:11:22,683 +To fetch personal recommendations, + +203 +00:11:22,716 --> 00:11:25,552 +simply create +a personal recommendations request. + +204 +00:11:25,586 --> 00:11:28,388 +The response is +a collection of recommendations. + +205 +00:11:28,422 --> 00:11:32,092 +When we log the first recommendation, +we can see that this particular element + +206 +00:11:32,125 --> 00:11:34,595 +represents the "Made for You" +recommendation. + +207 +00:11:34,628 --> 00:11:38,632 +Recommendations have an ID, +title, and nextRefreshDate. + +208 +00:11:38,665 --> 00:11:42,135 +The nextRefreshDate represents +the date time for when this recommendation + +209 +00:11:42,169 --> 00:11:45,372 +should be refreshed +for the most up-to-date suggestions. + +210 +00:11:45,405 --> 00:11:49,109 +The playlists property contains all +of the playlists that are made for me. + +211 +00:11:49,142 --> 00:11:52,446 +Let's take a look at another +example of a recommendation. + +212 +00:11:52,479 --> 00:11:56,250 +Here we'll print out the second element +of the recommendations response. + +213 +00:11:56,283 --> 00:11:59,820 +I listen to a ton of alternative music, +and this recommendation contains + +214 +00:11:59,853 --> 00:12:04,391 +a mix of different types, +in this case, Albums and Playlists. + +215 +00:12:04,424 --> 00:12:06,994 +Those are grouped +in a single collection of items, + +216 +00:12:07,027 --> 00:12:11,131 +which are ordered by relevancy, +much like top results for catalog search. + +217 +00:12:11,164 --> 00:12:14,868 +Now, let's take it a step further +and talk about how you can create + +218 +00:12:14,902 --> 00:12:17,371 +even more relevant experiences +around music + +219 +00:12:17,404 --> 00:12:21,175 +by incorporating content +from your user's library into your app. + +220 +00:12:21,208 --> 00:12:25,312 +This year, MusicKit allows your app +to fetch items from the library + +221 +00:12:25,345 --> 00:12:28,749 +with two types of requests: +the library request + +222 +00:12:28,782 --> 00:12:31,485 +and the library sectioned request, + +223 +00:12:31,518 --> 00:12:34,588 +search for content in the user's library, + +224 +00:12:34,621 --> 00:12:37,524 +and load extended attributes +and relationships + +225 +00:12:37,558 --> 00:12:39,927 +specifically from the library. + +226 +00:12:39,960 --> 00:12:42,296 +Before we see the technical details, + +227 +00:12:42,329 --> 00:12:45,566 +let's see how we can use library content +to enhance your app. + +228 +00:12:45,599 --> 00:12:48,435 +I've been working on a fitness app +called Music Marathon + +229 +00:12:48,468 --> 00:12:50,704 +that will track your outdoor runs. + +230 +00:12:50,737 --> 00:12:52,906 +By incorporating MusicKit in the project, + +231 +00:12:52,940 --> 00:12:55,509 +we allow people to play music +directly through the app + +232 +00:12:55,542 --> 00:12:59,546 +instead of context switching between +the Apple Music app and this one. + +233 +00:12:59,580 --> 00:13:03,016 +Let's start a new workout +and look for music content. + +234 +00:13:04,818 --> 00:13:07,154 +Here we see some +recommended playlists retrieved + +235 +00:13:07,187 --> 00:13:09,122 +from the personal recommendations request, + +236 +00:13:09,156 --> 00:13:12,860 +to give people quick access +to playlists we think they'll love. + +237 +00:13:12,893 --> 00:13:17,531 +Going to the library tab, +we can see it's an empty view. + +238 +00:13:17,564 --> 00:13:20,267 +It would be great to be able to see +all of my personal playlists, + +239 +00:13:20,300 --> 00:13:21,835 +so let's write that feature. + +240 +00:13:21,869 --> 00:13:25,439 +I already have some UI set up +to handle the basics of this view, + +241 +00:13:25,472 --> 00:13:29,109 +and now I want to load +the playlists from my library. + +242 +00:13:29,142 --> 00:13:30,777 +First, I'll make library request... + +243 +00:13:33,647 --> 00:13:35,749 +Specifying playlist +in the generic parameter + +244 +00:13:35,782 --> 00:13:38,785 +to indicate that we want the playlists +from the user's library. + +245 +00:13:42,256 --> 00:13:44,958 +And I'll store it in a local variable +I'll name "request." + +246 +00:13:47,928 --> 00:13:51,098 +Next I'll take this request +and call the response function. + +247 +00:13:53,867 --> 00:13:56,403 +This method is an async throwing method, + +248 +00:13:56,436 --> 00:13:58,572 +so let's add the try and await keywords. + +249 +00:13:58,605 --> 00:14:01,441 +and once again store it +in a response variable. + +250 +00:14:04,311 --> 00:14:07,614 +Then, I'll update the state object +to receive this response. + +251 +00:14:11,752 --> 00:14:14,555 +Now all that's left to do +is update the list + +252 +00:14:14,588 --> 00:14:17,457 +so we can see the playlists in my UI. + +253 +00:14:17,491 --> 00:14:21,228 +I'll iterate through the items +in the response using a ForEach... + +254 +00:14:25,432 --> 00:14:28,669 +And retrieve each playlist +in the MusicItemCollection. + +255 +00:14:30,404 --> 00:14:31,638 +Now that we have the playlists, + +256 +00:14:31,672 --> 00:14:33,941 +I'll pass them into a PlaylistCell +I've already made. + +257 +00:14:35,642 --> 00:14:37,644 +Now if we re-run... + +258 +00:14:44,017 --> 00:14:46,753 +And navigate back to the app, + +259 +00:14:46,787 --> 00:14:50,057 +we can see all of my personal +playlists in the library. + +260 +00:14:50,090 --> 00:14:52,826 +Now, I can choose to listen +to personal recommendations, + +261 +00:14:52,860 --> 00:14:57,097 +anything from the Apple Music catalog, +and my own personal library. + +262 +00:14:57,130 --> 00:15:00,467 +Now that we've seen how easy +it is to access library content, + +263 +00:15:00,501 --> 00:15:02,569 +let's look at what else +the library request can do. + +264 +00:15:02,603 --> 00:15:05,239 +The music library request +is a powerful API + +265 +00:15:05,272 --> 00:15:07,741 +to fetch items from the user's library. + +266 +00:15:07,774 --> 00:15:11,845 +On iOS, unlike other requests +to fetch content from the music catalog, + +267 +00:15:11,879 --> 00:15:15,682 +MusicLibraryRequest will not +actually load data from the network. + +268 +00:15:15,716 --> 00:15:19,152 +Instead, it will load items +from the copy of the user's library + +269 +00:15:19,186 --> 00:15:21,622 +that is stored on device. + +270 +00:15:21,655 --> 00:15:24,591 +The basics of this request +only require you to specify + +271 +00:15:24,625 --> 00:15:27,027 +which music item type is desired. + +272 +00:15:27,060 --> 00:15:29,429 +This item type is passed +through the generic parameter + +273 +00:15:29,463 --> 00:15:32,332 +of the MusicLibraryRequest. + +274 +00:15:32,366 --> 00:15:36,003 +You can apply a variety of filters +and sort options on the request + +275 +00:15:36,036 --> 00:15:39,873 +in order to finely tune your call +to match your requirements. + +276 +00:15:39,907 --> 00:15:43,710 +This request is also capable +of fetching already downloaded content, + +277 +00:15:43,744 --> 00:15:46,947 +supporting a fully offline experience. + +278 +00:15:46,980 --> 00:15:50,450 +Let's start with the simple, base request, +the same request we wrote + +279 +00:15:50,484 --> 00:15:55,322 +in the Music Marathon app, but this time, +ask for the Albums in the library. + +280 +00:15:55,355 --> 00:15:58,625 +The album type is specified +through the generic parameter. + +281 +00:15:58,659 --> 00:16:01,695 +To perform the request, +call the response function. + +282 +00:16:01,728 --> 00:16:04,898 +Looking at the output, +we have a MusicLibraryResponse, + +283 +00:16:04,932 --> 00:16:07,334 +where its items are a MusicItemCollection + +284 +00:16:07,367 --> 00:16:11,104 +of all the albums found +within the user's music library. + +285 +00:16:11,138 --> 00:16:14,541 +Here we notice that these Albums are +the same Album structure that you would + +286 +00:16:14,575 --> 00:16:19,346 +get from one of our various catalog +requests and have the same capabilities. + +287 +00:16:19,379 --> 00:16:22,649 +Whereas in this example we are +fetching every album in the library, + +288 +00:16:22,683 --> 00:16:27,221 +we know there are scenarios where +you only want a specific subset of albums. + +289 +00:16:27,254 --> 00:16:31,458 +That's why MusicLibraryRequest +also enables you to be more specific + +290 +00:16:31,491 --> 00:16:34,595 +about what items +you want to fetch from the library. + +291 +00:16:34,628 --> 00:16:38,665 +Let's take the same request +we wrote before and add a filter. + +292 +00:16:38,699 --> 00:16:42,369 +Here, we want to load all albums +where the isCompilation property + +293 +00:16:42,402 --> 00:16:43,971 +is equal to true. + +294 +00:16:44,004 --> 00:16:47,941 +When you call filter method, +Xcode's autocompletion will only offer + +295 +00:16:47,975 --> 00:16:52,212 +specific key paths that are supported +for the type of item you are requesting. + +296 +00:16:52,246 --> 00:16:56,283 +Now, the response only has +albums which are compilations. + +297 +00:16:56,316 --> 00:16:59,653 +But that's not all the power +the MusicLibraryRequest has to offer. + +298 +00:16:59,686 --> 00:17:01,655 +You can chain multiple filters, + +299 +00:17:01,688 --> 00:17:04,958 +giving you a more refined request +with each addition. + +300 +00:17:04,992 --> 00:17:08,862 +What if we wanted all of +the compilations of a particular genre? + +301 +00:17:08,896 --> 00:17:11,331 +We can add another filter to the request. + +302 +00:17:11,365 --> 00:17:15,002 +For example, here we have +an instance of Genre named "Dance." + +303 +00:17:15,035 --> 00:17:18,305 +You can filter by the genre's relationship +to restrict the results + +304 +00:17:18,338 --> 00:17:22,309 +to only include compilations +that contain this specific genre. + +305 +00:17:22,342 --> 00:17:25,746 +Now our response +only contains dance compilations. + +306 +00:17:25,779 --> 00:17:29,283 +How about only including dance +compilations that are already downloaded? + +307 +00:17:29,316 --> 00:17:32,252 +To do that, +just set the includeOnlyDownloadedContent + +308 +00:17:32,286 --> 00:17:33,787 +to true on the request. + +309 +00:17:33,820 --> 00:17:34,955 +And that's it. + +310 +00:17:34,988 --> 00:17:37,891 +The response is +the same MusicLibraryResponse, + +311 +00:17:37,925 --> 00:17:41,662 +but the items now only contain +elements that are downloaded. + +312 +00:17:41,695 --> 00:17:45,165 +As you can see, +the music library request is very powerful + +313 +00:17:45,199 --> 00:17:50,204 +and unlocks new capabilities that weren't +possible with a custom MusicDataRequest. + +314 +00:17:50,237 --> 00:17:54,842 +But MusicKit offers even more options +to fetch data from the user's library. + +315 +00:17:54,875 --> 00:17:57,611 +Meet the Library Sectioned Request. + +316 +00:17:57,644 --> 00:18:02,082 +The sectioned request is able +to fetch items grouped by sections. + +317 +00:18:02,115 --> 00:18:06,987 +As a result, the sectioned request +takes in two distinct generic parameters. + +318 +00:18:07,020 --> 00:18:11,758 +The first representing the section type, +and the second, the item type. + +319 +00:18:11,792 --> 00:18:14,661 +The library sectioned request +supports the same capabilities + +320 +00:18:14,695 --> 00:18:18,098 +as the regular library request, +such as a variety of filter + +321 +00:18:18,131 --> 00:18:23,470 +and sort methods which you can apply +to either the sections or the items. + +322 +00:18:23,504 --> 00:18:26,874 +Here's how you can fetch +all albums sectioned by their genres + +323 +00:18:26,907 --> 00:18:29,743 +using the library sectioned request. + +324 +00:18:29,776 --> 00:18:33,647 +The sectioned Response holds a property +named "sections" where each element + +325 +00:18:33,680 --> 00:18:38,318 +corresponds to the first generic parameter +of the request, Genre in this case. + +326 +00:18:38,352 --> 00:18:42,456 +Each genre not only exposes +its own attributes, but it also contains + +327 +00:18:42,489 --> 00:18:46,660 +a collection of albums, +accessible via the items property. + +328 +00:18:46,693 --> 00:18:50,030 +Those items correspond +to the second generic argument. + +329 +00:18:50,063 --> 00:18:53,367 +Here, the highlight shows albums +whose genre is Alternative. + +330 +00:18:53,400 --> 00:18:56,937 +And as mentioned before, +the capabilities of filtering and sorting + +331 +00:18:56,970 --> 00:18:59,706 +are also available +for this sectioned request. + +332 +00:18:59,740 --> 00:19:02,643 +Let's say we want the same albums, +sectioned by genres, + +333 +00:19:02,676 --> 00:19:05,979 +but the albums sorted +by their artist's name. + +334 +00:19:06,013 --> 00:19:07,681 +We add a sort filter. + +335 +00:19:07,714 --> 00:19:11,585 +By specifying the artistName keyPath +on Albums and saying that we want + +336 +00:19:11,618 --> 00:19:15,055 +these to be ascending, +we're sorting the response. + +337 +00:19:15,088 --> 00:19:18,592 +Notice that the method is sortItems +as we are specifying the sorting + +338 +00:19:18,625 --> 00:19:21,662 +to be applied to the items +and not the sections. + +339 +00:19:21,695 --> 00:19:23,564 +Had we wanted to specify the sections, + +340 +00:19:23,597 --> 00:19:27,668 +a set of filterSections +and sortSection methods are available. + +341 +00:19:27,701 --> 00:19:29,670 +Let's take a look at the new response. + +342 +00:19:32,139 --> 00:19:35,776 +We can now see that our albums +are ordered alphabetically by artist name + +343 +00:19:35,809 --> 00:19:37,811 +instead of by their titles. + +344 +00:19:37,845 --> 00:19:42,516 +Both the library request and library +sectioned request are extremely powerful, + +345 +00:19:42,549 --> 00:19:45,485 +but you might also want to complement +your music search UI + +346 +00:19:45,519 --> 00:19:48,655 +by adding search results +from the user's library. + +347 +00:19:48,689 --> 00:19:52,593 +So we've added a new structured request +which operates almost identically + +348 +00:19:52,626 --> 00:19:54,962 +to catalog search, +but instead of loading results + +349 +00:19:54,995 --> 00:19:59,666 +from the catalog, it finds +relevant items in the user's library. + +350 +00:19:59,700 --> 00:20:02,669 +Just like its catalog counterpart, +the library search request + +351 +00:20:02,703 --> 00:20:06,773 +only requires a search term +and an array of types. + +352 +00:20:06,807 --> 00:20:10,244 +Now that we've seen the different ways +to retrieve items from the user's library, + +353 +00:20:10,277 --> 00:20:13,814 +what about loading extended +attributes and relationships? + +354 +00:20:13,847 --> 00:20:17,784 +As you may know, the initial release +of MusicKit introduced the with method, + +355 +00:20:17,818 --> 00:20:22,189 +loading these properties from +Apple Music API in a straightforward way. + +356 +00:20:22,222 --> 00:20:24,658 +This year, +we're augmenting the current with method + +357 +00:20:24,691 --> 00:20:28,195 +to also take in +a preferred source parameter. + +358 +00:20:28,228 --> 00:20:32,299 +This preferred source indicates where +to load data from, for extended attributes + +359 +00:20:32,332 --> 00:20:36,170 +and relationships that are available +in both the Apple Music catalog + +360 +00:20:36,203 --> 00:20:38,438 +and the user's library. + +361 +00:20:38,472 --> 00:20:41,942 +And for the properties that only live +in either the catalog or the library, + +362 +00:20:41,975 --> 00:20:44,711 +they will still fetched +regardless of the preferred source + +363 +00:20:44,745 --> 00:20:47,581 +to make sure nothing is ignored. + +364 +00:20:47,614 --> 00:20:49,950 +In addition, +you can use this functionality + +365 +00:20:49,983 --> 00:20:52,419 +no matter where +the initial item came from, + +366 +00:20:52,452 --> 00:20:56,390 +whether it be a catalog request, +a library request, or elsewhere. + +367 +00:20:56,423 --> 00:20:57,958 +It all just works. + +368 +00:20:58,959 --> 00:21:02,963 +Here we have the known way of +receiving a relationship of a music item. + +369 +00:21:02,996 --> 00:21:06,700 +We're loading the tracks of an album, +and when we display the output, + +370 +00:21:06,733 --> 00:21:09,269 +we can see +all of the tracks for that album. + +371 +00:21:09,303 --> 00:21:13,574 +However, with the new addition +of the preferredSource property, + +372 +00:21:13,607 --> 00:21:15,242 +we can specify that we would like to fetch + +373 +00:21:15,275 --> 00:21:17,578 +this relationship from the library. + +374 +00:21:17,611 --> 00:21:22,349 +Now our output only contains the tracks +of the album found in the library. + +375 +00:21:22,382 --> 00:21:25,886 +With the various ways you can +now fetch items from the user's library, + +376 +00:21:25,919 --> 00:21:28,655 +it only makes sense to allow users +to be able to interact + +377 +00:21:28,689 --> 00:21:31,358 +with their library +directly through MusicKit. + +378 +00:21:31,391 --> 00:21:34,228 +Let's jump back into my sample app, +Music Marathon, + +379 +00:21:34,261 --> 00:21:37,164 +to see some of the capabilities +the library offers. + +380 +00:21:37,197 --> 00:21:39,333 +As I'm working out, +I want to browse through some + +381 +00:21:39,366 --> 00:21:40,834 +of my personal recommendations. + +382 +00:21:43,270 --> 00:21:45,839 +As I look through the tracks, +I realize that one of these songs + +383 +00:21:45,873 --> 00:21:47,975 +would be perfect for my workout playlist. + +384 +00:21:48,008 --> 00:21:51,311 +If hold down one of these cells, +a contextual menu pops up, + +385 +00:21:51,345 --> 00:21:53,881 +allowing me +to add this song to a playlist. + +386 +00:21:53,914 --> 00:21:57,851 +When we press it, a pop-up +of all of my playlists appear again. + +387 +00:21:57,885 --> 00:22:01,555 +Let's write code to add the selected track +to whichever playlist I pick. + +388 +00:22:01,588 --> 00:22:05,025 +I've already piped the selected item +to our AddToPlaylistCell cell, + +389 +00:22:05,058 --> 00:22:09,229 +so all we have to do is access the +MusicLibrary through the shared instance. + +390 +00:22:13,534 --> 00:22:16,703 +We'll call the "add" method, + +391 +00:22:16,737 --> 00:22:18,972 +specifying our selected track + +392 +00:22:19,006 --> 00:22:21,275 +and which playlist we want to add to. + +393 +00:22:23,343 --> 00:22:25,812 +This method is also +an async throwing function, + +394 +00:22:25,846 --> 00:22:28,815 +so we add once again +the try and await keywords. + +395 +00:22:31,084 --> 00:22:33,053 +Lastly, we'll dismiss the picker + +396 +00:22:33,086 --> 00:22:36,490 +by setting the isShowingPlaylistPicker +binding variable to false. + +397 +00:22:38,225 --> 00:22:40,827 +Now if we re-run + +398 +00:22:40,861 --> 00:22:44,598 +and add a track to a playlist +and select one of our playlists, + +399 +00:22:44,631 --> 00:22:47,901 +we should expect to see this item added. + +400 +00:22:47,935 --> 00:22:50,704 +Navigating back to the library tab +within the app, + +401 +00:22:50,737 --> 00:22:53,607 +we can see the song is +now added to our workout playlist. + +402 +00:22:53,640 --> 00:22:56,443 +And that's how simple it is +to add an item to a playlist. + +403 +00:22:56,476 --> 00:22:59,913 +Let's look at some of the other +functionality the library offers. + +404 +00:22:59,947 --> 00:23:03,317 +The various other ways to interact +with the library are adding content + +405 +00:23:03,350 --> 00:23:06,420 +to the library, creating playlists, + +406 +00:23:06,453 --> 00:23:10,524 +and editing playlists' metadata +and track list. + +407 +00:23:10,557 --> 00:23:14,728 +Adding content to the user's music library +allows people to find specific songs + +408 +00:23:14,761 --> 00:23:18,532 +or albums in the library tab +of the Apple Music app, + +409 +00:23:18,565 --> 00:23:21,134 +as well as synchronizes across all devices + +410 +00:23:21,168 --> 00:23:24,338 +when "Sync Library" is turned on +in Settings. + +411 +00:23:24,371 --> 00:23:27,474 +Providing this functionality +directly in your app saves people + +412 +00:23:27,508 --> 00:23:30,878 +from context switching between +the Apple Music app and yours, + +413 +00:23:30,911 --> 00:23:34,515 +so they can stay engaged +in the content you're providing. + +414 +00:23:34,548 --> 00:23:36,917 +Also, by integrating adding to the library + +415 +00:23:36,950 --> 00:23:39,586 +along with the newly introduced +library requests, + +416 +00:23:39,620 --> 00:23:42,322 +your app can immediately benefit +from these results, + +417 +00:23:42,356 --> 00:23:46,026 +giving users easy access +to content they love. + +418 +00:23:46,059 --> 00:23:47,928 +Even with this powerful service, + +419 +00:23:47,961 --> 00:23:51,331 +you may still want to craft +specific musical experiences. + +420 +00:23:51,365 --> 00:23:56,236 +So this year, we're bringing +playlist creation and editing to MusicKit. + +421 +00:23:56,270 --> 00:23:59,806 +You can now create playlists +on behalf of your users. + +422 +00:23:59,840 --> 00:24:03,277 +We're also allowing items, +such as songs or even whole albums, + +423 +00:24:03,310 --> 00:24:07,447 +to be added to any eligible playlist +in the user's library. + +424 +00:24:07,481 --> 00:24:11,151 +Creating playlists are fantastic +for grouping content that people love + +425 +00:24:11,185 --> 00:24:14,188 +or fitting any mood your app wants to set. + +426 +00:24:14,221 --> 00:24:16,557 +And by adding content +to existing playlists, + +427 +00:24:16,590 --> 00:24:19,860 +you allow for the various +music discovery tools MusicKit offers + +428 +00:24:19,893 --> 00:24:22,629 +to directly affect people. + +429 +00:24:22,663 --> 00:24:24,898 +You can now also edit playlists +that you've created, + +430 +00:24:24,932 --> 00:24:28,402 +being able to edit the track list +and metadata to make sure + +431 +00:24:28,435 --> 00:24:30,504 +everything is just as you want it. + +432 +00:24:30,537 --> 00:24:33,507 +And those are the ways you can +interact with users' libraries + +433 +00:24:33,540 --> 00:24:35,776 +from within your app. + +434 +00:24:35,809 --> 00:24:39,713 +To wrap up, MusicKit received +some major upgrades this year. + +435 +00:24:39,746 --> 00:24:43,750 +Easily incorporate our catalog +enhancements for new types, properties, + +436 +00:24:43,784 --> 00:24:47,988 +and search augmentations to your existing +apps for an even better experience. + +437 +00:24:49,256 --> 00:24:51,458 +Integrate library content +and functionality + +438 +00:24:51,491 --> 00:24:53,260 +to unlock brand-new capabilities + +439 +00:24:53,293 --> 00:24:55,596 +and let users be in control +of their experience. + +440 +00:24:57,130 --> 00:25:00,367 +And using MusicKit can enhance +multiple different types of apps. + +441 +00:25:00,400 --> 00:25:03,637 +Fitness apps, games, +social media apps, mapping apps, + +442 +00:25:03,670 --> 00:25:07,975 +and more can all benefit +from playing or sharing music. + +443 +00:25:08,008 --> 00:25:12,012 +To go even further, make sure +to check out some related sessions. + +444 +00:25:12,045 --> 00:25:14,681 +Dive deeper into Swift +and learn about the new additions + +445 +00:25:14,715 --> 00:25:19,620 +to the language to get the most out of +MusicKit and other Apple Frameworks. + +446 +00:25:19,653 --> 00:25:23,257 +Check out the MusicKit session from 2021 +to learn how to set up your app + +447 +00:25:23,290 --> 00:25:28,662 +to use the framework, initiate playback, +and present subscription offers. + +448 +00:25:28,695 --> 00:25:32,432 +And if you're interested in integrating +with Apple Music on Android or the web, + +449 +00:25:32,466 --> 00:25:36,370 +we have another session that goes over +how to use Apple Music API directly. + +450 +00:25:37,538 --> 00:25:40,207 +I hope you enjoyed our session, +and make sure to stay updated + +451 +00:25:40,240 --> 00:25:42,442 +and engaged through our developer forums. + +452 +00:25:42,476 --> 00:25:46,847 +Thank you for watching, +and enjoy WWDC 2022. + diff --git a/eng/2022 Session 110348 Build your first app in Swift Playgrounds en.srt b/eng/2022 Session 110348 Build your first app in Swift Playgrounds en.srt new file mode 100644 index 0000000..eeb7718 --- /dev/null +++ b/eng/2022 Session 110348 Build your first app in Swift Playgrounds en.srt @@ -0,0 +1,1039 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:13,447 +- Welcome to "Build your first +app in Swift Playgrounds." + +3 +00:00:13,480 --> 00:00:17,684 +I am Collett Charlton, an engineer +on the Swift Playgrounds team. + +4 +00:00:17,718 --> 00:00:19,019 +- And I'm Connor Wakamo, + +5 +00:00:19,052 --> 00:00:21,588 +another engineer +on the Swift Playgrounds team. + +6 +00:00:21,622 --> 00:00:26,159 +- Swift Playgrounds has been a great +tool for learning to code in Swift, + +7 +00:00:26,193 --> 00:00:31,498 +and now you can take things +a step further and build apps too! + +8 +00:00:31,532 --> 00:00:36,170 +Today, we're going to walk through +building an app in Swift Playgrounds, + +9 +00:00:36,203 --> 00:00:38,472 +starting with a blank template. + +10 +00:00:38,505 --> 00:00:42,809 +We'll then show how to debug issues +using previews and the console, + +11 +00:00:42,843 --> 00:00:46,446 +and finally, +we'll submit our app to TestFlight. + +12 +00:00:48,282 --> 00:00:52,719 +Something our team really loves +is making and drinking tea. + +13 +00:00:52,753 --> 00:00:56,857 +We love it so much +that we want to make an app for that. + +14 +00:00:56,890 --> 00:01:01,094 +Connor and I are going to build +a little app to help us at tea time, + +15 +00:01:01,128 --> 00:01:06,033 +giving us a list of teas to +help us pick what to drink each day. + +16 +00:01:06,066 --> 00:01:09,970 +Swift Playgrounds +works great on Mac and iPad. + +17 +00:01:10,003 --> 00:01:11,738 +I've got my Mac with me today, + +18 +00:01:11,772 --> 00:01:15,008 +so I'm going to start +building this app there. + +19 +00:01:17,044 --> 00:01:20,614 +Whether you are new to coding, +or are an experienced developer, + +20 +00:01:20,647 --> 00:01:25,018 +Swift Playgrounds offers a variety +of templates and instructional content + +21 +00:01:25,052 --> 00:01:26,753 +to get you started. + +22 +00:01:26,787 --> 00:01:31,391 +For our tea app, we'll begin by clicking +the blank App template + +23 +00:01:31,425 --> 00:01:33,827 +in the bottom left corner of the screen. + +24 +00:01:37,431 --> 00:01:40,801 +Now that we have our template, +let's double click to open. + +25 +00:01:42,636 --> 00:01:48,442 +Great. On the right is a live interactive +preview showing the Hello World text. + +26 +00:01:50,777 --> 00:01:55,549 +Before we start coding, let's customize +the app a little using the App Settings. + +27 +00:01:55,582 --> 00:01:58,485 +To do this, +I'll click the app settings button + +28 +00:01:58,519 --> 00:02:00,988 +in the top left corner of the sidebar. + +29 +00:02:02,356 --> 00:02:06,660 +Here, you can customize various +project properties like the app name + +30 +00:02:06,693 --> 00:02:08,695 +and the accent color. + +31 +00:02:08,729 --> 00:02:12,399 +You can also add a custom +or placeholder app icon, + +32 +00:02:12,432 --> 00:02:17,437 +capabilities, or purpose string, +and a bundleID. + +33 +00:02:17,471 --> 00:02:20,274 +I'll update the app's name to Tea Time. + +34 +00:02:23,477 --> 00:02:25,679 +I'll set the accent color to brown. + +35 +00:02:27,047 --> 00:02:30,217 +And I'll update +the placeholder icon to the mug. + +36 +00:02:32,252 --> 00:02:35,756 +Great. Now that the important part's +out of the way, + +37 +00:02:35,789 --> 00:02:39,393 +let's start writing some code +by selecting the template text + +38 +00:02:39,426 --> 00:02:43,163 +and replacing it +with our first View from the Library. + +39 +00:02:44,131 --> 00:02:50,237 +The Library can be accessed by clicking +the plus button in the project toolbar. + +40 +00:02:50,270 --> 00:02:53,807 +It contains easy-to-use snippets +of different Views, + +41 +00:02:53,841 --> 00:02:59,379 +Modifiers, SF Symbols, and Colors. + +42 +00:02:59,413 --> 00:03:03,016 +We'll be using a List View +to display our list of teas, + +43 +00:03:03,050 --> 00:03:07,921 +so I'll type list into the search field +and click it to insert one. + +44 +00:03:11,258 --> 00:03:14,895 +Now that we have our List View, +let's add some tea items to it. + +45 +00:03:16,363 --> 00:03:19,132 +I'll start typing Text... + +46 +00:03:19,166 --> 00:03:20,734 +and use the return key + +47 +00:03:20,767 --> 00:03:23,670 +to accept and insert +the code completion suggestion + +48 +00:03:23,704 --> 00:03:26,240 +from the inline code completion panel. + +49 +00:03:30,711 --> 00:03:35,048 +Alright, now we have our List View +with one tea added. + +50 +00:03:35,082 --> 00:03:36,884 +Let's add some more. + +51 +00:03:42,289 --> 00:03:46,460 +Wait, it looks like I accidentally +added Jasmine Green twice. + +52 +00:03:46,493 --> 00:03:51,431 +In order to avoid duplicating our teas, +we should store them as an orderedSet. + +53 +00:03:51,465 --> 00:03:56,069 +Luckily for us, Apple's swift-collection +package offers just that. + +54 +00:03:56,103 --> 00:03:59,306 +So, let's add the swift-collection package +to our project. + +55 +00:04:00,574 --> 00:04:04,578 +To do this, let's open the File Menu +and select Add Package. + +56 +00:04:07,681 --> 00:04:11,985 +I'll begin by entering the URL +for the swift-collections package, + +57 +00:04:12,019 --> 00:04:12,853 +and then press the return key. + +58 +00:04:17,357 --> 00:04:20,294 +After the package is fetched, +we can see the package version + +59 +00:04:20,327 --> 00:04:23,664 +and other products +that can be added to our project. + +60 +00:04:23,697 --> 00:04:28,335 +For this app, we'll only select +Collections then click Add to Project. + +61 +00:04:31,772 --> 00:04:36,577 +Now we have swift-collections added to our +project in the sidebar under Packages. + +62 +00:04:36,610 --> 00:04:41,381 +Let's create an OrderedSet of String +to store our list of teas. + +63 +00:04:47,421 --> 00:04:50,824 +Wait. It seems like we have an issue. + +64 +00:04:50,858 --> 00:04:53,393 +Let's take a look +by clicking the issue icon. + +65 +00:04:54,928 --> 00:04:57,497 +"Cannot find type ordered set +in scope." + +66 +00:04:57,531 --> 00:05:00,701 +Oh, I see what the problem is. + +67 +00:05:00,734 --> 00:05:03,937 +I forgot to import the Collections module +in our project. + +68 +00:05:03,971 --> 00:05:06,373 +Let's import that, +and the issue should be resolved. + +69 +00:05:13,046 --> 00:05:15,582 +Now that we have resolved that issue, + +70 +00:05:15,616 --> 00:05:19,419 +let's update our List View to use +the collection we just created. + +71 +00:05:19,453 --> 00:05:22,656 +To do this, we'll use a ForEach View. + +72 +00:05:43,577 --> 00:05:45,712 +Alright, there we have it-- + +73 +00:05:45,746 --> 00:05:49,449 +our list of teas being displayed +from our tea collection. + +74 +00:05:49,483 --> 00:05:51,919 +As I'm working on this project, + +75 +00:05:51,952 --> 00:05:55,789 +I'm getting more and more ideas +on features to add. + +76 +00:05:55,822 --> 00:06:00,127 +It would be so cool if our app +could listen for whistling tea kettles + +77 +00:06:00,160 --> 00:06:02,896 +to let us know when it's time to pour. + +78 +00:06:02,930 --> 00:06:05,165 +I'm not going to implement this now, + +79 +00:06:05,199 --> 00:06:07,835 +but let's walk through the steps +we would take + +80 +00:06:07,868 --> 00:06:12,105 +to explain to users +why our app needs to use the microphone. + +81 +00:06:13,173 --> 00:06:17,344 +To add this, +let's go back into our app settings... + +82 +00:06:17,377 --> 00:06:19,346 +and click Capabilities. + +83 +00:06:20,714 --> 00:06:23,650 +Using the plus button +in the top righthand corner, + +84 +00:06:23,684 --> 00:06:27,020 +we'll get a list of capabilities +we can add to our project. + +85 +00:06:27,054 --> 00:06:30,490 +Let's find microphone and click it to add. + +86 +00:06:31,358 --> 00:06:35,963 +For the purpose string, we'll write: +"Tea Time uses the microphone + +87 +00:06:35,996 --> 00:06:38,265 +to listen for whistling tea kettles." + +88 +00:06:38,298 --> 00:06:41,702 +I'll click add, +and then close app settings. + +89 +00:06:44,738 --> 00:06:49,476 +Alright, we've done a lot today, +and I'm excited to share the project + +90 +00:06:49,510 --> 00:06:52,212 +and the cool ideas I have with Connor. + +91 +00:06:53,780 --> 00:06:57,818 +I'll share it with Connor by adding it +to our shared iCloud folder. + +92 +00:06:57,851 --> 00:07:01,955 +But first, let's give it +a better filename than My App. + +93 +00:07:08,795 --> 00:07:12,566 +Now, I'll drag it to the shared +iCloud Folder. + +94 +00:07:15,335 --> 00:07:20,007 +Now that I'm done, I'm going to hand it +over to Connor to finish the app. + +95 +00:07:20,040 --> 00:07:23,844 +Thanks Collett. +I'm going to pick things up on iPad. + +96 +00:07:23,877 --> 00:07:26,980 +Since we're sharing our project +via an iCloud Shared Folder, + +97 +00:07:27,014 --> 00:07:29,583 +it doesn't show up +in the main list of projects. + +98 +00:07:29,616 --> 00:07:32,119 +But if I tap on "Locations," + +99 +00:07:32,152 --> 00:07:34,888 +I can get access to projects +from elsewhere in iCloud, + +100 +00:07:34,922 --> 00:07:37,758 +or even from +third-party document providers. + +101 +00:07:37,791 --> 00:07:39,760 +I'm already in our Shared Folder, + +102 +00:07:39,793 --> 00:07:42,829 +so I'll tap on Tea Time +to open the project. + +103 +00:07:42,863 --> 00:07:47,067 +Any changes I make will automatically +be reflected in the shared project. + +104 +00:07:47,100 --> 00:07:49,803 +It looks like Collett +is such a good engineer + +105 +00:07:49,837 --> 00:07:53,473 +that just uploading the project to iCloud +added some extra features! + +106 +00:07:53,507 --> 00:07:57,578 +She implemented a TabView here +so we don't just have a list of teas + +107 +00:07:57,611 --> 00:07:59,847 +but also an Assistant. + +108 +00:07:59,880 --> 00:08:01,782 +If I tap on the Assistant tab, + +109 +00:08:01,815 --> 00:08:04,952 +it's a little bit bare-bones, +but it does the job. + +110 +00:08:04,985 --> 00:08:08,555 +I can ask for a recommendation, +and it'll give me a tea I should drink. + +111 +00:08:09,957 --> 00:08:12,726 +Seems today I should have +the Jasmine Green. + +112 +00:08:13,694 --> 00:08:17,064 +Now I know Collett was working on +a fun new way to pick teas + +113 +00:08:17,097 --> 00:08:18,966 +to give this a little extra pizzaz. + +114 +00:08:18,999 --> 00:08:21,134 +Let's open the sidebar to try to find it. + +115 +00:08:23,403 --> 00:08:26,874 +TeaWheelView seems promising, +so let's tap on that to open it. + +116 +00:08:28,408 --> 00:08:31,945 +We've got a View +which takes a collection of data. + +117 +00:08:31,979 --> 00:08:34,982 +Let's add a view preview +so we can try TeaWheelView out + +118 +00:08:35,015 --> 00:08:37,584 +before it's a part of the main app. + +119 +00:08:37,618 --> 00:08:39,520 +I'll scroll to the bottom of the file... + +120 +00:08:42,856 --> 00:08:45,325 +And I'll begin typing "preview provider." + +121 +00:08:47,060 --> 00:08:50,531 +I'll accept the code completion +suggestion by pressing the Return key, + +122 +00:08:50,564 --> 00:08:54,234 +and I'll name it TeaWheelView_Previews. + +123 +00:08:56,436 --> 00:08:59,506 +Page dots have now appeared +at the bottom of the preview area, + +124 +00:08:59,540 --> 00:09:03,544 +which tells me that Swift Playgrounds +recognizes my preview provider. + +125 +00:09:03,577 --> 00:09:07,681 +If I tap on the right chevron +underneath the app preview… + +126 +00:09:07,714 --> 00:09:11,618 +then I can use my view preview +instead of the app preview. + +127 +00:09:11,652 --> 00:09:13,620 +Right now it just says "Hello, world!" + +128 +00:09:13,654 --> 00:09:16,657 +so let's add in some code +to create a TeaWheelView. + +129 +00:09:16,690 --> 00:09:20,694 +First I'll add an array with a few items +in it as a static property + +130 +00:09:20,727 --> 00:09:23,030 +so it can be used by my preview. + +131 +00:09:29,436 --> 00:09:31,972 +I'll leave the insertion point +between the two square brackets + +132 +00:09:32,005 --> 00:09:35,976 +and then I'll drag on the closing bracket +to create placeholders for a few items. + +133 +00:09:40,447 --> 00:09:44,551 +Next, I'll replace the placeholders with +a few strings that'll serve as our items. + +134 +00:09:52,626 --> 00:09:56,029 +Now that we've got a few items, +let's add in the TeaWheelView. + +135 +00:09:56,063 --> 00:09:57,865 +I'll select the Hello, world! example + +136 +00:09:57,898 --> 00:10:01,001 +and replace it with a TeaWheelView +that displays my items. + +137 +00:10:10,811 --> 00:10:12,813 +I'll also add in a little bit of padding. + +138 +00:10:16,049 --> 00:10:19,019 +Great! Now our view preview +is showing a wheel, + +139 +00:10:19,052 --> 00:10:20,888 +and what a beautiful wheel it is! + +140 +00:10:20,921 --> 00:10:26,527 +I can spin it, and it'll pick different +items based on where it lands. + +141 +00:10:26,560 --> 00:10:30,631 +Let's go back to the assistant tab +and add this wheel in. + +142 +00:10:30,664 --> 00:10:34,768 +I'll use the sidebar to open +the AssistantTab Swift file, + +143 +00:10:34,801 --> 00:10:38,238 +and I'm going to delete the Button and +replace it with a TeaWheelView instead. + +144 +00:10:48,749 --> 00:10:51,451 +TeaWheelView +optionally takes an action closure + +145 +00:10:51,485 --> 00:10:53,720 +which is called +when the wheel stops spinning. + +146 +00:10:55,622 --> 00:11:00,494 +I'll use it to set last picked tea +to the selected tea, + +147 +00:11:00,527 --> 00:11:05,399 +and I'll set show pick alert to true +so SwiftUI knows to show the alert. + +148 +00:11:08,869 --> 00:11:12,339 +Okay, great! We've got our wheel in here, +so now let's try it! + +149 +00:11:12,372 --> 00:11:17,411 +I'll swipe to spin it... +and it told me to drink Byte's Oolong. + +150 +00:11:17,444 --> 00:11:19,546 +I'll swipe it again... + +151 +00:11:22,015 --> 00:11:24,251 +And it's still Byte's Oolong. + +152 +00:11:24,284 --> 00:11:25,919 +One more time. + +153 +00:11:28,856 --> 00:11:31,391 +Hmm. Something seems wrong. + +154 +00:11:31,425 --> 00:11:33,627 +Even though it's landing on +different spots on the wheel, + +155 +00:11:33,660 --> 00:11:36,830 +it's always telling me +to drink the Byte's Oolong. + +156 +00:11:36,864 --> 00:11:40,634 +While that is a good tea, +I'd like a little bit of variety. + +157 +00:11:40,667 --> 00:11:43,370 +Let's switch back to the wheel view +and try to figure out what's up. + +158 +00:11:45,405 --> 00:11:47,341 +It's not obvious from this +what's going wrong, + +159 +00:11:47,374 --> 00:11:50,410 +since the wheel does spin +and land on different spots. + +160 +00:11:50,444 --> 00:11:52,880 +Let's add a print statement +into our view preview + +161 +00:11:52,913 --> 00:11:55,048 +to check if the preview is broken too. + +162 +00:12:02,656 --> 00:12:04,458 +Now when I spin the wheel... + +163 +00:12:05,993 --> 00:12:09,463 +A console message pops up +at the bottom left of the source editor. + +164 +00:12:09,496 --> 00:12:11,765 +Item one... + +165 +00:12:11,798 --> 00:12:15,035 +item one…item one. + +166 +00:12:15,068 --> 00:12:18,338 +Aha! Each spin gives us item one, + +167 +00:12:18,372 --> 00:12:21,175 +which suggests something +isn't quite hooked up right! + +168 +00:12:21,208 --> 00:12:23,877 +Since it's giving me +the first one every time, + +169 +00:12:23,911 --> 00:12:28,215 +I'm going to use project-wide find +to search for first. + +170 +00:12:28,248 --> 00:12:30,484 +I'll tap in the search field +at the top of the sidebar + +171 +00:12:30,517 --> 00:12:33,120 +on the left hand side of the screen + +172 +00:12:33,153 --> 00:12:36,557 +and then type "first" and press Return. + +173 +00:12:38,458 --> 00:12:41,428 +That result seems promising, +so I'll tap on it. + +174 +00:12:42,896 --> 00:12:45,933 +Ah, it looks like Collett left +some debugging code in here + +175 +00:12:45,966 --> 00:12:50,103 +that made it return the first item +every time instead of the right result. + +176 +00:12:50,137 --> 00:12:52,940 +Let's fix that up real quick +and then give it another spin. + +177 +00:13:00,047 --> 00:13:01,715 +Item two... + +178 +00:13:01,748 --> 00:13:03,217 +item four. + +179 +00:13:03,250 --> 00:13:05,652 +Great! It seems to be working now. + +180 +00:13:05,686 --> 00:13:07,788 +If we switch back to our app preview + +181 +00:13:07,821 --> 00:13:10,524 +by tapping on the left chevron +underneath the preview, + +182 +00:13:10,557 --> 00:13:13,260 +we can try it out in the real app. + +183 +00:13:13,293 --> 00:13:18,198 +I'll spin the wheel, and it's telling me +to have the English Breakfast. + +184 +00:13:18,232 --> 00:13:20,701 +So now we've got a working assistant. + +185 +00:13:20,734 --> 00:13:24,571 +Awesome! Just to confirm +the app works well at all sizes, + +186 +00:13:24,605 --> 00:13:28,008 +I can run it in its own window +by pressing the Run button + +187 +00:13:28,041 --> 00:13:29,943 +in the upper left of the screen. + +188 +00:13:33,413 --> 00:13:35,616 +Okay, seems like everything is here. + +189 +00:13:35,649 --> 00:13:38,685 +I've got my list of teas, +and I've got the assistant here + +190 +00:13:38,719 --> 00:13:41,255 +with the wheel and everything. + +191 +00:13:41,288 --> 00:13:43,524 +I can go back to the project +in Swift Playgrounds + +192 +00:13:43,557 --> 00:13:46,260 +by tapping on the little Swift icon +in the status bar, + +193 +00:13:46,293 --> 00:13:50,130 +and then by selecting the "Show Project" +button in the sheet that pops up. + +194 +00:13:52,299 --> 00:13:56,103 +I'm ready to test this app myself +and with my friends and family. + +195 +00:13:56,136 --> 00:13:58,405 +Swift Playgrounds makes it easy to test + +196 +00:13:58,438 --> 00:14:00,340 +because you can submit +directly to TestFlight + +197 +00:14:00,374 --> 00:14:02,009 +from within Swift Playgrounds! + +198 +00:14:02,042 --> 00:14:07,481 +If I bring up the app settings sheet +and scroll to the bottom, + +199 +00:14:07,514 --> 00:14:10,250 +there's an +"Upload to App Store Connect" button. + +200 +00:14:10,284 --> 00:14:14,121 +If I tap on that, Swift Playgrounds +does all of the hard work + +201 +00:14:14,154 --> 00:14:17,658 +of creating an app record and +uploading my app to App Store Connect + +202 +00:14:17,691 --> 00:14:21,495 +so I can distribute it on TestFlight +and eventually the App Store. + +203 +00:14:27,935 --> 00:14:31,405 +Now that my app is uploaded, +I can go to App Store Connect + +204 +00:14:31,438 --> 00:14:33,740 +and submit it for Beta App Review. + +205 +00:14:33,774 --> 00:14:36,844 +After waiting a bit, +we can go over to the TestFlight app + +206 +00:14:36,877 --> 00:14:39,980 +and install it from there, even on iPhone! + +207 +00:14:42,549 --> 00:14:45,118 +I'll tap "Install" to install Tea Time. + +208 +00:14:46,486 --> 00:14:50,290 +Now that it's installed, +I'll tap "Open" to open it. + +209 +00:14:50,324 --> 00:14:52,826 +I'll tap to the test notes, + +210 +00:14:52,860 --> 00:14:55,596 +as well as these instructions +on how to provide feedback. + +211 +00:14:57,030 --> 00:15:00,534 +And there you have it-- +our app is running on iPhone. + +212 +00:15:00,567 --> 00:15:02,603 +What tea should I have today? + +213 +00:15:04,771 --> 00:15:07,374 +Looks like I'm having +the Matt P's Tea Party. + +214 +00:15:07,407 --> 00:15:10,944 +Today, Collett and I showed you +how you can use Swift Playgrounds + +215 +00:15:10,978 --> 00:15:13,480 +on your Mac and iPad to build apps. + +216 +00:15:13,514 --> 00:15:17,217 +We demonstrated using the library +and code completion to insert new code, + +217 +00:15:17,251 --> 00:15:20,487 +we shared our project +via iCloud Shared Folders, + +218 +00:15:20,521 --> 00:15:24,758 +and we used view previews and the +console to debug an issue with our code. + +219 +00:15:24,791 --> 00:15:28,328 +We even submitted an app to TestFlight, +right from iPad! + +220 +00:15:28,362 --> 00:15:30,697 +Hopefully you've learned +a thing or two along the way, + +221 +00:15:30,731 --> 00:15:33,667 +and we can't wait to see what +you build with Swift Playgrounds. + +222 +00:15:33,700 --> 00:15:37,938 +Thank you for watching, +and enjoy the rest of WWDC! + diff --git a/eng/2022 Session 110349 Create engaging content for Swift Playgrounds en.srt b/eng/2022 Session 110349 Create engaging content for Swift Playgrounds en.srt new file mode 100644 index 0000000..ce4ebee --- /dev/null +++ b/eng/2022 Session 110349 Create engaging content for Swift Playgrounds en.srt @@ -0,0 +1,1795 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,877 --> 00:00:12,012 +Hi. +I'm Stephanie Angulo. + +3 +00:00:12,045 --> 00:00:13,814 +and I'm Marcus Jackson. + +4 +00:00:13,847 --> 00:00:16,550 +We're software engineers +on the Swift Playgrounds Content team. + +5 +00:00:16,583 --> 00:00:17,885 +Today, we will give you +the tools to create + +6 +00:00:17,918 --> 00:00:20,187 +engaging content for Swift Playgrounds. + +7 +00:00:21,088 --> 00:00:24,291 +Swift Playgrounds 4 introduced +app development on iPad and Mac. + +8 +00:00:24,324 --> 00:00:27,594 +It's the best way to learn +about building apps for the App Store. + +9 +00:00:27,628 --> 00:00:30,631 +Our team has released a number +of tutorial and sample code products + +10 +00:00:30,664 --> 00:00:33,267 +that help you learn the fundamentals +of app development. + +11 +00:00:33,300 --> 00:00:34,935 +We cover topics like + +12 +00:00:34,968 --> 00:00:38,705 +building dynamic SwiftUI apps +using observable data models, + +13 +00:00:38,739 --> 00:00:42,442 +customizing views with fun +SwiftUI animations and shapes, + +14 +00:00:42,476 --> 00:00:45,679 +and more advanced topics, +like asynchronous data fetching. + +15 +00:00:45,712 --> 00:00:48,115 +In today's session, +we'll provide an overview + +16 +00:00:48,148 --> 00:00:50,083 +of our new instructional system, + +17 +00:00:50,117 --> 00:00:52,819 +write content +using the project's guide module, + +18 +00:00:52,853 --> 00:00:57,791 +and build an immersive learning experience +with walkthrough and experiment tasks. + +19 +00:00:57,824 --> 00:00:59,159 +Let's get started. + +20 +00:00:59,193 --> 00:01:02,729 +Imagine a learner completing our tutorial, +"Keep Going with Apps." + +21 +00:01:02,763 --> 00:01:04,831 +They'll end up with an app called, +"Emoji App." + +22 +00:01:04,865 --> 00:01:09,703 +In this app, they can keep track +of all their favorite animals in a list, + +23 +00:01:09,736 --> 00:01:13,574 +change their color and size, + +24 +00:01:13,607 --> 00:01:15,742 +and tap on the animals +to watch them get their groove on + +25 +00:01:15,776 --> 00:01:17,411 +in "Creature Dance" view. + +26 +00:01:19,479 --> 00:01:21,348 +This dance view is fun, +but I want the vibe to be + +27 +00:01:21,381 --> 00:01:22,850 +more of an actual dance party. + +28 +00:01:22,883 --> 00:01:25,719 +So let's go ahead and add some extra +features we can show our learners. + +29 +00:01:27,221 --> 00:01:30,290 +Here, I added a bit more code +to "Creature Dance" view. + +30 +00:01:30,324 --> 00:01:32,593 +Every party needs a dance floor. + +31 +00:01:32,626 --> 00:01:35,863 +So I made a 10x10 grid +that I set as my view's background. + +32 +00:01:37,331 --> 00:01:39,566 +And each tile in the grid +updates its color randomly + +33 +00:01:39,600 --> 00:01:41,735 +using a custom view modifier. + +34 +00:01:42,636 --> 00:01:44,304 +Looks groovy huh? + +35 +00:01:46,640 --> 00:01:48,976 +I also wanted to make sure +our favorite animals can dance + +36 +00:01:49,009 --> 00:01:51,478 +without our help, +so I made a few more custom modifiers + +37 +00:01:51,512 --> 00:01:57,251 +that help animate the animals' scale, +position offset, and rotation. + +38 +00:01:58,352 --> 00:02:02,489 +In all of these custom modifiers, +these animations are set to repeatForever, + +39 +00:02:02,523 --> 00:02:06,193 +which means our animals can dance +all night on their brand-new dance floor. + +40 +00:02:07,394 --> 00:02:09,630 +And finally, +to really light up the dance floor, + +41 +00:02:09,663 --> 00:02:12,432 +I added an animated disco ball +at the top of our view. + +42 +00:02:13,634 --> 00:02:16,036 +Adding this final touch +really pulls it all together + +43 +00:02:16,069 --> 00:02:18,038 +for the ultimate dance party. + +44 +00:02:18,071 --> 00:02:19,873 +I've made quite a few changes +to this project, + +45 +00:02:19,907 --> 00:02:22,776 +and I didn't even to dive into the details +of custom view modifiers. + +46 +00:02:22,809 --> 00:02:25,379 +So how should we explain this +to our learners? + +47 +00:02:25,412 --> 00:02:27,748 +You could direct a learner +to Apple's documentation, + +48 +00:02:27,781 --> 00:02:30,284 +but now you also have +the option to teach these concepts + +49 +00:02:30,317 --> 00:02:33,220 +alongside your project's code +in Swift Playgrounds. + +50 +00:02:33,253 --> 00:02:35,189 +Our team has built +this new instructional system + +51 +00:02:35,222 --> 00:02:37,157 +that's designed to help authors like you + +52 +00:02:37,191 --> 00:02:40,794 +create engaging in-app experiences +for your learners. + +53 +00:02:40,827 --> 00:02:43,330 +Today, we'll walk you through +how to build the learning content + +54 +00:02:43,363 --> 00:02:45,332 +for this app in Swift Playgrounds 4. + +55 +00:02:45,365 --> 00:02:48,368 +Let me give you a sneak peek +of what you'll be walking away with. + +56 +00:02:48,402 --> 00:02:50,904 +When a learner first opens +your content on Swift Playgrounds, + +57 +00:02:50,938 --> 00:02:53,974 +you can introduce them to the project +with an optional welcome message, + +58 +00:02:54,007 --> 00:02:57,010 +as shown here from our friend Byte. + +59 +00:02:57,044 --> 00:02:59,680 +The welcome message sits at the top +of the project's source editor + +60 +00:02:59,713 --> 00:03:02,082 +on the left hand side of the screen, + +61 +00:03:02,115 --> 00:03:04,818 +while on the right hand side +of the screen is the learning center. + +62 +00:03:06,587 --> 00:03:09,389 +The learning center is a designated area +where you can add images + +63 +00:03:09,423 --> 00:03:12,492 +and instructional text that describes +your content to your learners. + +64 +00:03:13,927 --> 00:03:15,929 +In our welcome message +and our learning center, + +65 +00:03:15,963 --> 00:03:18,799 +we're letting our learners know that this +project will be pumping up the jams + +66 +00:03:18,832 --> 00:03:22,269 +with the help of SwiftUI colors, +shapes, and animations. + +67 +00:03:24,271 --> 00:03:26,907 +The learning center also contains +a section for tasks. + +68 +00:03:26,940 --> 00:03:31,144 +Tasks are coding objectives you the author +can write to help guide your learners. + +69 +00:03:31,178 --> 00:03:33,547 +They're a fundamental +building block for content. + +70 +00:03:34,781 --> 00:03:36,850 +By tapping a task button +in the learning center, + +71 +00:03:36,884 --> 00:03:39,052 +our instructional system will open up +a Swift file + +72 +00:03:39,086 --> 00:03:42,723 +and render a card with learning material +at the top of that file. + +73 +00:03:42,756 --> 00:03:48,829 +This card can contain a series of pages +with text, images, and code snippets. + +74 +00:03:48,862 --> 00:03:51,832 +Later, Marcus will go over +two tasks types: + +75 +00:03:51,865 --> 00:03:54,368 +walkthroughs and experiments. + +76 +00:03:55,502 --> 00:03:58,739 +At a high level, that's what +our instructional system has to offer. + +77 +00:03:58,772 --> 00:04:00,941 +With the right prose and the right tasks, + +78 +00:04:00,974 --> 00:04:05,012 +you can build a compelling +educational experience for your learners. + +79 +00:04:05,045 --> 00:04:07,214 +Now, in order to start +creating your own content, + +80 +00:04:07,247 --> 00:04:09,483 +we first need to talk +about the guide module. + +81 +00:04:09,516 --> 00:04:12,586 +By default, +the file structure of a swiftpm project + +82 +00:04:12,619 --> 00:04:14,488 +keeps all its source code at its root. + +83 +00:04:14,521 --> 00:04:15,989 +In order to upgrade your project + +84 +00:04:16,023 --> 00:04:17,891 +to take advantage +of the instructional system, + +85 +00:04:17,925 --> 00:04:20,194 +you'll need to change its file structure. + +86 +00:04:20,227 --> 00:04:22,563 +We first need to create an App module. + +87 +00:04:22,596 --> 00:04:25,265 +Once it's created, we need to move +all our project's source code + +88 +00:04:25,299 --> 00:04:26,934 +and assets into it. + +89 +00:04:26,967 --> 00:04:29,937 +The Package.swift file should be +left at the root of our project. + +90 +00:04:32,072 --> 00:04:34,341 +Then we need to create a guide module. + +91 +00:04:34,374 --> 00:04:39,079 +This module should be at the same level +as the App module and Package.swift file. + +92 +00:04:39,112 --> 00:04:41,982 +Inside the guide module, +you'll need a guide file. + +93 +00:04:42,015 --> 00:04:45,285 +This file will include all the prose +of your learning content. + +94 +00:04:45,319 --> 00:04:46,887 +I've already started on my guide file, + +95 +00:04:46,920 --> 00:04:49,256 +so let's check out the content +I've written so far. + +96 +00:04:51,592 --> 00:04:54,795 +The guide file contains a combination +of directives and markdown. + +97 +00:04:54,828 --> 00:04:57,698 +Directives are an extension of markdown +that can take in primitive types + +98 +00:04:57,731 --> 00:05:01,401 +as attributes, such as strings, +as well as more complex types, + +99 +00:05:01,435 --> 00:05:03,604 +like markdown elements +and other directives. + +100 +00:05:04,838 --> 00:05:07,007 +Directives can act as containers +for other directives, + +101 +00:05:07,040 --> 00:05:11,111 +but they can also represent UI elements +in our instructional system. + +102 +00:05:11,144 --> 00:05:14,248 +First in the guide file, I've added +the necessary guidebook directive + +103 +00:05:14,281 --> 00:05:16,550 +that's wrapped +around the entirety of the file. + +104 +00:05:16,583 --> 00:05:19,753 +It acts as the main container +for all our directives. + +105 +00:05:19,786 --> 00:05:23,223 +Its parameters include a title, +icon and background image, + +106 +00:05:23,257 --> 00:05:26,727 +and the file you first want opened +when you open the project. + +107 +00:05:26,760 --> 00:05:30,163 +Under the guidebook directive, +I've added a welcome message directive. + +108 +00:05:30,197 --> 00:05:32,666 +Welcome messages are optional +and as mentioned earlier, + +109 +00:05:32,699 --> 00:05:36,603 +they are presented to the learner +when they first open up the project. + +110 +00:05:36,637 --> 00:05:39,873 +Below the welcome message directive, +I've added a guide directive + +111 +00:05:39,907 --> 00:05:41,909 +that's wrapped around a step directive. + +112 +00:05:41,942 --> 00:05:44,378 +The guide directive acts as a container +for your steps + +113 +00:05:44,411 --> 00:05:45,812 +and steps map out to your content + +114 +00:05:45,846 --> 00:05:48,615 +displayed in the learning center +and tasks. + +115 +00:05:48,649 --> 00:05:51,685 +To start adding images and instructional +text in your learning center, + +116 +00:05:51,718 --> 00:05:54,888 +you'll need to include a ContentAndMedia +directive inside your step. + +117 +00:05:56,456 --> 00:05:58,725 +So I got this party started +by adding a dance floor, + +118 +00:05:58,759 --> 00:06:01,795 +a nice welcome message, +and the prose for the learning center. + +119 +00:06:01,828 --> 00:06:03,797 +Marcus, +do you wanna keep this party going? + +120 +00:06:03,830 --> 00:06:05,532 +Marcus: Definitely. + +121 +00:06:05,566 --> 00:06:08,202 +What a dope dance floor +for our creatures to party on. + +122 +00:06:08,235 --> 00:06:11,405 +While this effect is really cool, +I think it might be too much + +123 +00:06:11,438 --> 00:06:13,307 +for someone who is still learning. + +124 +00:06:13,340 --> 00:06:17,077 +To help explain this code, +we can use walkthrough tasks. + +125 +00:06:17,110 --> 00:06:19,479 +Let's start with a one-page walkthrough. + +126 +00:06:19,513 --> 00:06:22,583 +Later, I'll show you +how to fill out the rest. + +127 +00:06:22,616 --> 00:06:25,519 +Stephanie already showed you +the beginnings of our guidebook, + +128 +00:06:25,552 --> 00:06:27,721 +as well as a helpful welcome message. + +129 +00:06:27,754 --> 00:06:30,591 +We already have the first directive +you need to create tasks, + +130 +00:06:30,624 --> 00:06:32,226 +the step directive. + +131 +00:06:32,259 --> 00:06:35,562 +The step directive is where +our walkthrough content will live. + +132 +00:06:35,596 --> 00:06:39,166 +To make a step, you need to fill it +with two other directives. + +133 +00:06:39,199 --> 00:06:42,703 +Here, we've already added +a content and media directive. + +134 +00:06:42,736 --> 00:06:45,973 +This directive contains markdown +that goes into the learning center + +135 +00:06:46,006 --> 00:06:47,574 +on the right hand side. + +136 +00:06:47,608 --> 00:06:51,578 +The body of this directive can contain +any form of markdown text. + +137 +00:06:51,612 --> 00:06:54,481 +This is the place to put longer prose +and larger images + +138 +00:06:54,515 --> 00:06:56,617 +that might help cover your topic. + +139 +00:06:56,650 --> 00:07:00,654 +Here is the content and media directive +being displayed in Playgrounds. + +140 +00:07:00,687 --> 00:07:02,856 +While the area seems small +in this example, + +141 +00:07:02,890 --> 00:07:07,060 +this view can extend further down +and is contained in a scroll view. + +142 +00:07:07,094 --> 00:07:09,963 +This makes it a great place +to write longer bits of prose + +143 +00:07:09,997 --> 00:07:13,400 +and show complex content such as diagrams. + +144 +00:07:13,433 --> 00:07:15,836 +Once you have +your content and media written, + +145 +00:07:15,869 --> 00:07:19,273 +we can add in the second +required directive, tasks. + +146 +00:07:19,306 --> 00:07:23,277 +We add our tasks into another directive +called a task group. + +147 +00:07:23,310 --> 00:07:27,047 +Task groups are an optional directive +you can put inside of steps, + +148 +00:07:27,080 --> 00:07:29,850 +if you want to collect +a group of tasks together. + +149 +00:07:29,883 --> 00:07:33,086 +You might consider this if you have +content which covers the same topic + +150 +00:07:33,120 --> 00:07:36,957 +across multiple files +or different types of tasks. + +151 +00:07:36,990 --> 00:07:40,194 +Within the task group, +we can add a short bit of text. + +152 +00:07:40,227 --> 00:07:43,263 +This will be displayed +in the learning center as a subtitle. + +153 +00:07:44,731 --> 00:07:47,234 +Here is how a task group +displays in Playgrounds. + +154 +00:07:48,302 --> 00:07:51,338 +Now that I have +my task group and my subtitle, + +155 +00:07:51,371 --> 00:07:54,575 +I can start adding task directives. + +156 +00:07:54,608 --> 00:07:56,810 +Tasks have a few parameters + +157 +00:07:56,844 --> 00:07:58,579 +The first parameter is type. + +158 +00:07:58,612 --> 00:08:00,214 +This lets the instructional system know + +159 +00:08:00,247 --> 00:08:03,684 +what UI to generate +when displaying this task. + +160 +00:08:03,717 --> 00:08:06,453 +Next, every task needs an ID. + +161 +00:08:06,486 --> 00:08:08,956 +IDs are strings +that can be anything you want. + +162 +00:08:08,989 --> 00:08:12,593 +However, every ID in the guide +must be unique. + +163 +00:08:12,626 --> 00:08:15,329 +The title parameter is also a string. + +164 +00:08:15,362 --> 00:08:18,866 +This can also be whatever you want +and does not have to be unique. + +165 +00:08:18,899 --> 00:08:21,935 +This title will be rendered +by the task card UI. + +166 +00:08:21,969 --> 00:08:24,671 +Finally, the file parameter tells +the learning center + +167 +00:08:24,705 --> 00:08:29,142 +which file in the project to open +when the learner starts this task. + +168 +00:08:29,176 --> 00:08:32,145 +Here is how +a task displays on Playgrounds. + +169 +00:08:32,179 --> 00:08:34,314 +The title sits inside of a button, + +170 +00:08:34,348 --> 00:08:36,950 +and the file of the walkthrough +is listed above it. + +171 +00:08:36,984 --> 00:08:39,520 +Now we have our walkthrough task written. + +172 +00:08:39,553 --> 00:08:41,655 +Let's add our first page. + +173 +00:08:41,688 --> 00:08:44,124 +Page directives go +inside the body of a task + +174 +00:08:44,157 --> 00:08:46,994 +and have the following +mandatory parameters: + +175 +00:08:47,027 --> 00:08:50,330 +The ID parameter behaves +just like the ID for a task, + +176 +00:08:50,364 --> 00:08:53,667 +so they must be unique +for the entire guide file. + +177 +00:08:53,700 --> 00:08:56,637 +The title parameter behaves +a lot like the one for tasks. + +178 +00:08:56,670 --> 00:09:00,107 +However, when you leave +the title string empty on a page, + +179 +00:09:00,140 --> 00:09:02,075 +this lets the instructional system know + +180 +00:09:02,109 --> 00:09:05,479 +to use the task's title +when displaying this page. + +181 +00:09:05,512 --> 00:09:08,649 +Inside of a page, +you can add any markdown text, + +182 +00:09:08,682 --> 00:09:11,118 +similar to the content +and media directive. + +183 +00:09:11,151 --> 00:09:14,855 +However, the task view is +much smaller than the learning center. + +184 +00:09:14,888 --> 00:09:18,492 +Keep your text short and avoid +using complex images like diagrams + +185 +00:09:18,525 --> 00:09:21,328 +because they may be hard +for the learner to read. + +186 +00:09:21,361 --> 00:09:25,065 +This is the first page of our walkthrough +as rendered by Swift Playgrounds. + +187 +00:09:25,098 --> 00:09:27,434 +We're almost done +with our first walkthrough, + +188 +00:09:27,467 --> 00:09:29,970 +but first I need to show you +how to highlight the code + +189 +00:09:30,003 --> 00:09:33,040 +as shown in the last screenshot. + +190 +00:09:33,073 --> 00:09:36,743 +For that, we need to add some markers +to CreatureDance.swift. + +191 +00:09:36,777 --> 00:09:38,545 +When my walkthrough is shown, + +192 +00:09:38,579 --> 00:09:40,848 +I'd like to highlight +the first custom modifier, + +193 +00:09:40,881 --> 00:09:43,317 +the animatedScalingEffect. + +194 +00:09:43,350 --> 00:09:46,954 +To add highlights to a line, +I'll add a pair of comments on the lines + +195 +00:09:46,987 --> 00:09:49,389 +before and after the code. + +196 +00:09:49,423 --> 00:09:53,427 +We start with the multiline +comment syntax, /* + +197 +00:09:53,460 --> 00:09:57,231 +Inside the comment, +we write #-code + +198 +00:09:57,264 --> 00:10:01,635 +-walkthrough, +followed by a pair of parentheses. + +199 +00:10:01,668 --> 00:10:03,704 +Inside the parentheses, we write the ID + +200 +00:10:03,737 --> 00:10:05,906 +of the page directive +we want to highlight. + +201 +00:10:05,939 --> 00:10:08,575 +In this case, 1.modifier. + +202 +00:10:10,210 --> 00:10:12,679 +Now, let's test this out in Playgrounds. + +203 +00:10:12,713 --> 00:10:15,215 +Let's open the Emoji App project. + +204 +00:10:18,752 --> 00:10:21,221 +When you open the project, +you're greeted with the source editor + +205 +00:10:21,255 --> 00:10:23,957 +on the left and the preview on the right. + +206 +00:10:23,991 --> 00:10:26,560 +Above the source editor +is our welcome message, + +207 +00:10:26,593 --> 00:10:30,831 +where our buddy Byte gives you an overview +of what learning content there is to do. + +208 +00:10:30,864 --> 00:10:33,066 +I'll tap on the Learn More button. + +209 +00:10:36,370 --> 00:10:39,940 +The preview on the right +is swapped out for the learning center. + +210 +00:10:39,973 --> 00:10:43,744 +At the top is the prose we wrote +in the ContentAndMedia directive. + +211 +00:10:43,777 --> 00:10:45,946 +Below that is the task group, + +212 +00:10:45,979 --> 00:10:49,183 +as well as the button +with the title of our walkthrough. + +213 +00:10:49,216 --> 00:10:51,818 +Walkthroughs are denoted +in the learning center as buttons + +214 +00:10:51,852 --> 00:10:55,589 +with pictures of another one +of Byte's friends, Expert. + +215 +00:10:58,058 --> 00:11:00,460 +Tapping on this button does a few things: + +216 +00:11:00,494 --> 00:11:04,298 +First, the learning center is once again +swapped out for the preview. + +217 +00:11:04,331 --> 00:11:07,734 +Second, if it isn't already open, +the file specified + +218 +00:11:07,768 --> 00:11:11,104 +in the task's file parameter +is opened in the source editor. + +219 +00:11:11,138 --> 00:11:14,975 +Third, the task view drops down +above the source editor. + +220 +00:11:15,008 --> 00:11:17,878 +Finally, the source editor highlights +the code marked + +221 +00:11:17,911 --> 00:11:19,880 +in the code walkthrough comments. + +222 +00:11:19,913 --> 00:11:23,584 +If the content is not on screen, +the source editor will scroll + +223 +00:11:23,617 --> 00:11:27,020 +until the lines of code that need +to be highlighted appear. + +224 +00:11:27,054 --> 00:11:30,257 +And that is how you write +a walkthrough in Swift Playgrounds, + +225 +00:11:30,290 --> 00:11:32,359 +but I think it's fair to say +you're probably curious + +226 +00:11:32,392 --> 00:11:35,162 +what a walkthrough +with multiple pages looks like. + +227 +00:11:35,195 --> 00:11:39,833 +To do that, I'll open the project in Xcode +to fill out the rest of my walkthrough. + +228 +00:11:41,034 --> 00:11:44,638 +Now with the guide file open in Xcode, +I'd like to add a couple more pages + +229 +00:11:44,671 --> 00:11:46,139 +to my walkthrough. + +230 +00:11:46,173 --> 00:11:49,276 +I've explained a little bit +about what a view modifier is, + +231 +00:11:49,309 --> 00:11:53,146 +but I'd like to explain more +about how to build a custom view modifier. + +232 +00:11:53,180 --> 00:11:55,182 +I'll go ahead and add those pages. + +233 +00:11:59,686 --> 00:12:00,888 +Great. + +234 +00:12:00,921 --> 00:12:04,091 +Now we have our walkthrough +for custom view modifiers. + +235 +00:12:05,292 --> 00:12:08,762 +I think now is also a good time +to explain the ViewModifier protocol. + +236 +00:12:08,795 --> 00:12:13,433 +This way, learners can try to make +their own ViewModifiers if they want to. + +237 +00:12:13,467 --> 00:12:16,436 +To do this, I'll add another walkthrough +to our task group. + +238 +00:12:20,240 --> 00:12:23,210 +We now have +a fully fleshed out pair of walkthroughs. + +239 +00:12:23,243 --> 00:12:26,213 +I'll switch back over to my iPad +to see how it looks. + +240 +00:12:28,081 --> 00:12:29,583 +When we open our project, + +241 +00:12:29,616 --> 00:12:32,019 +there are now two walkthroughs +in the learning center. + +242 +00:12:32,052 --> 00:12:34,788 +I'll start by tapping +on the first walkthrough. + +243 +00:12:37,324 --> 00:12:40,694 +Just like before, +the line with the view modifier highlights + +244 +00:12:40,727 --> 00:12:44,498 +and our task view drops down +to explain what this code is. + +245 +00:12:44,531 --> 00:12:46,700 +Now I can tap the next button. + +246 +00:12:49,703 --> 00:12:52,506 +The source editor now scrolls down +to the modifier struct + +247 +00:12:52,539 --> 00:12:55,209 +and explains what this struct is for. + +248 +00:12:57,945 --> 00:13:02,416 +Tapping on the next button again moves +to the final page of this walkthrough, + +249 +00:13:02,449 --> 00:13:06,687 +which explains more about the body method +inside the modifier struct. + +250 +00:13:06,720 --> 00:13:11,158 +In the bottom corner of the task view, +there is a button marked Next Walkthrough. + +251 +00:13:13,994 --> 00:13:17,898 +Tapping on this automatically begins +the next walkthrough task. + +252 +00:13:17,931 --> 00:13:21,602 +This functionality is given to you +for free by the instructional system + +253 +00:13:21,635 --> 00:13:24,438 +as long as there is another task +to progress to. + +254 +00:13:24,471 --> 00:13:27,908 +Now that I am here, I will tap +through the rest of this walkthrough. + +255 +00:13:33,814 --> 00:13:36,884 +And that is how you build walkthroughs +in Swift Playgrounds. + +256 +00:13:36,917 --> 00:13:40,354 +Next, I'd like to show you how to create +a different kind of task + +257 +00:13:40,387 --> 00:13:44,424 +that will allow learners to try adding +code themselves and seeing what happens. + +258 +00:13:44,458 --> 00:13:47,561 +So at this point, +we have a good party going. + +259 +00:13:47,594 --> 00:13:51,098 +Our creatures are dancing, and they have +some lights in the background. + +260 +00:13:51,131 --> 00:13:53,200 +While it basically looks +like a nightclub in there, + +261 +00:13:53,233 --> 00:13:55,636 +I think we can do a little bit better. + +262 +00:13:55,669 --> 00:13:58,372 +I think it would be great +to add some colors to our creatures + +263 +00:13:58,405 --> 00:14:01,909 +so it looks like they're dancing under +the strobe lights of their little club. + +264 +00:14:01,942 --> 00:14:04,878 +But that's just me. What would you do? + +265 +00:14:04,912 --> 00:14:08,115 +This is where experiment tasks come in. + +266 +00:14:08,148 --> 00:14:12,152 +Experiments are optional bits of code +learners can add + +267 +00:14:12,186 --> 00:14:14,021 +if they are feeling extra curious + +268 +00:14:14,054 --> 00:14:17,558 +or if they want a way +of making the app unique to them. + +269 +00:14:17,591 --> 00:14:20,961 +Back in the guide file, +we can add our experiment task + +270 +00:14:20,994 --> 00:14:23,630 +to the same step +we were already working on. + +271 +00:14:23,664 --> 00:14:26,600 +I've created a new task group +to hold our experiments + +272 +00:14:26,633 --> 00:14:28,969 +which I've named "Experiments." + +273 +00:14:29,002 --> 00:14:31,839 +I've populated it with a subtitle +as well as the beginnings + +274 +00:14:31,872 --> 00:14:33,674 +of our first experiment task. + +275 +00:14:35,142 --> 00:14:38,545 +The first difference between +an experiment task and a walkthrough + +276 +00:14:38,579 --> 00:14:40,914 +is what goes into the type parameter. + +277 +00:14:40,948 --> 00:14:45,252 +The other parameters follow +a similar convention to walkthrough tasks. + +278 +00:14:45,285 --> 00:14:48,522 +Page directives work the same way +they do in walkthroughs. + +279 +00:14:48,555 --> 00:14:53,861 +However, for experiments, we add +one optional parameter, isAddable. + +280 +00:14:53,894 --> 00:14:57,297 +The isAddable parameter allows +experiment tasks to add code + +281 +00:14:57,331 --> 00:14:59,633 +directly into the source editor. + +282 +00:14:59,666 --> 00:15:02,536 +When isAddable is set to true, +an add button appears + +283 +00:15:02,569 --> 00:15:05,506 +in the learning task card +next to the code snippet. + +284 +00:15:05,539 --> 00:15:08,442 +The code in a page directive +must be wrapped in a code block + +285 +00:15:08,475 --> 00:15:11,111 +using the triple back tick +markdown syntax. + +286 +00:15:11,144 --> 00:15:15,148 +It's best practice to keep +your code blocks to ten lines or less. + +287 +00:15:15,182 --> 00:15:18,452 +While the task view can show +longer code snippets if need be, + +288 +00:15:18,485 --> 00:15:21,221 +it's better +if learners don't have to scroll. + +289 +00:15:21,255 --> 00:15:24,191 +Here is how the code view +displays in Playgrounds. + +290 +00:15:24,224 --> 00:15:26,560 +To the right of the code snippet, +is an add button, + +291 +00:15:26,593 --> 00:15:30,030 +since the isAddable parameter +was set to true. + +292 +00:15:30,063 --> 00:15:33,600 +That is almost everything we need +to write an experiment task. + +293 +00:15:33,634 --> 00:15:35,769 +But remember that isAddable parameter? + +294 +00:15:35,802 --> 00:15:39,806 +This allows the experiment task +to add code to the source editor, + +295 +00:15:39,840 --> 00:15:43,310 +but we need to tell Playgrounds +where in the code to add the snippet. + +296 +00:15:43,343 --> 00:15:47,114 +Here we are again in CreatureDance.swift. + +297 +00:15:47,147 --> 00:15:51,785 +I want learners to add the color modifier +right below the opacity modifier. + +298 +00:15:51,818 --> 00:15:55,455 +So that's where I'll add +my experiment task comment. + +299 +00:15:55,489 --> 00:16:00,561 +Experiment task comments are single line, +meaning they start with a double slash. + +300 +00:16:00,594 --> 00:16:06,033 +Then, we write #- +learning-task. + +301 +00:16:06,066 --> 00:16:08,468 +After, comes a pair of parentheses, + +302 +00:16:08,502 --> 00:16:12,239 +and inside we write the ID +of the experiment task. + +303 +00:16:12,272 --> 00:16:16,510 +Now we have everything we need +to test out our experiment task. + +304 +00:16:16,543 --> 00:16:20,547 +Once again, I have all this +already written in the swiftpm project + +305 +00:16:20,581 --> 00:16:22,382 +Stephanie and I are working on. + +306 +00:16:22,416 --> 00:16:24,084 +Let's check it out. + +307 +00:16:24,117 --> 00:16:26,787 +We're back once again +to the learning center. + +308 +00:16:26,820 --> 00:16:29,957 +This time I want to focus +on the bottom task group, + +309 +00:16:29,990 --> 00:16:31,992 +where our first experiment is. + +310 +00:16:32,025 --> 00:16:34,761 +Experiments are noted +in the instructional system + +311 +00:16:34,795 --> 00:16:37,397 +by another one of Byte's friends, Blu. + +312 +00:16:37,431 --> 00:16:39,433 +Let's tap on the experiment task. + +313 +00:16:41,401 --> 00:16:43,971 +What happens next should seem familiar. + +314 +00:16:44,004 --> 00:16:45,739 +The task view drops down. + +315 +00:16:45,772 --> 00:16:49,276 +However, this time, +the task view contains a code view. + +316 +00:16:49,309 --> 00:16:52,012 +On the right of the code view +is an add button. + +317 +00:16:52,045 --> 00:16:55,215 +Tapping on this adds the code +right to the source editor. + +318 +00:16:57,117 --> 00:16:59,253 +With the code now added, +I'd like to check out + +319 +00:16:59,286 --> 00:17:01,722 +what changes that made +to the CreatureDanceView. + +320 +00:17:01,755 --> 00:17:03,590 +Let's start this party! + +321 +00:17:06,126 --> 00:17:07,227 +Sweet. + +322 +00:17:07,261 --> 00:17:09,563 +Now we can see the lights +hitting our creatures. + +323 +00:17:09,596 --> 00:17:13,000 +This is pretty groovy, but I think +we can take it up one more notch + +324 +00:17:13,033 --> 00:17:17,271 +by using a timer to give the creatures +a random color every few seconds. + +325 +00:17:17,304 --> 00:17:20,174 +To do that, we'll need to add +another experiment, + +326 +00:17:20,207 --> 00:17:23,610 +so let's take this project back to Xcode +and add our new task. + +327 +00:17:23,644 --> 00:17:25,746 +Before we add in our second experiment, + +328 +00:17:25,779 --> 00:17:30,250 +I think it's a good idea to add a page +to the experiment that's already there. + +329 +00:17:30,284 --> 00:17:33,320 +For a learner, +it can be confusing to add a block of code + +330 +00:17:33,353 --> 00:17:35,956 +and not know why or what it does. + +331 +00:17:35,989 --> 00:17:40,594 +To help with that, I'll add a page +with some text before our code page. + +332 +00:17:45,766 --> 00:17:49,870 +Now, we are ready to add our second task. + +333 +00:17:49,903 --> 00:17:53,807 +Again, I want the learner +to add some code to their project, + +334 +00:17:53,841 --> 00:17:58,512 +so I'll add a page explaining the code +followed by an addable code snippet. + +335 +00:17:59,813 --> 00:18:03,450 +And with that, we have made +a new piece of content to teach learners + +336 +00:18:03,483 --> 00:18:07,487 +about some of the things you can do +with custom view modifiers. + +337 +00:18:07,521 --> 00:18:10,157 +Hey, Stephanie, you ready to show them +what we've built? + +338 +00:18:10,190 --> 00:18:11,525 +Stephanie: Yeah, let's do it. + +339 +00:18:13,026 --> 00:18:15,362 +I'll open the final version of our content +on my iPad + +340 +00:18:15,395 --> 00:18:18,198 +and check out how my changes +and Marcus' changes flow together. + +341 +00:18:18,232 --> 00:18:20,100 +When I first open the project, + +342 +00:18:20,133 --> 00:18:23,937 +the welcome message animates in, +introducing us to Creature Party. + +343 +00:18:23,971 --> 00:18:27,441 +When I tap on the Learn More button +in the welcome message, + +344 +00:18:27,474 --> 00:18:29,643 +it opens the learning center for me. + +345 +00:18:29,676 --> 00:18:30,511 +Awesome. + +346 +00:18:30,544 --> 00:18:32,546 +Our learning center does have +my description at the top + +347 +00:18:32,579 --> 00:18:34,481 +and the four tasks Marcus added. + +348 +00:18:34,515 --> 00:18:36,416 +Let's tap on the first walkthrough. + +349 +00:18:37,885 --> 00:18:40,320 +Here, Marcus used +my AnimatedScalingModifier + +350 +00:18:40,354 --> 00:18:43,590 +as an example to explain +how to use custom view modifiers. + +351 +00:18:45,926 --> 00:18:49,763 +When I tap on the Next Walkthrough Button, +the second walkthrough animates in. + +352 +00:18:51,198 --> 00:18:56,003 +Marcus used the View Modifier protocol as +an example to describe how protocols work. + +353 +00:18:56,036 --> 00:18:58,639 +After finishing the second walkthrough, +when I tap Done, + +354 +00:18:58,672 --> 00:19:00,908 +the first experiment task segues in. + +355 +00:19:03,076 --> 00:19:04,945 +The Dancing in the Strobe Light task +tells me + +356 +00:19:04,978 --> 00:19:07,648 +I can add some color to our creatures +by adding this code snippet + +357 +00:19:07,681 --> 00:19:10,050 +containing a colorMultiply modifier. + +358 +00:19:10,083 --> 00:19:12,486 +Let's remind ourselves +what this dance party looks like + +359 +00:19:12,519 --> 00:19:14,121 +before adding the code snippet. + +360 +00:19:16,356 --> 00:19:17,791 +All right, cool. + +361 +00:19:17,824 --> 00:19:20,027 +I'll add the code snippet by tapping Add + +362 +00:19:20,060 --> 00:19:22,529 +and tap Start the Party again +to check out the changes. + +363 +00:19:24,631 --> 00:19:26,934 +Nice, the creatures changed color. + +364 +00:19:26,967 --> 00:19:30,337 +I'll complete this experiment task now +and transition to the last one. + +365 +00:19:32,139 --> 00:19:34,942 +The Switch It Up experiment task +tells me I can customize the color + +366 +00:19:34,975 --> 00:19:37,377 +of the animals +with a tap gesture and a timer. + +367 +00:19:37,411 --> 00:19:40,414 +I'll add the code snippet +and start the party once more. + +368 +00:19:41,648 --> 00:19:45,118 +And now when I tap on the animals, +they change color. + +369 +00:19:45,152 --> 00:19:46,753 +Nice. + +370 +00:19:46,787 --> 00:19:49,957 +I'll complete this last task +and head back to the learning center. + +371 +00:19:52,593 --> 00:19:54,962 +Now, all tasks are marked as completed +in the learning center, + +372 +00:19:54,995 --> 00:19:56,763 +which means we've completed this sample. + +373 +00:19:58,832 --> 00:20:00,801 +And that's how you take advantage +of our new content features + +374 +00:20:00,834 --> 00:20:02,336 +in Swift Playgrounds 4. + +375 +00:20:02,369 --> 00:20:04,571 +We hope you enjoyed today's session +and we're so excited + +376 +00:20:04,605 --> 00:20:06,540 +to see what sorts +of learning experiences you'll build. + +377 +00:20:06,573 --> 00:20:09,009 +Don't forget to check out +the other Swift Playgrounds session, + +378 +00:20:09,042 --> 00:20:11,211 +Build your First App in Swift Playgrounds. + +379 +00:20:11,245 --> 00:20:12,713 +Enjoy the rest of WWDC. + +380 +00:20:12,746 --> 00:20:16,083 +Marcus: And now, if you'll excuse us, +we have a party to attend. + diff --git a/eng/2022 Session 110350 Visualize and optimize Swift concurrency en.srt b/eng/2022 Session 110350 Visualize and optimize Swift concurrency en.srt new file mode 100644 index 0000000..592ee20 --- /dev/null +++ b/eng/2022 Session 110350 Visualize and optimize Swift concurrency en.srt @@ -0,0 +1,1879 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,810 --> 00:00:12,779 +Welcome to Visualize and Optimize +Swift Concurrency. + +3 +00:00:12,813 --> 00:00:16,016 +My name is Mike, +and I work on the Swift runtime library. + +4 +00:00:16,049 --> 00:00:18,919 +Hi, I'm Harjas, +and I work on Instruments. + +5 +00:00:18,952 --> 00:00:21,421 +Together, we're going to +discuss ways to better understand + +6 +00:00:21,455 --> 00:00:24,124 +your Swift Concurrency code +and make it go faster, + +7 +00:00:24,157 --> 00:00:28,395 +including a new visualization tool +available in Instruments 14. + +8 +00:00:28,428 --> 00:00:32,299 +Let's start off with a really quick recap +of the various parts of Swift Concurrency + +9 +00:00:32,332 --> 00:00:35,969 +and how they work together, +to make sure you're up to speed. + +10 +00:00:36,003 --> 00:00:38,906 +After that, we'll demo +the new concurrency instrument. + +11 +00:00:38,939 --> 00:00:41,775 +We'll show you how we use it +to solve some real performance issues + +12 +00:00:41,808 --> 00:00:44,778 +with an app using Swift Concurrency. + +13 +00:00:44,811 --> 00:00:48,115 +Finally, we'll discuss the potential +problems of thread pool exhaustion + +14 +00:00:48,148 --> 00:00:51,552 +and continuation misuse +and how to avoid them. + +15 +00:00:51,585 --> 00:00:54,321 +Last year, +we introduced Swift Concurrency. + +16 +00:00:54,354 --> 00:00:58,258 +This was a new language feature +that includes async/await, + +17 +00:00:58,292 --> 00:01:01,195 +structured concurrency, and Actors. + +18 +00:01:01,228 --> 00:01:04,498 +We've been pleased to see a great deal +of adoption of these features since then, + +19 +00:01:04,531 --> 00:01:07,634 +both inside and outside Apple. + +20 +00:01:07,668 --> 00:01:10,103 +Swift concurrency adds +several new features to the language + +21 +00:01:10,137 --> 00:01:14,875 +which work together to make concurrent +programming easier and safer. + +22 +00:01:14,908 --> 00:01:19,012 +Async/await are basic syntactic +building blocks for concurrent code. + +23 +00:01:19,046 --> 00:01:22,449 +They allow you to create and call +functions that can suspend their work + +24 +00:01:22,482 --> 00:01:25,352 +in the middle of execution, +then resume that work later, + +25 +00:01:25,385 --> 00:01:27,988 +without blocking an execution thread. + +26 +00:01:29,256 --> 00:01:32,960 +Tasks are the basic unit of work +in concurrent code. + +27 +00:01:32,993 --> 00:01:37,531 +Tasks execute concurrent code +and manage its state and associated data. + +28 +00:01:37,564 --> 00:01:40,267 +They contain local variables, +handle cancellation, + +29 +00:01:40,300 --> 00:01:44,471 +and begin and suspend execution +of async code. + +30 +00:01:44,505 --> 00:01:47,307 +Structured concurrency makes it easy +to spawn child tasks + +31 +00:01:47,341 --> 00:01:50,310 +to run in parallel +and wait for them to complete. + +32 +00:01:50,344 --> 00:01:53,614 +The language provides syntax +which keeps the work grouped together + +33 +00:01:53,647 --> 00:01:59,019 +and ensures that tasks are awaited +or automatically canceled if not used. + +34 +00:01:59,052 --> 00:02:03,290 +Actors coordinate multiple tasks +that need to access shared data. + +35 +00:02:03,323 --> 00:02:05,492 +They isolate data from the outside, + +36 +00:02:05,526 --> 00:02:09,296 +and allow only one task at a time +to manipulate their internal state, + +37 +00:02:09,329 --> 00:02:12,733 +avoiding data races +from concurrent mutation. + +38 +00:02:12,766 --> 00:02:16,003 +New in Instruments 14, +we're introducing a set of instruments + +39 +00:02:16,036 --> 00:02:19,506 +that can capture and visualize +all of this activity in your app, + +40 +00:02:19,540 --> 00:02:21,842 +helping you to understand +what your app is doing, + +41 +00:02:21,875 --> 00:02:24,645 +locate problems, and improve performance. + +42 +00:02:24,678 --> 00:02:28,515 +For a more in-depth discussion +of the fundamentals of Swift Concurrency, + +43 +00:02:28,549 --> 00:02:30,417 +we have several videos +about these features + +44 +00:02:30,450 --> 00:02:32,819 +linked in the Related Videos section. + +45 +00:02:34,288 --> 00:02:38,559 +Let's take a look at optimizing an app +using Swift Concurrency code. + +46 +00:02:38,592 --> 00:02:41,628 +Swift concurrency makes it easy to +to write correct concurrent + +47 +00:02:41,662 --> 00:02:43,330 +and parallel code. + +48 +00:02:43,363 --> 00:02:48,235 +However, it's still possible to write code +that misuses concurrency constructs. + +49 +00:02:48,268 --> 00:02:50,237 +It's also possible to use them correctly + +50 +00:02:50,270 --> 00:02:52,606 +but in a way that doesn't get the +performance benefits you were aiming for. + +51 +00:02:54,875 --> 00:02:57,778 +There are a few common problems +that can arise when writing code + +52 +00:02:57,811 --> 00:03:02,349 +using Swift concurrency that can cause +poor performance or bugs. + +53 +00:03:02,382 --> 00:03:06,353 +Main Actor blocking +can cause your app to hang. + +54 +00:03:06,386 --> 00:03:10,324 +Actor contention and thread pool +exhaustion hurt performance + +55 +00:03:10,357 --> 00:03:13,594 +by reducing parallel execution. + +56 +00:03:13,627 --> 00:03:17,164 +Continuation misuse +causes leaks or crashes. + +57 +00:03:17,197 --> 00:03:22,135 +The new Swift Concurrency instrument can +help you discover and fix these problems. + +58 +00:03:22,169 --> 00:03:26,073 +Let's take a look at each of these, +starting with main Actor blocking. + +59 +00:03:26,106 --> 00:03:28,876 +Main Actor blocking occurs +when a long-running task + +60 +00:03:28,909 --> 00:03:31,378 +runs on the main Actor. + +61 +00:03:31,411 --> 00:03:34,581 +The main Actor is a special Actor +which executes all of its work + +62 +00:03:34,615 --> 00:03:36,483 +on the main thread. + +63 +00:03:36,517 --> 00:03:39,052 +UI work must be done on the main thread, + +64 +00:03:39,086 --> 00:03:43,357 +and the main Actor allows you to integrate +UI code into Swift Concurrency. + +65 +00:03:43,390 --> 00:03:46,293 +However, because the main thread +is so important for UI, + +66 +00:03:46,326 --> 00:03:50,964 +it needs to be available and can't be +occupied by a long-running unit of work. + +67 +00:03:50,998 --> 00:03:55,903 +When this happens, your app appears +to lock up and becomes unresponsive. + +68 +00:03:55,936 --> 00:03:58,772 +Code running on the main Actor +must finish quickly, + +69 +00:03:58,805 --> 00:04:01,208 +and either complete its work +or move computation + +70 +00:04:01,241 --> 00:04:04,011 +off of the main Actor +and into the background. + +71 +00:04:04,044 --> 00:04:07,447 +Work can be moved into the background +by putting it in a normal Actor + +72 +00:04:07,481 --> 00:04:09,550 +or in a detached task. + +73 +00:04:09,583 --> 00:04:13,353 +Small units of work can be executed +on the main Actor to update UI + +74 +00:04:13,387 --> 00:04:16,723 +or perform other tasks +that must be done on the main thread. + +75 +00:04:16,757 --> 00:04:18,825 +Let's see a demo of this in action. + +76 +00:04:18,859 --> 00:04:19,993 +Thanks, Mike. + +77 +00:04:20,027 --> 00:04:22,496 +Here we have +our File Squeezer application. + +78 +00:04:22,529 --> 00:04:25,499 +We built this application +to be able to quickly compress + +79 +00:04:25,532 --> 00:04:27,434 +all the files in a folder. + +80 +00:04:27,467 --> 00:04:30,304 +It seems to work alright for small files. + +81 +00:04:30,337 --> 00:04:34,675 +However, when I use larger files, +it takes much longer than expected + +82 +00:04:34,708 --> 00:04:39,379 +and the UI is completely frozen +and does not respond to any interactions. + +83 +00:04:39,413 --> 00:04:42,349 +This behavior is very off-putting to users + +84 +00:04:42,382 --> 00:04:45,252 +and may make them think +that the application has crashed + +85 +00:04:45,285 --> 00:04:46,587 +or will never finish. + +86 +00:04:46,620 --> 00:04:50,057 +We should strive to ensure +that our UI is always responsive + +87 +00:04:50,090 --> 00:04:51,892 +for the best user experience. + +88 +00:04:51,925 --> 00:04:54,328 +To investigate this performance problem, + +89 +00:04:54,361 --> 00:04:58,131 +we can use the new Swift Concurrency +template in Instruments. + +90 +00:04:58,165 --> 00:05:02,369 +The Swift Tasks and Swift Actors +instruments provide a full suite of tools + +91 +00:05:02,402 --> 00:05:06,139 +to help you visualize +and optimize your concurrency code. + +92 +00:05:06,173 --> 00:05:08,909 +When you're just starting +to investigate a performance problem + +93 +00:05:08,942 --> 00:05:12,379 +you should first take a look +at the top-level statistics + +94 +00:05:12,412 --> 00:05:15,215 +provided to you +by the Swift Tasks instrument. + +95 +00:05:15,249 --> 00:05:18,085 +The first of these is Running Tasks, + +96 +00:05:18,118 --> 00:05:22,055 +which show you how many tasks +are executing simultaneously. + +97 +00:05:22,089 --> 00:05:24,191 +Next, we have Alive Tasks, + +98 +00:05:24,224 --> 00:05:28,529 +which show how many tasks are present +at a given point in time. + +99 +00:05:28,562 --> 00:05:30,998 +And finally, Total Tasks; + +100 +00:05:31,031 --> 00:05:33,834 +graph the total number of tasks +that have been created + +101 +00:05:33,867 --> 00:05:36,270 +up until that point in time. + +102 +00:05:36,303 --> 00:05:39,540 +When you're attempting to reduce +your application memory footprint, + +103 +00:05:39,573 --> 00:05:44,178 +you should take a close look at +the Alive and Total Tasks statistics. + +104 +00:05:44,211 --> 00:05:47,047 +The combination of all of these statistics + +105 +00:05:47,080 --> 00:05:50,617 +give you a good picture of how well your +code is parallelizing + +106 +00:05:50,651 --> 00:05:53,854 +and how many resources you are consuming. + +107 +00:05:53,887 --> 00:05:57,991 +One of the many detail views +for this instrument is the Task Forest; + +108 +00:05:58,025 --> 00:06:02,729 +shown in the bottom half of this window, +it provides a graphical representation + +109 +00:06:02,763 --> 00:06:05,599 +of the parent-child relationships + +110 +00:06:05,632 --> 00:06:09,269 +between Tasks +in structured concurrency code. + +111 +00:06:09,303 --> 00:06:12,005 +Next, we have our Task Summary view. + +112 +00:06:12,039 --> 00:06:17,277 +This shows how much time +each Task spends in different states. + +113 +00:06:17,311 --> 00:06:21,348 +We have super-charged the view by allowing +you to right-click on a Task + +114 +00:06:21,381 --> 00:06:25,152 +to be able to pin a Track +containing all the information + +115 +00:06:25,185 --> 00:06:27,588 +about the selected Task to the timeline. + +116 +00:06:27,621 --> 00:06:31,992 +This allows you to quickly find and learn +about Tasks of interest + +117 +00:06:32,025 --> 00:06:34,494 +that may be running for a very long time + +118 +00:06:34,528 --> 00:06:38,966 +or stuck waiting to get access +to an Actor. + +119 +00:06:38,999 --> 00:06:43,237 +Once you pin a Swift Task to the timeline, +you get four key features. + +120 +00:06:43,270 --> 00:06:48,275 +First, is the track that shows you +what state your Swift Task is in. + +121 +00:06:48,308 --> 00:06:53,113 +Second, is the Task creation backtrace +in the extended detail view. + +122 +00:06:53,146 --> 00:06:56,583 +Third, is the narrative view +that provides more context + +123 +00:06:56,617 --> 00:06:58,385 +about the state a Swift Task is in. + +124 +00:06:58,418 --> 00:07:00,988 +Such as, if it's waiting on a Task, + +125 +00:07:01,021 --> 00:07:04,324 +it will inform you +which Task you are waiting on. + +126 +00:07:04,358 --> 00:07:08,428 +Lastly, you have access to +the same pin action in the narrative view + +127 +00:07:08,462 --> 00:07:10,364 +as you did in the summary view. + +128 +00:07:10,397 --> 00:07:13,467 +So, you can pin a child Task, a thread, + +129 +00:07:13,500 --> 00:07:16,370 +or even a Swift Actor to the timeline. + +130 +00:07:16,403 --> 00:07:21,808 +This narrative view will be instrumental +in finding how a Swift Task is related + +131 +00:07:21,842 --> 00:07:25,979 +to your other concurrency primitives +and the CPU. + +132 +00:07:26,013 --> 00:07:28,348 +Now that we have seen a brief overview + +133 +00:07:28,382 --> 00:07:30,717 +of some of the features +in the new instrument, + +134 +00:07:30,751 --> 00:07:33,787 +let's profile our application +and optimize our code. + +135 +00:07:33,820 --> 00:07:38,025 +We can do this by pulling up our project +in Xcode and pressing Command-I. + +136 +00:07:38,058 --> 00:07:40,894 +This will compile our application, +open up instruments, + +137 +00:07:40,928 --> 00:07:44,364 +and pre-select the target +to the File Squeezer application. + +138 +00:07:44,398 --> 00:07:48,068 +From here you can pick the Swift +Concurrency option in the template picker + +139 +00:07:48,101 --> 00:07:49,603 +and start recording. + +140 +00:07:52,005 --> 00:07:54,942 +Once again, +I'll drop the large files onto the app. + +141 +00:07:56,844 --> 00:08:01,415 +Again, we see that the app starts spinning +and the UI is not responsive. + +142 +00:08:01,448 --> 00:08:03,650 +We will let this run +for a few more seconds + +143 +00:08:03,684 --> 00:08:07,287 +so that Instruments can capture +all the information about our application. + +144 +00:08:08,922 --> 00:08:11,925 +Now that we have a trace, +we can start investigating. + +145 +00:08:11,959 --> 00:08:15,095 +I'm going to fullscreen this trace +to better see all the information. + +146 +00:08:17,798 --> 00:08:21,935 +We can use option-drag +to zoom in on our area of interest. + +147 +00:08:24,304 --> 00:08:26,573 +In the process track, + +148 +00:08:26,607 --> 00:08:31,111 +Instruments shows us exactly +where this UI hang was occurring. + +149 +00:08:31,144 --> 00:08:34,581 +This can be useful for cases +when it is not clear + +150 +00:08:34,615 --> 00:08:37,451 +when the hang occurred +or how long it lasted. + +151 +00:08:37,484 --> 00:08:39,820 +As I mentioned earlier, +a good place to start + +152 +00:08:39,853 --> 00:08:42,589 +is the top-level Swift Task statistics. + +153 +00:08:42,623 --> 00:08:46,593 +What catches my eye right away +is the Running Tasks count. + +154 +00:08:46,627 --> 00:08:49,696 +For most of the time, +only one Task is running. + +155 +00:08:49,730 --> 00:08:51,832 +This tells us part of the problem is that + +156 +00:08:51,865 --> 00:08:55,002 +all of our work is being forced +to serialize. + +157 +00:08:55,035 --> 00:08:59,039 +We can use the Task State summary +to find our longest running Task + +158 +00:08:59,072 --> 00:09:02,009 +and use the pin action +to pin it to the timeline. + +159 +00:09:03,544 --> 00:09:08,115 +The narrative view for this Task tells us +that it ran on a background thread + +160 +00:09:08,148 --> 00:09:09,917 +for a short amount of time, + +161 +00:09:09,950 --> 00:09:14,321 +and then ran on the Main Thread +for a long time. + +162 +00:09:14,354 --> 00:09:18,358 +To further investigate, we can pin +the Main Thread to the timeline. + +163 +00:09:23,864 --> 00:09:27,668 +The Main Thread is being blocked +by several long running Tasks. + +164 +00:09:27,701 --> 00:09:31,872 +This demonstrates the issue of +Main Actor blocking that Mike spoke about. + +165 +00:09:31,905 --> 00:09:34,842 +So the questions +we have to ask ourselves are, + +166 +00:09:34,875 --> 00:09:38,111 +"What is this Task doing?" +and "Where did this Task come from?" + +167 +00:09:38,145 --> 00:09:43,217 +We can switch back to the narrative view +to answer both of these questions. + +168 +00:09:43,250 --> 00:09:45,853 +The creation backtrace in +the extended detail view + +169 +00:09:45,886 --> 00:09:50,457 +shows that the task was created +in the compressAllFiles function. + +170 +00:09:50,490 --> 00:09:55,095 +The narrative shows that +the Task is executing closure number one + +171 +00:09:55,128 --> 00:09:56,964 +in compressAllFiles. + +172 +00:09:56,997 --> 00:10:01,268 +By right-clicking on this symbol, +we can open this in the source viewer. + +173 +00:10:03,837 --> 00:10:08,041 +Closure number one inside this function +is calling our compression work. + +174 +00:10:08,075 --> 00:10:12,112 +Now that we know where this Task +was created and what it is doing, + +175 +00:10:12,145 --> 00:10:14,581 +we can open our code in Xcode and adapt it + +176 +00:10:14,615 --> 00:10:16,917 +so that we don't run +these heavy computations + +177 +00:10:16,950 --> 00:10:18,185 +on the Main Thread. + +178 +00:10:18,218 --> 00:10:24,024 +The compress file function is located +within the CompressionState class. + +179 +00:10:24,057 --> 00:10:29,129 +The entire CompressionState class +is annotated to run on the @MainActor. + +180 +00:10:29,162 --> 00:10:32,432 +This explains why the Task also ran +on the Main Thread. + +181 +00:10:32,466 --> 00:10:35,903 +We need this entire class to be on the MainActor + +182 +00:10:35,936 --> 00:10:38,505 +because the @Published property here + +183 +00:10:38,539 --> 00:10:40,908 +must only be updated from the Main Thread, + +184 +00:10:40,941 --> 00:10:44,378 +otherwise, +we could run into runtime issues. + +185 +00:10:44,411 --> 00:10:49,917 +So, instead we could try to convert +this class into its own Actor. + +186 +00:10:49,950 --> 00:10:53,187 +However, the compiler will tell us +that we can't do this + +187 +00:10:53,220 --> 00:10:57,891 +because essentially we would be saying +that this shared mutable state + +188 +00:10:57,925 --> 00:11:01,361 +needs to be protected +by two different Actors. + +189 +00:11:01,395 --> 00:11:04,898 +But that does give us a hint +for what the real solution is. + +190 +00:11:04,932 --> 00:11:09,136 +We have two different pieces +of mutable state here within this class. + +191 +00:11:09,169 --> 00:11:11,471 +One piece of state, +the 'files' property, + +192 +00:11:11,505 --> 00:11:16,677 +needs to be isolated to the MainActor +because it is observed by SwiftUI. + +193 +00:11:16,710 --> 00:11:19,413 +But access to the other piece of state, +the logs, + +194 +00:11:19,446 --> 00:11:22,482 +needs to be protected +from concurrent access, + +195 +00:11:22,516 --> 00:11:27,054 +but which thread accesses logs +at any given point doesn't matter. + +196 +00:11:27,087 --> 00:11:32,092 +Thus, it doesn't actually need +to be on the Main Actor. + +197 +00:11:32,125 --> 00:11:35,229 +We still want to protect it +from concurrent access, though, + +198 +00:11:35,262 --> 00:11:38,799 +so we wrap it in its own Actor. + +199 +00:11:38,832 --> 00:11:45,672 +All we need now is add a way for Tasks +to hop between the two as needed. + +200 +00:11:45,706 --> 00:11:50,644 +We can create a new Actor +and call it ParallelCompressor. + +201 +00:11:53,480 --> 00:11:59,419 +We can then copy the log state into the +new Actor, + +202 +00:11:59,453 --> 00:12:02,122 +and add some extra setup code. + +203 +00:12:04,324 --> 00:12:08,428 +From here, we need to make these Actors +communicate with each other. + +204 +00:12:08,462 --> 00:12:12,266 +First, let's remove the code +that referred to the logs variable + +205 +00:12:12,299 --> 00:12:18,472 +from the CompressionState class, + +206 +00:12:18,505 --> 00:12:22,543 +and add it +to our ParallelCompressor Actor. + +207 +00:12:27,681 --> 00:12:30,551 +Then finally, +we need to update CompressionState + +208 +00:12:30,584 --> 00:12:35,255 +to invoke compressFile +on the ParallelCompressor. + +209 +00:12:43,297 --> 00:12:48,702 +With these changes, +let's test our application again. + +210 +00:12:48,735 --> 00:12:52,773 +Once again, I'll drop the large files +onto our application. + +211 +00:12:55,375 --> 00:12:58,912 +The UI is no longer hung, +which is a great improvement, + +212 +00:12:58,946 --> 00:13:01,915 +but we aren't getting the speed +that we would expect. + +213 +00:13:01,949 --> 00:13:06,119 +We really want to take full advantage +of all the cores in the machine + +214 +00:13:06,153 --> 00:13:08,522 +to do this work as fast as possible. + +215 +00:13:08,555 --> 00:13:11,291 +Mike, what else +should we be watching out for? + +216 +00:13:12,926 --> 00:13:16,129 +Mike: We've solved our hang +by moving work off of the Main Actor, + +217 +00:13:16,163 --> 00:13:19,233 +but we're still not getting +the performance we want. + +218 +00:13:19,266 --> 00:13:22,402 +To see why, +we need to take a closer look at Actors. + +219 +00:13:22,436 --> 00:13:26,740 +Actors make it safe for multiple tasks +to manipulate shared state. + +220 +00:13:26,773 --> 00:13:31,078 +However, they do this by serializing +access to that shared state. + +221 +00:13:31,111 --> 00:13:34,781 +Only one task at a time +is allowed to occupy the Actor, + +222 +00:13:34,815 --> 00:13:38,519 +and other tasks +that need to use that Actor will wait. + +223 +00:13:38,552 --> 00:13:42,890 +Swift concurrency allows for parallel +computation using unstructured tasks, + +224 +00:13:42,923 --> 00:13:45,425 +task groups, and async let. + +225 +00:13:45,459 --> 00:13:49,563 +Ideally, these constructs are able +to use many CPU cores simultaneously. + +226 +00:13:49,596 --> 00:13:53,767 +When using Actors from such code, +beware of performing large amounts of work + +227 +00:13:53,800 --> 00:13:57,004 +on an Actor +that's shared among these tasks. + +228 +00:13:57,037 --> 00:14:00,474 +When multiple tasks attempt +to use the same Actor simultaneously, + +229 +00:14:00,507 --> 00:14:04,178 +the Actor serializes +execution of those tasks. + +230 +00:14:04,211 --> 00:14:08,448 +Because of this, we lose the performance +benefits of parallel computation. + +231 +00:14:11,151 --> 00:14:16,023 +This is because each task must wait +for the Actor to become available. + +232 +00:14:16,056 --> 00:14:19,693 +To fix this, we need make sure +that tasks only run on the Actor + +233 +00:14:19,726 --> 00:14:23,130 +when they really need exclusive access +to the Actor's data. + +234 +00:14:23,163 --> 00:14:25,632 +Everything else should run +off of the Actor. + +235 +00:14:25,666 --> 00:14:28,735 +We divide the task into chunks. + +236 +00:14:28,769 --> 00:14:32,239 +Some chunks must run on the Actor, +and the others don't. + +237 +00:14:32,272 --> 00:14:36,076 +The non-Actor isolated chunks +can be executed in parallel, + +238 +00:14:36,109 --> 00:14:39,446 +which means the computer +can finish the work much faster. + +239 +00:14:39,479 --> 00:14:41,248 +Let's see a demo of this in action. + +240 +00:14:41,281 --> 00:14:43,183 +Harjas: Thanks, Mike. + +241 +00:14:43,217 --> 00:14:47,154 +Let's take a look at the trace +of our updated "File Squeezer" application + +242 +00:14:47,187 --> 00:14:49,790 +and keep in mind +what Mike has just taught us. + +243 +00:14:49,823 --> 00:14:53,827 +The Task Summary view shows us +that our concurrency code + +244 +00:14:53,861 --> 00:14:57,197 +is spending an alarming amount of time +in the Enqueued state. + +245 +00:14:57,231 --> 00:15:02,336 +This means we have a lot of Tasks waiting +to get exclusive access to an Actor. + +246 +00:15:02,369 --> 00:15:05,072 +Let's pin one of these Tasks to learn why. + +247 +00:15:07,841 --> 00:15:11,278 +This Task spends quite a while +waiting to get onto + +248 +00:15:11,311 --> 00:15:16,016 +the ParallelCompressor Actor +before it runs the compression work. + +249 +00:15:16,049 --> 00:15:18,819 +Let's go ahead +and pin the Actor to our timeline. + +250 +00:15:23,090 --> 00:15:27,995 +Here we have some top-level data +for the ParallelCompressor Actor. + +251 +00:15:28,028 --> 00:15:33,267 +This Actor Queue seems to be getting +blocked by some long running Tasks. + +252 +00:15:33,300 --> 00:15:37,271 +Tasks should really only stay on an Actor +for as long as needed. + +253 +00:15:37,304 --> 00:15:39,673 +Let's go back to the Task narrative. + +254 +00:15:42,543 --> 00:15:45,345 +After the enqueue on ParallelCompressor, + +255 +00:15:45,379 --> 00:15:50,184 +the Task runs in closure number one +in compressAllFiles. + +256 +00:15:50,217 --> 00:15:52,653 +So let's start our investigation there. + +257 +00:15:52,686 --> 00:15:59,226 +The source code shows us that this closure +is primarily running our compression work. + +258 +00:15:59,259 --> 00:16:04,831 +Since the compressFile function +is part of the ParallelCompressor Actor, + +259 +00:16:04,865 --> 00:16:09,369 +the entire execution of this function +happens on the Actor; + +260 +00:16:09,403 --> 00:16:11,872 +blocking all other compression work. + +261 +00:16:11,905 --> 00:16:15,742 +To resolve this issue, +we need to pull the compressFile function + +262 +00:16:15,776 --> 00:16:20,047 +out of Actor-isolation +and into a detached task. + +263 +00:16:22,583 --> 00:16:27,054 +By doing this, we can have +the detached task only on an Actor + +264 +00:16:27,087 --> 00:16:31,859 +for as long as needed +to update the relevant mutable state. + +265 +00:16:31,892 --> 00:16:35,929 +So now the compress function +can be executed freely, + +266 +00:16:35,963 --> 00:16:38,031 +on any thread +in the thread pool, + +267 +00:16:38,065 --> 00:16:42,135 +until it needs to access +Actor-protected state. + +268 +00:16:42,169 --> 00:16:45,739 +For example, when it needs +to access the 'files' property, + +269 +00:16:45,772 --> 00:16:48,742 +it'll move onto the Main Actor. + +270 +00:16:48,775 --> 00:16:53,847 +But as soon as it's done there, it moves +into the "sea of concurrency" again, + +271 +00:16:53,881 --> 00:16:57,784 +until it needs to access +the logs property, + +272 +00:16:57,818 --> 00:17:01,488 +for which it moves on +to the ParallelCompressor Actor. + +273 +00:17:01,522 --> 00:17:03,590 +But again, as soon as it's done there, + +274 +00:17:03,624 --> 00:17:08,495 +it leaves the Actor again +to be executed on the thread pool. + +275 +00:17:08,529 --> 00:17:12,466 +But of course, we don't have just one task +doing compression work; + +276 +00:17:12,499 --> 00:17:14,034 +we have many. + +277 +00:17:14,067 --> 00:17:19,473 +And by not being constrained to an Actor, +they can all be executed concurrently, + +278 +00:17:19,506 --> 00:17:22,276 +only limited by the number of threads. + +279 +00:17:23,610 --> 00:17:28,982 +Of course, each Actor +can only execute one task at a time, + +280 +00:17:29,016 --> 00:17:33,620 +but most of the time, +our Tasks don't need to be on an Actor. + +281 +00:17:33,654 --> 00:17:37,724 +So like Mike explained, +this allows our compression tasks + +282 +00:17:37,758 --> 00:17:43,463 +to executed in parallel +and utilize all available CPU cores. + +283 +00:17:43,497 --> 00:17:45,532 +So let's make this change now. + +284 +00:17:47,134 --> 00:17:50,971 +We can mark the compressFile function +as nonisolated. + +285 +00:17:53,006 --> 00:17:56,176 +This does result in a few compiler errors. + +286 +00:17:56,210 --> 00:18:00,314 +By marking it as nonisolated, +we told the Swift compiler + +287 +00:18:00,347 --> 00:18:04,251 +that we don't need access +to the shared state of this Actor. + +288 +00:18:04,284 --> 00:18:06,720 +But that isn't entirely true. + +289 +00:18:06,753 --> 00:18:13,727 +This log function is Actor-isolated and it +needs access to the shared mutable state. + +290 +00:18:13,760 --> 00:18:19,499 +In order to fix this, +we need to make this function async + +291 +00:18:19,533 --> 00:18:24,805 +and then mark all of our log invocations +with the await keyword. + +292 +00:18:29,376 --> 00:18:35,282 +Now we need to update our task creation +to create a detached task. + +293 +00:18:40,554 --> 00:18:43,690 +We do this to ensure +the Task does not inherit + +294 +00:18:43,724 --> 00:18:47,194 +the Actor-context that it was created in. + +295 +00:18:47,227 --> 00:18:51,632 +For detached tasks, +we need to explicitly capture self. + +296 +00:18:56,203 --> 00:18:58,005 +Let's test our application again. + +297 +00:19:05,546 --> 00:19:09,650 +The app is able to compress +all the files simultaneously + +298 +00:19:09,683 --> 00:19:12,219 +and the UI remains responsive. + +299 +00:19:12,252 --> 00:19:18,258 +To verify our improvements, +we can check the Swift Actors instrument. + +300 +00:19:18,292 --> 00:19:20,928 +Looking at the ParallelCompressor Actor, + +301 +00:19:20,961 --> 00:19:23,597 +most of the work executed on the Actor + +302 +00:19:23,630 --> 00:19:26,066 +is only for a short amount of time + +303 +00:19:26,099 --> 00:19:30,571 +and the queue size never gets out of hand. + +304 +00:19:30,604 --> 00:19:35,209 +To recap, we used the Instrument +to Isolate the cause of a UI hang, + +305 +00:19:35,242 --> 00:19:38,612 +we restructured our concurrency code +for better parallelism, + +306 +00:19:38,645 --> 00:19:42,382 +and verified performance improvements +using data. + +307 +00:19:42,416 --> 00:19:46,553 +Now Mike is gonna tell us about +some other potential performance issues. + +308 +00:19:46,587 --> 00:19:48,822 +Mike: There are two common problems +I'd like to cover + +309 +00:19:48,856 --> 00:19:50,624 +beyond what we've seen in the demo. + +310 +00:19:50,657 --> 00:19:53,527 +First, let's talk about +thread pool exhaustion. + +311 +00:19:53,560 --> 00:19:55,796 +Thread pool exhaustion +can hurt performance + +312 +00:19:55,829 --> 00:19:57,965 +or even deadlock an application. + +313 +00:19:57,998 --> 00:20:02,803 +Swift concurrency requires tasks to make +forward progress when they're running. + +314 +00:20:02,836 --> 00:20:06,807 +When a task waits for something, +it normally does so by suspending. + +315 +00:20:06,840 --> 00:20:10,911 +However, it's possible for code +within a task to perform a blocking call, + +316 +00:20:10,944 --> 00:20:15,949 +such as blocking file or network IO, +or acquiring locks, without suspending. + +317 +00:20:15,983 --> 00:20:20,354 +This breaks the requirement +for tasks to make forward progress. + +318 +00:20:20,387 --> 00:20:23,390 +When this happens, +the task continues to occupy the thread + +319 +00:20:23,423 --> 00:20:27,561 +where it's executing, +but it isn't actually using a CPU core. + +320 +00:20:27,594 --> 00:20:30,898 +Because the pool of threads is limited +and some of them are blocked, + +321 +00:20:30,931 --> 00:20:34,902 +the concurrency runtime is +unable to fully use all CPU cores. + +322 +00:20:34,935 --> 00:20:37,137 +This reduces the amount +of parallel computation + +323 +00:20:37,171 --> 00:20:40,974 +that can be done +and the maximum performance of your app. + +324 +00:20:41,008 --> 00:20:45,546 +In extreme cases, when the entire +thread pool is occupied by blocked tasks, + +325 +00:20:45,579 --> 00:20:48,115 +and they're waiting on something +that requires a new task + +326 +00:20:48,148 --> 00:20:52,586 +to run on the thread pool, +the concurrency runtime can deadlock. + +327 +00:20:52,619 --> 00:20:55,756 +Be sure to avoid blocking calls in tasks. + +328 +00:20:55,789 --> 00:21:00,160 +File and network IO must be performed +using async APIs. + +329 +00:21:00,194 --> 00:21:03,564 +Avoid waiting on condition variables +or semaphores. + +330 +00:21:03,597 --> 00:21:07,367 +Fine-grained, briefly-held locks +are acceptable if necessary, + +331 +00:21:07,401 --> 00:21:09,436 +but avoid locks +that have a lot of contention + +332 +00:21:09,469 --> 00:21:12,172 +or are held for long periods of time. + +333 +00:21:12,206 --> 00:21:14,641 +If you have code +that needs to do these things, + +334 +00:21:14,675 --> 00:21:17,611 +move that code +outside of the concurrency thread pool– + +335 +00:21:17,644 --> 00:21:20,113 +for example, +by running it on a Dispatch queue– + +336 +00:21:20,147 --> 00:21:23,951 +and bridge it to the concurrency world +using continuations. + +337 +00:21:23,984 --> 00:21:27,654 +Whenever possible, +use async APIs for blocking operations + +338 +00:21:27,688 --> 00:21:30,757 +to keep the system operating smoothly. + +339 +00:21:30,791 --> 00:21:35,562 +When you're using continuations, +you must be careful to use them correctly. + +340 +00:21:35,596 --> 00:21:40,667 +Continuations are the bridge between Swift +concurrency and other forms of async code. + +341 +00:21:40,701 --> 00:21:43,270 +A continuation suspends +the current task + +342 +00:21:43,303 --> 00:21:47,140 +and provides a callback +which resumes the task when called. + +343 +00:21:47,174 --> 00:21:51,111 +This can then be used +with callback-based async APIs. + +344 +00:21:51,144 --> 00:21:54,781 +From the perspective of Swift concurrency, +the task suspends, + +345 +00:21:54,815 --> 00:21:58,252 +and then it resumes +when the continuation is resumed. + +346 +00:21:58,285 --> 00:22:02,256 +From the perspective of the callback-based +async API, the work begins, + +347 +00:22:02,289 --> 00:22:05,025 +and then the callback is called +when the work completes. + +348 +00:22:05,058 --> 00:22:07,895 +The Swift Concurrency instrument +knows about continuations + +349 +00:22:07,928 --> 00:22:10,097 +and will mark +the time interval accordingly, + +350 +00:22:10,130 --> 00:22:14,101 +showing you that the task was waiting on +a continuation to be called. + +351 +00:22:14,134 --> 00:22:16,837 +Continuation callbacks have +a special requirement: + +352 +00:22:16,870 --> 00:22:20,574 +they must be called exactly once, +no more, no less. + +353 +00:22:20,607 --> 00:22:23,577 +This is a common requirement +in callback-based APIs, + +354 +00:22:23,610 --> 00:22:25,312 +but it tends to be an informal one + +355 +00:22:25,345 --> 00:22:27,814 +and is not enforced by the language, + +356 +00:22:27,848 --> 00:22:29,616 +and oversights are common. + +357 +00:22:29,650 --> 00:22:32,586 +Swift concurrency +makes this a hard requirement. + +358 +00:22:32,619 --> 00:22:36,890 +If the callback is called twice, +the program will crash or misbehave. + +359 +00:22:36,924 --> 00:22:40,761 +If the callback is never called, +the task will leak. + +360 +00:22:40,794 --> 00:22:43,497 +In this code snippet +we use withCheckedContinuation + +361 +00:22:43,530 --> 00:22:45,399 +to get a continuation. + +362 +00:22:45,432 --> 00:22:48,402 +We then invoke a callback-based API. + +363 +00:22:48,435 --> 00:22:51,205 +In the callback, +we resume the continuation. + +364 +00:22:51,238 --> 00:22:54,842 +This meets the requirement of calling it +exactly once. + +365 +00:22:54,875 --> 00:22:58,212 +It's important to be careful +when the code is more complex. + +366 +00:22:58,245 --> 00:23:00,214 +On the left, we've modified the callback + +367 +00:23:00,247 --> 00:23:02,950 +to only resume the continuation +on success. + +368 +00:23:02,983 --> 00:23:04,618 +This is a bug. + +369 +00:23:04,651 --> 00:23:07,287 +On failure, +the continuation will not be resumed, + +370 +00:23:07,321 --> 00:23:10,224 +and the task will be suspended forever. + +371 +00:23:10,257 --> 00:23:13,627 +On the right, +we're resuming the continuation twice. + +372 +00:23:13,660 --> 00:23:17,598 +This is also a bug, +and the app will misbehave or crash. + +373 +00:23:17,631 --> 00:23:19,867 +Both of these snippets +violate the requirement + +374 +00:23:19,900 --> 00:23:23,036 +to resume the continuation exactly once. + +375 +00:23:23,070 --> 00:23:27,908 +Two kinds of continuations +are available: checked and unsafe. + +376 +00:23:27,941 --> 00:23:32,479 +Always use the withCheckedContinuation API +for continuations + +377 +00:23:32,513 --> 00:23:35,849 +unless performance is absolutely critical. + +378 +00:23:35,883 --> 00:23:40,254 +Checked continuations automatically +detect misuse and flag an error. + +379 +00:23:40,287 --> 00:23:44,958 +When a checked continuation +is called twice, the continuation traps. + +380 +00:23:44,992 --> 00:23:47,394 +When the continuation +is never called at all, + +381 +00:23:47,427 --> 00:23:50,931 +a message is printed to the console +when the continuation is destroyed + +382 +00:23:50,964 --> 00:23:53,567 +warning you that the continuation leaked. + +383 +00:23:53,600 --> 00:23:56,703 +The Swift Concurrency instrument +will show the corresponding task + +384 +00:23:56,737 --> 00:24:00,107 +stuck indefinitely +in the continuation state. + +385 +00:24:00,140 --> 00:24:03,610 +There is much more to look into +for the new Swift Concurrency template + +386 +00:24:03,644 --> 00:24:04,912 +in Instruments. + +387 +00:24:04,945 --> 00:24:08,115 +You can get graphic visualization +of structured concurrency, + +388 +00:24:08,148 --> 00:24:12,119 +view task creation calltrees, and inspect +the exact assembly instructions + +389 +00:24:12,152 --> 00:24:15,255 +to get a full picture +of the Swift Concurrency runtime. + +390 +00:24:15,289 --> 00:24:18,292 +To learn more about how Swift Concurrency +works under the hood, + +391 +00:24:18,325 --> 00:24:22,162 +watch last year's session on +"Swift Concurrency: Behind the Scenes." + +392 +00:24:22,196 --> 00:24:23,931 +And to learn more about data races, + +393 +00:24:23,964 --> 00:24:27,100 +watch "Eliminate data races +using Swift Concurrency." + +394 +00:24:27,134 --> 00:24:28,302 +Thanks for watching! + +395 +00:24:28,335 --> 00:24:31,405 +And have fun +debugging your concurrency code. + diff --git a/eng/2022 Session 110351 Eliminate data races using Swift Concurrency en.srt b/eng/2022 Session 110351 Eliminate data races using Swift Concurrency en.srt new file mode 100644 index 0000000..42edd99 --- /dev/null +++ b/eng/2022 Session 110351 Eliminate data races using Swift Concurrency en.srt @@ -0,0 +1,2770 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,443 +♪ + +3 +00:00:09,443 --> 00:00:10,444 +Hello. + +4 +00:00:10,444 --> 00:00:13,213 +I'm Doug from the Swift team, +and I'm here to talk about + +5 +00:00:13,213 --> 00:00:17,150 +Swift Concurrency's approach +to eliminating data races. + +6 +00:00:17,150 --> 00:00:20,554 +We introduced Swift Concurrency +as a set of language features + +7 +00:00:20,554 --> 00:00:23,590 +that make it easier to write +concurrent programs. + +8 +00:00:23,590 --> 00:00:27,060 +For the mechanics of these +individual language features, + +9 +00:00:27,060 --> 00:00:32,399 +we refer you to the 2021 WWDC +talks covering each of them. + +10 +00:00:32,399 --> 00:00:33,800 +This talk takes a different, + +11 +00:00:33,800 --> 00:00:36,336 +more holistic view +of Swift Concurrency + +12 +00:00:36,336 --> 00:00:38,505 +as a way of structuring +your program to make + +13 +00:00:38,505 --> 00:00:42,576 +efficient use of concurrency +without introducing data races. + +14 +00:00:42,576 --> 00:00:45,345 +But to do so, +we need a great analogy, + +15 +00:00:45,345 --> 00:00:50,918 +so we invite you to sail with us +on the high seas of concurrency. + +16 +00:00:50,918 --> 00:00:53,020 +The sea of concurrency +is unpredictable, + +17 +00:00:53,020 --> 00:00:55,055 +with many things +going on at once, + +18 +00:00:55,055 --> 00:00:58,091 +but with you at the helm +and Swift helping you navigate + +19 +00:00:58,091 --> 00:01:01,161 +the waters, +it can produce amazing things. + +20 +00:01:01,161 --> 00:01:02,829 +Let's dive in! + +21 +00:01:02,829 --> 00:01:05,999 +We'll start +by talking about isolation, + +22 +00:01:05,999 --> 00:01:09,469 +which is one of the key ideas +of Swift's concurrency model, + +23 +00:01:09,469 --> 00:01:11,972 +ensuring that data +is not shared in a manner + +24 +00:01:11,972 --> 00:01:14,241 +that can introduce data races. + +25 +00:01:14,241 --> 00:01:17,644 +Let's start with task isolation. + +26 +00:01:17,644 --> 00:01:22,716 +In our sea of concurrency, +tasks are represented by boats. + +27 +00:01:22,716 --> 00:01:25,819 +Boats are our main workers -- +they have a job to do, + +28 +00:01:25,819 --> 00:01:29,589 +which they perform sequentially +from start to finish. + +29 +00:01:29,589 --> 00:01:32,926 +They are asynchronous, +and their work can be suspended + +30 +00:01:32,926 --> 00:01:37,931 +any number of times at +"await" operations in the code. + +31 +00:01:37,931 --> 00:01:40,400 +Finally, +they are self-contained: + +32 +00:01:40,400 --> 00:01:44,738 +each task has its own resources, +so it can operate by itself, + +33 +00:01:44,738 --> 00:01:48,542 +independently of all +of the other boats in the sea. + +34 +00:01:48,542 --> 00:01:51,278 +If our boats are +completely independent, + +35 +00:01:51,278 --> 00:01:54,281 +we have concurrency +without data races, + +36 +00:01:54,281 --> 00:01:59,753 +but it's not very useful +without some way to communicate. + +37 +00:01:59,753 --> 00:02:01,922 +Let's add some communication! + +38 +00:02:01,922 --> 00:02:03,857 +For example, one boat might have + +39 +00:02:03,857 --> 00:02:07,794 +a pineapple that it wants +to share with another boat. + +40 +00:02:07,794 --> 00:02:09,997 +So the boats meet +on the open sea, + +41 +00:02:09,997 --> 00:02:13,600 +and we transfer the pineapple +from one boat to the other. + +42 +00:02:13,600 --> 00:02:15,435 +Now, this is where +the physical analogy + +43 +00:02:15,435 --> 00:02:18,071 +breaks down a little bit, +because this pineapple + +44 +00:02:18,071 --> 00:02:22,142 +is not a physical item that +moves from one boat to the next. + +45 +00:02:22,142 --> 00:02:25,445 +It's data, and in Swift we have +a couple of different ways + +46 +00:02:25,445 --> 00:02:29,116 +we could represent that data. + +47 +00:02:29,116 --> 00:02:31,685 +How do we define +our pineapple type? + +48 +00:02:31,685 --> 00:02:34,888 +We like value types in Swift, +so let's make the pineapple + +49 +00:02:34,888 --> 00:02:39,626 +a struct that's defined +by its weight and ripeness. + +50 +00:02:39,626 --> 00:02:41,528 +Let's see how this works. + +51 +00:02:41,528 --> 00:02:43,630 +When the boats meet +on the open sea, + +52 +00:02:43,630 --> 00:02:46,867 +we're really passing a copy +of the pineapple instance + +53 +00:02:46,867 --> 00:02:48,869 +from one boat to the next, + +54 +00:02:48,869 --> 00:02:52,906 +and each boat goes away +with its own copy. + +55 +00:02:52,906 --> 00:02:55,142 +If you were to +mutate the copies, + +56 +00:02:55,142 --> 00:02:58,178 +such as by calling +the slice() and ripen() methods, + +57 +00:02:58,178 --> 00:03:01,348 +it won't have any effect +on the other one. + +58 +00:03:01,348 --> 00:03:03,550 +Swift has always +preferred value types + +59 +00:03:03,550 --> 00:03:05,819 +for exactly this reason -- + +60 +00:03:05,819 --> 00:03:09,289 +mutations have +only local effects. + +61 +00:03:09,289 --> 00:03:14,294 +That principle helps +value types maintain isolation. + +62 +00:03:14,294 --> 00:03:18,398 +Now, let's extend our data model +a bit and add chickens! + +63 +00:03:18,398 --> 00:03:20,367 +Unlike pineapples, +which are pretty much + +64 +00:03:20,367 --> 00:03:23,503 +only good for eating, +chickens are beautiful creatures + +65 +00:03:23,503 --> 00:03:26,339 +with their own +unique personalities. + +66 +00:03:26,339 --> 00:03:30,844 +So, we're going to model +them with a class, like this. + +67 +00:03:30,844 --> 00:03:34,648 +Let's have our intrepid +seafarers exchange a chicken. + +68 +00:03:34,648 --> 00:03:37,417 +When our boats meet, +we share the chicken, + +69 +00:03:37,417 --> 00:03:40,253 +except that copying +a reference type like chicken + +70 +00:03:40,253 --> 00:03:43,323 +doesn't give you another +full copy of the chicken, + +71 +00:03:43,323 --> 00:03:47,594 +it gives you a reference +to that specific object. + +72 +00:03:47,594 --> 00:03:49,963 +So once our boats have +gone their separate ways, + +73 +00:03:49,963 --> 00:03:52,699 +we can see +that we have a problem: + +74 +00:03:52,699 --> 00:03:55,836 +both boats are doing +their work concurrently, + +75 +00:03:55,836 --> 00:03:57,237 +but they are not independent + +76 +00:03:57,237 --> 00:04:00,373 +because they both reference +the same chicken object. + +77 +00:04:00,373 --> 00:04:04,311 +That shared mutable data +is prone to data races, + +78 +00:04:04,311 --> 00:04:06,813 +such as when one boat +is trying to feed the chicken + +79 +00:04:06,813 --> 00:04:08,715 +and the other +wants to play with it, + +80 +00:04:08,715 --> 00:04:12,052 +leading to one +very confused chicken. + +81 +00:04:12,052 --> 00:04:15,589 +We need a way to know that it +was safe to share pineapples + +82 +00:04:15,589 --> 00:04:18,191 +amongst boats, but not chickens. + +83 +00:04:18,191 --> 00:04:21,828 +And then we need some checking +in place in the Swift compiler + +84 +00:04:21,828 --> 00:04:24,164 +to ensure that chickens +aren't accidentally passed + +85 +00:04:24,164 --> 00:04:27,000 +from one boat to another. + +86 +00:04:27,000 --> 00:04:30,570 +Swift protocols are a great way +of categorizing types + +87 +00:04:30,570 --> 00:04:33,473 +so you can reason +about their behavior. + +88 +00:04:33,473 --> 00:04:36,176 +The Sendable protocol +is used to describe types + +89 +00:04:36,176 --> 00:04:40,280 +that can safely be shared across +different isolation domains, + +90 +00:04:40,280 --> 00:04:43,416 +without creating data races. + +91 +00:04:43,416 --> 00:04:47,020 +A type can be made Sendable +by writing a conformance. + +92 +00:04:47,020 --> 00:04:49,389 +The Pineapple struct +conforms to Sendable + +93 +00:04:49,389 --> 00:04:53,093 +because it's a value type, +but the Chicken class cannot + +94 +00:04:53,093 --> 00:04:58,031 +because it's an unsynchronized +reference type. + +95 +00:04:58,031 --> 00:05:01,234 +Modeling Sendable as a protocol +allows us to describe + +96 +00:05:01,234 --> 00:05:03,703 +the places where data +is going to be shared + +97 +00:05:03,703 --> 00:05:06,273 +across isolation domains. + +98 +00:05:06,273 --> 00:05:09,276 +For example, +when a task returns a value, + +99 +00:05:09,276 --> 00:05:12,045 +this value is provided +to any of the tasks + +100 +00:05:12,045 --> 00:05:15,015 +that are waiting for that value. + +101 +00:05:15,015 --> 00:05:18,552 +Here, we are trying to return +a Chicken from our Task, + +102 +00:05:18,552 --> 00:05:21,188 +and we get an error +stating that this is unsafe + +103 +00:05:21,188 --> 00:05:25,158 +because Chicken is not Sendable. + +104 +00:05:25,158 --> 00:05:28,495 +The actual Sendable constraint +comes from the definition + +105 +00:05:28,495 --> 00:05:31,765 +of the Task struct itself, +which specifies that + +106 +00:05:31,765 --> 00:05:35,101 +the result type of a Task, +called Success, + +107 +00:05:35,101 --> 00:05:37,771 +must conform +to the Sendable protocol. + +108 +00:05:37,771 --> 00:05:39,406 +You should use +Sendable constraints + +109 +00:05:39,406 --> 00:05:41,541 +where you have +generic parameters whose values + +110 +00:05:41,541 --> 00:05:45,845 +will be passed across +different isolation domains. + +111 +00:05:45,845 --> 00:05:50,717 +Now, let's revisit the idea +of sharing data among boats. + +112 +00:05:50,717 --> 00:05:54,354 +When two boats meet on the high +seas and want to share data, + +113 +00:05:54,354 --> 00:05:57,324 +we need someone to consistently +check all of the goods + +114 +00:05:57,324 --> 00:06:00,160 +to make sure +they're safe to share. + +115 +00:06:00,160 --> 00:06:02,996 +That's the role of our friendly +customs inspector -- + +116 +00:06:02,996 --> 00:06:05,198 +played here +by the Swift compiler -- + +117 +00:06:05,198 --> 00:06:09,002 +to make sure that only +Sendable types are exchanged. + +118 +00:06:09,002 --> 00:06:12,105 +The pineapple is fine +and can be exchanged freely, + +119 +00:06:12,105 --> 00:06:14,040 +because it's Sendable. + +120 +00:06:14,040 --> 00:06:16,977 +However, the chicken +cannot be exchanged, + +121 +00:06:16,977 --> 00:06:19,646 +and our friendly customs +inspector will prevent us + +122 +00:06:19,646 --> 00:06:23,183 +from making that mistake. + +123 +00:06:23,183 --> 00:06:25,952 +The compiler is involved +in checking Sendable correctness + +124 +00:06:25,952 --> 00:06:28,488 +at many different points. + +125 +00:06:28,488 --> 00:06:31,625 +Sendable types must be +correct by construction, + +126 +00:06:31,625 --> 00:06:35,895 +and cannot allow any shared data +to be smuggled through them. + +127 +00:06:35,895 --> 00:06:39,099 +Enums and structs +generally define value types, + +128 +00:06:39,099 --> 00:06:41,801 +which copy all of their +instance data along with them + +129 +00:06:41,801 --> 00:06:44,137 +to produce independent values. + +130 +00:06:44,137 --> 00:06:46,339 +Therefore, they can be Sendable + +131 +00:06:46,339 --> 00:06:51,745 +so long as all of their +instance data is also Sendable. + +132 +00:06:51,745 --> 00:06:54,281 +Sendable can be propagated +through collections + +133 +00:06:54,281 --> 00:06:57,651 +and other generic types +using conditional conformance. + +134 +00:06:57,651 --> 00:07:00,086 +An array of Sendable types +is Sendable, + +135 +00:07:00,086 --> 00:07:04,691 +so a Crate full of pineapples +is also Sendable. + +136 +00:07:04,691 --> 00:07:07,060 +All of these Sendable +conformances can even be + +137 +00:07:07,060 --> 00:07:10,563 +inferred by the Swift compiler +for non-public types, + +138 +00:07:10,563 --> 00:07:12,532 +so Ripeness, +Pineapple, and Crate + +139 +00:07:12,532 --> 00:07:15,135 +are all implicitly Sendable. + +140 +00:07:15,135 --> 00:07:19,539 +But let's say we create a coop +to house our flock of chickens. + +141 +00:07:19,539 --> 00:07:22,075 +This type cannot +be marked as Sendable, + +142 +00:07:22,075 --> 00:07:24,811 +because it contains +non-Sendable state: + +143 +00:07:24,811 --> 00:07:27,213 +Chicken isn't Sendable, +so the array of chickens + +144 +00:07:27,213 --> 00:07:29,015 +isn't Sendable. + +145 +00:07:29,015 --> 00:07:31,084 +We'll get an error message +from our compiler to indicate + +146 +00:07:31,084 --> 00:07:35,322 +that this type +cannot safely be shared. + +147 +00:07:35,322 --> 00:07:38,658 +Classes are reference types, +so they can only be + +148 +00:07:38,658 --> 00:07:42,095 +made Sendable under +very narrow circumstances, + +149 +00:07:42,095 --> 00:07:46,099 +such as when a final class +only has immutable storage. + +150 +00:07:46,099 --> 00:07:48,535 +Our attempt to make +the Chicken class Sendable + +151 +00:07:48,535 --> 00:07:53,406 +will produce an error because +it contains mutable state. + +152 +00:07:53,406 --> 00:07:55,842 +Now, it is possible +to implement reference types + +153 +00:07:55,842 --> 00:07:59,145 +that do their own internal +synchronization, for example, + +154 +00:07:59,145 --> 00:08:01,414 +by using a lock consistently. + +155 +00:08:01,414 --> 00:08:04,184 +These types are +conceptually Sendable, + +156 +00:08:04,184 --> 00:08:07,687 +but there is no way for Swift +to reason about that. + +157 +00:08:07,687 --> 00:08:12,058 +Use unchecked Sendable to +disable the compiler's checking. + +158 +00:08:12,058 --> 00:08:14,427 +Be careful with this, +because smuggling mutable state + +159 +00:08:14,427 --> 00:08:16,029 +through @unchecked Sendable + +160 +00:08:16,029 --> 00:08:21,167 +undermines the data race safety +guarantees Swift is providing. + +161 +00:08:21,167 --> 00:08:25,038 +Task creation involves +executing a closure in a new, + +162 +00:08:25,038 --> 00:08:30,377 +independent task, like sending +off a rowboat from your boat. + +163 +00:08:30,377 --> 00:08:33,813 +When we do this, we can capture +values from the original task + +164 +00:08:33,813 --> 00:08:36,383 +and pass them +into the new task, + +165 +00:08:36,383 --> 00:08:38,218 +so we need Sendable checking + +166 +00:08:38,218 --> 00:08:41,488 +to ensure we don't +introduce data races. + +167 +00:08:41,488 --> 00:08:44,257 +If we do try to share +a non-Sendable type + +168 +00:08:44,257 --> 00:08:47,394 +across this boundary, the +Swift compiler has us covered, + +169 +00:08:47,394 --> 00:08:51,398 +producing an error message +like this one. + +170 +00:08:51,398 --> 00:08:53,833 +This isn't magic +for task creation. + +171 +00:08:53,833 --> 00:08:57,737 +The closure is being inferred +to be a Sendable closure, + +172 +00:08:57,737 --> 00:09:01,374 +which could have been written +explicitly with At-Sendable. + +173 +00:09:01,374 --> 00:09:07,113 +Sendable closures are values +of Sendable function type. + +174 +00:09:07,113 --> 00:09:10,216 +At-Sendable can be written +on a function type to indicate + +175 +00:09:10,216 --> 00:09:13,753 +that the function type conforms +to the Sendable protocol. + +176 +00:09:13,753 --> 00:09:16,089 +That implies that values +of that function type + +177 +00:09:16,089 --> 00:09:19,459 +can be passed to other isolation +domains and called there + +178 +00:09:19,459 --> 00:09:23,163 +without introducing data races +on their captured state. + +179 +00:09:23,163 --> 00:09:26,332 +Normally, function types +cannot conform to protocols, + +180 +00:09:26,332 --> 00:09:29,335 +but Sendable is special +because the compiler validates + +181 +00:09:29,335 --> 00:09:31,971 +the semantic requirements +for it. + +182 +00:09:31,971 --> 00:09:34,707 +There is similar support +for tuples of Sendable types + +183 +00:09:34,707 --> 00:09:36,910 +conforming to the +Sendable protocol, + +184 +00:09:36,910 --> 00:09:38,344 +which allows Sendable +to be used + +185 +00:09:38,344 --> 00:09:41,915 +throughout the entire language. + +186 +00:09:41,915 --> 00:09:44,984 +The system we've described +has many concurrently executing + +187 +00:09:44,984 --> 00:09:48,655 +tasks that are isolated +from each other. + +188 +00:09:48,655 --> 00:09:50,623 +The Sendable protocol +describes types + +189 +00:09:50,623 --> 00:09:52,959 +that can be safely shared +among tasks, + +190 +00:09:52,959 --> 00:09:55,261 +and the Swift compiler +checks Sendable conformances + +191 +00:09:55,261 --> 00:09:59,566 +at every level to maintain +isolation of the tasks. + +192 +00:09:59,566 --> 00:10:03,603 +However, without any notion +of shared mutable data anywhere, + +193 +00:10:03,603 --> 00:10:04,804 +it's hard for the tasks + +194 +00:10:04,804 --> 00:10:07,073 +to coordinate +in a meaningful manner. + +195 +00:10:07,073 --> 00:10:10,276 +So we need some way +to share data amongst our tasks + +196 +00:10:10,276 --> 00:10:13,480 +that doesn't re-introduce +data races. + +197 +00:10:13,480 --> 00:10:16,416 +This is where actors come in. + +198 +00:10:16,416 --> 00:10:19,285 +Actors provide a way +to isolate state + +199 +00:10:19,285 --> 00:10:21,855 +that can be accessed +by different tasks, + +200 +00:10:21,855 --> 00:10:26,793 +but in a coordinated manner +that eliminates data races. + +201 +00:10:26,793 --> 00:10:30,830 +Actors are the islands +in our sea of concurrency. + +202 +00:10:30,830 --> 00:10:33,967 +Like boats, +each island is self-contained, + +203 +00:10:33,967 --> 00:10:36,002 +with its own state +that is isolated + +204 +00:10:36,002 --> 00:10:38,671 +from everything else in the sea. + +205 +00:10:38,671 --> 00:10:41,374 +To access that state, +your code needs to be running + +206 +00:10:41,374 --> 00:10:43,109 +on the island. + +207 +00:10:43,109 --> 00:10:45,044 +For example, +the advanceTime method + +208 +00:10:45,044 --> 00:10:47,113 +is isolated to this island. + +209 +00:10:47,113 --> 00:10:49,148 +It lives on the island +and has access + +210 +00:10:49,148 --> 00:10:51,918 +to all of the island's state. + +211 +00:10:51,918 --> 00:10:56,422 +To actually run code +on an island, you need a boat. + +212 +00:10:56,422 --> 00:10:59,325 +A boat can visit the island +to run code on the island, + +213 +00:10:59,325 --> 00:11:02,962 +at which point it has +access to that state. + +214 +00:11:02,962 --> 00:11:07,066 +Only one boat can visit the +island to run code at a time, + +215 +00:11:07,066 --> 00:11:09,335 +which ensures that +there is no concurrent access + +216 +00:11:09,335 --> 00:11:11,304 +to the island's state. + +217 +00:11:11,304 --> 00:11:14,207 +If other boats show up, +they must await their turn + +218 +00:11:14,207 --> 00:11:16,009 +to visit the island. + +219 +00:11:16,009 --> 00:11:19,245 +And because it might be +a long time before a given boat + +220 +00:11:19,245 --> 00:11:22,382 +gets a chance to visit the +island, entering into an actor + +221 +00:11:22,382 --> 00:11:27,053 +is a potential suspension point +marked by the “await” keyword. + +222 +00:11:27,053 --> 00:11:30,356 +Once the island frees up -- +again, at a suspension point -- + +223 +00:11:30,356 --> 00:11:34,227 +another boat can visit. + +224 +00:11:34,227 --> 00:11:36,996 +Just like with two boats +meeting on the open sea, + +225 +00:11:36,996 --> 00:11:40,433 +interactions between a boat +and an island need to maintain + +226 +00:11:40,433 --> 00:11:43,336 +isolation of both, +by making sure + +227 +00:11:43,336 --> 00:11:47,140 +that non-Sendable types +don't pass between the two. + +228 +00:11:47,140 --> 00:11:50,276 +For example, perhaps we try +to add a chicken from our boat + +229 +00:11:50,276 --> 00:11:52,378 +to the flock on the island. + +230 +00:11:52,378 --> 00:11:55,415 +This would create two references +to the same chicken object + +231 +00:11:55,415 --> 00:11:57,650 +from different +isolation domains, + +232 +00:11:57,650 --> 00:12:00,553 +so the Swift compiler +rejects it. + +233 +00:12:00,553 --> 00:12:03,890 +Similarly, if we try to adopt +a pet chicken from the island + +234 +00:12:03,890 --> 00:12:06,960 +and take it away on our boat, +Sendable checking ensures + +235 +00:12:06,960 --> 00:12:11,898 +that we cannot create +this data race. + +236 +00:12:11,898 --> 00:12:15,702 +Actors are reference types, +but unlike classes, they isolate + +237 +00:12:15,702 --> 00:12:19,606 +all of their properties and code +to prevent concurrent access. + +238 +00:12:19,606 --> 00:12:21,975 +Therefore, +having a reference to an actor + +239 +00:12:21,975 --> 00:12:25,511 +from a different +isolation domain is safe. + +240 +00:12:25,511 --> 00:12:28,047 +It's like having a map +to an island: + +241 +00:12:28,047 --> 00:12:30,583 +you can use the map +to go visit the island, + +242 +00:12:30,583 --> 00:12:32,885 +but you still need to go +through the docking procedure + +243 +00:12:32,885 --> 00:12:34,921 +to access its state. + +244 +00:12:34,921 --> 00:12:40,660 +Therefore, all actor types +are implicitly Sendable. + +245 +00:12:40,660 --> 00:12:42,795 +You might be wondering +how to know + +246 +00:12:42,795 --> 00:12:46,599 +what code is isolated to +the actor and what code isn't. + +247 +00:12:46,599 --> 00:12:50,470 +Actor isolation is determined +by the context you're in. + +248 +00:12:50,470 --> 00:12:52,138 +The instance properties +of an actor + +249 +00:12:52,138 --> 00:12:54,841 +are isolated to that actor. + +250 +00:12:54,841 --> 00:12:58,144 +Instance methods on the actor +or an extension of the actor + +251 +00:12:58,144 --> 00:13:03,249 +are also isolated by default, +like this advanceTime method. + +252 +00:13:03,249 --> 00:13:06,886 +Closures that are not Sendable, +such as the closure passed + +253 +00:13:06,886 --> 00:13:09,789 +to the reduce algorithm, +stay on the actor and are + +254 +00:13:09,789 --> 00:13:14,260 +actor-isolated when they are +in an actor-isolated context. + +255 +00:13:14,260 --> 00:13:17,330 +The task initializer +also inherits actor isolation + +256 +00:13:17,330 --> 00:13:20,033 +from its context, +so the created task will be + +257 +00:13:20,033 --> 00:13:23,603 +scheduled on the same actor +as it was initiated from. + +258 +00:13:23,603 --> 00:13:27,540 +Here, that grants +access to the flock. + +259 +00:13:27,540 --> 00:13:29,809 +On the other hand, +a detached task + +260 +00:13:29,809 --> 00:13:33,046 +does not inherit actor isolation +from its context, + +261 +00:13:33,046 --> 00:13:35,181 +because it is completely +independent of the context + +262 +00:13:35,181 --> 00:13:37,116 +where it was created. + +263 +00:13:37,116 --> 00:13:38,985 +We can see that the code +in the closure here + +264 +00:13:38,985 --> 00:13:42,388 +is considered to be outside +the actor because it needs + +265 +00:13:42,388 --> 00:13:46,759 +to use “await” to refer +to the isolated “food” property. + +266 +00:13:46,759 --> 00:13:52,265 +We have a term for this closure: +it's non-isolated code. + +267 +00:13:52,265 --> 00:13:53,700 +No-isolated code is code + +268 +00:13:53,700 --> 00:13:56,502 +that does not run +on any actor at all. + +269 +00:13:56,502 --> 00:13:58,738 +You can explicitly make +a function that's within + +270 +00:13:58,738 --> 00:14:02,875 +an actor non-isolated by using +the non-isolated keyword, + +271 +00:14:02,875 --> 00:14:05,011 +putting it outside of the actor. + +272 +00:14:05,011 --> 00:14:07,714 +Just like what happened +implicitly with the closure used + +273 +00:14:07,714 --> 00:14:10,016 +for the detached task. + +274 +00:14:10,016 --> 00:14:12,652 +That means if we want to read +some of the state + +275 +00:14:12,652 --> 00:14:16,489 +that's isolated to the actor, +we'll need to use “await” + +276 +00:14:16,489 --> 00:14:20,860 +to visit the island and grab +a copy of the state we need. + +277 +00:14:20,860 --> 00:14:22,762 +Non-isolated async code + +278 +00:14:22,762 --> 00:14:26,165 +always runs +on the global cooperative pool. + +279 +00:14:26,165 --> 00:14:29,836 +Think of it as only running when +a boat is out on the open sea, + +280 +00:14:29,836 --> 00:14:33,773 +so you have to leave the island +you're visiting to do the work. + +281 +00:14:33,773 --> 00:14:35,508 +That means checking to ensure + +282 +00:14:35,508 --> 00:14:39,178 +that you aren't taking any +non-Sendable data with you! + +283 +00:14:39,178 --> 00:14:42,482 +Here, the compiler detects +the potential data race, + +284 +00:14:42,482 --> 00:14:44,717 +where an instance +of the non-Sendable Chicken + +285 +00:14:44,717 --> 00:14:47,887 +is trying to leave the island. + +286 +00:14:47,887 --> 00:14:52,191 +Let's consider one more case +of non-isolated code. + +287 +00:14:52,191 --> 00:14:56,028 +The “greet” operation is +non-isolated, synchronous code. + +288 +00:14:56,028 --> 00:14:58,531 +It knows nothing +about boats or islands + +289 +00:14:58,531 --> 00:15:00,767 +or concurrency in general. + +290 +00:15:00,767 --> 00:15:01,968 +And here, we're calling it + +291 +00:15:01,968 --> 00:15:04,470 +from the actor-isolated +greetOne function, + +292 +00:15:04,470 --> 00:15:05,805 +and that's okay! + +293 +00:15:05,805 --> 00:15:07,140 +This synchronous code, + +294 +00:15:07,140 --> 00:15:10,176 +when called from the island, +will stay on the island, + +295 +00:15:10,176 --> 00:15:14,180 +so it's free to operate +on the chicken from the flock. + +296 +00:15:14,180 --> 00:15:17,517 +If instead we had a non-isolated +async operation + +297 +00:15:17,517 --> 00:15:18,818 +that calls “greet,” + +298 +00:15:18,818 --> 00:15:23,089 +then “greet” will run there, +on a boat, in the open sea. + +299 +00:15:23,089 --> 00:15:25,057 +Most Swift code is like this: + +300 +00:15:25,057 --> 00:15:27,760 +synchronous, +non-isolated to any actor, + +301 +00:15:27,760 --> 00:15:30,530 +and only operates on +the parameters it's been given, + +302 +00:15:30,530 --> 00:15:35,868 +so it stays in the isolation +domain where it is called. + +303 +00:15:35,868 --> 00:15:37,870 +Actors hold state +that is isolated + +304 +00:15:37,870 --> 00:15:40,206 +from the rest of the program. + +305 +00:15:40,206 --> 00:15:43,509 +Only one task can run +on an actor at a time, + +306 +00:15:43,509 --> 00:15:47,180 +so there is no concurrent +access to that state. + +307 +00:15:47,180 --> 00:15:49,215 +Sendable checking +applies any time + +308 +00:15:49,215 --> 00:15:52,819 +a task enters or exits +an actor to ensure + +309 +00:15:52,819 --> 00:15:56,856 +that no unsynchronized +mutable state escapes. + +310 +00:15:56,856 --> 00:15:59,892 +Altogether, this makes actors +one of the building blocks + +311 +00:15:59,892 --> 00:16:04,230 +for a concurrent program +in Swift. + +312 +00:16:04,230 --> 00:16:06,866 +There's another special actor +we often talk about + +313 +00:16:06,866 --> 00:16:09,202 +called the main actor. + +314 +00:16:09,202 --> 00:16:11,704 +Think of the main actor +as a big island + +315 +00:16:11,704 --> 00:16:13,606 +in the middle of the sea. + +316 +00:16:13,606 --> 00:16:16,609 +It represents the main thread, +where all of the drawing + +317 +00:16:16,609 --> 00:16:19,712 +and interaction for +your user interface occurs. + +318 +00:16:19,712 --> 00:16:20,913 +So if you want to draw +something, + +319 +00:16:20,913 --> 00:16:24,450 +you need to run the code +on the main actor's island. + +320 +00:16:24,450 --> 00:16:26,519 +It's so important for your UI, + +321 +00:16:26,519 --> 00:16:32,124 +that maybe we should even +call it the “U-I-land." + +322 +00:16:32,124 --> 00:16:34,594 +When we say +that the main actor is “big,” + +323 +00:16:34,594 --> 00:16:37,330 +what we mean is that +it contains a lot of state + +324 +00:16:37,330 --> 00:16:39,866 +related to the program's +user interface. + +325 +00:16:39,866 --> 00:16:40,999 +There's a lot of code, + +326 +00:16:40,999 --> 00:16:43,636 +both in the UI frameworks +and in your apps, + +327 +00:16:43,636 --> 00:16:45,805 +that needs to run on it. + +328 +00:16:45,805 --> 00:16:47,907 +However, it's still an actor, + +329 +00:16:47,907 --> 00:16:51,310 +so it only runs one job +at a time. + +330 +00:16:51,310 --> 00:16:53,613 +So you have to be careful +not to put too much + +331 +00:16:53,613 --> 00:16:55,781 +or long-running work +on the main actor, + +332 +00:16:55,781 --> 00:17:00,052 +because it can make +your UI unresponsive. + +333 +00:17:00,052 --> 00:17:01,554 +Isolation to the main actor + +334 +00:17:01,554 --> 00:17:04,657 +is expressed with +the MainActor attribute. + +335 +00:17:04,657 --> 00:17:07,994 +This attribute can be applied +to a function or closure + +336 +00:17:07,994 --> 00:17:12,131 +to indicate that the code +must run on the main actor. + +337 +00:17:12,131 --> 00:17:16,636 +Then, we say that this code +is isolated to the main actor. + +338 +00:17:16,636 --> 00:17:18,471 +The Swift compiler +will guarantee + +339 +00:17:18,471 --> 00:17:21,340 +that main-actor-isolated code +will only be executed + +340 +00:17:21,340 --> 00:17:24,143 +on the main thread, +using the same mechanism + +341 +00:17:24,143 --> 00:17:28,281 +that ensures mutually exclusive +access to other actors. + +342 +00:17:28,281 --> 00:17:29,882 +If one calls updateView + +343 +00:17:29,882 --> 00:17:32,818 +from a context that isn't +isolated to the main actor, + +344 +00:17:32,818 --> 00:17:34,954 +it will need to introduce +an “await” + +345 +00:17:34,954 --> 00:17:39,191 +to account for the switch +over to the main actor. + +346 +00:17:39,191 --> 00:17:42,528 +The main actor attribute +can also be applied to types, + +347 +00:17:42,528 --> 00:17:45,064 +in which case the instances +of those types + +348 +00:17:45,064 --> 00:17:47,566 +will be isolated +to the main actor. + +349 +00:17:47,566 --> 00:17:50,369 +Again, this is just like +any other actor -- + +350 +00:17:50,369 --> 00:17:52,204 +the properties +are only accessible + +351 +00:17:52,204 --> 00:17:55,074 +while on the main actor, +and the methods are isolated + +352 +00:17:55,074 --> 00:17:58,711 +to the main actor +unless they explicitly opt out. + +353 +00:17:58,711 --> 00:17:59,946 +Like normal actors, + +354 +00:17:59,946 --> 00:18:03,549 +references to main-actor classes +are themselves Sendable, + +355 +00:18:03,549 --> 00:18:07,019 +because their data is isolated. + +356 +00:18:07,019 --> 00:18:09,422 +This makes the main actor +annotation suitable + +357 +00:18:09,422 --> 00:18:12,224 +for your UI views +and view controllers, + +358 +00:18:12,224 --> 00:18:14,694 +which are necessarily +tied to the main thread + +359 +00:18:14,694 --> 00:18:16,963 +by the frameworks themselves. + +360 +00:18:16,963 --> 00:18:18,798 +You can share a reference +to your view controller + +361 +00:18:18,798 --> 00:18:21,801 +with other tasks +and actors in your program, + +362 +00:18:21,801 --> 00:18:24,737 +and they can asynchronously call +back into the view controller + +363 +00:18:24,737 --> 00:18:26,272 +to post results. + +364 +00:18:26,272 --> 00:18:30,910 +This has a direct effect +on your app's architecture. + +365 +00:18:30,910 --> 00:18:31,811 +In your app, + +366 +00:18:31,811 --> 00:18:35,881 +your views and view controllers +will be on the main actor. + +367 +00:18:35,881 --> 00:18:39,251 +Other program logic should be +separated from that main actor, + +368 +00:18:39,251 --> 00:18:42,288 +using other actors +to safely model shared state + +369 +00:18:42,288 --> 00:18:45,658 +and tasks to describe +independent work. + +370 +00:18:45,658 --> 00:18:48,260 +And those tasks can shuttle +between the main actor + +371 +00:18:48,260 --> 00:18:51,430 +and other actors as necessary. + +372 +00:18:51,430 --> 00:18:53,899 +There's a lot going on +in a concurrent app, + +373 +00:18:53,899 --> 00:18:57,136 +so we've built some great tools +to help you make sense of it. + +374 +00:18:57,136 --> 00:18:58,604 +I invite you to check out + +375 +00:18:58,604 --> 00:19:01,173 +the "Visualize and Optimize +Swift Concurrency" talk + +376 +00:19:01,173 --> 00:19:03,642 +to learn more. + +377 +00:19:03,642 --> 00:19:08,547 +Let's dive into some deeper +waters to talk about atomicity. + +378 +00:19:08,547 --> 00:19:10,383 +The goal of the +Swift Concurrency model + +379 +00:19:10,383 --> 00:19:12,651 +is to eliminate data races. + +380 +00:19:12,651 --> 00:19:15,054 +What that really means +is that it eliminates + +381 +00:19:15,054 --> 00:19:18,624 +low-level data races, +which involve data corruption. + +382 +00:19:18,624 --> 00:19:23,329 +You still need to reason about +atomicity at a high level. + +383 +00:19:23,329 --> 00:19:24,663 +As we've talked about before, + +384 +00:19:24,663 --> 00:19:28,134 +actors only run +one task at a time. + +385 +00:19:28,134 --> 00:19:30,569 +However, when you stop +running on an actor, + +386 +00:19:30,569 --> 00:19:32,905 +the actor can run other tasks. + +387 +00:19:32,905 --> 00:19:35,441 +This ensures that the program +makes progress, + +388 +00:19:35,441 --> 00:19:38,244 +eliminating the potential +for deadlocks. + +389 +00:19:38,244 --> 00:19:41,113 +However, it requires you to +consider your actor's invariants + +390 +00:19:41,113 --> 00:19:43,682 +carefully around +await statements. + +391 +00:19:43,682 --> 00:19:47,119 +Otherwise, you can end up +with a high-level data race + +392 +00:19:47,119 --> 00:19:49,622 +where the program +is in an unexpected state, + +393 +00:19:49,622 --> 00:19:52,992 +even though no data +is actually corrupted. + +394 +00:19:52,992 --> 00:19:56,395 +Let's break down +an example of this. + +395 +00:19:56,395 --> 00:19:58,731 +Here we have a function +that intends to deposit + +396 +00:19:58,731 --> 00:20:01,600 +some additional pineapples +on an island. + +397 +00:20:01,600 --> 00:20:06,539 +It's outside of an actor, +so it's non-isolated async code. + +398 +00:20:06,539 --> 00:20:10,042 +That means it runs out here +in the open sea. + +399 +00:20:10,042 --> 00:20:13,512 +It's been given some pineapples +and a map to the island + +400 +00:20:13,512 --> 00:20:16,649 +where it should deposit +those pineapples. + +401 +00:20:16,649 --> 00:20:18,751 +The first interesting +operation here + +402 +00:20:18,751 --> 00:20:22,021 +gets a copy of the food array +from the island. + +403 +00:20:22,021 --> 00:20:24,790 +To do that, the boat needs +to visit the island, + +404 +00:20:24,790 --> 00:20:27,326 +signaled by the “await” keyword. + +405 +00:20:27,326 --> 00:20:29,428 +As soon as it has a copy +of the food, + +406 +00:20:29,428 --> 00:20:31,464 +the boat heads back out +to the open sea + +407 +00:20:31,464 --> 00:20:33,432 +to continue its work. + +408 +00:20:33,432 --> 00:20:36,435 +That means adding the pineapple +from the pineapples parameter + +409 +00:20:36,435 --> 00:20:39,138 +to the two it got +from the island. + +410 +00:20:39,138 --> 00:20:43,375 +Now, we can move along to the last line +of the function. + +411 +00:20:43,375 --> 00:20:46,645 +Our boat now needs +to visit the island again + +412 +00:20:46,645 --> 00:20:50,649 +to set the island's food array +to those three pineapples. + +413 +00:20:50,649 --> 00:20:52,518 +Here, everything +worked out fine, + +414 +00:20:52,518 --> 00:20:55,287 +and we have the three pineapples +on the island! + +415 +00:20:55,287 --> 00:20:58,724 +But things could +have gone a bit differently. + +416 +00:20:58,724 --> 00:21:03,395 +Let's say a pirate ship snuck in +and stole all of the pineapples + +417 +00:21:03,395 --> 00:21:07,299 +while our first boat was waiting +its turn to visit the island. + +418 +00:21:07,299 --> 00:21:10,236 +Now, our original ship +deposits its three pineapples + +419 +00:21:10,236 --> 00:21:13,205 +on the island, +and we notice a problem. + +420 +00:21:13,205 --> 00:21:15,341 +The three pineapples +have suddenly turned + +421 +00:21:15,341 --> 00:21:17,977 +into five pineapples! + +422 +00:21:17,977 --> 00:21:19,645 +What happened here? + +423 +00:21:19,645 --> 00:21:22,448 +Well, notice that we have +two awaits + +424 +00:21:22,448 --> 00:21:25,551 +for access to state +on the same actor, + +425 +00:21:25,551 --> 00:21:27,520 +and we're making +an assumption here + +426 +00:21:27,520 --> 00:21:29,388 +that the food array +on the island + +427 +00:21:29,388 --> 00:21:32,758 +doesn't change +between those two awaits. + +428 +00:21:32,758 --> 00:21:34,226 +But these are awaits, + +429 +00:21:34,226 --> 00:21:36,929 +meaning that our task +could get suspended here + +430 +00:21:36,929 --> 00:21:39,565 +and the actor could do other +higher-priority work, + +431 +00:21:39,565 --> 00:21:42,501 +like battling pirates. + +432 +00:21:42,501 --> 00:21:45,471 +In this specific case, +the Swift compiler will reject + +433 +00:21:45,471 --> 00:21:49,775 +an attempt to outright modify +the state on another actor. + +434 +00:21:49,775 --> 00:21:52,545 +However, we should really +rewrite our deposit operation + +435 +00:21:52,545 --> 00:21:56,949 +as synchronous code +on the actor, like this. + +436 +00:21:56,949 --> 00:21:58,951 +Because this +is synchronous code, + +437 +00:21:58,951 --> 00:22:01,720 +it will run on the actor +without interruption. + +438 +00:22:01,720 --> 00:22:03,856 +So we can be sure +that the state of the island + +439 +00:22:03,856 --> 00:22:09,795 +will be unchanged by anyone else +throughout the entire function. + +440 +00:22:09,795 --> 00:22:11,130 +When you are writing your actor, + +441 +00:22:11,130 --> 00:22:14,567 +think in terms of synchronous, +transactional operations + +442 +00:22:14,567 --> 00:22:17,102 +that can be interleaved +in any way. + +443 +00:22:17,102 --> 00:22:19,538 +Every one of them +should ensure that the actor + +444 +00:22:19,538 --> 00:22:23,075 +is in a good state +when it exits. + +445 +00:22:23,075 --> 00:22:26,579 +For async actor operations, +keep them simple, + +446 +00:22:26,579 --> 00:22:28,914 +forming them primarily +from your synchronous, + +447 +00:22:28,914 --> 00:22:31,050 +transactional operations, + +448 +00:22:31,050 --> 00:22:33,586 +and take care that your actor +is in a good state + +449 +00:22:33,586 --> 00:22:35,921 +at each await operation. + +450 +00:22:35,921 --> 00:22:38,657 +This way, you can make full use +of actors to eliminate + +451 +00:22:38,657 --> 00:22:42,628 +both low-level +and high-level data races. + +452 +00:22:42,628 --> 00:22:44,230 +In a concurrent program, + +453 +00:22:44,230 --> 00:22:46,265 +many things +are happening at once, + +454 +00:22:46,265 --> 00:22:48,167 +so the order in which +those things happen + +455 +00:22:48,167 --> 00:22:51,837 +can vary from one execution +to the next. + +456 +00:22:51,837 --> 00:22:55,074 +And yet programs often rely +on handling events + +457 +00:22:55,074 --> 00:22:56,875 +in a consistent order. + +458 +00:22:56,875 --> 00:22:58,477 +For example, +the stream of events + +459 +00:22:58,477 --> 00:23:02,548 +that come in from user input +or messages from a server. + +460 +00:23:02,548 --> 00:23:04,149 +When these +event streams come in, + +461 +00:23:04,149 --> 00:23:07,686 +we expect their effects +to happen in order. + +462 +00:23:07,686 --> 00:23:11,156 +Swift Concurrency provides tools +for ordering operations, + +463 +00:23:11,156 --> 00:23:14,927 +however, actors are not +the tool for doing so. + +464 +00:23:14,927 --> 00:23:17,696 +Actors execute the +highest-priority work first, + +465 +00:23:17,696 --> 00:23:20,466 +to help the overall system +stay responsive. + +466 +00:23:20,466 --> 00:23:22,635 +This eliminates +priority inversions + +467 +00:23:22,635 --> 00:23:24,703 +where lower-priority work +ends up happening + +468 +00:23:24,703 --> 00:23:28,807 +before higher-priority work +on the same actor. + +469 +00:23:28,807 --> 00:23:31,243 +Note that this +is a significant difference + +470 +00:23:31,243 --> 00:23:33,279 +from serial Dispatch queues, + +471 +00:23:33,279 --> 00:23:38,617 +which execute in a strictly +First-In, First-Out order. + +472 +00:23:38,617 --> 00:23:41,620 +Swift Concurrency has several +tools for ordering work. + +473 +00:23:41,620 --> 00:23:45,824 +The first we've been talking +about a lot already -- tasks. + +474 +00:23:45,824 --> 00:23:48,560 +Tasks execute +from beginning to end, + +475 +00:23:48,560 --> 00:23:50,963 +with the normal control flow +you're used to, + +476 +00:23:50,963 --> 00:23:54,099 +so they naturally order work. + +477 +00:23:54,099 --> 00:23:58,270 +AsyncStream can be used to model +an actual stream of events. + +478 +00:23:58,270 --> 00:24:01,006 +One task can iterate +over the stream of events + +479 +00:24:01,006 --> 00:24:05,811 +with a for-await-in loop, +processing each event in turn. + +480 +00:24:05,811 --> 00:24:07,446 +An AsyncStream can be shared + +481 +00:24:07,446 --> 00:24:09,615 +with any number +of event producers, + +482 +00:24:09,615 --> 00:24:11,283 +which can add elements +to the stream + +483 +00:24:11,283 --> 00:24:14,820 +while maintaining order. + +484 +00:24:14,820 --> 00:24:17,856 +We've talked a lot about +how Swift's concurrency model + +485 +00:24:17,856 --> 00:24:20,025 +is designed to eliminate +data races + +486 +00:24:20,025 --> 00:24:23,095 +using the notion of isolation, +which is maintained + +487 +00:24:23,095 --> 00:24:26,799 +by Sendable checking +at task and actor boundaries. + +488 +00:24:26,799 --> 00:24:29,635 +However, we cannot all +just stop what we are doing + +489 +00:24:29,635 --> 00:24:32,905 +to go mark all of the +Sendable types everywhere. + +490 +00:24:32,905 --> 00:24:36,141 +Instead, we need +an incremental approach. + +491 +00:24:36,141 --> 00:24:39,445 +Swift 5.7 introduces +a build setting to specify + +492 +00:24:39,445 --> 00:24:44,550 +how strictly the Swift compiler +should check for Sendability. + +493 +00:24:44,550 --> 00:24:46,618 +The default setting is Minimal + +494 +00:24:46,618 --> 00:24:49,321 +meaning that the compiler +will only diagnose places + +495 +00:24:49,321 --> 00:24:53,225 +where one has explicitly tried +to mark something as Sendable. + +496 +00:24:53,225 --> 00:24:56,729 +This is similar to how Swift 5.5 +and 5.6 behaved, + +497 +00:24:56,729 --> 00:25:00,733 +and for the above, there won't +be any warnings or errors. + +498 +00:25:00,733 --> 00:25:03,635 +Now, if you add +a Sendable conformance, + +499 +00:25:03,635 --> 00:25:06,138 +the compiler will complain +that the Coop type + +500 +00:25:06,138 --> 00:25:09,708 +cannot be Sendable because +Chicken isn't Sendable. + +501 +00:25:09,708 --> 00:25:13,579 +However, this -- and other +Sendable-related problems -- + +502 +00:25:13,579 --> 00:25:17,282 +will be presented as warnings +in Swift 5, not errors, + +503 +00:25:17,282 --> 00:25:22,054 +to make it easier to work +through the problems one by one. + +504 +00:25:22,054 --> 00:25:24,623 +To move further +toward data race safety, + +505 +00:25:24,623 --> 00:25:28,026 +enable the “targeted” +strict concurrency setting. + +506 +00:25:28,026 --> 00:25:30,462 +This setting enables Sendable +checking for code + +507 +00:25:30,462 --> 00:25:33,132 +that has already adopted +Swift Concurrency features + +508 +00:25:33,132 --> 00:25:37,069 +like async/await, tasks, +or actors. + +509 +00:25:37,069 --> 00:25:38,804 +This will identify, for example, + +510 +00:25:38,804 --> 00:25:41,740 +attempts to capture values +of non-Sendable type + +511 +00:25:41,740 --> 00:25:45,477 +in a newly created task. + +512 +00:25:45,477 --> 00:25:50,082 +Sometimes the non-Sendable types +come from another module. + +513 +00:25:50,082 --> 00:25:52,551 +Perhaps it's some package +that hasn't been updated + +514 +00:25:52,551 --> 00:25:55,287 +for Sendable yet, +or even your own module + +515 +00:25:55,287 --> 00:25:57,956 +that you just haven't +gotten around to. + +516 +00:25:57,956 --> 00:26:01,293 +For those, you can temporarily +disable the Sendable warnings + +517 +00:26:01,293 --> 00:26:03,061 +for types that come +from that module + +518 +00:26:03,061 --> 00:26:07,433 +using the @preconcurrency +attribute. + +519 +00:26:07,433 --> 00:26:10,369 +This will silence Sendable +warnings for the Chicken type + +520 +00:26:10,369 --> 00:26:12,638 +within this source file. + +521 +00:26:12,638 --> 00:26:14,573 +At some point, +the FarmAnimals module + +522 +00:26:14,573 --> 00:26:17,142 +will get updated +with Sendable conformances. + +523 +00:26:17,142 --> 00:26:19,812 +Then, one of two things +will happen: + +524 +00:26:19,812 --> 00:26:22,347 +either Chicken +becomes Sendable somehow, + +525 +00:26:22,347 --> 00:26:25,484 +in which case the preconcurrency +attribute can be removed + +526 +00:26:25,484 --> 00:26:27,352 +from the import. + +527 +00:26:27,352 --> 00:26:30,589 +Or Chicken will be known +to be non-Sendable, + +528 +00:26:30,589 --> 00:26:33,225 +in which case the warning +will come back, + +529 +00:26:33,225 --> 00:26:35,961 +indicating that your assumptions +about Chicken being Sendable + +530 +00:26:35,961 --> 00:26:38,964 +are, in fact, not correct. + +531 +00:26:38,964 --> 00:26:42,100 +The targeted strictness setting +tries to strike a balance + +532 +00:26:42,100 --> 00:26:44,870 +between compatibility +with existing code + +533 +00:26:44,870 --> 00:26:47,806 +and identifying potential +data races. + +534 +00:26:47,806 --> 00:26:49,441 +However, if you'd like to see + +535 +00:26:49,441 --> 00:26:51,443 +everywhere that races +could occur, + +536 +00:26:51,443 --> 00:26:55,747 +there is one more option: +complete checking. + +537 +00:26:55,747 --> 00:26:59,251 +Complete checking approximates +the intended Swift 6 semantics + +538 +00:26:59,251 --> 00:27:01,753 +to completely eliminate +data races. + +539 +00:27:01,753 --> 00:27:04,656 +It checks everything +that the earlier two modes check + +540 +00:27:04,656 --> 00:27:07,759 +but does so for all code +in the module. + +541 +00:27:07,759 --> 00:27:09,261 +Here, we're not +actually making use + +542 +00:27:09,261 --> 00:27:11,563 +of Swift's concurrency +features at all. + +543 +00:27:11,563 --> 00:27:14,199 +Rather, it's performing work +on a dispatch queue, + +544 +00:27:14,199 --> 00:27:17,636 +which will execute +that code concurrently. + +545 +00:27:17,636 --> 00:27:20,005 +The async operation +on a dispatch queue + +546 +00:27:20,005 --> 00:27:22,608 +is actually known to take +a Sendable closure, + +547 +00:27:22,608 --> 00:27:25,043 +so the compiler produces +a warning indicating + +548 +00:27:25,043 --> 00:27:27,913 +that there is a data race +when the non-Sendable body + +549 +00:27:27,913 --> 00:27:31,884 +is captured by the code +running on the dispatch queue. + +550 +00:27:31,884 --> 00:27:36,889 +We can fix this by making +the body parameter Sendable. + +551 +00:27:36,889 --> 00:27:38,991 +That change eliminates +this warning, + +552 +00:27:38,991 --> 00:27:41,793 +and now all of the callers +of doWork know + +553 +00:27:41,793 --> 00:27:45,564 +that they need to provide +a Sendable closure. + +554 +00:27:45,564 --> 00:27:48,433 +That means we get better +checking for data races, + +555 +00:27:48,433 --> 00:27:50,736 +and we can see +that the visit function now + +556 +00:27:50,736 --> 00:27:53,472 +is the source of a data race. + +557 +00:27:53,472 --> 00:27:55,474 +Complete checking +will help flush out + +558 +00:27:55,474 --> 00:28:00,045 +the potential data races +in your program. + +559 +00:28:00,045 --> 00:28:03,415 +To achieve Swift's goal +of eliminating data races, + +560 +00:28:03,415 --> 00:28:06,818 +we'll eventually need to get +to complete checking. + +561 +00:28:06,818 --> 00:28:10,822 +We encourage you to work +incrementally toward that goal: + +562 +00:28:10,822 --> 00:28:12,691 +adopt Swift's concurrency model + +563 +00:28:12,691 --> 00:28:15,294 +to architect your app +for data race safety, + +564 +00:28:15,294 --> 00:28:18,297 +then enable progressively +stricter concurrency checking + +565 +00:28:18,297 --> 00:28:21,433 +to eliminate classes +of errors from your code. + +566 +00:28:21,433 --> 00:28:24,836 +And don't fret over marking your +imports with @preconcurrency + +567 +00:28:24,836 --> 00:28:27,339 +to suppress warnings +for imported types. + +568 +00:28:27,339 --> 00:28:30,509 +As those modules adopt +stricter concurrency checking, + +569 +00:28:30,509 --> 00:28:33,912 +the compiler will recheck +your assumptions. + +570 +00:28:33,912 --> 00:28:36,281 +At the end of this road, +your code will benefit + +571 +00:28:36,281 --> 00:28:39,351 +from both memory safety +and data race safety, + +572 +00:28:39,351 --> 00:28:42,854 +helping you focus +on building great apps. + +573 +00:28:42,854 --> 00:28:47,225 +And thank you for sailing with me +on the sea of concurrency. + +574 +00:28:47,225 --> 00:28:51,730 +♪ + diff --git a/eng/2022 Session 110352 Embrace Swift generics en.srt b/eng/2022 Session 110352 Embrace Swift generics en.srt new file mode 100644 index 0000000..d72f028 --- /dev/null +++ b/eng/2022 Session 110352 Embrace Swift generics en.srt @@ -0,0 +1,2588 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,743 +♪ + +3 +00:00:09,743 --> 00:00:13,580 +Hi everyone, I'm Holly +from the Swift Compiler team. + +4 +00:00:13,580 --> 00:00:17,351 +Welcome +to "Embrace Swift generics." + +5 +00:00:17,351 --> 00:00:19,486 +Generics are a fundamental tool + +6 +00:00:19,486 --> 00:00:21,922 +for writing abstract code +in Swift, + +7 +00:00:21,922 --> 00:00:26,526 +which is crucial for managing +complexity as your code evolves. + +8 +00:00:26,526 --> 00:00:31,465 +Abstraction separates ideas +from specific details. + +9 +00:00:31,465 --> 00:00:34,201 +In code, there are +a lot of different ways + +10 +00:00:34,201 --> 00:00:36,536 +abstraction is useful. + +11 +00:00:36,536 --> 00:00:40,173 +One form of abstraction +that you likely use all the time + +12 +00:00:40,173 --> 00:00:45,212 +is factoring code out into +a function or a local variable. + +13 +00:00:45,212 --> 00:00:47,481 +This can be really useful +if you need to use + +14 +00:00:47,481 --> 00:00:51,551 +the same functionality +or value multiple times. + +15 +00:00:51,551 --> 00:00:55,055 +When you extract the +functionality into a function, + +16 +00:00:55,055 --> 00:00:57,391 +the details are abstracted away, + +17 +00:00:57,391 --> 00:00:59,693 +and the code +that uses the abstraction + +18 +00:00:59,693 --> 00:01:01,929 +can express +the idea of what's happening + +19 +00:01:01,929 --> 00:01:04,464 +without repeating the details. + +20 +00:01:04,464 --> 00:01:09,102 +In Swift, you can also +abstract away concrete types. + +21 +00:01:09,102 --> 00:01:12,039 +If you have a set of types +that are all the same idea + +22 +00:01:12,039 --> 00:01:15,409 +with different details, +you can write abstract code + +23 +00:01:15,409 --> 00:01:18,979 +to work with all +of those concrete types. + +24 +00:01:18,979 --> 00:01:21,448 +Today, we'll walk through +the workflow + +25 +00:01:21,448 --> 00:01:24,518 +of modeling code +with concrete types, + +26 +00:01:24,518 --> 00:01:29,589 +identifying common capabilities +of a set of concrete types, + +27 +00:01:29,589 --> 00:01:32,926 +building an interface +to represent those capabilities, + +28 +00:01:32,926 --> 00:01:34,127 +and finally, + +29 +00:01:34,127 --> 00:01:38,432 +we'll dive into writing generic +code using that interface. + +30 +00:01:38,432 --> 00:01:40,801 +We'll dig into Swift's +abstraction tools + +31 +00:01:40,801 --> 00:01:44,271 +while building up some code +to simulate a farm. + +32 +00:01:44,271 --> 00:01:48,141 +So, let's start by writing +some concrete types. + +33 +00:01:48,141 --> 00:01:50,877 +We'll start with one struct +called "Cow." + +34 +00:01:50,877 --> 00:01:52,679 +Cow has a method called "eat," + +35 +00:01:52,679 --> 00:01:56,016 +which accepts +a parameter of type Hay. + +36 +00:01:56,016 --> 00:01:58,018 +Hay is another struct. + +37 +00:01:58,018 --> 00:02:00,253 +It has a static method +called "grow" + +38 +00:02:00,253 --> 00:02:02,422 +to grow the crop +that produces Hay, + +39 +00:02:02,422 --> 00:02:05,058 +which is Alfalfa. + +40 +00:02:05,058 --> 00:02:07,828 +The Alfalfa struct +has a method to harvest Hay + +41 +00:02:07,828 --> 00:02:10,664 +from an instance of Alfalfa. + +42 +00:02:10,664 --> 00:02:13,233 +Finally, +we'll add a struct called "Farm" + +43 +00:02:13,233 --> 00:02:16,570 +that has a method +for feeding a cow. + +44 +00:02:16,570 --> 00:02:18,305 +The feed method +can be implemented + +45 +00:02:18,305 --> 00:02:21,908 +by first growing some +alfalfa to produce hay, + +46 +00:02:21,908 --> 00:02:23,977 +then harvesting the hay, + +47 +00:02:23,977 --> 00:02:26,880 +and finally, +feeding the hay to the cow. + +48 +00:02:26,880 --> 00:02:30,317 +And now, I can feed cows +on my farm. + +49 +00:02:30,317 --> 00:02:34,421 +But I want to add +more kinds of animals. + +50 +00:02:34,421 --> 00:02:37,324 +I can add more structs +to represent other animals, + +51 +00:02:37,324 --> 00:02:39,760 +like Horse and Chicken. + +52 +00:02:39,760 --> 00:02:43,630 +And I want to be able to feed +cows, horses, and chickens + +53 +00:02:43,630 --> 00:02:46,033 +on the farm. + +54 +00:02:46,033 --> 00:02:48,368 +I could overload the feed method + +55 +00:02:48,368 --> 00:02:51,671 +to accept each type +of parameter separately, + +56 +00:02:51,671 --> 00:02:56,910 +but each overload will have +a really similar implementation. + +57 +00:02:56,910 --> 00:02:59,079 +This will become +extra boilerplate + +58 +00:02:59,079 --> 00:03:01,515 +as I add more types of animals, + +59 +00:03:01,515 --> 00:03:04,785 +and it's mostly +repeated code anyway. + +60 +00:03:04,785 --> 00:03:07,020 +If you find yourself +writing overloads + +61 +00:03:07,020 --> 00:03:09,256 +with repetitive implementations, + +62 +00:03:09,256 --> 00:03:11,691 +it might be a sign +to generalize. + +63 +00:03:11,691 --> 00:03:15,362 +Fundamentally, these +implementations are so similar + +64 +00:03:15,362 --> 00:03:17,330 +because different types +of animals + +65 +00:03:17,330 --> 00:03:20,233 +are similar in functionality. + +66 +00:03:20,233 --> 00:03:23,203 +The next step is to identify +the common capabilities + +67 +00:03:23,203 --> 00:03:25,672 +between the animal types. + +68 +00:03:25,672 --> 00:03:27,340 +We've built a set +of animal types + +69 +00:03:27,340 --> 00:03:31,311 +that all have the ability +to eat some type of food. + +70 +00:03:31,311 --> 00:03:34,548 +Each type of animal will have +a different way of eating, + +71 +00:03:34,548 --> 00:03:36,883 +so each implementation +of the eat method + +72 +00:03:36,883 --> 00:03:40,320 +will have differences +in behavior. + +73 +00:03:40,320 --> 00:03:41,521 +What we want to do + +74 +00:03:41,521 --> 00:03:44,758 +is allow abstract code +to call the eat method + +75 +00:03:44,758 --> 00:03:47,761 +and have that abstract code +behave differently + +76 +00:03:47,761 --> 00:03:51,331 +depending on the concrete type +it's operating on. + +77 +00:03:51,331 --> 00:03:54,568 +The ability of abstract code +to behave differently + +78 +00:03:54,568 --> 00:03:58,805 +for different concrete types +is called "polymorphism." + +79 +00:03:58,805 --> 00:04:03,510 +Polymorphism allows one piece +of code to have many behaviors + +80 +00:04:03,510 --> 00:04:07,214 +depending on how +the code is used. + +81 +00:04:07,214 --> 00:04:12,152 +Appropriately, polymorphism +itself comes in different forms. + +82 +00:04:12,152 --> 00:04:14,654 +The first is +function overloading, + +83 +00:04:14,654 --> 00:04:17,357 +where the same function call +can mean different things + +84 +00:04:17,357 --> 00:04:20,026 +depending on the argument type. + +85 +00:04:20,026 --> 00:04:23,130 +Overloading is called +"ad-hoc polymorphism" + +86 +00:04:23,130 --> 00:04:26,032 +because it isn't really +a general solution. + +87 +00:04:26,032 --> 00:04:30,570 +We just saw how overloading +can lead to repetitive code. + +88 +00:04:30,570 --> 00:04:33,140 +Next is subtype polymorphism, + +89 +00:04:33,140 --> 00:04:35,475 +where code +operating on a supertype + +90 +00:04:35,475 --> 00:04:38,545 +can have different behavior +based on the specific subtype + +91 +00:04:38,545 --> 00:04:41,281 +the code is using at runtime. + +92 +00:04:41,281 --> 00:04:44,684 +Finally, we have +parametric polymorphism, + +93 +00:04:44,684 --> 00:04:47,988 +which is achieved +using generics. + +94 +00:04:47,988 --> 00:04:51,391 +Generic code uses +type parameters to allow writing + +95 +00:04:51,391 --> 00:04:54,561 +one piece of code +that works with different types, + +96 +00:04:54,561 --> 00:04:58,865 +and concrete types themselves +are used as arguments. + +97 +00:04:58,865 --> 00:05:01,268 +We've already +ruled out overloading, + +98 +00:05:01,268 --> 00:05:04,971 +so let's try to use +subtype polymorphism. + +99 +00:05:04,971 --> 00:05:07,974 +One way to represent +subtype relationships + +100 +00:05:07,974 --> 00:05:10,277 +is with a class hierarchy. + +101 +00:05:10,277 --> 00:05:14,514 +We could introduce +a class called "Animal." + +102 +00:05:14,514 --> 00:05:19,953 +Next, we'd change each animal +type from a struct to a class. + +103 +00:05:19,953 --> 00:05:22,489 +Each specific animal class +will inherit + +104 +00:05:22,489 --> 00:05:27,394 +from the Animal superclass, +and override the eat method. + +105 +00:05:27,394 --> 00:05:31,231 +Now, we have +an abstract base-class Animal + +106 +00:05:31,231 --> 00:05:35,468 +that can represent all +of our specific animal types. + +107 +00:05:35,468 --> 00:05:38,305 +Code that calls eat +on the Animal class + +108 +00:05:38,305 --> 00:05:40,440 +will use subtype polymorphism + +109 +00:05:40,440 --> 00:05:43,143 +to call the subclass +implementation. + +110 +00:05:43,143 --> 00:05:44,878 +But we're not done. + +111 +00:05:44,878 --> 00:05:47,047 +We still haven't filled +in a parameter type + +112 +00:05:47,047 --> 00:05:49,049 +for the eat method on Animal, + +113 +00:05:49,049 --> 00:05:52,886 +and there are a few other +red flags in this code. + +114 +00:05:52,886 --> 00:05:57,324 +First, using classes forced us +into reference semantics, + +115 +00:05:57,324 --> 00:06:00,427 +even though we don't need +or want any state to be shared + +116 +00:06:00,427 --> 00:06:04,197 +between different +animal instances. + +117 +00:06:04,197 --> 00:06:06,900 +This strategy +also requires subclasses + +118 +00:06:06,900 --> 00:06:09,669 +to override methods +in the base class, + +119 +00:06:09,669 --> 00:06:11,771 +but forgetting to do this +wouldn't be caught + +120 +00:06:11,771 --> 00:06:13,974 +until runtime. + +121 +00:06:13,974 --> 00:06:16,676 +But the bigger problem +with this model of abstraction + +122 +00:06:16,676 --> 00:06:20,947 +is that each animal subtype +eats a different type of food, + +123 +00:06:20,947 --> 00:06:23,750 +and this dependency +is really difficult to express + +124 +00:06:23,750 --> 00:06:26,786 +with a class hierarchy. + +125 +00:06:26,786 --> 00:06:29,656 +One approach we could take +is to have the method accept + +126 +00:06:29,656 --> 00:06:33,226 +a less specific type, +such as Any. + +127 +00:06:33,226 --> 00:06:36,596 +But this strategy relies +on subclass implementations + +128 +00:06:36,596 --> 00:06:40,333 +to make sure the correct type +was passed at runtime. + +129 +00:06:40,333 --> 00:06:42,335 +So, we've imposed +extra boilerplate + +130 +00:06:42,335 --> 00:06:45,939 +in each overridden method, +but more importantly, + +131 +00:06:45,939 --> 00:06:49,309 +it allows you to accidentally +pass the wrong type of food, + +132 +00:06:49,309 --> 00:06:51,077 +leaving you with another bug + +133 +00:06:51,077 --> 00:06:53,780 +that could only be caught +at runtime. + +134 +00:06:53,780 --> 00:06:56,650 +So, let's try something else. + +135 +00:06:56,650 --> 00:06:59,552 +We could instead express +the animal's feed type + +136 +00:06:59,552 --> 00:07:01,488 +in a type-safe way + +137 +00:07:01,488 --> 00:07:06,226 +by introducing a type parameter +on the Animal superclass. + +138 +00:07:06,226 --> 00:07:09,062 +This type parameter serves +as a placeholder + +139 +00:07:09,062 --> 00:07:13,400 +for the specific feed type +for each subclass. + +140 +00:07:13,400 --> 00:07:15,935 +With this approach, +the Food type parameter + +141 +00:07:15,935 --> 00:07:20,440 +must be elevated to the +declaration of the Animal class. + +142 +00:07:20,440 --> 00:07:22,309 +This seems a little unnatural + +143 +00:07:22,309 --> 00:07:25,111 +because though animals +need food to operate, + +144 +00:07:25,111 --> 00:07:28,048 +eating food isn't +the core purpose of an animal, + +145 +00:07:28,048 --> 00:07:30,116 +and a lot of code +that works with animals + +146 +00:07:30,116 --> 00:07:33,253 +probably won't care +about food at all. + +147 +00:07:33,253 --> 00:07:36,523 +Despite that, all references +to the Animal class + +148 +00:07:36,523 --> 00:07:39,259 +need to specify the food type. + +149 +00:07:39,259 --> 00:07:41,995 +For example, +each Animal subclass + +150 +00:07:41,995 --> 00:07:45,865 +needs to explicitly specify +its food type in angle brackets + +151 +00:07:45,865 --> 00:07:48,702 +in the inheritance clause. + +152 +00:07:48,702 --> 00:07:52,138 +This boilerplate at each +use site of the Animal class + +153 +00:07:52,138 --> 00:07:55,675 +could become onerous +if we added more types + +154 +00:07:55,675 --> 00:07:58,311 +that are specific +to each animal. + +155 +00:07:58,311 --> 00:07:59,846 +So, none of our approaches here + +156 +00:07:59,846 --> 00:08:04,250 +have good ergonomics +or the right semantics. + +157 +00:08:04,250 --> 00:08:08,455 +The fundamental problem +is that a class is a data type, + +158 +00:08:08,455 --> 00:08:10,924 +and we're trying +to convolute a superclass + +159 +00:08:10,924 --> 00:08:15,695 +to make it represent abstract +ideas about concrete types. + +160 +00:08:15,695 --> 00:08:18,932 +Instead, we want +a language construct + +161 +00:08:18,932 --> 00:08:22,168 +that was designed to represent +capabilities of types + +162 +00:08:22,168 --> 00:08:26,106 +without the details +of how the capability works. + +163 +00:08:26,106 --> 00:08:29,442 +Animals have +two common capabilities. + +164 +00:08:29,442 --> 00:08:32,379 +Each animal has +a specific type of food, + +165 +00:08:32,379 --> 00:08:36,616 +along with an operation +for consuming some of that food. + +166 +00:08:36,616 --> 00:08:41,020 +We can build an interface that +represents those capabilities. + +167 +00:08:41,020 --> 00:08:44,924 +In Swift, this is done +using a protocol. + +168 +00:08:44,924 --> 00:08:47,193 +A protocol +is an abstraction tool + +169 +00:08:47,193 --> 00:08:52,031 +that describes the functionality +of conforming types. + +170 +00:08:52,031 --> 00:08:55,235 +Using a protocol, +you can separate the ideas + +171 +00:08:55,235 --> 00:08:59,372 +about what a type does +from the implementation details. + +172 +00:08:59,372 --> 00:09:01,641 +The ideas about +what a type does + +173 +00:09:01,641 --> 00:09:04,911 +are expressed +through an interface. + +174 +00:09:04,911 --> 00:09:07,514 +Let's translate +the capabilities of an animal + +175 +00:09:07,514 --> 00:09:10,049 +to a protocol interface. + +176 +00:09:10,049 --> 00:09:11,451 +The name of the protocol + +177 +00:09:11,451 --> 00:09:14,621 +represents the category of types +we're describing, + +178 +00:09:14,621 --> 00:09:17,190 +so I called +this protocol "Animal." + +179 +00:09:17,190 --> 00:09:21,394 +Each capability will map +to a protocol requirement. + +180 +00:09:21,394 --> 00:09:23,430 +The specific type +of food will map + +181 +00:09:23,430 --> 00:09:28,034 +to an associated type +of the Animal protocol. + +182 +00:09:28,034 --> 00:09:30,804 +Just like a type parameter, +an associated type + +183 +00:09:30,804 --> 00:09:34,073 +serves as a placeholder +for a concrete type. + +184 +00:09:34,073 --> 00:09:36,376 +What makes +associated types special + +185 +00:09:36,376 --> 00:09:38,611 +is that they depend +on the specific type + +186 +00:09:38,611 --> 00:09:40,947 +that conforms +to the protocol. + +187 +00:09:40,947 --> 00:09:43,316 +This relationship +is guaranteed, + +188 +00:09:43,316 --> 00:09:46,319 +so each instance +of a specific type of animal + +189 +00:09:46,319 --> 00:09:49,789 +always has +the same type of food. + +190 +00:09:49,789 --> 00:09:55,028 +Next, the operation to consume +food will map to a method. + +191 +00:09:55,028 --> 00:09:56,896 +This method is called "eat," + +192 +00:09:56,896 --> 00:10:00,266 +and it accepts a parameter +of the animal's feed type. + +193 +00:10:00,266 --> 00:10:03,536 +The protocol does not have an +implementation of this method, + +194 +00:10:03,536 --> 00:10:08,475 +and concrete animal types +are required to implement it. + +195 +00:10:08,475 --> 00:10:10,810 +Now that we have +the Animal protocol, + +196 +00:10:10,810 --> 00:10:14,848 +we can make each concrete +animal type conform to it. + +197 +00:10:14,848 --> 00:10:18,451 +You can annotate a concrete type +with a protocol conformance + +198 +00:10:18,451 --> 00:10:22,088 +at the declaration +or in an extension. + +199 +00:10:22,088 --> 00:10:24,791 +Protocols are not limited +to classes, + +200 +00:10:24,791 --> 00:10:29,596 +so we can use protocols with +structs, enums, and actors, too. + +201 +00:10:29,596 --> 00:10:31,998 +Once you write +this conformance annotation, + +202 +00:10:31,998 --> 00:10:34,434 +the compiler will check +that the concrete type + +203 +00:10:34,434 --> 00:10:38,705 +implements each +of the protocol requirements. + +204 +00:10:38,705 --> 00:10:42,208 +Each animal type must +implement the eat method, + +205 +00:10:42,208 --> 00:10:45,278 +and the compiler can infer +what the feed type is, + +206 +00:10:45,278 --> 00:10:48,781 +because it's used +in the parameter list. + +207 +00:10:48,781 --> 00:10:51,050 +The feed type can also +be written explicitly + +208 +00:10:51,050 --> 00:10:54,220 +using a type alias. + +209 +00:10:54,220 --> 00:10:56,789 +We've successfully identified +the common capabilities + +210 +00:10:56,789 --> 00:10:59,959 +of an animal and expressed +those capabilities, + +211 +00:10:59,959 --> 00:11:02,328 +using a protocol interface. + +212 +00:11:02,328 --> 00:11:06,132 +Now, we can start +to write generic code. + +213 +00:11:06,132 --> 00:11:08,001 +We can use +the Animal protocol + +214 +00:11:08,001 --> 00:11:10,737 +to implement +the feed method on Farm. + +215 +00:11:10,737 --> 00:11:12,805 +We want to write +one implementation + +216 +00:11:12,805 --> 00:11:15,875 +that works for all +concrete animal types. + +217 +00:11:15,875 --> 00:11:18,177 +We'll use +parametric polymorphism + +218 +00:11:18,177 --> 00:11:21,314 +and introduce a type parameter +that will be replaced + +219 +00:11:21,314 --> 00:11:25,285 +with a concrete type +when the method is called. + +220 +00:11:25,285 --> 00:11:28,254 +A type parameter is written +after the function name + +221 +00:11:28,254 --> 00:11:30,089 +in angle brackets. + +222 +00:11:30,089 --> 00:11:33,393 +Just like regular variables +and function parameters, + +223 +00:11:33,393 --> 00:11:37,163 +you can name a type +parameter whatever you like. + +224 +00:11:37,163 --> 00:11:39,098 +And just like +any other type, + +225 +00:11:39,098 --> 00:11:40,767 +you can reference +the type parameter + +226 +00:11:40,767 --> 00:11:44,304 +throughout the function +signature, using its name. + +227 +00:11:44,304 --> 00:11:47,373 +Here, I declared +a type parameter called “A”, + +228 +00:11:47,373 --> 00:11:51,711 +and I used A as the type of +the animal function parameter. + +229 +00:11:51,711 --> 00:11:53,746 +We always want +the concrete animal type + +230 +00:11:53,746 --> 00:11:56,683 +to conform +to the Animal protocol, + +231 +00:11:56,683 --> 00:11:58,384 +so we annotate +the type parameter + +232 +00:11:58,384 --> 00:12:00,820 +with a protocol conformance. + +233 +00:12:00,820 --> 00:12:04,457 +Protocol conformances can be +written in angle brackets, + +234 +00:12:04,457 --> 00:12:07,460 +or they can be written +in a trailing "where" clause, + +235 +00:12:07,460 --> 00:12:09,862 +where you can also +specify relationships + +236 +00:12:09,862 --> 00:12:13,032 +between different +type parameters. + +237 +00:12:13,032 --> 00:12:15,868 +Named type parameters +and trailing "where" clauses + +238 +00:12:15,868 --> 00:12:18,905 +are really powerful, +because they allow you to write + +239 +00:12:18,905 --> 00:12:22,809 +sophisticated requirements +and type relationships. + +240 +00:12:22,809 --> 00:12:26,312 +But most generic functions +don't need this generality. + +241 +00:12:26,312 --> 00:12:29,882 +Let's focus on +the feed method. + +242 +00:12:29,882 --> 00:12:34,253 +The type parameter A appears +once in the parameter list, + +243 +00:12:34,253 --> 00:12:37,490 +and the "where" clause lists +a conformance requirement + +244 +00:12:37,490 --> 00:12:39,292 +on the type parameter. + +245 +00:12:39,292 --> 00:12:40,660 +In this case, + +246 +00:12:40,660 --> 00:12:43,796 +naming the type parameter +and using the "where" clause + +247 +00:12:43,796 --> 00:12:46,999 +make the method look more +complicated than it really is. + +248 +00:12:46,999 --> 00:12:49,769 +This generic pattern +is really common, + +249 +00:12:49,769 --> 00:12:52,605 +so there's a simpler way +to express it. + +250 +00:12:52,605 --> 00:12:55,642 +Instead of writing +a type parameter explicitly, + +251 +00:12:55,642 --> 00:12:58,244 +we can express +this abstract type + +252 +00:12:58,244 --> 00:13:00,813 +in terms of the +protocol conformance + +253 +00:13:00,813 --> 00:13:03,349 +by writing "some Animal”. + +254 +00:13:03,349 --> 00:13:06,953 +This declaration is identical +to the previous one, + +255 +00:13:06,953 --> 00:13:09,355 +but the unnecessary +type parameter list + +256 +00:13:09,355 --> 00:13:11,290 +and "where" clause are gone, + +257 +00:13:11,290 --> 00:13:14,494 +because we didn't need the +expressiveness they provide. + +258 +00:13:14,494 --> 00:13:17,130 +Writing "some Animal" +is more straightforward, + +259 +00:13:17,130 --> 00:13:19,866 +because it reduces +syntactic noise, + +260 +00:13:19,866 --> 00:13:21,968 +and it includes +the semantic information + +261 +00:13:21,968 --> 00:13:23,703 +about the animal parameter + +262 +00:13:23,703 --> 00:13:27,140 +right in the parameter +declaration. + +263 +00:13:27,140 --> 00:13:30,543 +Let's break down +the some Animal syntax. + +264 +00:13:30,543 --> 00:13:32,378 +The "some" in "some Animal" + +265 +00:13:32,378 --> 00:13:34,847 +indicates that there is +a specific type + +266 +00:13:34,847 --> 00:13:37,150 +that you're working with. + +267 +00:13:37,150 --> 00:13:39,452 +The "some" keyword +is always followed + +268 +00:13:39,452 --> 00:13:42,055 +by a conformance +requirement. + +269 +00:13:42,055 --> 00:13:45,091 +In this case, +the specific type must conform + +270 +00:13:45,091 --> 00:13:47,794 +to the Animal protocol, +which will allow us + +271 +00:13:47,794 --> 00:13:50,263 +to use requirements +from the Animal protocol + +272 +00:13:50,263 --> 00:13:52,598 +on the parameter value. + +273 +00:13:52,598 --> 00:13:56,836 +The "some" keyword can be used +in parameter and result types. + +274 +00:13:56,836 --> 00:13:59,138 +If you've written +SwiftUI code before, + +275 +00:13:59,138 --> 00:14:01,841 +you've already used "some" +in result position + +276 +00:14:01,841 --> 00:14:05,645 +using "some View." + +277 +00:14:05,645 --> 00:14:10,316 +A result type of "some View" +is exactly the same concept. + +278 +00:14:10,316 --> 00:14:13,119 +In a SwiftUI view, +the body property + +279 +00:14:13,119 --> 00:14:15,822 +returns some specific +type of view, + +280 +00:14:15,822 --> 00:14:18,257 +but code that uses +the body property + +281 +00:14:18,257 --> 00:14:21,661 +doesn't need to know +what the specific type is. + +282 +00:14:21,661 --> 00:14:24,063 +Let's take a step back +to better understand + +283 +00:14:24,063 --> 00:14:28,267 +the concept +of a specific abstract type. + +284 +00:14:28,267 --> 00:14:31,170 +An abstract type +that represents a placeholder + +285 +00:14:31,170 --> 00:14:36,109 +for a specific concrete type +is called an opaque type. + +286 +00:14:36,109 --> 00:14:39,112 +The specific concrete type +that is substituted in + +287 +00:14:39,112 --> 00:14:42,115 +is called +the underlying type. + +288 +00:14:42,115 --> 00:14:44,383 +For values with opaque type, + +289 +00:14:44,383 --> 00:14:48,654 +the underlying type is fixed +for the scope of the value. + +290 +00:14:48,654 --> 00:14:52,625 +This way, generic code +using the value is guaranteed + +291 +00:14:52,625 --> 00:14:57,530 +to get the same underlying type +each time the value is accessed. + +292 +00:14:57,530 --> 00:15:01,400 +A type using the "some" keyword +and a named type parameter + +293 +00:15:01,400 --> 00:15:05,805 +in angle brackets +both declare an opaque type. + +294 +00:15:05,805 --> 00:15:09,408 +Opaque types can be used +for both inputs and outputs, + +295 +00:15:09,408 --> 00:15:11,911 +so they can be declared +in parameter position + +296 +00:15:11,911 --> 00:15:15,214 +or in result position. + +297 +00:15:15,214 --> 00:15:17,583 +The function arrow +is the dividing line + +298 +00:15:17,583 --> 00:15:19,852 +between these positions. + +299 +00:15:19,852 --> 00:15:22,855 +The position of +an opaque type determines + +300 +00:15:22,855 --> 00:15:25,725 +which part of the program +sees the abstract type + +301 +00:15:25,725 --> 00:15:30,129 +and which part of the program +determines the concrete type. + +302 +00:15:30,129 --> 00:15:34,133 +Named type parameters are always +declared on the input side, + +303 +00:15:34,133 --> 00:15:36,869 +so the caller decides +the underlying type, + +304 +00:15:36,869 --> 00:15:41,407 +and the implementation +uses the abstract type. + +305 +00:15:41,407 --> 00:15:44,644 +In general, the part of the +program supplying the value + +306 +00:15:44,644 --> 00:15:47,446 +for an opaque parameter +or result type + +307 +00:15:47,446 --> 00:15:50,917 +decides the underlying type, +and the part of the program + +308 +00:15:50,917 --> 00:15:53,953 +using the value +sees the abstract type. + +309 +00:15:53,953 --> 00:15:56,155 +Let's dig into +how this works, + +310 +00:15:56,155 --> 00:16:00,593 +following our intuition about +parameter and result values. + +311 +00:16:00,593 --> 00:16:04,030 +Because the underlying type +is inferred from a value, + +312 +00:16:04,030 --> 00:16:07,133 +the underlying type always +comes from the same place + +313 +00:16:07,133 --> 00:16:09,602 +as the value. + +314 +00:16:09,602 --> 00:16:12,705 +For a local variable, +the underlying type is inferred + +315 +00:16:12,705 --> 00:16:16,642 +from the value on the +right-hand side of assignment. + +316 +00:16:16,642 --> 00:16:19,378 +This means local variables +with opaque type + +317 +00:16:19,378 --> 00:16:22,381 +must always have +an initial value; + +318 +00:16:22,381 --> 00:16:23,983 +and if you don't +provide one, + +319 +00:16:23,983 --> 00:16:27,286 +the compiler will +report an error. + +320 +00:16:27,286 --> 00:16:29,455 +The underlying type +must be fixed + +321 +00:16:29,455 --> 00:16:31,591 +for the scope +of the variable, + +322 +00:16:31,591 --> 00:16:33,993 +so attempting to change +the underlying type + +323 +00:16:33,993 --> 00:16:37,230 +will also result +in an error. + +324 +00:16:37,230 --> 00:16:40,999 +For parameters with opaque type, +the underlying type is inferred + +325 +00:16:40,999 --> 00:16:44,904 +from the argument value +at the call site. + +326 +00:16:44,904 --> 00:16:50,409 +Using "some" in parameter +position is new in Swift 5.7. + +327 +00:16:50,409 --> 00:16:52,345 +The underlying type +only needs to be fixed + +328 +00:16:52,345 --> 00:16:54,513 +for the scope +of the parameter, + +329 +00:16:54,513 --> 00:16:58,417 +so each call can provide +a different argument type. + +330 +00:16:58,417 --> 00:17:01,921 +For an opaque result type, +the underlying type is inferred + +331 +00:17:01,921 --> 00:17:05,157 +from the return value +in the implementation. + +332 +00:17:05,157 --> 00:17:08,961 +A method or computed property +with an opaque result type + +333 +00:17:08,961 --> 00:17:11,397 +can be called from anywhere +in the program, + +334 +00:17:11,397 --> 00:17:15,067 +so the scope of +this named value is global. + +335 +00:17:15,067 --> 00:17:18,237 +This means the underlying +return type has to be the same + +336 +00:17:18,237 --> 00:17:20,873 +across all +return statements; + +337 +00:17:20,873 --> 00:17:24,210 +and if it isn't, the compiler +will report an error + +338 +00:17:24,210 --> 00:17:28,481 +that the underlying return +values have mismatched types. + +339 +00:17:28,481 --> 00:17:31,784 +For an opaque SwiftUI view, +the ViewBuilder DSL + +340 +00:17:31,784 --> 00:17:34,453 +can transform +control-flow statements + +341 +00:17:34,453 --> 00:17:38,090 +to have the same underlying +return type for each branch. + +342 +00:17:38,090 --> 00:17:40,826 +So in this case, +we can fix the issue + +343 +00:17:40,826 --> 00:17:43,396 +by using +the ViewBuilder DSL. + +344 +00:17:43,396 --> 00:17:46,532 +Writing an @ViewBuilder +annotation on the method + +345 +00:17:46,532 --> 00:17:49,769 +and removing return statements +will enable the result + +346 +00:17:49,769 --> 00:17:53,639 +to be built for us +by the ViewBuilder type. + +347 +00:17:53,639 --> 00:17:56,542 +Let's go back +to the feedAnimal method. + +348 +00:17:56,542 --> 00:17:58,844 +I can use "some" +in the parameter list + +349 +00:17:58,844 --> 00:18:00,246 +because I don't need +to reference + +350 +00:18:00,246 --> 00:18:02,815 +the opaque type +anywhere else. + +351 +00:18:02,815 --> 00:18:06,185 +When you need to refer to +the opaque type multiple times + +352 +00:18:06,185 --> 00:18:07,920 +in the function signature, + +353 +00:18:07,920 --> 00:18:11,057 +that's when a name type +parameter comes in handy. + +354 +00:18:11,057 --> 00:18:14,727 +For example, if we add +another associated type + +355 +00:18:14,727 --> 00:18:18,397 +to the animal protocol +called "Habitat," + +356 +00:18:18,397 --> 00:18:20,599 +we might want to be able +to build a habitat + +357 +00:18:20,599 --> 00:18:23,703 +on the farm +for a given animal. + +358 +00:18:23,703 --> 00:18:25,071 +In this case, + +359 +00:18:25,071 --> 00:18:28,507 +the result type depends +on the specific animal type, + +360 +00:18:28,507 --> 00:18:30,743 +so we need to use +the type parameter A + +361 +00:18:30,743 --> 00:18:34,447 +in the parameter type +and the return type. + +362 +00:18:34,447 --> 00:18:36,682 +Another common place +where you need to refer + +363 +00:18:36,682 --> 00:18:41,921 +to an opaque type multiple +times is in generic types. + +364 +00:18:41,921 --> 00:18:46,025 +Code often declares a type +parameter on a generic type, + +365 +00:18:46,025 --> 00:18:48,928 +uses the type parameter +for a stored property, + +366 +00:18:48,928 --> 00:18:52,465 +and again +in a memberwise initializer. + +367 +00:18:52,465 --> 00:18:55,568 +Referencing a generic type +in a different context + +368 +00:18:55,568 --> 00:18:59,105 +also requires you to explicitly +specify the type parameter + +369 +00:18:59,105 --> 00:19:01,874 +in angle brackets. + +370 +00:19:01,874 --> 00:19:03,909 +The angle brackets +at the declaration + +371 +00:19:03,909 --> 00:19:07,313 +can help clarify +how to use a generic type, + +372 +00:19:07,313 --> 00:19:12,118 +so opaque types must always +be named for generic types. + +373 +00:19:12,118 --> 00:19:14,353 +Now, let's build out +the implementation + +374 +00:19:14,353 --> 00:19:15,921 +of the feed method. + +375 +00:19:15,921 --> 00:19:18,124 +We can use the type +of the animal parameter + +376 +00:19:18,124 --> 00:19:20,459 +to access the crop type +to grow + +377 +00:19:20,459 --> 00:19:23,162 +through the +Feed-associated type. + +378 +00:19:23,162 --> 00:19:26,165 +We'll call Feed.grow() to +get an instance of the crop + +379 +00:19:26,165 --> 00:19:28,167 +that produces +this type of feed. + +380 +00:19:28,167 --> 00:19:31,504 +Next, we need to harvest +the produce from the crop, + +381 +00:19:31,504 --> 00:19:33,172 +which we can do +by calling a method + +382 +00:19:33,172 --> 00:19:36,308 +provided by the crop type +called "harvest." + +383 +00:19:36,308 --> 00:19:39,812 +And finally, we can feed +this produce to the animal. + +384 +00:19:39,812 --> 00:19:42,848 +Because the underlying +animal type is fixed, + +385 +00:19:42,848 --> 00:19:45,184 +the compiler knows +the relationship + +386 +00:19:45,184 --> 00:19:47,219 +between the plant type, +the produce type, + +387 +00:19:47,219 --> 00:19:50,756 +and the animal type across +the various method calls. + +388 +00:19:50,756 --> 00:19:53,592 +These static relationships +prevent us from making + +389 +00:19:53,592 --> 00:19:58,998 +the mistake of feeding the +animal the wrong type of food. + +390 +00:19:58,998 --> 00:20:01,767 +If we attempt to use a type +that is not guaranteed + +391 +00:20:01,767 --> 00:20:04,703 +to be the correct food type +for this animal, + +392 +00:20:04,703 --> 00:20:07,440 +the compiler will tell us. + +393 +00:20:07,440 --> 00:20:09,575 +To learn how +the other farm protocols + +394 +00:20:09,575 --> 00:20:12,411 +were crafted to express +the relationship + +395 +00:20:12,411 --> 00:20:15,181 +between the animal-feed type +and its plant, + +396 +00:20:15,181 --> 00:20:18,851 +check out "Design protocol +interfaces in Swift." + +397 +00:20:18,851 --> 00:20:23,222 +Lastly, let's add a method +for feeding all the animals. + +398 +00:20:23,222 --> 00:20:26,892 +I'll add a method called +feedAll that accepts an array. + +399 +00:20:26,892 --> 00:20:29,061 +I know the element type +needs to conform + +400 +00:20:29,061 --> 00:20:30,863 +to the Animal protocol, + +401 +00:20:30,863 --> 00:20:32,832 +but I want the array +to be able to store + +402 +00:20:32,832 --> 00:20:35,401 +different types of animals. + +403 +00:20:35,401 --> 00:20:39,538 +Let's see if some Animal +can help us here. + +404 +00:20:39,538 --> 00:20:42,741 +With "some" there is +a specific underlying type + +405 +00:20:42,741 --> 00:20:44,543 +that cannot vary. + +406 +00:20:44,543 --> 00:20:47,213 +Because the underlying type +is fixed, + +407 +00:20:47,213 --> 00:20:51,350 +all of the elements in the array +need to have the same type. + +408 +00:20:51,350 --> 00:20:55,321 +So, an array of some Animal +doesn't express the right thing, + +409 +00:20:55,321 --> 00:20:59,458 +because I want an array that +can hold different animal types. + +410 +00:20:59,458 --> 00:21:01,293 +Here, we really +need a supertype + +411 +00:21:01,293 --> 00:21:04,430 +that can represent +any type of animal. + +412 +00:21:04,430 --> 00:21:07,800 +We can express +an arbitrary type of animal + +413 +00:21:07,800 --> 00:21:11,036 +by writing "any Animal." + +414 +00:21:11,036 --> 00:21:14,340 +The "any" keyword indicates +that this type can store + +415 +00:21:14,340 --> 00:21:16,842 +any arbitrary +type of animal, + +416 +00:21:16,842 --> 00:21:21,814 +and the underlying type of +animal can vary at runtime. + +417 +00:21:21,814 --> 00:21:23,816 +Just like with +the "some" keyword, + +418 +00:21:23,816 --> 00:21:25,751 +the "any" keyword +is always followed + +419 +00:21:25,751 --> 00:21:28,487 +by a conformance +requirement. + +420 +00:21:28,487 --> 00:21:32,525 +any Animal is a single static +type that has the capability + +421 +00:21:32,525 --> 00:21:35,761 +to store any concrete +animal type dynamically, + +422 +00:21:35,761 --> 00:21:38,664 +which allows us to use +subtype polymorphism + +423 +00:21:38,664 --> 00:21:40,933 +with value types. + +424 +00:21:40,933 --> 00:21:43,402 +To allow for +this flexible storage, + +425 +00:21:43,402 --> 00:21:46,172 +the any Animal type has +a special representation + +426 +00:21:46,172 --> 00:21:48,240 +in memory. + +427 +00:21:48,240 --> 00:21:52,411 +You can think of this +representation like a box. + +428 +00:21:52,411 --> 00:21:54,813 +Sometimes, +a value is small enough + +429 +00:21:54,813 --> 00:21:57,816 +to fit inside +the box directly. + +430 +00:21:57,816 --> 00:22:00,886 +And other values +are too large for the box, + +431 +00:22:00,886 --> 00:22:03,722 +so the value has to be +allocated elsewhere, + +432 +00:22:03,722 --> 00:22:06,859 +and the box stores +a pointer to that value. + +433 +00:22:06,859 --> 00:22:10,496 +The static type any Animal +that can dynamically store + +434 +00:22:10,496 --> 00:22:13,666 +any concrete animal type +is formally called + +435 +00:22:13,666 --> 00:22:16,368 +an existential type. + +436 +00:22:16,368 --> 00:22:19,572 +And the strategy of using +the same representation + +437 +00:22:19,572 --> 00:22:23,576 +for different concrete types +is called "type erasure." + +438 +00:22:23,576 --> 00:22:27,313 +The concrete type is said +to be erased at compile time, + +439 +00:22:27,313 --> 00:22:30,783 +and the concrete type +is only known at runtime. + +440 +00:22:30,783 --> 00:22:34,420 +These two instances of the +existential type any Animal + +441 +00:22:34,420 --> 00:22:38,991 +have the same static type, +but different dynamic types. + +442 +00:22:38,991 --> 00:22:42,027 +Type erasure eliminates +the type-level distinction + +443 +00:22:42,027 --> 00:22:46,098 +between different animal values, +which allows us to use values + +444 +00:22:46,098 --> 00:22:49,101 +with different dynamic types +interchangeably + +445 +00:22:49,101 --> 00:22:51,270 +as the same static type. + +446 +00:22:51,270 --> 00:22:54,940 +We can use type erasure +to write a heterogeneous array + +447 +00:22:54,940 --> 00:22:56,375 +of value types, + +448 +00:22:56,375 --> 00:23:00,346 +which is exactly what we want +for the feedAll method. + +449 +00:23:00,346 --> 00:23:02,848 +So we'll use an array +of any Animal + +450 +00:23:02,848 --> 00:23:04,817 +as the parameter type. + +451 +00:23:04,817 --> 00:23:06,819 +Using the "any" keyword +for protocols + +452 +00:23:06,819 --> 00:23:11,824 +with associated types +is new in Swift 5.7. + +453 +00:23:11,824 --> 00:23:13,759 +To implement +the feedAll method, + +454 +00:23:13,759 --> 00:23:17,329 +we'll first iterate +over the animal's array. + +455 +00:23:17,329 --> 00:23:19,965 +For each animal, we want +to call the eat method + +456 +00:23:19,965 --> 00:23:22,268 +from the Animal protocol. + +457 +00:23:22,268 --> 00:23:23,702 +To call this method, + +458 +00:23:23,702 --> 00:23:25,804 +we need to get +the specific feed type + +459 +00:23:25,804 --> 00:23:29,408 +for the underlying animal +at this iteration. + +460 +00:23:29,408 --> 00:23:32,845 +But as soon as we try +to call eat on any Animal, + +461 +00:23:32,845 --> 00:23:35,447 +we'll get a compiler error. + +462 +00:23:35,447 --> 00:23:38,217 +Because we've eliminated +the type-level distinction + +463 +00:23:38,217 --> 00:23:40,653 +between specific +animal types, + +464 +00:23:40,653 --> 00:23:43,389 +we've also eliminated +all type relationships + +465 +00:23:43,389 --> 00:23:46,225 +that depend on +the specific animal type, + +466 +00:23:46,225 --> 00:23:48,761 +including associated types. + +467 +00:23:48,761 --> 00:23:53,065 +So, we can't know what type +of feed this animal expects. + +468 +00:23:53,065 --> 00:23:55,467 +To rely on type +relationships, + +469 +00:23:55,467 --> 00:23:57,503 +we need to get back +into a context + +470 +00:23:57,503 --> 00:24:00,372 +where the specific type +of animal is fixed. + +471 +00:24:00,372 --> 00:24:03,776 +Instead of calling eat +directly on any Animal, + +472 +00:24:03,776 --> 00:24:08,614 +we need to call the feed method +that accepts some Animal. + +473 +00:24:08,614 --> 00:24:12,518 +Now, any Animal is a different +type from some Animal, + +474 +00:24:12,518 --> 00:24:15,954 +but the compiler can convert +an instance of any Animal + +475 +00:24:15,954 --> 00:24:19,625 +to some Animal by unboxing +the underlying value + +476 +00:24:19,625 --> 00:24:24,797 +and passing it directly +to the some Animal parameter. + +477 +00:24:24,797 --> 00:24:30,436 +This capability of unboxing +arguments is new in Swift 5.7. + +478 +00:24:30,436 --> 00:24:34,139 +You can think of unboxing as +the compiler opening the box + +479 +00:24:34,139 --> 00:24:37,710 +and taking out the value +stored inside. + +480 +00:24:37,710 --> 00:24:40,112 +For the scope +of the some Animal parameter, + +481 +00:24:40,112 --> 00:24:43,082 +the value has +a fixed underlying type, + +482 +00:24:43,082 --> 00:24:45,617 +so we have access +to all of the operations + +483 +00:24:45,617 --> 00:24:47,319 +on the underlying type, + +484 +00:24:47,319 --> 00:24:51,023 +including access +to associated types. + +485 +00:24:51,023 --> 00:24:54,126 +This is really cool +because it allows us to opt + +486 +00:24:54,126 --> 00:24:56,528 +for flexible storage +when we need it, + +487 +00:24:56,528 --> 00:24:59,331 +while still allowing us +to get back to a context + +488 +00:24:59,331 --> 00:25:01,100 +where we have +the full expressivity + +489 +00:25:01,100 --> 00:25:04,937 +of the static type system +by fixing the underlying type + +490 +00:25:04,937 --> 00:25:07,139 +for the scope of a function. + +491 +00:25:07,139 --> 00:25:08,807 +And most of the time, + +492 +00:25:08,807 --> 00:25:10,909 +you don't have to think +about the unboxing + +493 +00:25:10,909 --> 00:25:13,479 +because it just works +in the way you'd expect, + +494 +00:25:13,479 --> 00:25:16,949 +similar to how calling +a protocol method on any Animal + +495 +00:25:16,949 --> 00:25:20,386 +really calls the method +on the underlying type. + +496 +00:25:20,386 --> 00:25:23,956 +So, we can pass each animal +to the feed method, + +497 +00:25:23,956 --> 00:25:26,492 +where we can grow and +harvest the appropriate crop + +498 +00:25:26,492 --> 00:25:31,430 +to feed to the specific +animal at each iteration. + +499 +00:25:31,430 --> 00:25:34,800 +Throughout this process, we've +seen that "some" and "any" + +500 +00:25:34,800 --> 00:25:38,003 +have different capabilities. + +501 +00:25:38,003 --> 00:25:42,007 +With "some," +the underlying type is fixed. + +502 +00:25:42,007 --> 00:25:44,777 +This allows you to rely +on type relationships + +503 +00:25:44,777 --> 00:25:47,780 +to the underlying type +in your generic code, + +504 +00:25:47,780 --> 00:25:51,517 +so you'll have full access +to the API and associated types + +505 +00:25:51,517 --> 00:25:54,453 +on the protocol +you're working with. + +506 +00:25:54,453 --> 00:25:59,057 +Use "any" when you need to store +arbitrary concrete types. + +507 +00:25:59,057 --> 00:26:01,427 +"any" provides type erasure, + +508 +00:26:01,427 --> 00:26:04,963 +which allows you represent +heterogeneous collections, + +509 +00:26:04,963 --> 00:26:07,366 +represent the absence +of an underlying type, + +510 +00:26:07,366 --> 00:26:08,867 +using optionals, + +511 +00:26:08,867 --> 00:26:12,838 +and make the abstraction +an implementation detail. + +512 +00:26:12,838 --> 00:26:15,707 +In general, +write "some" by default, + +513 +00:26:15,707 --> 00:26:17,276 +and change "some" to "any" + +514 +00:26:17,276 --> 00:26:20,946 +when you know you need +to store arbitrary values. + +515 +00:26:20,946 --> 00:26:23,415 +With this approach, +you'll only pay the cost + +516 +00:26:23,415 --> 00:26:26,418 +of type erasure +and its semantic limitations + +517 +00:26:26,418 --> 00:26:30,389 +when you need the storage +flexibility it provides. + +518 +00:26:30,389 --> 00:26:32,958 +This workflow is similar +to writing let-constants + +519 +00:26:32,958 --> 00:26:36,628 +by default, until you know +you need mutation. + +520 +00:26:36,628 --> 00:26:38,797 +In this session, +we walked through + +521 +00:26:38,797 --> 00:26:40,732 +the workflow +of generalizing code + +522 +00:26:40,732 --> 00:26:44,169 +as it evolves and gains +more functionality. + +523 +00:26:44,169 --> 00:26:47,573 +We started by writing +concrete types. + +524 +00:26:47,573 --> 00:26:49,875 +As the code gained +more functionality, + +525 +00:26:49,875 --> 00:26:54,446 +we noticed repetition between +different concrete types. + +526 +00:26:54,446 --> 00:26:57,649 +From there, we identified +common capabilities + +527 +00:26:57,649 --> 00:27:00,853 +and generalized them +using a protocol. + +528 +00:27:00,853 --> 00:27:05,224 +Finally, we wrote abstract +code using "some" and "any”, + +529 +00:27:05,224 --> 00:27:09,828 +and we discussed preferring +"some" for more expressive code. + +530 +00:27:09,828 --> 00:27:12,130 +To dig deeper +into crafting protocols + +531 +00:27:12,130 --> 00:27:14,166 +and understanding +type erasure, + +532 +00:27:14,166 --> 00:27:18,170 +check out "Design protocol +interfaces in Swift." + +533 +00:27:18,170 --> 00:27:21,373 +Thank you joining me +and have a great WWDC. + +534 +00:27:21,373 --> 00:27:26,411 +♪ + diff --git a/eng/2022 Session 110353 Design protocol interfaces in Swift en.srt b/eng/2022 Session 110353 Design protocol interfaces in Swift en.srt new file mode 100644 index 0000000..8493179 --- /dev/null +++ b/eng/2022 Session 110353 Design protocol interfaces in Swift en.srt @@ -0,0 +1,1828 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,643 --> 00:00:12,779 +Hi, I'm Slava +from the swift compiler team. + +3 +00:00:12,813 --> 00:00:16,850 +Welcome to Design +Protocol Interfaces in Swift. + +4 +00:00:17,684 --> 00:00:21,822 +I'm going to pick up where the Embrace +Swift Generics talk left off, + +5 +00:00:21,855 --> 00:00:26,527 +and show you some advanced techniques +for abstracting over concrete types + +6 +00:00:26,560 --> 00:00:30,430 +and modeling type relationships +using protocols. + +7 +00:00:30,464 --> 00:00:34,001 +This talk will cover both +existing language features, + +8 +00:00:34,034 --> 00:00:39,540 +as well as some of the new capabilities +introduced in Swift 5.7. + +9 +00:00:40,340 --> 00:00:43,010 +This talk has three main themes: + +10 +00:00:43,043 --> 00:00:46,980 +First, I'll show you how protocols with +associated types + +11 +00:00:47,014 --> 00:00:49,883 +interact with existential 'any' types, + +12 +00:00:49,917 --> 00:00:53,887 +by explaining +how 'result type erasure' works. + +13 +00:00:53,921 --> 00:00:57,758 +Next, I'll explain using +opaque result types + +14 +00:00:57,791 --> 00:00:59,693 +to improve encapsulation + +15 +00:00:59,726 --> 00:01:03,497 +by separating +interface from implementation. + +16 +00:01:03,530 --> 00:01:05,132 +For the final topic, + +17 +00:01:05,165 --> 00:01:08,702 +you will see how +same-type requirements in protocols + +18 +00:01:08,735 --> 00:01:13,607 +can model relationships between +multiple different sets of concrete types. + +19 +00:01:14,942 --> 00:01:18,946 +Let's start by learning +how protocols with associated types + +20 +00:01:18,979 --> 00:01:22,216 +interact with existential types. + +21 +00:01:22,249 --> 00:01:25,619 +Here, we have a data model +with a pair of protocols, + +22 +00:01:25,652 --> 00:01:28,055 +and four concrete types. + +23 +00:01:28,088 --> 00:01:31,558 +There are two types of animals, +chickens and cows, + +24 +00:01:31,592 --> 00:01:35,529 +and two types of food, eggs and milk. + +25 +00:01:35,562 --> 00:01:39,933 +Chickens produce eggs, +and cows produce milk. + +26 +00:01:39,967 --> 00:01:42,503 +To abstract over the production of food, + +27 +00:01:42,536 --> 00:01:46,640 +I'm going to add a produce() method +to the Animal protocol. + +28 +00:01:46,673 --> 00:01:50,244 +You might remember +from the 'Embrace swift generics' talk + +29 +00:01:50,277 --> 00:01:52,980 +that that the best way to abstract the +different return types + +30 +00:01:53,013 --> 00:01:55,315 +of produce() on Cow and Chicken + +31 +00:01:55,349 --> 00:01:58,118 +is to use an associated type. + +32 +00:01:58,151 --> 00:02:01,522 +By using an associated type, +we're declaring that: + +33 +00:02:01,555 --> 00:02:04,191 +given some concrete type of Animal, + +34 +00:02:04,224 --> 00:02:08,495 +calling produce() returns +some specific type of Food, + +35 +00:02:08,529 --> 00:02:12,332 +that depends on the concrete Animal type. + +36 +00:02:12,366 --> 00:02:15,736 +We can show +this relationship with a diagram. + +37 +00:02:15,769 --> 00:02:20,040 +The protocol 'Self' type stands in +for the actual concrete type + +38 +00:02:20,073 --> 00:02:22,876 +conforming to the 'Animal' protocol. + +39 +00:02:22,910 --> 00:02:26,213 +The 'Self' type has an associated +'Commodity' type, + +40 +00:02:26,246 --> 00:02:28,515 +conforming to 'Food'. + +41 +00:02:28,549 --> 00:02:32,719 +Let's look at the relationships between +the concrete Chicken and Cow types, + +42 +00:02:32,753 --> 00:02:36,423 +and the associated type diagram +for the Animal protocol. + +43 +00:02:37,424 --> 00:02:42,529 +The Chicken type conforms to the Animal +protocol with a CommodityType of 'Egg'. + +44 +00:02:42,563 --> 00:02:48,101 +And the Cow type conforms to the Animal +protocol with a CommodityType of 'Milk'. + +45 +00:02:48,135 --> 00:02:51,471 +Now, let's say we have a farm +full of animals. + +46 +00:02:51,505 --> 00:02:57,978 +The 'animals' stored property on Farm +is a heterogenous array of 'any Animal'. + +47 +00:02:58,011 --> 00:03:02,182 +In embrace Swift generics, +we saw how the 'any Animal' type + +48 +00:03:02,216 --> 00:03:05,953 +has a box representation +that has the ability to store + +49 +00:03:05,986 --> 00:03:09,323 +any concrete type of animal dynamically. + +50 +00:03:09,356 --> 00:03:12,259 +This strategy of using +the same representation + +51 +00:03:12,292 --> 00:03:15,529 +for different concrete types +is called type erasure. + +52 +00:03:17,030 --> 00:03:21,001 +The produceCommodities() method +maps over the array of animals, + +53 +00:03:21,034 --> 00:03:24,037 +calling the produce() method on each one. + +54 +00:03:24,071 --> 00:03:27,007 +The method looks simple, +but we know that type erasure + +55 +00:03:27,040 --> 00:03:31,912 +will eliminate static type relationships +to the underlying type of animal, + +56 +00:03:31,945 --> 00:03:36,350 +so it's worth digging deeper +to understand why this code type checks. + +57 +00:03:37,684 --> 00:03:42,990 +The 'animal' parameter in +the map() closure has type 'any Animal'. + +58 +00:03:43,023 --> 00:03:47,261 +The return type of 'produce()' +is an associated type. + +59 +00:03:47,294 --> 00:03:52,165 +When you call a method returning +an associated type on an existential type, + +60 +00:03:52,199 --> 00:03:57,971 +the compiler will use type erasure +to determine the result type of the call. + +61 +00:03:58,005 --> 00:04:01,308 +Type erasure replaces +these associated types + +62 +00:04:01,341 --> 00:04:05,846 +with corresponding existential types +that have equivalent constraints. + +63 +00:04:05,879 --> 00:04:09,683 +We've erased the relationship +between the concrete Animal type + +64 +00:04:09,716 --> 00:04:12,119 +and the associated CommodityType + +65 +00:04:12,152 --> 00:04:16,089 +by replacing them with 'any Animal' +and 'any Food'. + +66 +00:04:16,123 --> 00:04:22,429 +The type 'any Food' is called the upper +bound of the associated CommodityType. + +67 +00:04:22,462 --> 00:04:25,666 +Since the produce() method +is called on an 'any Animal', + +68 +00:04:25,699 --> 00:04:31,038 +the return value is type erased, +giving us a value of type 'any Food'. + +69 +00:04:31,071 --> 00:04:33,640 +This is exactly the type we expect here. + +70 +00:04:34,775 --> 00:04:38,378 +Let's take a closer look at +how associated-type erasure works, + +71 +00:04:38,412 --> 00:04:41,882 +which is a new feature in Swift 5.7. + +72 +00:04:41,915 --> 00:04:46,420 +An associated type appearing in +the result type of a protocol method – + +73 +00:04:46,453 --> 00:04:48,755 +on the right-hand side of the arrow – + +74 +00:04:48,789 --> 00:04:51,291 +is said to be in 'producing position', + +75 +00:04:51,325 --> 00:04:56,129 +because calling the method +will produce a value of this type. + +76 +00:04:56,163 --> 00:04:59,066 +When we call this method on 'any Animal', + +77 +00:04:59,099 --> 00:05:02,269 +we don't know the concrete result type +at compile time, + +78 +00:05:02,302 --> 00:05:06,440 +but we do know that it is a subtype +of the upper bound. + +79 +00:05:06,473 --> 00:05:10,477 +Here in this example, +we're calling produce() on an 'any Animal' + +80 +00:05:10,511 --> 00:05:13,447 +that holds a Cow at runtime. + +81 +00:05:13,480 --> 00:05:17,851 +In our case, +the produce() method on Cow returns Milk. + +82 +00:05:17,885 --> 00:05:21,255 +Milk can be stored +inside of an 'any Food', + +83 +00:05:21,288 --> 00:05:26,159 +which is the upper bound of the associated +CommodityType of the Animal protocol. + +84 +00:05:27,127 --> 00:05:32,733 +This is always safe, for all concrete +types that conform to the Animal protocol. + +85 +00:05:34,034 --> 00:05:38,205 +On the other hand, let's think about +what happens if the associated type + +86 +00:05:38,238 --> 00:05:42,543 +appears in the parameter list +of a method or initializer. + +87 +00:05:42,576 --> 00:05:45,379 +Here, the eat() method on +the Animal protocol + +88 +00:05:45,412 --> 00:05:49,483 +has the associated FeedType +in consuming position. + +89 +00:05:49,516 --> 00:05:53,687 +We need to pass in a value of this type +to call the method. + +90 +00:05:53,720 --> 00:05:56,456 +Since the conversion goes +in the other direction, + +91 +00:05:56,490 --> 00:05:59,226 +type erasure cannot be performed. + +92 +00:05:59,259 --> 00:06:03,230 +The upper bound existential type +for the associated type + +93 +00:06:03,263 --> 00:06:06,867 +does not safety convert +to the actual concrete type, + +94 +00:06:06,900 --> 00:06:09,970 +because the concrete type is unknown. + +95 +00:06:10,003 --> 00:06:11,772 +Let's look at an example. + +96 +00:06:11,805 --> 00:06:15,876 +Once again, +we have an 'any Animal' storing a Cow. + +97 +00:06:15,909 --> 00:06:19,179 +Suppose that the 'eat' method +on Cow takes Hay. + +98 +00:06:19,213 --> 00:06:25,752 +The upper bound of the Animal protocol's +associated 'FeedType' is 'any AnimalFeed'. + +99 +00:06:25,786 --> 00:06:28,755 +But given an arbitrary 'any AnimalFeed', + +100 +00:06:28,789 --> 00:06:31,592 +there is no way to statically guarantee + +101 +00:06:31,625 --> 00:06:34,294 +that it stores the 'Hay' concrete type. + +102 +00:06:34,328 --> 00:06:38,465 +Type erasure does not allow us +to work with associated types + +103 +00:06:38,498 --> 00:06:40,334 +in consuming position. + +104 +00:06:40,367 --> 00:06:44,271 +Instead, you must unbox +the existential 'any' type + +105 +00:06:44,304 --> 00:06:48,308 +by passing it to a function +that takes an opaque 'some' type. + +106 +00:06:49,076 --> 00:06:53,347 +This type erasure behavior with +associated types is actually similar + +107 +00:06:53,380 --> 00:06:58,752 +to an existing language feature +you may have seen in Swift 5.6. + +108 +00:06:58,785 --> 00:07:02,289 +Consider a protocol +for cloning reference types. + +109 +00:07:02,322 --> 00:07:06,894 +This protocol defines a single +clone() method, returning Self. + +110 +00:07:06,927 --> 00:07:10,931 +When you call clone() +on a value of type 'any Cloneable', + +111 +00:07:10,964 --> 00:07:16,236 +the result type 'Self', +is type erased to its upper bound. + +112 +00:07:16,270 --> 00:07:20,774 +The upper bound of the Self type +is always the protocol itself, + +113 +00:07:20,807 --> 00:07:25,279 +so we get back a new value +of type 'any Cloneable'. + +114 +00:07:25,312 --> 00:07:29,983 +So to summarize: you can use +'any' to declare that the type of a value + +115 +00:07:30,017 --> 00:07:36,089 +is an existential type that stores some +concrete type conforming to a protocol. + +116 +00:07:36,123 --> 00:07:40,661 +This even works with protocols +that have associated types. + +117 +00:07:40,694 --> 00:07:45,866 +When calling a protocol method with +an associated type in producing position, + +118 +00:07:45,899 --> 00:07:49,703 +the associated type is type-erased +to its upper bound, + +119 +00:07:49,736 --> 00:07:54,908 +which is another existential type that +carries the associated type's constraints. + +120 +00:07:54,942 --> 00:08:00,247 +Abstracting over concrete types +isn't only useful for function inputs - + +121 +00:08:00,280 --> 00:08:02,850 +it's useful for function outputs, too, + +122 +00:08:02,883 --> 00:08:07,454 +so that concrete types are only visible +from the implementation. + +123 +00:08:07,487 --> 00:08:11,491 +Let's take a look at how to abstract away +concrete result types + +124 +00:08:11,525 --> 00:08:14,962 +to separate the essential interface +of a piece of code + +125 +00:08:14,995 --> 00:08:17,264 +from its implementation details, + +126 +00:08:17,297 --> 00:08:22,836 +making static type assignments more +modular and robust in the face of changes. + +127 +00:08:22,870 --> 00:08:27,007 +Let's generalize the Animal protocol +to allow feeding Animals. + +128 +00:08:27,040 --> 00:08:30,911 +Animals get hungry, +and when they're hungry they need to eat. + +129 +00:08:30,944 --> 00:08:35,115 +Let's add an isHungry property +to the Animal protocol. + +130 +00:08:35,148 --> 00:08:40,787 +The feedAnimals() method on Farm will feed +the subset of animals that are hungry. + +131 +00:08:40,821 --> 00:08:44,691 +I've split off the computation +of this subset of hungry animals + +132 +00:08:44,725 --> 00:08:47,594 +into a hungryAnimals property. + +133 +00:08:47,628 --> 00:08:51,899 +This initial implementation of +hungryAnimals() uses the filter() method + +134 +00:08:51,932 --> 00:08:56,837 +to select the subset of animals +where the isHungry property is true. + +135 +00:08:56,870 --> 00:08:59,907 +Calling filter() on an array +of 'any Animal' + +136 +00:08:59,940 --> 00:09:03,744 +returns a new array of 'any Animal'. + +137 +00:09:03,777 --> 00:09:05,913 +Now you might notice +that feedAnimals() + +138 +00:09:05,946 --> 00:09:09,483 +only iterates over the result +of hungryAnimals once, + +139 +00:09:09,516 --> 00:09:13,120 +and then immediately discards +this temporary array. + +140 +00:09:13,153 --> 00:09:18,425 +This is inefficient if the farm contains +a large number of hungry animals. + +141 +00:09:18,458 --> 00:09:21,094 +One way to avoid +this temporary allocation + +142 +00:09:21,128 --> 00:09:25,199 +is to use the standard library's +lazy collections feature. + +143 +00:09:25,232 --> 00:09:29,736 +By replacing the call to 'filter' +with 'lazy.filter', + +144 +00:09:29,770 --> 00:09:33,574 +we get what is known as a lazy collection. + +145 +00:09:33,607 --> 00:09:38,011 +A lazy collection has the same elements +as the array returned + +146 +00:09:38,045 --> 00:09:43,584 +by a plain call to 'filter', +but it avoids the temporary allocation. + +147 +00:09:43,617 --> 00:09:47,221 +However, now the type of +the 'hungryAnimals' property + +148 +00:09:47,254 --> 00:09:51,291 +must be declared +as this rather complex concrete type, + +149 +00:09:51,325 --> 00:09:55,963 +'LazyFilterSequence +of Array of any Animal'. + +150 +00:09:55,996 --> 00:10:00,334 +This exposes +an unnecessary implementation detail. + +151 +00:10:00,367 --> 00:10:05,939 +The client, feedAnimals(), doesn't care +that we used 'lazy.filter' + +152 +00:10:05,973 --> 00:10:08,709 +in the implementation of 'hungryAnimals'; + +153 +00:10:08,742 --> 00:10:14,181 +it only needs to know that it's getting +some collection that it can iterate over. + +154 +00:10:14,214 --> 00:10:19,019 +An opaque result type can be used +to hide the complex concrete type + +155 +00:10:19,052 --> 00:10:23,023 +behind the abstract interface +of a Collection. + +156 +00:10:23,056 --> 00:10:28,595 +Now clients calling 'hungryAnimals' only +know they're getting some concrete type + +157 +00:10:28,629 --> 00:10:31,231 +conforming to the Collection protocol, + +158 +00:10:31,265 --> 00:10:35,135 +but they don't know the specific +concrete type of collection. + +159 +00:10:36,170 --> 00:10:39,606 +However as written, +this actually hides too much + +160 +00:10:39,640 --> 00:10:43,210 +static type information from the client. + +161 +00:10:43,243 --> 00:10:47,648 +We're declaring that hungryAnimals +outputs some concrete type + +162 +00:10:47,681 --> 00:10:49,483 +conforming to Collection, + +163 +00:10:49,516 --> 00:10:53,754 +but we don't know anything +about this Collection's Element type. + +164 +00:10:53,787 --> 00:10:57,925 +Without the knowledge +that the element type is 'any Animal', + +165 +00:10:57,958 --> 00:11:01,562 +all we can do with the element type +is pass it around; + +166 +00:11:01,595 --> 00:11:06,233 +we can't call any of the methods +of the Animal protocol. + +167 +00:11:06,266 --> 00:11:10,771 +Let's focus on the opaque result type +'some Collection'. + +168 +00:11:10,804 --> 00:11:15,042 +We can strike the right balance +between hiding implementation details + +169 +00:11:15,075 --> 00:11:18,145 +and exposing a sufficiently-rich interface + +170 +00:11:18,178 --> 00:11:22,049 +by using a constrained opaque result type. + +171 +00:11:22,082 --> 00:11:26,653 +Constrained opaque result types +are new in Swift 5.7. + +172 +00:11:26,687 --> 00:11:31,458 +A constrained opaque result type +is written by applying type arguments + +173 +00:11:31,491 --> 00:11:34,361 +in angle brackets +after the protocol name. + +174 +00:11:34,394 --> 00:11:39,032 +The Collection protocol has +a single type argument, the Element type. + +175 +00:11:39,066 --> 00:11:44,171 +Now once 'hungryAnimals' is declared +with a constrained opaque result type, + +176 +00:11:44,204 --> 00:11:49,076 +the fact that it is actually +a 'LazyFilterSequence of an array + +177 +00:11:49,109 --> 00:11:52,946 +of any Animal' is hidden from the client; + +178 +00:11:52,980 --> 00:11:55,215 +but the client still has the knowledge + +179 +00:11:55,249 --> 00:11:59,152 +that it is some concrete type +conforming to Collection, + +180 +00:11:59,186 --> 00:12:03,524 +whose Element associated type +is equal to 'any Animal'. + +181 +00:12:03,557 --> 00:12:07,961 +This is precisely the interface +that we want here. + +182 +00:12:07,995 --> 00:12:11,064 +Inside the for loop in 'feedAnimals()', + +183 +00:12:11,098 --> 00:12:14,568 +the 'animal' variable +has the type 'any Animal', + +184 +00:12:14,601 --> 00:12:20,874 +allowing methods of the Animal protocol +to be called on each hungry animal. + +185 +00:12:20,908 --> 00:12:24,511 +This all works because +the Collection protocol declares + +186 +00:12:24,545 --> 00:12:30,050 +that the Element associated type +is a primary associated type. + +187 +00:12:30,083 --> 00:12:34,321 +You can declare your own protocols +with primary associated types + +188 +00:12:34,354 --> 00:12:39,760 +by naming one or more associated types +in angle brackets after the protocol name, + +189 +00:12:39,793 --> 00:12:41,695 +like this. + +190 +00:12:41,728 --> 00:12:46,533 +The associated types that work best +as primary associated types + +191 +00:12:46,567 --> 00:12:49,403 +are those that are usually provided +by the caller, + +192 +00:12:49,436 --> 00:12:52,206 +such as an Element type of a collection, + +193 +00:12:52,239 --> 00:12:55,042 +as opposed to implementation details, + +194 +00:12:55,075 --> 00:12:58,212 +such as the collection's Iterator type. + +195 +00:12:58,245 --> 00:13:01,949 +Often, you will see a correspondence +between the primary associated types + +196 +00:13:01,982 --> 00:13:05,919 +of a protocol, and the generic parameters +of a concrete type + +197 +00:13:05,953 --> 00:13:07,788 +conforming to this protocol. + +198 +00:13:07,821 --> 00:13:11,792 +Here, you can see that the Element primary +associated type of 'Collection' + +199 +00:13:11,825 --> 00:13:16,263 +is implemented by the 'Element' +generic parameter of Array and Set, + +200 +00:13:16,296 --> 00:13:21,635 +two concrete types defined by the standard +library that both conform to Collection. + +201 +00:13:21,668 --> 00:13:25,439 +'Collection of Element' can be used +with opaque result types + +202 +00:13:25,472 --> 00:13:27,241 +using the 'some' keyword, + +203 +00:13:27,274 --> 00:13:32,112 +as well as with constrained +existential types using the 'any' keyword. + +204 +00:13:32,145 --> 00:13:36,850 +Before Swift 5.7, you would've needed +to write your own data type + +205 +00:13:36,884 --> 00:13:41,522 +to represent an existential type +with a specific generic argument. + +206 +00:13:41,555 --> 00:13:45,058 +Swift 5.7 builds this concept +into the language + +207 +00:13:45,092 --> 00:13:47,628 +with constrained existential types. + +208 +00:13:48,662 --> 00:13:51,632 +If we wanted hungryAnimals +to have the option + +209 +00:13:51,665 --> 00:13:56,470 +of whether to compute +the hungryAnimals lazily or eagerly, + +210 +00:13:56,503 --> 00:14:02,042 +using an opaque Collection +of any Animal would result in an error + +211 +00:14:02,075 --> 00:14:06,346 +that the function returns +two different underlying types. + +212 +00:14:06,380 --> 00:14:11,051 +We can fix this by instead +returning 'any Collection of any Animal', + +213 +00:14:11,084 --> 00:14:16,523 +signaling that this API can return +different types across calls. + +214 +00:14:16,557 --> 00:14:20,227 +The ability to constrain primary +associated types + +215 +00:14:20,260 --> 00:14:26,033 +gives opaque types and existential types +a new level of expressivity. + +216 +00:14:26,066 --> 00:14:31,104 +This can be used with various standard +library protocols such as Collection; + +217 +00:14:31,138 --> 00:14:36,076 +you can also declare your own protocols +to have primary associated types. + +218 +00:14:36,910 --> 00:14:43,317 +Writing generic code using opaque types +must rely on abstract type relationships. + +219 +00:14:43,350 --> 00:14:48,722 +Let's discuss how to identify +and guarantee necessary type relationships + +220 +00:14:48,755 --> 00:14:53,527 +between multiple abstract types +using related protocols. + +221 +00:14:54,127 --> 00:14:58,131 +We're going to add a new associated type +to the Animal protocol + +222 +00:14:58,165 --> 00:15:02,269 +for the concrete type of animal feed +that this animal eats, + +223 +00:15:02,302 --> 00:15:07,774 +together with an eat() method that tells +the animal to consume this type of feed. + +224 +00:15:07,808 --> 00:15:10,777 +To make things more interesting, +I'm going to introduce + +225 +00:15:10,811 --> 00:15:12,779 +an additional complication: + +226 +00:15:12,813 --> 00:15:15,282 +before we can feed an animal, + +227 +00:15:15,315 --> 00:15:18,318 +we must grow the appropriate type of crop, + +228 +00:15:18,352 --> 00:15:21,588 +and harvest the crop to produce the feed. + +229 +00:15:21,622 --> 00:15:24,224 +Here is the first set of concrete types. + +230 +00:15:24,258 --> 00:15:26,727 +A cow eats hay, + +231 +00:15:26,760 --> 00:15:28,996 +so given a cow, + +232 +00:15:29,029 --> 00:15:31,798 +we first need to grow some hay. + +233 +00:15:31,832 --> 00:15:33,934 +This gives us alfalfa, + +234 +00:15:33,967 --> 00:15:37,571 +which is harvested and processed into hay, + +235 +00:15:37,604 --> 00:15:40,407 +that the cow can eat. + +236 +00:15:40,440 --> 00:15:43,577 +Here's the second set of concrete types. + +237 +00:15:43,610 --> 00:15:46,113 +A chicken eats scratch, + +238 +00:15:46,146 --> 00:15:48,615 +so if you bring me a chicken, + +239 +00:15:48,649 --> 00:15:52,319 +we first need to grow a type of grain +called millet + +240 +00:15:52,352 --> 00:15:54,321 +that we harvest and process + +241 +00:15:54,354 --> 00:15:56,590 +to produce chicken scratch, + +242 +00:15:56,623 --> 00:15:59,927 +which we feed to our chicken. + +243 +00:15:59,960 --> 00:16:04,932 +I want to abstract over these two sets of +related concrete types, + +244 +00:16:04,965 --> 00:16:07,801 +so I can implement +the feedAnimal() method once, + +245 +00:16:07,835 --> 00:16:10,704 +and have it feed both cows and chickens, + +246 +00:16:10,737 --> 00:16:15,909 +as well as any new types of animals +I might adopt in the future. + +247 +00:16:15,943 --> 00:16:20,747 +Since feedAnimal() needs to work with +the eat() method of the Animal protocol, + +248 +00:16:20,781 --> 00:16:24,384 +which has an associated type +in consuming position, + +249 +00:16:24,418 --> 00:16:29,489 +I'm going to unbox the existential +by declaring that the feedAnimal() method + +250 +00:16:29,523 --> 00:16:33,360 +takes 'some Animal' as a parameter type. + +251 +00:16:33,393 --> 00:16:36,530 +To start, I'll define a pair of protocols, + +252 +00:16:36,563 --> 00:16:38,332 +AnimalFeed and Crop, + +253 +00:16:38,365 --> 00:16:43,737 +using what we know about protocols +and associated types so far. + +254 +00:16:43,770 --> 00:16:46,673 +AnimalFeed has an associated CropType, + +255 +00:16:46,707 --> 00:16:48,842 +which conforms to Crop, + +256 +00:16:48,876 --> 00:16:51,678 +and Crop has an associated FeedType, + +257 +00:16:51,712 --> 00:16:54,448 +which conforms to AnimalFeed. + +258 +00:16:54,481 --> 00:17:00,354 +As before, we can look at a diagram +of type parameters for each protocol. + +259 +00:17:00,387 --> 00:17:03,490 +First, let's look at AnimalFeed. + +260 +00:17:03,524 --> 00:17:05,692 +Every protocol has a 'Self' type, + +261 +00:17:05,726 --> 00:17:09,096 +which stands for +the concrete conforming type. + +262 +00:17:09,129 --> 00:17:12,032 +Our protocol has an associated 'CropType', + +263 +00:17:12,065 --> 00:17:14,434 +which conforms to Crop. + +264 +00:17:14,468 --> 00:17:18,705 +The associated 'CropType' has a nested +associated 'FeedType', + +265 +00:17:18,739 --> 00:17:21,241 +which conforms to AnimalFeed, + +266 +00:17:21,275 --> 00:17:27,247 +which has a nested associated 'CropType' +conforming to Crop, and so on. + +267 +00:17:27,281 --> 00:17:30,584 +In fact, this back-and-forth +continues forever, + +268 +00:17:30,617 --> 00:17:33,787 +with an infinite nesting +of associated types + +269 +00:17:33,820 --> 00:17:38,425 +that alternate between conforming +to AnimalFeed and Crop. + +270 +00:17:39,660 --> 00:17:44,998 +With the Crop protocol, we have +a similar situation, just shifted by one. + +271 +00:17:45,032 --> 00:17:48,802 +We start with the 'Self' type, +conforming to 'Crop', + +272 +00:17:48,836 --> 00:17:53,607 +which has an associated 'FeedType', +conforming to AnimalFeed. + +273 +00:17:53,640 --> 00:17:59,112 +This has a nested associated 'CropType', +conforming to Crop and so on... + +274 +00:18:02,082 --> 00:18:04,618 +To infinity. + +275 +00:18:04,651 --> 00:18:08,388 +Let's see if these protocols +correctly model the relationship + +276 +00:18:08,422 --> 00:18:11,024 +between our concrete types. + +277 +00:18:11,058 --> 00:18:13,527 +Recall that before we can feed an animal, + +278 +00:18:13,560 --> 00:18:15,062 +we need to grow the crop + +279 +00:18:15,095 --> 00:18:18,999 +that is then processed into +the correct type of animal feed. + +280 +00:18:19,032 --> 00:18:22,903 +grow() is a static method +in the AnimalFeed protocol, + +281 +00:18:22,936 --> 00:18:27,708 +which means it must be called directly +on a type conforming to AnimalFeed, + +282 +00:18:27,741 --> 00:18:32,846 +and not on a specific value +whose type conforms to AnimalFeed. + +283 +00:18:32,880 --> 00:18:37,017 +We need to write down a the name of a type +conforming to AnimalFeed, + +284 +00:18:37,050 --> 00:18:39,520 +but all we have is a specific value, + +285 +00:18:39,553 --> 00:18:43,557 +of some type conforming to Animal, +a different protocol. + +286 +00:18:43,590 --> 00:18:47,494 +Well, we can get the type of this value, +which we know is some type + +287 +00:18:47,528 --> 00:18:49,396 +conforming to Animal, + +288 +00:18:49,429 --> 00:18:51,865 +and Animal has an associated FeedType, + +289 +00:18:51,899 --> 00:18:54,168 +which conforms to AnimalFeed. + +290 +00:18:56,703 --> 00:19:00,741 +This type can be used as the base +of the method call grow(). + +291 +00:19:00,774 --> 00:19:04,044 +The grow() method on AnimalFeed +returns a value + +292 +00:19:04,077 --> 00:19:09,249 +whose type is the nested +associated CropType of AnimalFeed. + +293 +00:19:09,283 --> 00:19:11,852 +We know that CropType conforms to Crop, + +294 +00:19:11,885 --> 00:19:13,921 +so I can call harvest() on it. + +295 +00:19:13,954 --> 00:19:15,889 +But what do I get back? + +296 +00:19:15,923 --> 00:19:19,426 +harvest() is declared to return +the associated FeedType + +297 +00:19:19,459 --> 00:19:21,795 +of the Crop protocol. + +298 +00:19:21,828 --> 00:19:29,236 +In our case, since the base of the call is +(some Animal).FeedType.CropType, + +299 +00:19:29,269 --> 00:19:32,239 +harvest() will output a value of type + +300 +00:19:32,272 --> 00:19:37,411 +(some Animal).FeedType.CropType.FeedType. + +301 +00:19:37,444 --> 00:19:40,347 +Unfortunately, this is the wrong type. + +302 +00:19:40,380 --> 00:19:45,352 +The eat() method on (some Animal) expects +(some Animal).FeedType, + +303 +00:19:45,385 --> 00:19:51,158 +and not +(some Animal).FeedType.CropType.FeedType. + +304 +00:19:51,191 --> 00:19:53,927 +The program is not well-typed. + +305 +00:19:53,961 --> 00:19:58,765 +These protocol definitions, as written, +do not actually guarantee + +306 +00:19:58,799 --> 00:20:01,235 +that if we start +with a type of animal feed, + +307 +00:20:01,268 --> 00:20:04,137 +and then grow and harvest this crop, + +308 +00:20:04,171 --> 00:20:06,740 +we'll get back +the same type of animal feed + +309 +00:20:06,773 --> 00:20:10,944 +that we started with, +which is what our animal expects to eat. + +310 +00:20:10,978 --> 00:20:14,181 +Another way to think about it +is that these protocol definitions + +311 +00:20:14,214 --> 00:20:17,251 +are too general - +they don't accurately model + +312 +00:20:17,284 --> 00:20:21,455 +the desired relationship +between our concrete types. + +313 +00:20:21,488 --> 00:20:26,293 +To understand why, +let's look at our Hay and Alfalfa types. + +314 +00:20:26,326 --> 00:20:29,096 +When I grow hay, I get alfalfa, + +315 +00:20:29,129 --> 00:20:33,066 +and when I harvest alfalfa, +I get hay, and so on. + +316 +00:20:33,100 --> 00:20:35,636 +Now imagine I'm refactoring my code, + +317 +00:20:35,669 --> 00:20:40,507 +and I accidentally change the return type +of the harvest() method on Alfalfa + +318 +00:20:40,541 --> 00:20:43,777 +to return Scratch instead of Hay. + +319 +00:20:43,810 --> 00:20:46,046 +After this accidental change, + +320 +00:20:46,079 --> 00:20:49,349 +the concrete types +still satisfy the requirements + +321 +00:20:49,383 --> 00:20:51,885 +of the AnimalFeed and Crop protocols, + +322 +00:20:51,919 --> 00:20:56,423 +even though we violate our desired +invariant that growing and harvesting + +323 +00:20:56,456 --> 00:21:02,062 +a crop produces the same type +of animal feed that we started with. + +324 +00:21:02,095 --> 00:21:05,632 +Let's look +at the AnimalFeed protocol again. + +325 +00:21:05,666 --> 00:21:08,435 +the real problem here is that in a sense, + +326 +00:21:08,468 --> 00:21:12,139 +we have too many +distinct associated types. + +327 +00:21:12,172 --> 00:21:16,743 +We need to write down the fact +that two of these associated types + +328 +00:21:16,777 --> 00:21:19,813 +are actually the same concrete type. + +329 +00:21:19,847 --> 00:21:23,417 +This will prevent +incorrectly-written concrete types + +330 +00:21:23,450 --> 00:21:26,019 +from conforming to our protocols; + +331 +00:21:26,053 --> 00:21:28,555 +it will also to give +the feedAnimal() method + +332 +00:21:28,589 --> 00:21:30,657 +the guarantee that it needs. + +333 +00:21:30,691 --> 00:21:34,761 +We can express the relationship +between these associated types + +334 +00:21:34,795 --> 00:21:39,733 +using a same-type requirement, +written in a 'where' clause. + +335 +00:21:39,766 --> 00:21:43,737 +A same-type requirement +expresses a static guarantee + +336 +00:21:43,770 --> 00:21:47,207 +that two different, +possibly nested associated types + +337 +00:21:47,241 --> 00:21:50,477 +must in fact be the same concrete type. + +338 +00:21:50,511 --> 00:21:54,281 +Adding a same-type requirement here +imposes a restriction + +339 +00:21:54,314 --> 00:21:58,785 +on the concrete types that conform +to the AnimalFeed protocol. + +340 +00:21:58,819 --> 00:22:02,689 +In this same-type requirement here, +we're declaring that + +341 +00:22:02,723 --> 00:22:09,296 +`Self dot CropType dot FeedType' +is the same type as 'Self'. + +342 +00:22:09,329 --> 00:22:12,366 +what does this look like in our diagram? + +343 +00:22:12,399 --> 00:22:15,235 +Well, here is how we can visualize it: + +344 +00:22:15,269 --> 00:22:19,506 +Each concrete type conforming +to AnimalFeed has a CropType, + +345 +00:22:19,540 --> 00:22:21,508 +which conforms to Crop. + +346 +00:22:21,542 --> 00:22:24,945 +However, the FeedType of this CropType, + +347 +00:22:24,978 --> 00:22:28,615 +is not just some other type +conforming to AnimalFeed, + +348 +00:22:28,649 --> 00:22:34,588 +it is the same concrete type +as the original AnimalFeed. + +349 +00:22:34,621 --> 00:22:38,759 +Instead of an infinite tower +of nested associated types, + +350 +00:22:38,792 --> 00:22:42,429 +I've collapsed all relationships down +to a single pair + +351 +00:22:42,462 --> 00:22:45,599 +of related associated types. + +352 +00:22:45,632 --> 00:22:48,135 +What about the 'Crop' protocol? + +353 +00:22:48,168 --> 00:22:53,440 +Here, the Crop's FeedType +has collapsed down to a pair of types, + +354 +00:22:53,473 --> 00:22:57,878 +but we still have +one too many associated types. + +355 +00:22:57,911 --> 00:23:02,182 +We want to say +that the Crop's FeedType's Crop Type + +356 +00:23:02,216 --> 00:23:06,620 +is the same type as the Crop +that we originally started with. + +357 +00:23:10,591 --> 00:23:13,260 +Now that these two protocols +have been equipped + +358 +00:23:13,293 --> 00:23:15,329 +with same-type requirements, + +359 +00:23:15,362 --> 00:23:19,132 +we can revisit the 'feedAnimal()' +method again. + +360 +00:23:19,166 --> 00:23:22,803 +We start with the type of some Animal, +as before. + +361 +00:23:22,836 --> 00:23:25,205 +and we get the animal's feed type, + +362 +00:23:25,239 --> 00:23:29,309 +which we know conforms +to the AnimalFeed protocol. + +363 +00:23:29,343 --> 00:23:35,249 +When we grow this crop, we get +some animal's feed type's crop type. + +364 +00:23:35,282 --> 00:23:37,985 +But now, when we harvest this crop, + +365 +00:23:38,018 --> 00:23:41,622 +instead of getting yet another +nested associated type, + +366 +00:23:41,655 --> 00:23:45,392 +we get exactly the feed type +that our animal expects, + +367 +00:23:45,425 --> 00:23:47,794 +and the happy animal is now guaranteed + +368 +00:23:47,828 --> 00:23:52,499 +to eat() the correct type of animal feed +that we just grew. + +369 +00:23:52,533 --> 00:23:57,804 +Finally, let's look at an associated type +diagram for the Animal protocol, + +370 +00:23:57,838 --> 00:24:01,175 +which pulls everything together +we've seen so far. + +371 +00:24:03,677 --> 00:24:06,480 +Here are the two sets of conforming types: + +372 +00:24:06,513 --> 00:24:10,184 +first, we have Cow, Hay, and Alfalfa. + +373 +00:24:10,217 --> 00:24:13,887 +Second, we have Chicken, Scratch +and Millet. + +374 +00:24:13,921 --> 00:24:18,125 +Notice how our three protocols +precisely model the relationships + +375 +00:24:18,158 --> 00:24:21,728 +between each set of three concrete types. + +376 +00:24:21,762 --> 00:24:24,131 +By understanding your data model, + +377 +00:24:24,164 --> 00:24:26,300 +you can use same-type requirements + +378 +00:24:26,333 --> 00:24:31,872 +to define equivalences between +these different nested associated types. + +379 +00:24:31,905 --> 00:24:35,075 +Generic code can then rely on +these relationships + +380 +00:24:35,108 --> 00:24:40,013 +when chaining together multiple calls +to protocol requirements. + +381 +00:24:40,047 --> 00:24:44,284 +During this session, +we explored when type erasure is safe, + +382 +00:24:44,318 --> 00:24:49,556 +and when we need to be in a context +where type relationships are guaranteed. + +383 +00:24:49,590 --> 00:24:52,759 +Then, we discussed how to strike +the right balance + +384 +00:24:52,793 --> 00:24:55,495 +between preserving rich type information + +385 +00:24:55,529 --> 00:25:00,100 +and hiding implementation details +using primary associated types, + +386 +00:25:00,133 --> 00:25:05,939 +which can be used with both opaque +result types and existential types. + +387 +00:25:05,973 --> 00:25:10,410 +Finally, we saw how to identify +and guarantee type relationships + +388 +00:25:10,444 --> 00:25:14,314 +between sets of concrete types +using same-type requirements + +389 +00:25:14,348 --> 00:25:18,952 +across the protocols that represent +those related sets of types. + +390 +00:25:18,986 --> 00:25:20,888 +Thank you for joining me. + +391 +00:25:20,921 --> 00:25:23,590 +I hope you have a great WWDC. + diff --git a/eng/2022 Session 110354 What's new in Swift en.srt b/eng/2022 Session 110354 What's new in Swift en.srt new file mode 100644 index 0000000..df5ca80 --- /dev/null +++ b/eng/2022 Session 110354 What's new in Swift en.srt @@ -0,0 +1,4107 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,810 +♪ + +3 +00:00:09,810 --> 00:00:11,545 +Hi, I'm Angela. + +4 +00:00:11,545 --> 00:00:13,413 +And I'm Becca. + +5 +00:00:13,413 --> 00:00:15,415 +Welcome +to what's new in Swift! + +6 +00:00:15,415 --> 00:00:17,317 +We're really excited +to talk to you today + +7 +00:00:17,317 --> 00:00:21,154 +about all of the great +new features in Swift 5.7. + +8 +00:00:21,154 --> 00:00:22,956 +Many of the things +we'll talk about today + +9 +00:00:22,956 --> 00:00:28,195 +demonstrate Swift's goal to make +your life as a developer easier. + +10 +00:00:28,195 --> 00:00:29,429 +We'll look at new tooling + +11 +00:00:29,429 --> 00:00:31,732 +to help you customize +your workflow + +12 +00:00:31,732 --> 00:00:35,569 +and some amazing +under-the-hood improvements. + +13 +00:00:35,569 --> 00:00:38,772 +Then we'll talk about the latest +in Swift's concurrency model + +14 +00:00:38,772 --> 00:00:43,243 +and the road to Swift 6, +including full-thread safety. + +15 +00:00:43,243 --> 00:00:44,912 +Then I'll finish up +by taking you through + +16 +00:00:44,912 --> 00:00:47,447 +some language improvements +that make Swift easier to read + +17 +00:00:47,447 --> 00:00:50,784 +and write, including cleaner, +simpler generics, + +18 +00:00:50,784 --> 00:00:54,688 +and powerful new +string processing facilities. + +19 +00:00:54,688 --> 00:00:57,424 +But first, +let's start by talking about + +20 +00:00:57,424 --> 00:01:01,228 +one of the things +that makes Swift so special -- + +21 +00:01:01,228 --> 00:01:02,763 +all of you. + +22 +00:01:02,763 --> 00:01:05,499 +Your input and contributions +are what have enabled Swift + +23 +00:01:05,499 --> 00:01:08,268 +to expand so rapidly. + +24 +00:01:08,268 --> 00:01:11,038 +Community involvement +is at Swift's core. + +25 +00:01:11,038 --> 00:01:14,174 +This year, more of the Swift +project became available + +26 +00:01:14,174 --> 00:01:16,209 +to the community when docC -- + +27 +00:01:16,209 --> 00:01:19,446 +the documentation generation +tool announced last year -- + +28 +00:01:19,446 --> 00:01:23,150 +and the Swift.org website +were open sourced. + +29 +00:01:23,150 --> 00:01:24,651 +Open source works best + +30 +00:01:24,651 --> 00:01:28,889 +when you have an active +community shepherding it. + +31 +00:01:28,889 --> 00:01:31,591 +We've been using the workgroup +model for Swift on Server + +32 +00:01:31,591 --> 00:01:34,528 +and Diversity in Swift +to provide stewardship + +33 +00:01:34,528 --> 00:01:36,196 +and support +for community members + +34 +00:01:36,196 --> 00:01:38,732 +interested in specific areas. + +35 +00:01:38,732 --> 00:01:40,400 +This has been working +really well + +36 +00:01:40,400 --> 00:01:42,336 +so we've started +two new workgroups. + +37 +00:01:42,336 --> 00:01:44,171 +One for iterating +on the Swift website + +38 +00:01:44,171 --> 00:01:46,340 +and making it more +of a community resource, + +39 +00:01:46,340 --> 00:01:49,409 +and another +for C++ interoperability, + +40 +00:01:49,409 --> 00:01:54,581 +to shape the design of the model +between C++ and Swift. + +41 +00:01:54,581 --> 00:01:57,551 +As we venture into new areas, +we all need support + +42 +00:01:57,551 --> 00:02:00,020 +from members +within the community. + +43 +00:02:00,020 --> 00:02:02,856 +As a part of that, the +Diversity in Swift workgroup + +44 +00:02:02,856 --> 00:02:05,792 +introduced the Swift +Mentorship Program last year. + +45 +00:02:05,792 --> 00:02:07,427 +The program provides pathways + +46 +00:02:07,427 --> 00:02:09,563 +to contribute to all +of the workgroup areas + +47 +00:02:09,563 --> 00:02:11,398 +for folks who don't know +how to start + +48 +00:02:11,398 --> 00:02:15,869 +or are looking to deepen their +expertise in a particular area. + +49 +00:02:15,869 --> 00:02:18,805 +Last year's program +was a huge success. + +50 +00:02:18,805 --> 00:02:21,708 +There were a lot +of interested mentees; + +51 +00:02:21,708 --> 00:02:25,912 +and with that, we were able +to create 41 mentorship pairs. + +52 +00:02:25,912 --> 00:02:29,082 +This success is why the program +is being brought back + +53 +00:02:29,082 --> 00:02:30,617 +for year two. + +54 +00:02:30,617 --> 00:02:32,419 +The program would love +to include everyone + +55 +00:02:32,419 --> 00:02:35,655 +who's interested; +but to do that, we need you -- + +56 +00:02:35,655 --> 00:02:38,792 +the excited and experienced +developers listening now + +57 +00:02:38,792 --> 00:02:40,827 +who are ready to share +their breadth of knowledge + +58 +00:02:40,827 --> 00:02:42,763 +and make new connections. + +59 +00:02:42,763 --> 00:02:45,599 +Because the mentorship program +is not just about the code + +60 +00:02:45,599 --> 00:02:49,002 +but about building relationships +within the community. + +61 +00:02:49,002 --> 00:02:52,005 +And a little guidance +can have a lasting effect. + +62 +00:02:52,005 --> 00:02:54,641 +Don't just take my word for it. + +63 +00:02:54,641 --> 00:02:58,045 +Last year, Amrit participated +in the mentorship program + +64 +00:02:58,045 --> 00:03:00,847 +and focused on compiler +and language design. + +65 +00:03:00,847 --> 00:03:02,749 +What started off +as intrigue for Amrit + +66 +00:03:02,749 --> 00:03:06,019 +transformed into +tangible contributions. + +67 +00:03:06,019 --> 00:03:09,056 +Diving into a new domain +is not easy. + +68 +00:03:09,056 --> 00:03:12,092 +Even so, she walked away +finding success + +69 +00:03:12,092 --> 00:03:14,828 +and feeling inspired +to contribute more. + +70 +00:03:14,828 --> 00:03:16,229 +Like many others, + +71 +00:03:16,229 --> 00:03:19,499 +this experience +opened a door for Amrit. + +72 +00:03:19,499 --> 00:03:22,202 +In addition to compiler +and language design, + +73 +00:03:22,202 --> 00:03:26,339 +last year there were a wide +range of available focus areas, + +74 +00:03:26,339 --> 00:03:28,008 +from technical writing +and testing + +75 +00:03:28,008 --> 00:03:30,744 +to contributing +to Swift packages. + +76 +00:03:30,744 --> 00:03:33,080 +This year, +we're adding even more + +77 +00:03:33,080 --> 00:03:36,116 +and there's always opportunities +for new topics. + +78 +00:03:36,116 --> 00:03:39,252 +If you don't see something +in this list that interests you, + +79 +00:03:39,252 --> 00:03:42,255 +you can still mention it +in your application. + +80 +00:03:42,255 --> 00:03:45,425 +Another addition is that +this year's program will offer + +81 +00:03:45,425 --> 00:03:48,261 +mentorship year-round +for starter bug contributions + +82 +00:03:48,261 --> 00:03:50,730 +to help accommodate anyone +who may have a lower capacity + +83 +00:03:50,730 --> 00:03:54,734 +to participate but is still +excited to get involved. + +84 +00:03:54,734 --> 00:03:56,503 +If you're interested +in applying, + +85 +00:03:56,503 --> 00:03:58,238 +or just eager to hear more, + +86 +00:03:58,238 --> 00:04:00,474 +check out the most +recent Swift blog post. + +87 +00:04:00,474 --> 00:04:02,809 +There, you can find links +to detailed reflections + +88 +00:04:02,809 --> 00:04:05,512 +from the highlighted mentees. + +89 +00:04:05,512 --> 00:04:08,048 +The mentorship program +is just one initiative + +90 +00:04:08,048 --> 00:04:10,717 +under Diversity in Swift +umbrella. + +91 +00:04:10,717 --> 00:04:12,552 +To learn more about +the mentorship program + +92 +00:04:12,552 --> 00:04:14,754 +and other Diversity in Swift +efforts, + +93 +00:04:14,754 --> 00:04:17,691 +you can visit +Swift.org/diversity. + +94 +00:04:17,691 --> 00:04:20,494 +To open the door even further, +we want to make it + +95 +00:04:20,494 --> 00:04:24,965 +as easy as possible to use Swift +with the resources you have! + +96 +00:04:24,965 --> 00:04:27,868 +We have streamlined the Swift +toolchain distribution process + +97 +00:04:27,868 --> 00:04:29,269 +for the Linux platform + +98 +00:04:29,269 --> 00:04:32,072 +by adding support +for Linux package formats. + +99 +00:04:32,072 --> 00:04:33,940 +With the new native +toolchain installers, + +100 +00:04:33,940 --> 00:04:37,110 +you can now download RPMs +for Amazon Linux 2 + +101 +00:04:37,110 --> 00:04:40,380 +and CentOS 7 +directly from Swift.org. + +102 +00:04:40,380 --> 00:04:42,115 +These toolchains +are experimental, + +103 +00:04:42,115 --> 00:04:45,418 +so be sure to share feedback +on the Swift.org forums. + +104 +00:04:45,418 --> 00:04:48,155 +Swift is primarily used +for building apps. + +105 +00:04:48,155 --> 00:04:52,626 +However, the vision has always +been for Swift to be scalable -- + +106 +00:04:52,626 --> 00:04:54,661 +used from everything +from high-level scripts + +107 +00:04:54,661 --> 00:04:57,531 +down to bare-metal environments. + +108 +00:04:57,531 --> 00:04:58,932 +To encourage Swift to be used + +109 +00:04:58,932 --> 00:05:01,234 +where it's never +been used before, + +110 +00:05:01,234 --> 00:05:04,171 +Swift underwent some +major changes this year. + +111 +00:05:04,171 --> 00:05:05,705 +To make the standard +library smaller + +112 +00:05:05,705 --> 00:05:08,441 +for standalone, +statically linked binaries, + +113 +00:05:08,441 --> 00:05:09,943 +we dropped the dependency + +114 +00:05:09,943 --> 00:05:12,445 +on an external +Unicode support library, + +115 +00:05:12,445 --> 00:05:17,384 +replacing it with a faster +native implementation. + +116 +00:05:17,384 --> 00:05:20,187 +Smaller, faster binaries +are a huge benefit + +117 +00:05:20,187 --> 00:05:22,756 +when running on +event-driven server solutions. + +118 +00:05:22,756 --> 00:05:24,925 +You get static linking +on Linux by default + +119 +00:05:24,925 --> 00:05:28,595 +to better support containerized +deployments for the server. + +120 +00:05:28,595 --> 00:05:30,530 +This size reduction +makes Swift suitable + +121 +00:05:30,530 --> 00:05:32,432 +for even restricted +environments, + +122 +00:05:32,432 --> 00:05:33,633 +which allowed us to use it + +123 +00:05:33,633 --> 00:05:36,269 +in Apple's +Secure Enclave Processor. + +124 +00:05:36,269 --> 00:05:38,738 +Swift is useful +from apps to servers + +125 +00:05:38,738 --> 00:05:41,675 +all the way down +to restricted processors; + +126 +00:05:41,675 --> 00:05:46,713 +tying it all together +is the package ecosystem. + +127 +00:05:46,713 --> 00:05:48,782 +This year's new features +in Swift packages + +128 +00:05:48,782 --> 00:05:50,584 +will make your life better. + +129 +00:05:50,584 --> 00:05:55,655 +To start, Swift Package Manager +has introduced TOFU. + +130 +00:05:55,655 --> 00:05:58,592 +No, not the delicious snack. + +131 +00:05:58,592 --> 00:06:01,928 +TOFU is an acronym that stands +for Trust On First Use. + +132 +00:06:01,928 --> 00:06:04,231 +It's a new security protocol +where the fingerprint + +133 +00:06:04,231 --> 00:06:06,066 +of a package +is now being recorded + +134 +00:06:06,066 --> 00:06:08,468 +when the package +is first downloaded. + +135 +00:06:08,468 --> 00:06:11,037 +Subsequent downloads +will validate this fingerprint + +136 +00:06:11,037 --> 00:06:14,674 +and report an error if +the fingerprints are different. + +137 +00:06:14,674 --> 00:06:17,377 +This is just one example +of how trust and security + +138 +00:06:17,377 --> 00:06:20,146 +are built into the core +of the package ecosystem + +139 +00:06:20,146 --> 00:06:23,383 +to help you feel +confident using it. + +140 +00:06:23,383 --> 00:06:26,353 +Command plug-ins are a great way +to improve the workflow + +141 +00:06:26,353 --> 00:06:28,288 +for Swift developers. + +142 +00:06:28,288 --> 00:06:30,657 +They are the first step +in providing more extensible + +143 +00:06:30,657 --> 00:06:32,926 +and secure build tools. + +144 +00:06:32,926 --> 00:06:35,962 +Command plug-ins can be used +for documentation generation, + +145 +00:06:35,962 --> 00:06:38,632 +source code reformatting +and more. + +146 +00:06:38,632 --> 00:06:41,067 +Instead of writing your +automation in a shell script + +147 +00:06:41,067 --> 00:06:44,437 +and having to maintain separate +workflows, you can use Swift! + +148 +00:06:44,437 --> 00:06:46,906 +Think open source +formatters and linters. + +149 +00:06:46,906 --> 00:06:50,644 +Now, all of those open source +tools are available within Xcode + +150 +00:06:50,644 --> 00:06:53,947 +and Swift Package Manager. + +151 +00:06:53,947 --> 00:06:57,050 +Command plug-ins are the glue +between open source tools + +152 +00:06:57,050 --> 00:06:58,818 +and Swift Package Manager. + +153 +00:06:58,818 --> 00:07:01,054 +The Swift project +is embracing developer tools + +154 +00:07:01,054 --> 00:07:03,957 +in the open source community +to provide seamless integration + +155 +00:07:03,957 --> 00:07:06,693 +with your automated workflows. + +156 +00:07:06,693 --> 00:07:09,296 +docC is great tool +to integrate documentation + +157 +00:07:09,296 --> 00:07:10,697 +into your source code. + +158 +00:07:10,697 --> 00:07:14,734 +This year, it got even better +with Objective-C and C support. + +159 +00:07:14,734 --> 00:07:16,536 +Let's take a look +at what it would take + +160 +00:07:16,536 --> 00:07:19,072 +to create a plug-in +with docC. + +161 +00:07:19,072 --> 00:07:20,907 +Plug-ins are just +simple Swift code. + +162 +00:07:20,907 --> 00:07:23,109 +You can define a plug-in +by creating a struct + +163 +00:07:23,109 --> 00:07:25,478 +that conforms to +the CommandPlugin protocol. + +164 +00:07:25,478 --> 00:07:28,014 +And then you just add a function +that tells your plug-in + +165 +00:07:28,014 --> 00:07:30,250 +which tool you'd like to invoke. + +166 +00:07:30,250 --> 00:07:34,454 +Within this function +is where we want to call docC. + +167 +00:07:34,454 --> 00:07:37,290 +Once you've defined your +plug-in, it becomes available + +168 +00:07:37,290 --> 00:07:39,025 +through the Swift PM +command line interface + +169 +00:07:39,025 --> 00:07:40,927 +and Xcode as a menu entry. + +170 +00:07:40,927 --> 00:07:44,364 +Now, we can tell Swift PM +to generate documentation + +171 +00:07:44,364 --> 00:07:48,568 +and it knows to pass this action +to the docC executable. + +172 +00:07:48,568 --> 00:07:50,337 +It doesn't stop there. + +173 +00:07:50,337 --> 00:07:54,240 +There's a second plug-in +known as build tool plug-ins. + +174 +00:07:54,240 --> 00:07:56,376 +These plug-ins +are packages that allow you + +175 +00:07:56,376 --> 00:07:59,245 +to inject additional steps +during the build. + +176 +00:07:59,245 --> 00:08:01,381 +When you implement +a build tool plug-in, + +177 +00:08:01,381 --> 00:08:02,982 +that will create a command +for the build system + +178 +00:08:02,982 --> 00:08:05,085 +to execute in a sandbox. + +179 +00:08:05,085 --> 00:08:06,786 +They differ from +command plug-ins + +180 +00:08:06,786 --> 00:08:09,089 +which you execute +directly at any time + +181 +00:08:09,089 --> 00:08:11,224 +and can be granted +explicit permission + +182 +00:08:11,224 --> 00:08:13,993 +to change files in your package. + +183 +00:08:13,993 --> 00:08:16,996 +Build tool plug-ins can be used +for source code generation + +184 +00:08:16,996 --> 00:08:20,233 +or custom processing +for special types of files. + +185 +00:08:20,233 --> 00:08:23,570 +With build tool plug-ins, this +would be the package layout. + +186 +00:08:23,570 --> 00:08:25,839 +In this example, +the plugin.Swift + +187 +00:08:25,839 --> 00:08:27,607 +is the Swift script +that implements + +188 +00:08:27,607 --> 00:08:29,142 +the package plug-in target. + +189 +00:08:29,142 --> 00:08:33,480 +The plug-in is treated +as a Swift executable. + +190 +00:08:33,480 --> 00:08:35,482 +And you write the plug-in +in the same way + +191 +00:08:35,482 --> 00:08:38,852 +you write any +Swift executable. + +192 +00:08:38,852 --> 00:08:40,653 +You can implement +your plug-in by defining + +193 +00:08:40,653 --> 00:08:43,456 +a set of build commands +that tells the build system + +194 +00:08:43,456 --> 00:08:46,559 +what executable command to run +and what outputs are expected + +195 +00:08:46,559 --> 00:08:48,294 +as a result. + +196 +00:08:48,294 --> 00:08:50,630 +Package plug-ins +are secure solutions + +197 +00:08:50,630 --> 00:08:53,366 +that provide extensibility +in your packages. + +198 +00:08:53,366 --> 00:08:55,702 +You can learn more +about how plug-ins work + +199 +00:08:55,702 --> 00:08:58,705 +and how to implement your own +plug-in, in two sessions, + +200 +00:08:58,705 --> 00:09:00,206 +"Meet Swift Package plugins" + +201 +00:09:00,206 --> 00:09:03,410 +and "Create Swift Package +plugins." + +202 +00:09:03,410 --> 00:09:05,545 +As you expand +your use of packages, + +203 +00:09:05,545 --> 00:09:08,681 +you might have encountered +module collisions. + +204 +00:09:08,681 --> 00:09:10,283 +That's when two +separate packages + +205 +00:09:10,283 --> 00:09:12,786 +define a module +with the same name. + +206 +00:09:12,786 --> 00:09:14,320 +To solve this situation, + +207 +00:09:14,320 --> 00:09:19,225 +Swift 5.7 introduces +module disambiguation. + +208 +00:09:19,225 --> 00:09:22,896 +Module disambiguation +is a feature that allows you + +209 +00:09:22,896 --> 00:09:26,900 +to rename modules from outside +the packages that define them. + +210 +00:09:26,900 --> 00:09:28,902 +Here in our +Stunning application, + +211 +00:09:28,902 --> 00:09:31,905 +we're bringing in two packages +that define a Logging module, + +212 +00:09:31,905 --> 00:09:33,139 +so they clash. + +213 +00:09:33,139 --> 00:09:35,909 +To fix this for +our Stunning application, + +214 +00:09:35,909 --> 00:09:38,545 +you'll just need to add +the moduleAliases keyword + +215 +00:09:38,545 --> 00:09:42,949 +to the dependencies section +of your package manifest. + +216 +00:09:42,949 --> 00:09:44,717 +That way you can use +two different names + +217 +00:09:44,717 --> 00:09:46,519 +to distinguish between modules + +218 +00:09:46,519 --> 00:09:49,456 +that previously had +the same name. + +219 +00:09:49,456 --> 00:09:53,560 +Swift 5.7 brings some fantastic +performance improvements. + +220 +00:09:53,560 --> 00:09:56,129 +Let's start +by looking at build times. + +221 +00:09:56,129 --> 00:09:58,698 +Last year, we told you +about how we had rewritten + +222 +00:09:58,698 --> 00:10:00,633 +the Swift Driver -- +the program that coordinates + +223 +00:10:00,633 --> 00:10:04,103 +the compilation +of Swift source code in Swift. + +224 +00:10:04,103 --> 00:10:05,371 +Last year's rearchitecture + +225 +00:10:05,371 --> 00:10:07,140 +unlocked some really +important changes + +226 +00:10:07,140 --> 00:10:09,442 +that speed up builds +significantly. + +227 +00:10:09,442 --> 00:10:11,611 +The driver can now be used +as a framework + +228 +00:10:11,611 --> 00:10:13,746 +directly inside +the Xcode build system + +229 +00:10:13,746 --> 00:10:16,316 +instead of +as a separate executable. + +230 +00:10:16,316 --> 00:10:18,451 +This allows it to coordinate +builds more closely + +231 +00:10:18,451 --> 00:10:22,622 +with the build system to allow +things like parallelization. + +232 +00:10:22,622 --> 00:10:25,124 +If you're someone who loves +the sound of quick builds, + +233 +00:10:25,124 --> 00:10:26,893 +you can get +more details in the + +234 +00:10:26,893 --> 00:10:31,030 +"Demystify parallelization +in Xcode builds" session. + +235 +00:10:31,030 --> 00:10:33,132 +To show you +how much faster builds are, + +236 +00:10:33,132 --> 00:10:36,002 +let's look at some examples +of how long it takes to build + +237 +00:10:36,002 --> 00:10:39,305 +some of the tools we use often +that are written in Swift. + +238 +00:10:39,305 --> 00:10:41,841 +On a 10-core iMac, +the improvements have ranged + +239 +00:10:41,841 --> 00:10:46,579 +from 5 percent +all the way up to 25 percent. + +240 +00:10:46,579 --> 00:10:49,883 +Next, there are improvements +to the speed of type checking. + +241 +00:10:49,883 --> 00:10:52,585 +This year, we improved +the type-checker performance + +242 +00:10:52,585 --> 00:10:56,155 +by reimplementing a key part +of the generics system -- + +243 +00:10:56,155 --> 00:10:58,191 +the part that computes +a function signature + +244 +00:10:58,191 --> 00:11:00,727 +from things like protocols +and "where" clauses. + +245 +00:11:00,727 --> 00:11:03,530 +In the old implementation, +time and memory usage + +246 +00:11:03,530 --> 00:11:07,166 +could scale exponentially +as more protocols were involved. + +247 +00:11:07,166 --> 00:11:10,870 +For example, here, we have +a complicated set of protocols + +248 +00:11:10,870 --> 00:11:13,339 +that define a coordinate system, +with a lot + +249 +00:11:13,339 --> 00:11:16,576 +of generic requirements +on the many associated types. + +250 +00:11:16,576 --> 00:11:20,713 +Previously, this would take 17 +seconds to type-check this code. + +251 +00:11:20,713 --> 00:11:24,217 +But now, in Swift 5.7, +this example is able + +252 +00:11:24,217 --> 00:11:28,888 +to type-check significantly +quicker, in under a second. + +253 +00:11:28,888 --> 00:11:32,825 +We also have some equally +impressive runtime improvements. + +254 +00:11:32,825 --> 00:11:36,629 +Before Swift 5.7, we've seen +protocol checking on app startup + +255 +00:11:36,629 --> 00:11:39,632 +take as long +as four seconds on iOS. + +256 +00:11:39,632 --> 00:11:42,969 +Protocols needed to be computed +every time we launched apps, + +257 +00:11:42,969 --> 00:11:44,771 +resulting in launch times +that got longer + +258 +00:11:44,771 --> 00:11:46,739 +the more protocols you added. + +259 +00:11:46,739 --> 00:11:48,708 +Now, they're cached. + +260 +00:11:48,708 --> 00:11:50,643 +Depending on how an app +was written + +261 +00:11:50,643 --> 00:11:52,679 +and how many protocols it used, + +262 +00:11:52,679 --> 00:11:55,715 +this can mean launch times +being cut in half in some apps + +263 +00:11:55,715 --> 00:11:58,051 +when running on iOS 16. + +264 +00:11:58,051 --> 00:12:01,220 +The session "Improve app size +and runtime performance" + +265 +00:12:01,220 --> 00:12:03,356 +will dive deeper +into how you can leverage + +266 +00:12:03,356 --> 00:12:07,160 +these improvements +in your own application. + +267 +00:12:07,160 --> 00:12:09,629 +Now, it's time for something +I'm sure a lot of you + +268 +00:12:09,629 --> 00:12:12,332 +have been eager to hear about. + +269 +00:12:12,332 --> 00:12:15,902 +Last year, we introduced +the new concurrency model, + +270 +00:12:15,902 --> 00:12:19,439 +bringing together actors +and async/await. + +271 +00:12:19,439 --> 00:12:23,409 +This had a transformative effect +on the concurrency architecture + +272 +00:12:23,409 --> 00:12:25,078 +of your applications. + +273 +00:12:25,078 --> 00:12:29,515 +Async/await and actors are safer +and easier than callbacks + +274 +00:12:29,515 --> 00:12:31,718 +and manual queue management. + +275 +00:12:31,718 --> 00:12:34,220 +This year, we further +fleshed out the model + +276 +00:12:34,220 --> 00:12:37,423 +with data race safety +at the forefront. + +277 +00:12:37,423 --> 00:12:39,559 +Because concurrency +was such a fundamental + +278 +00:12:39,559 --> 00:12:41,928 +and important improvement +to your app's codebase, + +279 +00:12:41,928 --> 00:12:44,430 +we made it possible +to back-deploy these changes + +280 +00:12:44,430 --> 00:12:48,201 +all the way back to iOS 13 +and macOS Catalina. + +281 +00:12:48,201 --> 00:12:50,503 +In order to deploy +to older operating systems, + +282 +00:12:50,503 --> 00:12:54,207 +your app bundles a copy of the +Swift 5.5 concurrency runtime + +283 +00:12:54,207 --> 00:12:55,975 +for older OSes. + +284 +00:12:55,975 --> 00:12:58,177 +This is similar +to back-deploying Swift + +285 +00:12:58,177 --> 00:13:01,914 +to operating systems +before ABI stability. + +286 +00:13:01,914 --> 00:13:05,151 +Next, we've taken this model +in new directions. + +287 +00:13:05,151 --> 00:13:06,586 +We've introduced +language features + +288 +00:13:06,586 --> 00:13:08,521 +and supporting packages. + +289 +00:13:08,521 --> 00:13:12,759 +First, let's talk about +data race avoidance. + +290 +00:13:12,759 --> 00:13:14,027 +Before I jump into that, + +291 +00:13:14,027 --> 00:13:15,728 +I should probably +take a step back + +292 +00:13:15,728 --> 00:13:18,498 +and say that one of the really +important features of Swift, + +293 +00:13:18,498 --> 00:13:20,867 +is memory safety by default. + +294 +00:13:20,867 --> 00:13:23,870 +Swift users can't do things +with unpredictable behavior, + +295 +00:13:23,870 --> 00:13:25,071 +like reading a value + +296 +00:13:25,071 --> 00:13:27,573 +while you're in the middle +of modifying it. + +297 +00:13:27,573 --> 00:13:31,277 +In this example, we're removing +all of the numbers in an array + +298 +00:13:31,277 --> 00:13:33,446 +that match +the same array's count. + +299 +00:13:33,446 --> 00:13:36,182 +Initially, +the array's count is 3, + +300 +00:13:36,182 --> 00:13:38,985 +so we'll remove +the 3 from the array. + +301 +00:13:38,985 --> 00:13:41,954 +But once we've done that, +the count will be 2. + +302 +00:13:41,954 --> 00:13:44,524 +Do we remove the 3 +and the 2 from the array, + +303 +00:13:44,524 --> 00:13:46,526 +or just the 3? + +304 +00:13:46,526 --> 00:13:48,327 +The answer is neither. + +305 +00:13:48,327 --> 00:13:50,163 +Swift will prevent you +from doing this + +306 +00:13:50,163 --> 00:13:52,865 +because it's not safe +to access the array's count + +307 +00:13:52,865 --> 00:13:55,435 +while you're in the middle +of modifying it. + +308 +00:13:55,435 --> 00:13:58,838 +Our goal is to do something +similar for thread safety. + +309 +00:13:58,838 --> 00:14:00,073 +We envision a language + +310 +00:14:00,073 --> 00:14:03,376 +that eliminates low-level +data races by default. + +311 +00:14:03,376 --> 00:14:06,379 +In other words, we want +to prevent concurrency bugs + +312 +00:14:06,379 --> 00:14:09,215 +that can cause +unpredictable behavior. + +313 +00:14:09,215 --> 00:14:10,950 +Here's another example. + +314 +00:14:10,950 --> 00:14:14,620 +Using the same number's array, +we create a background task + +315 +00:14:14,620 --> 00:14:17,223 +that appends 0 to the array, + +316 +00:14:17,223 --> 00:14:20,560 +and then we remove +the array's last element. + +317 +00:14:20,560 --> 00:14:23,062 +But wait, does removing +the last element + +318 +00:14:23,062 --> 00:14:26,566 +happen before or after +we append 0? + +319 +00:14:26,566 --> 00:14:29,669 +The answer, again, is neither. + +320 +00:14:29,669 --> 00:14:32,405 +Swift will block you from doing +this because it's not safe + +321 +00:14:32,405 --> 00:14:34,307 +to modify the array +from a background task + +322 +00:14:34,307 --> 00:14:38,678 +without synchronizing access +with something like an actor. + +323 +00:14:38,678 --> 00:14:42,749 +Actors were the first major step +towards eliminating data races. + +324 +00:14:42,749 --> 00:14:45,184 +This year we've refined +the concurrency model + +325 +00:14:45,184 --> 00:14:49,122 +to push us even further +towards the end goal. + +326 +00:14:49,122 --> 00:14:52,325 +You can think of each actor +as its own island, + +327 +00:14:52,325 --> 00:14:56,095 +isolated from everything else +in the sea of concurrency. + +328 +00:14:56,095 --> 00:14:58,798 +But what happens when +different threads want to query + +329 +00:14:58,798 --> 00:15:03,002 +the information stored by +each of the isolated actors? + +330 +00:15:03,002 --> 00:15:06,072 +This metaphor will be explored +in depth in the session + +331 +00:15:06,072 --> 00:15:10,643 +"Eliminate data races +using Swift Concurrency." + +332 +00:15:10,643 --> 00:15:14,013 +From memory safety +to thread safety by default; + +333 +00:15:14,013 --> 00:15:16,849 +that is the goal for Swift 6. + +334 +00:15:16,849 --> 00:15:19,051 +To get us there, +we first improved last year's + +335 +00:15:19,051 --> 00:15:21,521 +concurrency model +with the new language features + +336 +00:15:21,521 --> 00:15:23,122 +I just mentioned. + +337 +00:15:23,122 --> 00:15:24,991 +The second thing +I haven't mentioned yet + +338 +00:15:24,991 --> 00:15:26,692 +is the new opt-in safety checks + +339 +00:15:26,692 --> 00:15:30,163 +that identify +potential data races. + +340 +00:15:30,163 --> 00:15:32,865 +You can experiment with +stricter concurrency checking + +341 +00:15:32,865 --> 00:15:36,202 +by enabling it +in your build settings. + +342 +00:15:36,202 --> 00:15:38,371 +Let's take a look +at actors again. + +343 +00:15:38,371 --> 00:15:41,641 +We can take this notion +of actor isolation, + +344 +00:15:41,641 --> 00:15:44,544 +and take it further +with distributed actors. + +345 +00:15:44,544 --> 00:15:47,313 +Distributed actors put those +islands on different machines + +346 +00:15:47,313 --> 00:15:49,248 +with a network between them. + +347 +00:15:49,248 --> 00:15:52,185 +This new language feature makes +developing distributed systems + +348 +00:15:52,185 --> 00:15:54,353 +much simpler. + +349 +00:15:54,353 --> 00:15:56,589 +Let's say you want +to create a game app; + +350 +00:15:56,589 --> 00:15:59,425 +you can now easily write +the back end in Swift. + +351 +00:15:59,425 --> 00:16:02,395 +Here, the distributed actor +is like an actor + +352 +00:16:02,395 --> 00:16:04,831 +but it might be +on a different machine. + +353 +00:16:04,831 --> 00:16:07,500 +In this example, +we're looking at computer player + +354 +00:16:07,500 --> 00:16:11,270 +that will maintain state +during a game with a user. + +355 +00:16:11,270 --> 00:16:14,173 +The distributed keyword can +also be added to a function + +356 +00:16:14,173 --> 00:16:15,842 +that we expect +will need to be called + +357 +00:16:15,842 --> 00:16:19,579 +on an actor that might be +on a remote machine. + +358 +00:16:19,579 --> 00:16:22,448 +Let's add another function +called endOfRound. + +359 +00:16:22,448 --> 00:16:26,385 +It will loop over the players +and call makeMove on each one. + +360 +00:16:26,385 --> 00:16:28,921 +Some of these players +might be local or remote, + +361 +00:16:28,921 --> 00:16:30,022 +but we have the benefit + +362 +00:16:30,022 --> 00:16:33,025 +of not needing to care +about which is which. + +363 +00:16:33,025 --> 00:16:35,428 +The only difference +from a regular actor call + +364 +00:16:35,428 --> 00:16:38,197 +is that a distributed actor +call can potentially fail + +365 +00:16:38,197 --> 00:16:40,233 +because of network errors. + +366 +00:16:40,233 --> 00:16:41,968 +In the event +of a network failure, + +367 +00:16:41,968 --> 00:16:44,437 +the actor method +would throw an error. + +368 +00:16:44,437 --> 00:16:46,272 +So, you need to add +the try keyword + +369 +00:16:46,272 --> 00:16:49,108 +as well as the usual +await keyword that's needed + +370 +00:16:49,108 --> 00:16:52,411 +when you call a function +outside of the actor. + +371 +00:16:52,411 --> 00:16:54,413 +Building on these core +language primitives, + +372 +00:16:54,413 --> 00:16:57,416 +we also built an open source +Distributed Actors package + +373 +00:16:57,416 --> 00:16:59,752 +that is focused on +building server-side, + +374 +00:16:59,752 --> 00:17:02,588 +clustered distributed +systems in Swift. + +375 +00:17:02,588 --> 00:17:05,224 +The package includes an +integrated networking layer + +376 +00:17:05,224 --> 00:17:09,195 +using SwiftNIO and implements +the SWIM consensus protocol + +377 +00:17:09,195 --> 00:17:11,597 +to manage state +across the cluster. + +378 +00:17:11,597 --> 00:17:13,633 +The "Meet distributed actors +in Swift" session + +379 +00:17:13,633 --> 00:17:15,568 +will go into more details +on how to build + +380 +00:17:15,568 --> 00:17:19,472 +distributed systems +with these new features. + +381 +00:17:19,472 --> 00:17:23,075 +We also launched a new set +of open source algorithms + +382 +00:17:23,075 --> 00:17:27,113 +to provide easy out-of-the-box +solutions to common operations + +383 +00:17:27,113 --> 00:17:29,515 +when dealing with AsyncSequence, + +384 +00:17:29,515 --> 00:17:32,151 +which was released +with Swift 5.5. + +385 +00:17:32,151 --> 00:17:34,153 +Releasing these APIs +as a package + +386 +00:17:34,153 --> 00:17:37,556 +gives developers flexibility +in deploying across platforms + +387 +00:17:37,556 --> 00:17:40,760 +and operating system versions. + +388 +00:17:40,760 --> 00:17:44,630 +There are several ways to +combine multiple async sequences + +389 +00:17:44,630 --> 00:17:47,366 +and to group values +into collections. + +390 +00:17:47,366 --> 00:17:49,035 +These are just some +of the algorithms + +391 +00:17:49,035 --> 00:17:51,070 +included in the package. + +392 +00:17:51,070 --> 00:17:53,940 +Check out the "Meet Swift +Async Algorithms" talk + +393 +00:17:53,940 --> 00:17:58,110 +to see how you can use +this new powerful API. + +394 +00:17:58,110 --> 00:18:00,646 +But there's another aspect +of concurrency, + +395 +00:18:00,646 --> 00:18:02,515 +which is performance. + +396 +00:18:02,515 --> 00:18:04,817 +This year, +with actor prioritization, + +397 +00:18:04,817 --> 00:18:08,654 +actors now execute +the highest-priority work first. + +398 +00:18:08,654 --> 00:18:10,456 +And continuing +our deep integration + +399 +00:18:10,456 --> 00:18:12,291 +with the operating system +scheduler, + +400 +00:18:12,291 --> 00:18:15,428 +the model has priority-inversion +prevention built in, + +401 +00:18:15,428 --> 00:18:20,299 +so less important work can't +block higher-priority work. + +402 +00:18:20,299 --> 00:18:23,069 +Historically, it has been +really hard to visualize + +403 +00:18:23,069 --> 00:18:26,072 +the performance impact +of concurrency in your app. + +404 +00:18:26,072 --> 00:18:31,410 +But now, we have a great +new tool for doing exactly that. + +405 +00:18:31,410 --> 00:18:34,080 +The new Swift Concurrency view +in Instruments + +406 +00:18:34,080 --> 00:18:36,716 +can help you investigate +performance issues. + +407 +00:18:36,716 --> 00:18:39,552 +The Swift Tasks +and Swift Actors instruments + +408 +00:18:39,552 --> 00:18:42,688 +provide a full suite of tools +to help you visualize + +409 +00:18:42,688 --> 00:18:46,158 +and optimize +your concurrency code. + +410 +00:18:46,158 --> 00:18:49,095 +At the top level, +the Swift Tasks Instrument + +411 +00:18:49,095 --> 00:18:52,131 +provides useful statistics, +including the number of tasks + +412 +00:18:52,131 --> 00:18:54,834 +running simultaneously +and the total tasks + +413 +00:18:54,834 --> 00:18:58,004 +that have been created +up until that point in time. + +414 +00:18:58,004 --> 00:18:59,872 +In the bottom half +of this window, + +415 +00:18:59,872 --> 00:19:03,776 +you can see what's referred to +as a Task Forest. + +416 +00:19:03,776 --> 00:19:06,212 +It provides a graphical +representation + +417 +00:19:06,212 --> 00:19:07,980 +of the parent-child +relationships + +418 +00:19:07,980 --> 00:19:11,550 +between tasks +in structured concurrent code. + +419 +00:19:11,550 --> 00:19:13,519 +This is just one +of the detailed views + +420 +00:19:13,519 --> 00:19:16,989 +for the Swift Actor +Instrument. + +421 +00:19:16,989 --> 00:19:19,492 +To learn how to use +this exciting new tool, + +422 +00:19:19,492 --> 00:19:20,960 +you'll want to hop over +to the talk + +423 +00:19:20,960 --> 00:19:24,196 +"Visualize and optimize +Swift concurrency." + +424 +00:19:24,196 --> 00:19:26,866 +And don't forget to give +those new packages a try. + +425 +00:19:26,866 --> 00:19:29,702 +Don't be shy to let us know +how it's going on the forums. + +426 +00:19:29,702 --> 00:19:32,004 +Now, I'll hand it over +to Becca to talk about + +427 +00:19:32,004 --> 00:19:36,142 +the many improvements +to Swift language usability. + +428 +00:19:36,142 --> 00:19:38,411 +Languages are tools, +and there's a funny thing + +429 +00:19:38,411 --> 00:19:40,546 +about tools -- +they can really affect + +430 +00:19:40,546 --> 00:19:42,848 +the things you build +with them. + +431 +00:19:42,848 --> 00:19:44,950 +When all you have +is a hammer, + +432 +00:19:44,950 --> 00:19:48,120 +you're going to build things +with nails instead of screws. + +433 +00:19:48,120 --> 00:19:50,856 +And even if you have +a full set of tools, + +434 +00:19:50,856 --> 00:19:52,658 +if your hammer has +a big, grippy handle + +435 +00:19:52,658 --> 00:19:55,661 +while your screwdriver +is plasticky and hard to hold, + +436 +00:19:55,661 --> 00:19:58,097 +you might still lean +towards the nails. + +437 +00:19:58,097 --> 00:19:59,999 +A language is the same way. + +438 +00:19:59,999 --> 00:20:02,501 +If Swift has a good tool +for expressing something, + +439 +00:20:02,501 --> 00:20:05,204 +people will use +it more often. + +440 +00:20:05,204 --> 00:20:07,540 +And this year, Swift's tools +for expressing what you want + +441 +00:20:07,540 --> 00:20:11,510 +your code to do +have improved in many ways. + +442 +00:20:11,510 --> 00:20:13,579 +Some of these changes +are simple conveniences + +443 +00:20:13,579 --> 00:20:15,414 +for things you do often. + +444 +00:20:15,414 --> 00:20:18,784 +For example, it's really common +in Swift to use if let + +445 +00:20:18,784 --> 00:20:22,154 +with the same name on both +sides of the equal sign. + +446 +00:20:22,154 --> 00:20:24,090 +After all, there probably +isn't a better name + +447 +00:20:24,090 --> 00:20:25,591 +for the unwrapped value + +448 +00:20:25,591 --> 00:20:28,561 +than the name you gave +the optional one. + +449 +00:20:28,561 --> 00:20:30,096 +But when the name +is really long, + +450 +00:20:30,096 --> 00:20:33,099 +that repetition starts +to get cumbersome. + +451 +00:20:33,099 --> 00:20:35,734 +You might be tempted +to abbreviate the name, + +452 +00:20:35,734 --> 00:20:39,105 +but then your code +becomes kind of cryptic. + +453 +00:20:39,105 --> 00:20:41,373 +And if you later rename +the optional variable, + +454 +00:20:41,373 --> 00:20:45,244 +the abbreviation +might get out of sync. + +455 +00:20:45,244 --> 00:20:47,780 +Swift 5.7 introduces +a new shorthand + +456 +00:20:47,780 --> 00:20:49,648 +for this common pattern. + +457 +00:20:49,648 --> 00:20:51,016 +If you're unwrapping +an optional + +458 +00:20:51,016 --> 00:20:53,519 +and want the unwrapped value +to have the same name, + +459 +00:20:53,519 --> 00:20:55,788 +just drop +the right-hand side. + +460 +00:20:55,788 --> 00:20:58,491 +Swift will assume +it's the same. + +461 +00:20:58,491 --> 00:21:00,759 +And of course, this also +works with guard, too, + +462 +00:21:00,759 --> 00:21:04,029 +and even while, +for that matter. + +463 +00:21:04,029 --> 00:21:06,832 +We also looked at places where +a feature suddenly stops working + +464 +00:21:06,832 --> 00:21:09,201 +when you make +a minor change. + +465 +00:21:09,201 --> 00:21:11,370 +For instance, Swift has always +been able to figure out + +466 +00:21:11,370 --> 00:21:13,939 +what type a call will return +based on the code + +467 +00:21:13,939 --> 00:21:16,542 +written inside +a one-statement closure. + +468 +00:21:16,542 --> 00:21:19,512 +In this compactMap call, +the closure returns the value + +469 +00:21:19,512 --> 00:21:22,448 +of parseLine, +and the parseLine function + +470 +00:21:22,448 --> 00:21:25,351 +returns a MailmapEntry, +so Swift can figure out + +471 +00:21:25,351 --> 00:21:30,189 +that entries should be +an array of MailmapEntry. + +472 +00:21:30,189 --> 00:21:32,158 +This now works for more +complicated closures + +473 +00:21:32,158 --> 00:21:35,361 +that have multiple statements +or control flow features. + +474 +00:21:35,361 --> 00:21:37,596 +So you can use do-catch, +or if...else, + +475 +00:21:37,596 --> 00:21:39,798 +or just add a print call, +without having + +476 +00:21:39,798 --> 00:21:43,302 +to manually specify +the closure's result type. + +477 +00:21:43,302 --> 00:21:45,971 +Another thing +we looked at is danger flags + +478 +00:21:45,971 --> 00:21:49,575 +that aren't really flagging +any actual danger. + +479 +00:21:49,575 --> 00:21:52,912 +Swift is very concerned +with type and memory safety. + +480 +00:21:52,912 --> 00:21:54,446 +To keep you +from making mistakes, + +481 +00:21:54,446 --> 00:21:56,015 +it never +automatically converts + +482 +00:21:56,015 --> 00:21:58,317 +between pointers +with different pointer types, + +483 +00:21:58,317 --> 00:22:01,320 +nor between raw pointers +and typed pointers. + +484 +00:22:01,320 --> 00:22:02,922 +This is very +different from C, + +485 +00:22:02,922 --> 00:22:05,257 +which allows +certain conversions. + +486 +00:22:05,257 --> 00:22:07,993 +For example, you can change +the signed-ness of the pointee, + +487 +00:22:07,993 --> 00:22:11,730 +or cast any pointer to char star +to access it as bytes, + +488 +00:22:11,730 --> 00:22:15,401 +without violating any +of C's pointer rules. + +489 +00:22:15,401 --> 00:22:17,770 +But sometimes these differences +in pointer behavior + +490 +00:22:17,770 --> 00:22:22,041 +will cause problems when +a C API is imported into Swift. + +491 +00:22:22,041 --> 00:22:24,176 +The original developer may +have designed their APIs + +492 +00:22:24,176 --> 00:22:26,078 +with slight mismatches +that are handled + +493 +00:22:26,078 --> 00:22:30,783 +by automatic conversions in C +but are errors in Swift. + +494 +00:22:30,783 --> 00:22:33,118 +In Swift, accessing +a pointer of one type + +495 +00:22:33,118 --> 00:22:35,988 +as though it were a different +type is very dangerous, + +496 +00:22:35,988 --> 00:22:39,391 +so you have to describe what +you're doing very explicitly. + +497 +00:22:39,391 --> 00:22:41,160 +But that's all pointless +if we're passing the pointer + +498 +00:22:41,160 --> 00:22:43,829 +directly to C, +because in C, + +499 +00:22:43,829 --> 00:22:47,499 +that pointer mismatch +is perfectly legal! + +500 +00:22:47,499 --> 00:22:50,169 +So in this case, we've treated +something really straightforward + +501 +00:22:50,169 --> 00:22:52,638 +as though it were dangerous. + +502 +00:22:52,638 --> 00:22:55,975 +This matters because, as much as +Swift values type safety, + +503 +00:22:55,975 --> 00:23:00,045 +it also values easy access +to C-family code. + +504 +00:23:00,045 --> 00:23:01,814 +That's why C +and Objective-C interop + +505 +00:23:01,814 --> 00:23:05,084 +are so rich and seamless, +and it's why the Swift project + +506 +00:23:05,084 --> 00:23:08,254 +formed the C++ working group +Angela mentioned earlier + +507 +00:23:08,254 --> 00:23:11,590 +to start building equally +capable C++ interop. + +508 +00:23:11,590 --> 00:23:13,459 +We don't want +using C functions like these + +509 +00:23:13,459 --> 00:23:16,762 +to be unnecessarily painful. + +510 +00:23:16,762 --> 00:23:18,564 +So Swift now has +a separate set of rules + +511 +00:23:18,564 --> 00:23:21,433 +for calls to imported +functions and methods. + +512 +00:23:21,433 --> 00:23:24,169 +It allows pointer conversions +that would be legal in C + +513 +00:23:24,169 --> 00:23:26,972 +even though they normally +aren't in Swift. + +514 +00:23:26,972 --> 00:23:31,510 +That way, your Swift code +can use these APIs seamlessly. + +515 +00:23:31,510 --> 00:23:33,746 +So far we've talked +about small improvements + +516 +00:23:33,746 --> 00:23:35,414 +to the tools +you already had. + +517 +00:23:35,414 --> 00:23:38,150 +But this year, Swift also +has a brand-new tool + +518 +00:23:38,150 --> 00:23:41,587 +for extracting information +from strings. + +519 +00:23:41,587 --> 00:23:43,389 +Here's a function +that parses some information + +520 +00:23:43,389 --> 00:23:45,090 +out of a string. + +521 +00:23:45,090 --> 00:23:46,592 +This sort of task +has always been + +522 +00:23:46,592 --> 00:23:48,794 +a bit of a challenge +in Swift. + +523 +00:23:48,794 --> 00:23:51,797 +You end up searching, splitting, +and slicing over and over + +524 +00:23:51,797 --> 00:23:54,767 +until you get what you want. + +525 +00:23:54,767 --> 00:23:56,568 +When people notice this, +they tend to focus + +526 +00:23:56,568 --> 00:23:58,704 +on the little things, +like how wordy it can be + +527 +00:23:58,704 --> 00:24:00,739 +to manipulate +string indices, + +528 +00:24:00,739 --> 00:24:03,876 +but I think that's kind of +missing the bigger picture. + +529 +00:24:03,876 --> 00:24:06,745 +Because even if we changed +this syntax, + +530 +00:24:06,745 --> 00:24:09,181 +it doesn't help you answer +the basic question you're asking + +531 +00:24:09,181 --> 00:24:12,384 +when you look at this code -- +what does the line variable + +532 +00:24:12,384 --> 00:24:15,387 +that's passed into it +actually look like? + +533 +00:24:15,387 --> 00:24:18,457 +What sort of string is it +trying to take apart? + +534 +00:24:18,457 --> 00:24:20,659 +If you stare at it long +enough, you might realize + +535 +00:24:20,659 --> 00:24:23,529 +that it's parsing a simplified +version of a mailmap -- + +536 +00:24:23,529 --> 00:24:25,731 +a file you put +in a git repository + +537 +00:24:25,731 --> 00:24:29,068 +to correct a developer's +name in old commits. + +538 +00:24:29,068 --> 00:24:31,837 +But extracting that information +by searching and slicing + +539 +00:24:31,837 --> 00:24:35,841 +is so involved that it's +hard to figure that out. + +540 +00:24:35,841 --> 00:24:38,344 +You get so lost in how +to slice up the string + +541 +00:24:38,344 --> 00:24:42,314 +that you kind of lose track +of what that string is. + +542 +00:24:42,314 --> 00:24:45,951 +The problem is not +these two expressions; + +543 +00:24:45,951 --> 00:24:48,354 +the problem is +the whole thing. + +544 +00:24:48,354 --> 00:24:49,888 +We need to rip +out all of this + +545 +00:24:49,888 --> 00:24:52,091 +and replace it +with something better. + +546 +00:24:52,091 --> 00:24:54,493 +We need a +different approach; + +547 +00:24:54,493 --> 00:24:56,962 +one where your code +sort of draws a picture + +548 +00:24:56,962 --> 00:24:58,831 +of the string you want +to match, + +549 +00:24:58,831 --> 00:25:01,600 +and the language figures +out how to do it. + +550 +00:25:01,600 --> 00:25:05,771 +A declarative approach, +not an imperative one. + +551 +00:25:05,771 --> 00:25:10,809 +In Swift 5.7, you can now +do that by writing a regex. + +552 +00:25:10,809 --> 00:25:14,213 +A regex is a way to describe +a pattern in a string. + +553 +00:25:14,213 --> 00:25:16,715 +For over 50 years, +languages and tools + +554 +00:25:16,715 --> 00:25:18,617 +have allowed developers +to write regexes + +555 +00:25:18,617 --> 00:25:21,587 +in a dense, +information-packed syntax. + +556 +00:25:21,587 --> 00:25:24,490 +Some of you already use them +in the Xcode find bar, + +557 +00:25:24,490 --> 00:25:26,425 +in command-line tools +like grep, + +558 +00:25:26,425 --> 00:25:29,094 +in Foundation's +NSRegularExpression class, + +559 +00:25:29,094 --> 00:25:31,463 +or in other +programming languages. + +560 +00:25:31,463 --> 00:25:34,500 +That syntax is now supported +by Swift's regex literals, + +561 +00:25:34,500 --> 00:25:37,970 +and it works just like it does +in any other developer tool. + +562 +00:25:37,970 --> 00:25:40,172 +But some of you haven't +used regexes before + +563 +00:25:40,172 --> 00:25:42,841 +and you're probably going, +"Is that real code + +564 +00:25:42,841 --> 00:25:46,278 +or did a cat walk +across her keyboard?" + +565 +00:25:46,278 --> 00:25:47,946 +And I don't blame you. + +566 +00:25:47,946 --> 00:25:50,449 +Regex literals are written +in symbols and mnemonics + +567 +00:25:50,449 --> 00:25:53,719 +that you have to memorize +in order to read them. + +568 +00:25:53,719 --> 00:25:55,187 +To someone who knows +the language, + +569 +00:25:55,187 --> 00:25:56,822 +even the gnarliest parts +of this regex, + +570 +00:25:56,822 --> 00:25:59,258 +like the part that matches +the developer's name + +571 +00:25:59,258 --> 00:26:02,761 +are just combinations of +several simple matching rules. + +572 +00:26:02,761 --> 00:26:06,765 +But that's a lot of behavior +to cram into 11 characters. + +573 +00:26:06,765 --> 00:26:10,202 +Regex literals are so compact +that even experienced developers + +574 +00:26:10,202 --> 00:26:13,405 +sometimes need a minute to +understand a complicated one. + +575 +00:26:13,405 --> 00:26:15,808 +But what if you could write +the same kind of matching rules, + +576 +00:26:15,808 --> 00:26:18,944 +just with words +instead of symbols? + +577 +00:26:18,944 --> 00:26:21,914 +That seems like it'd be +easier to understand. + +578 +00:26:21,914 --> 00:26:23,148 +In fact, put it +all together, + +579 +00:26:23,148 --> 00:26:26,885 +and you get something +that looks a lot like SwiftUI. + +580 +00:26:26,885 --> 00:26:30,289 +That'd be a great alternative +to a regex literal, wouldn't it? + +581 +00:26:30,289 --> 00:26:33,459 +So it's a good thing +Swift supports that! + +582 +00:26:33,459 --> 00:26:35,327 +The RegexBuilder library +provides a whole new + +583 +00:26:35,327 --> 00:26:39,064 +SwiftUI-style language +for regexes that's easier to use + +584 +00:26:39,064 --> 00:26:42,234 +and more readable +than the traditional syntax. + +585 +00:26:42,234 --> 00:26:44,670 +It can do the same things +a regex literal can, + +586 +00:26:44,670 --> 00:26:47,339 +but it describes its behavior +in words you can understand + +587 +00:26:47,339 --> 00:26:50,175 +or look up, instead of +symbols and abbreviations + +588 +00:26:50,175 --> 00:26:52,277 +you have to memorize. + +589 +00:26:52,277 --> 00:26:54,413 +Regex builders +are great for beginners, + +590 +00:26:54,413 --> 00:26:57,149 +but this is far from +a beginner-only feature. + +591 +00:26:57,149 --> 00:26:59,251 +It has powerful capabilities +that go way beyond + +592 +00:26:59,251 --> 00:27:01,620 +what a regex literal can do. + +593 +00:27:01,620 --> 00:27:03,455 +To start with, +you can turn a regex + +594 +00:27:03,455 --> 00:27:05,991 +into a reusable regex +component, + +595 +00:27:05,991 --> 00:27:08,494 +just as you can turn +a SwiftUI view hierarchy + +596 +00:27:08,494 --> 00:27:10,596 +into a view. + +597 +00:27:10,596 --> 00:27:12,831 +You can use these components +from other regexes + +598 +00:27:12,831 --> 00:27:14,766 +created with +the builder syntax, + +599 +00:27:14,766 --> 00:27:17,402 +and you can even +make them recursive. + +600 +00:27:17,402 --> 00:27:19,738 +Regex builders also support +dropping some Swift types + +601 +00:27:19,738 --> 00:27:22,140 +directly into a regex. + +602 +00:27:22,140 --> 00:27:24,109 +For example, +string literals just match + +603 +00:27:24,109 --> 00:27:28,547 +the exact text inside them -- +no special escaping needed. + +604 +00:27:28,547 --> 00:27:30,215 +You can also use +regex literals + +605 +00:27:30,215 --> 00:27:32,351 +in the middle +of a regex builder. + +606 +00:27:32,351 --> 00:27:34,386 +So you can strike a balance +between the clarity + +607 +00:27:34,386 --> 00:27:38,557 +of a regex builder and the +conciseness of a regex literal. + +608 +00:27:38,557 --> 00:27:41,660 +And other types -- like this +Foundation date-format style -- + +609 +00:27:41,660 --> 00:27:44,530 +can integrate custom parsing +logic with regex builders, + +610 +00:27:44,530 --> 00:27:48,700 +and even convert the data to a +richer type before capturing it. + +611 +00:27:48,700 --> 00:27:50,836 +Finally, no matter +which syntax you use, + +612 +00:27:50,836 --> 00:27:54,139 +regexes support a bunch +of useful matching methods + +613 +00:27:54,139 --> 00:27:58,210 +and strongly typed captures +that are easy to use. + +614 +00:27:58,210 --> 00:28:00,112 +Now, for the regex nerds +who have been squirming + +615 +00:28:00,112 --> 00:28:02,381 +in their seats, + +616 +00:28:02,381 --> 00:28:05,217 +Swift Regex uses a brand-new +open source matching engine, + +617 +00:28:05,217 --> 00:28:06,351 +with a feature +set comparable + +618 +00:28:06,351 --> 00:28:09,621 +to the most advanced +regex implementations. + +619 +00:28:09,621 --> 00:28:13,191 +The literal syntax is compatible +with the Unicode regex standard, + +620 +00:28:13,191 --> 00:28:16,261 +and it has an uncommon level +of Unicode correctness. + +621 +00:28:16,261 --> 00:28:19,164 +For instance, dot matches +a whole character by default, + +622 +00:28:19,164 --> 00:28:22,968 +not a Unicode.Scalar +or a UTF-8 byte. + +623 +00:28:22,968 --> 00:28:25,170 +To use Swift Regex, your app +will need to be running + +624 +00:28:25,170 --> 00:28:28,006 +on an OS with the Swift +Regex engine built into it, + +625 +00:28:28,006 --> 00:28:32,210 +like macOS 13 or iOS 16. + +626 +00:28:32,210 --> 00:28:34,513 +Swift Regex is +an entire language -- + +627 +00:28:34,513 --> 00:28:36,415 +well, two languages, really -- + +628 +00:28:36,415 --> 00:28:38,817 +so there's much more +to say about it. + +629 +00:28:38,817 --> 00:28:40,786 +These two sessions -- +"Meet Swift Regex" + +630 +00:28:40,786 --> 00:28:43,155 +and "Swift Regex: +Beyond The Basics" -- + +631 +00:28:43,155 --> 00:28:46,792 +will give you lots +more details about using it. + +632 +00:28:46,792 --> 00:28:48,026 +Finally, there's one place + +633 +00:28:48,026 --> 00:28:50,462 +where we took a comprehensive +look at the tools we have + +634 +00:28:50,462 --> 00:28:53,198 +and made a bunch of changes +to improve them. + +635 +00:28:53,198 --> 00:28:56,268 +That's in generics +and protocols. + +636 +00:28:56,268 --> 00:28:58,103 +To show you how these tools +have improved, + +637 +00:28:58,103 --> 00:29:00,472 +I'll need an +example protocol. + +638 +00:29:00,472 --> 00:29:02,207 +Let's say you're writing +a git client + +639 +00:29:02,207 --> 00:29:05,844 +and you have to represent +mailmaps in two different ways. + +640 +00:29:05,844 --> 00:29:08,947 +When you're displaying commits, +you use a type with a dictionary + +641 +00:29:08,947 --> 00:29:11,583 +to quickly look up names. + +642 +00:29:11,583 --> 00:29:13,952 +But when you're letting +users edit the mailmap, + +643 +00:29:13,952 --> 00:29:16,154 +you use a type with an array +to keep the entries + +644 +00:29:16,154 --> 00:29:18,857 +in their original order. + +645 +00:29:18,857 --> 00:29:20,626 +And you have a protocol +called Mailmap + +646 +00:29:20,626 --> 00:29:22,327 +that both of +them conform to, + +647 +00:29:22,327 --> 00:29:26,698 +so your mailmap parser can +add entries to either type. + +648 +00:29:26,698 --> 00:29:28,500 +But there are two ways +the parser could use + +649 +00:29:28,500 --> 00:29:30,702 +the Mailmap protocol. + +650 +00:29:30,702 --> 00:29:31,937 +I've written two +different versions + +651 +00:29:31,937 --> 00:29:34,573 +of this addEntries function +to illustrate them, + +652 +00:29:34,573 --> 00:29:36,174 +but it's actually +kind of hard to explain + +653 +00:29:36,174 --> 00:29:37,476 +how they're different, + +654 +00:29:37,476 --> 00:29:41,613 +because Swift is using the same +syntax for two different things. + +655 +00:29:41,613 --> 00:29:45,350 +It turns out that the word +"Mailmap" means one thing here + +656 +00:29:45,350 --> 00:29:47,753 +but it means something +subtly different here. + +657 +00:29:50,288 --> 00:29:53,058 +When you name a protocol +in an inheritance list, + +658 +00:29:53,058 --> 00:29:56,461 +generic parameter list, +generic conformance constraint, + +659 +00:29:56,461 --> 00:29:58,196 +or an opaque result type, + +660 +00:29:58,196 --> 00:30:01,900 +it means "an instance that +conforms to this protocol." + +661 +00:30:01,900 --> 00:30:04,369 +But in a variable type, +a generic argument, + +662 +00:30:04,369 --> 00:30:06,371 +a generic same-type constraint, + +663 +00:30:06,371 --> 00:30:08,707 +or a function parameter +or result type, + +664 +00:30:08,707 --> 00:30:10,976 +it actually means +"a box which contains + +665 +00:30:10,976 --> 00:30:14,613 +an instance that conforms +to this protocol." + +666 +00:30:14,613 --> 00:30:16,415 +This distinction is important +because the box + +667 +00:30:16,415 --> 00:30:20,252 +typically uses more space, +takes more time to operate on, + +668 +00:30:20,252 --> 00:30:22,020 +and doesn't have all +of the capabilities + +669 +00:30:22,020 --> 00:30:24,389 +of the instance inside it. + +670 +00:30:24,389 --> 00:30:25,991 +But the places where +you're using a box + +671 +00:30:25,991 --> 00:30:28,360 +look just like the places +where you aren't, + +672 +00:30:28,360 --> 00:30:31,163 +so it's hard to figure out +if you're using one. + +673 +00:30:31,163 --> 00:30:34,800 +Swift 5.7 fixes +this oversight. + +674 +00:30:34,800 --> 00:30:36,368 +When you're using one +of these boxes + +675 +00:30:36,368 --> 00:30:38,236 +containing +a conforming type, + +676 +00:30:38,236 --> 00:30:41,973 +Swift will now expect you +to write the any keyword. + +677 +00:30:41,973 --> 00:30:45,877 +This is not mandatory in code +that was valid before Swift 5.7, + +678 +00:30:45,877 --> 00:30:47,946 +but it is encouraged +and you will see it + +679 +00:30:47,946 --> 00:30:50,716 +in generated interfaces +and error messages, + +680 +00:30:50,716 --> 00:30:53,618 +even if you don't +write it out explicitly. + +681 +00:30:53,618 --> 00:30:55,620 +So the preferred way +to write all of those things + +682 +00:30:55,620 --> 00:30:58,824 +in the right-hand column +is with the any keyword. + +683 +00:30:58,824 --> 00:31:00,759 +If you do that, +you'll be able to tell + +684 +00:31:00,759 --> 00:31:03,562 +when you're using +one of these boxes. + +685 +00:31:03,562 --> 00:31:05,330 +Now that the any keyword +marks one of the parameters + +686 +00:31:05,330 --> 00:31:07,833 +in this example, +it's a lot easier to explain + +687 +00:31:07,833 --> 00:31:10,335 +the difference between +these two functions. + +688 +00:31:10,335 --> 00:31:14,072 +addEntries1 takes the +Mailmap as a generic type; + +689 +00:31:14,072 --> 00:31:17,275 +addEntries2 +takes it as an any type. + +690 +00:31:17,275 --> 00:31:18,877 +And it's also easier +for error messages + +691 +00:31:18,877 --> 00:31:20,378 +to explain what's happening + +692 +00:31:20,378 --> 00:31:23,448 +when you hit one of +the limitations of any types. + +693 +00:31:23,448 --> 00:31:26,451 +For instance, this mergeMailmaps +function tries to pass + +694 +00:31:26,451 --> 00:31:30,388 +an any Mailmap to a generic +Mailmap parameter. + +695 +00:31:30,388 --> 00:31:31,590 +This used to +produce an error + +696 +00:31:31,590 --> 00:31:34,493 +saying that Mailmap +cannot conform to itself, + +697 +00:31:34,493 --> 00:31:37,763 +which always seemed +kind of paradoxical. + +698 +00:31:37,763 --> 00:31:39,998 +But now that we have +the concept of any types, + +699 +00:31:39,998 --> 00:31:42,801 +we can explain what's happening +more clearly. + +700 +00:31:42,801 --> 00:31:44,870 +The problem is +that any Mailmap -- + +701 +00:31:44,870 --> 00:31:47,105 +the box containing a mailmap -- + +702 +00:31:47,105 --> 00:31:49,941 +doesn't conform +to the Mailmap protocol. + +703 +00:31:49,941 --> 00:31:51,743 +But the box is what +you're trying to pass, + +704 +00:31:51,743 --> 00:31:55,013 +and it doesn't fit +into the generic parameter. + +705 +00:31:55,013 --> 00:31:57,549 +If you want to pass the instance +inside the box here, + +706 +00:31:57,549 --> 00:32:00,652 +you'd have to somehow +open up the box, + +707 +00:32:00,652 --> 00:32:04,489 +take out the mailmap inside +it, and pass that instead. + +708 +00:32:04,489 --> 00:32:08,460 +But actually, +in simple cases like this one, + +709 +00:32:08,460 --> 00:32:10,629 +Swift will now +just do that for you. + +710 +00:32:10,629 --> 00:32:13,465 +Open up the box, +take out the instance inside it, + +711 +00:32:13,465 --> 00:32:16,101 +and pass it +to the generic parameter. + +712 +00:32:16,101 --> 00:32:17,702 +So you won't be seeing +this error message + +713 +00:32:17,702 --> 00:32:19,638 +nearly as much anymore. + +714 +00:32:19,638 --> 00:32:21,273 +But there's an even more +exciting improvement + +715 +00:32:21,273 --> 00:32:23,875 +to any types than that one. + +716 +00:32:23,875 --> 00:32:26,878 +Previously, a protocol could +not be used as an any type + +717 +00:32:26,878 --> 00:32:30,782 +if it either used the self +type or had associated types, + +718 +00:32:30,782 --> 00:32:32,918 +or even just conformed +to a protocol that did, + +719 +00:32:32,918 --> 00:32:34,886 +like Equatable. + +720 +00:32:34,886 --> 00:32:38,490 +But in Swift 5.7, +this error is just -- + +721 +00:32:38,490 --> 00:32:39,791 +poof -- + +722 +00:32:39,791 --> 00:32:42,494 +gone. + +723 +00:32:42,494 --> 00:32:44,296 +A lot of developers +have struggled with this one, + +724 +00:32:44,296 --> 00:32:47,499 +so we're thrilled to have +fixed it at the source. + +725 +00:32:47,499 --> 00:32:50,368 +Now, that's exciting enough +just for protocols like Mailmap, + +726 +00:32:50,368 --> 00:32:52,571 +but this goes even further. + +727 +00:32:52,571 --> 00:32:55,640 +Because even very sophisticated +protocols, like Collection, + +728 +00:32:55,640 --> 00:32:58,276 +can be used as any types. + +729 +00:32:58,276 --> 00:33:00,245 +You can even specify +the element type, + +730 +00:33:00,245 --> 00:33:04,249 +thanks to a new feature called +"primary associated types." + +731 +00:33:04,249 --> 00:33:05,417 +A lot of associated types + +732 +00:33:05,417 --> 00:33:08,720 +are basically just +implementation details. + +733 +00:33:08,720 --> 00:33:11,056 +You usually don't care +which type a collection uses + +734 +00:33:11,056 --> 00:33:13,925 +for its index, iterator, +or subsequence; + +735 +00:33:13,925 --> 00:33:16,895 +you just need to use +the type it supports. + +736 +00:33:16,895 --> 00:33:20,065 +But its Element +is a different story. + +737 +00:33:20,065 --> 00:33:21,933 +You might not always care +exactly which Element type + +738 +00:33:21,933 --> 00:33:23,802 +a collection uses, +but you're probably + +739 +00:33:23,802 --> 00:33:25,770 +going to do something +with the elements, + +740 +00:33:25,770 --> 00:33:29,875 +so you'll need to constrain them +or return them or something. + +741 +00:33:29,875 --> 00:33:32,010 +When you have an associated +type like Element + +742 +00:33:32,010 --> 00:33:35,247 +that nearly every user +of a protocol will care about, + +743 +00:33:35,247 --> 00:33:37,015 +you can put its name +after the protocol's name + +744 +00:33:37,015 --> 00:33:41,253 +in angle brackets to make it +a primary associated type. + +745 +00:33:41,253 --> 00:33:43,722 +Once you do that, you can +constrain the protocol's + +746 +00:33:43,722 --> 00:33:46,658 +primary associated types +with the angle bracket syntax + +747 +00:33:46,658 --> 00:33:49,594 +pretty much anywhere you can +write the protocol's name, + +748 +00:33:49,594 --> 00:33:52,163 +including in any Collection. + +749 +00:33:52,163 --> 00:33:54,900 +Now, some of you might be +looking at this type and going, + +750 +00:33:54,900 --> 00:33:57,269 +"Wait a minute. + +751 +00:33:57,269 --> 00:33:59,638 +Isn't there already something +called AnyCollection, + +752 +00:33:59,638 --> 00:34:03,842 +just run together +and with the 'any' capitalized?" + +753 +00:34:03,842 --> 00:34:06,378 +And you're right, there is! + +754 +00:34:06,378 --> 00:34:09,648 +The old AnyCollection +is a type-erasing wrapper -- + +755 +00:34:09,648 --> 00:34:12,083 +a handwritten struct which +serves the same purpose + +756 +00:34:12,083 --> 00:34:13,885 +as an any type. + +757 +00:34:13,885 --> 00:34:15,887 +The difference is that +the AnyCollection struct + +758 +00:34:15,887 --> 00:34:18,990 +is just line after line of +the most boring boilerplate code + +759 +00:34:18,990 --> 00:34:22,627 +you've ever seen in your +life; whereas the any type + +760 +00:34:22,627 --> 00:34:24,129 +is a built-in +language feature + +761 +00:34:24,129 --> 00:34:26,064 +that does basically +the same thing -- + +762 +00:34:26,064 --> 00:34:27,599 +for free! + +763 +00:34:27,599 --> 00:34:29,434 +Now, the AnyCollection +struct will stick around + +764 +00:34:29,434 --> 00:34:31,336 +for backwards compatibility +and because it has + +765 +00:34:31,336 --> 00:34:34,873 +a couple of features that any +types can't quite match yet. + +766 +00:34:34,873 --> 00:34:36,775 +But if you have your own +type-erasing wrappers + +767 +00:34:36,775 --> 00:34:40,045 +in your code, you might want to +see if you can reimplement them + +768 +00:34:40,045 --> 00:34:44,282 +using built-in any types instead +of box classes or closures. + +769 +00:34:44,282 --> 00:34:47,919 +Or maybe even just replace +them with type aliases. + +770 +00:34:47,919 --> 00:34:51,456 +So Swift has dramatically +improved any types. + +771 +00:34:51,456 --> 00:34:52,991 +It's introduced +the any keyword + +772 +00:34:52,991 --> 00:34:55,593 +so you can see +where you're using them. + +773 +00:34:55,593 --> 00:34:58,863 +It allows you to pass them +to generic arguments. + +774 +00:34:58,863 --> 00:35:01,099 +It's abolished the restriction +that kept many protocols + +775 +00:35:01,099 --> 00:35:03,501 +from being used with them. + +776 +00:35:03,501 --> 00:35:04,602 +And it even lets +you constrain + +777 +00:35:04,602 --> 00:35:08,440 +an any type's primary +associated types. + +778 +00:35:08,440 --> 00:35:10,508 +But even with all +of those improvements, + +779 +00:35:10,508 --> 00:35:13,478 +any types still +have limitations. + +780 +00:35:13,478 --> 00:35:16,514 +For example, even though +you can now use any Mailmaps + +781 +00:35:16,514 --> 00:35:18,850 +when Mailmap conforms +to Equatable, + +782 +00:35:18,850 --> 00:35:21,619 +you still can't use the +equals operator with them, + +783 +00:35:21,619 --> 00:35:24,255 +because the equals operator +requires both mailmaps + +784 +00:35:24,255 --> 00:35:27,459 +to have the same concrete type, +but that's not guaranteed + +785 +00:35:27,459 --> 00:35:30,028 +when you're using +two any Mailmaps. + +786 +00:35:30,028 --> 00:35:32,597 +So even though Swift has +improved any types a lot, + +787 +00:35:32,597 --> 00:35:34,733 +they still have +important limitations, + +788 +00:35:34,733 --> 00:35:37,969 +in both capabilities +and performance. + +789 +00:35:37,969 --> 00:35:41,006 +And that's why a lot of the +time, you shouldn't use them -- + +790 +00:35:41,006 --> 00:35:43,375 +you should use +generics instead. + +791 +00:35:43,375 --> 00:35:46,011 +So let's go back to the +two versions of addEntries + +792 +00:35:46,011 --> 00:35:48,013 +and apply that wisdom. + +793 +00:35:48,013 --> 00:35:50,648 +Both versions do exactly +the same thing, + +794 +00:35:50,648 --> 00:35:53,284 +but the one on the top +uses generic types, + +795 +00:35:53,284 --> 00:35:56,755 +and the one on the bottom +uses any types. + +796 +00:35:56,755 --> 00:35:59,157 +The generic version +will likely be more efficient + +797 +00:35:59,157 --> 00:36:02,827 +and more capable, +so you ought to use that one. + +798 +00:36:02,827 --> 00:36:05,597 +And yet, you're probably +tempted to use any types, + +799 +00:36:05,597 --> 00:36:08,733 +because they're just so much +easier to read and write. + +800 +00:36:08,733 --> 00:36:10,001 +To write the generic version, + +801 +00:36:10,001 --> 00:36:12,437 +you need to declare +two generic type names, + +802 +00:36:12,437 --> 00:36:14,639 +constrain them both, +and finally, + +803 +00:36:14,639 --> 00:36:18,743 +use those generic type names +as the types of the parameters. + +804 +00:36:18,743 --> 00:36:21,479 +That's just exhausting compared +to writing "any Collection" + +805 +00:36:21,479 --> 00:36:23,415 +and "any Mailmap." + +806 +00:36:23,415 --> 00:36:27,385 +So you'd be tempted to use any +types despite their drawbacks. + +807 +00:36:27,385 --> 00:36:30,188 +But that's the same thing +I was talking about earlier -- + +808 +00:36:30,188 --> 00:36:32,190 +using your hammer +instead of your screwdriver + +809 +00:36:32,190 --> 00:36:34,726 +because the hammer +has a big, grippy handle. + +810 +00:36:34,726 --> 00:36:37,262 +You shouldn't have +to make that choice. + +811 +00:36:37,262 --> 00:36:40,098 +So Swift is making generics +just as easy to use + +812 +00:36:40,098 --> 00:36:42,167 +as any types. + +813 +00:36:42,167 --> 00:36:44,936 +If a generic parameter +is only used in one place, + +814 +00:36:44,936 --> 00:36:48,440 +you can now write it with +the some keyword as a shorthand. + +815 +00:36:48,440 --> 00:36:50,842 +And it even supports +primary associated types, + +816 +00:36:50,842 --> 00:36:53,178 +so you can accept all +collections of mailmap entries + +817 +00:36:53,178 --> 00:36:55,847 +with code that's a lot +easier to understand. + +818 +00:36:55,847 --> 00:36:57,215 +With that in your toolbox, + +819 +00:36:57,215 --> 00:37:00,351 +there's no reason +to avoid generics anymore. + +820 +00:37:00,351 --> 00:37:02,887 +If you have a choice between +generics and any types, + +821 +00:37:02,887 --> 00:37:05,190 +generics will be +just as easy to use -- + +822 +00:37:05,190 --> 00:37:07,992 +just write "some" +instead of "any". + +823 +00:37:07,992 --> 00:37:11,196 +So you might as well use +the best tool for the job. + +824 +00:37:11,196 --> 00:37:13,164 +I've only scratched +the surface of these changes + +825 +00:37:13,164 --> 00:37:15,066 +to protocols and generics. + +826 +00:37:15,066 --> 00:37:17,168 +For an in-depth look, +as well as a great review + +827 +00:37:17,168 --> 00:37:19,104 +of all of Swift's +generics features, + +828 +00:37:19,104 --> 00:37:21,339 +we have two more talks +for you this year: + +829 +00:37:21,339 --> 00:37:23,007 +"Embrace Swift generics," + +830 +00:37:23,007 --> 00:37:25,343 +and "Design protocol +interfaces in Swift." + +831 +00:37:28,179 --> 00:37:29,547 +Now, Angela and I +have talked about + +832 +00:37:29,547 --> 00:37:31,783 +nearly two dozen changes +to Swift, + +833 +00:37:31,783 --> 00:37:35,253 +but there are lots more that we +couldn't fit into this session. + +834 +00:37:35,253 --> 00:37:38,690 +Every one of these changes +was pitched, proposed, reviewed, + +835 +00:37:38,690 --> 00:37:41,559 +and accepted publicly +in the Evolution board + +836 +00:37:41,559 --> 00:37:43,428 +on the Swift Forums. + +837 +00:37:43,428 --> 00:37:45,897 +And all of them were shaped +and realized with the help + +838 +00:37:45,897 --> 00:37:48,833 +of community members +from outside Apple. + +839 +00:37:48,833 --> 00:37:52,704 +If you're one of those people, +thank you for making Swift 5.7 + +840 +00:37:52,704 --> 00:37:55,073 +the great release it is. + +841 +00:37:55,073 --> 00:37:57,208 +And if you want to help +decide what comes next, + +842 +00:37:57,208 --> 00:38:02,213 +visit Swift.org/contributing +to find out how to participate. + +843 +00:38:02,213 --> 00:38:03,348 +Thanks for your time. + +844 +00:38:03,348 --> 00:38:04,949 +And happy coding. + +845 +00:38:06,985 --> 00:38:08,753 +[LAUGHS] + +846 +00:38:08,753 --> 00:38:12,824 +♪ + diff --git a/eng/2022 Session 110355 Meet Swift Async Algorithms en.srt b/eng/2022 Session 110355 Meet Swift Async Algorithms en.srt new file mode 100644 index 0000000..fcd99a1 --- /dev/null +++ b/eng/2022 Session 110355 Meet Swift Async Algorithms en.srt @@ -0,0 +1,1060 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,710 --> 00:00:11,612 +Hi, my name's Philippe. + +3 +00:00:11,645 --> 00:00:15,148 +Swift has a growing catalog +of open source packages. + +4 +00:00:15,182 --> 00:00:18,352 +I am pleased to introduce you to one +of the newest additions: + +5 +00:00:18,385 --> 00:00:20,354 +Swift Async Algorithms. + +6 +00:00:20,387 --> 00:00:23,090 +This package is +alongside the other packages, + +7 +00:00:23,123 --> 00:00:26,493 +like Swift Collections +and Swift Algorithms. + +8 +00:00:26,527 --> 00:00:29,997 +The Swift Async Algorithms package +is a set of algorithms + +9 +00:00:30,030 --> 00:00:35,435 +specifically focused on processing values +over time using AsyncSequence. + +10 +00:00:35,469 --> 00:00:37,171 +But before we get into it, + +11 +00:00:37,204 --> 00:00:40,207 +let's take a brief moment +to recap AsyncSequence. + +12 +00:00:40,240 --> 00:00:43,644 +AsyncSequence is a protocol +that lets you describe values + +13 +00:00:43,677 --> 00:00:45,612 +produced asynchronously. + +14 +00:00:45,646 --> 00:00:50,817 +Basically, it's just like Sequence, +but has two key differences. + +15 +00:00:50,851 --> 00:00:54,288 +The next function +from its iterator is asynchronous, + +16 +00:00:54,321 --> 00:00:58,325 +being that it can deliver values +using Swift concurrency. + +17 +00:00:58,358 --> 00:01:03,297 +It also lets you handle any potential +failures using Swift's throw effect. + +18 +00:01:03,330 --> 00:01:06,233 +And just like sequence, +you can iterate it, + +19 +00:01:06,266 --> 00:01:09,002 +using the for-await-in syntax. + +20 +00:01:09,036 --> 00:01:11,905 +In short, if you know how to use Sequence, + +21 +00:01:11,939 --> 00:01:15,409 +you already know how to use AsyncSequence. + +22 +00:01:15,442 --> 00:01:19,847 +Now, when AsyncSequence was introduced, +we added in almost all the tools + +23 +00:01:19,880 --> 00:01:23,984 +you would expect to find with Sequence +right there with the async versions. + +24 +00:01:24,017 --> 00:01:29,990 +You have algorithms like map, +filter, reduce, and more. + +25 +00:01:30,023 --> 00:01:33,861 +The Swift Async Algorithms package +takes this a step further + +26 +00:01:33,894 --> 00:01:37,664 +by incorporating more advanced algorithms, +as well as interoperating + +27 +00:01:37,698 --> 00:01:41,535 +with clocks to give you +some really powerful stuff. + +28 +00:01:41,568 --> 00:01:45,305 +This is an open source package +of AsyncSequence algorithms + +29 +00:01:45,339 --> 00:01:48,008 +that augment Swift concurrency. + +30 +00:01:48,041 --> 00:01:51,011 +Last year we introduced +the Swift Algorithms package. + +31 +00:01:51,044 --> 00:01:53,680 +To demonstrate the uses +of those algorithms, + +32 +00:01:53,714 --> 00:01:55,115 +we made a messaging app. + +33 +00:01:55,148 --> 00:01:58,485 +This was a great example of some +of the rich and powerful things + +34 +00:01:58,519 --> 00:02:00,454 +you can do with that package. + +35 +00:02:00,487 --> 00:02:03,457 +We decided there were a number +of really good opportunities + +36 +00:02:03,490 --> 00:02:07,661 +to take advantage of migrating the app +to use Swift concurrency. + +37 +00:02:07,694 --> 00:02:10,998 +To highlight just a few +of the asynchronous algorithms, + +38 +00:02:11,031 --> 00:02:13,834 +I'm gonna take you through some +of the things that we used + +39 +00:02:13,867 --> 00:02:16,036 +and how they work. + +40 +00:02:16,069 --> 00:02:18,972 +First off, we have a family +of algorithms for working + +41 +00:02:19,006 --> 00:02:21,675 +with multiple input AsyncSequences. + +42 +00:02:21,708 --> 00:02:27,581 +These are algorithms focused on combining +AsyncSequences together in different ways. + +43 +00:02:27,614 --> 00:02:30,083 +But they all share one characteristic: + +44 +00:02:30,117 --> 00:02:35,822 +They take multiple input AsyncSequences +and produce one output AsyncSequence. + +45 +00:02:36,857 --> 00:02:40,260 +One you might +already be familiar with is Zip. + +46 +00:02:40,294 --> 00:02:44,064 +The Zip algorithm takes +multiple inputs and iterates them + +47 +00:02:44,097 --> 00:02:48,101 +such that it produces a tuple +of the results from each of the bases. + +48 +00:02:48,135 --> 00:02:52,539 +Each of the inputs to Zip are the bases +that the Zip is constructed from. + +49 +00:02:52,573 --> 00:02:56,276 +The asynchronous Zip algorithm works +just like the Zip algorithm + +50 +00:02:56,310 --> 00:02:57,744 +in the standard library, + +51 +00:02:57,778 --> 00:03:00,347 +but it iterates each +of the bases concurrently + +52 +00:03:00,380 --> 00:03:04,785 +and rethrows errors if a failure occurs +on iterating any of them. + +53 +00:03:04,818 --> 00:03:07,187 +Now, accomplishing +that concurrent iteration + +54 +00:03:07,221 --> 00:03:10,424 +with rethrowing errors +can be rather involved. + +55 +00:03:10,457 --> 00:03:13,460 +But the Swift Async Algorithms package +took care of all of that + +56 +00:03:13,493 --> 00:03:15,629 +for us in our messaging app. + +57 +00:03:15,662 --> 00:03:17,297 +We previously had a lot of code + +58 +00:03:17,331 --> 00:03:19,900 +coordinating asynchronously +generating previews + +59 +00:03:19,933 --> 00:03:22,469 +of video recordings +and transcoding video + +60 +00:03:22,503 --> 00:03:26,673 +into multiple sizes for efficient storage +and transmission. + +61 +00:03:26,707 --> 00:03:31,211 +By using Zip we can ensure +that the transcoded video gets a preview + +62 +00:03:31,245 --> 00:03:33,213 +when we send it off to the server. + +63 +00:03:33,247 --> 00:03:36,416 +Since Zip is concurrent, +neither the transcoding + +64 +00:03:36,450 --> 00:03:39,152 +or the preview will delay each other. + +65 +00:03:39,186 --> 00:03:40,988 +But this goes a bit further. + +66 +00:03:41,021 --> 00:03:43,924 +Zip itself has no preference on which side + +67 +00:03:43,957 --> 00:03:48,095 +produced a value first or not, +so a video could be produced first + +68 +00:03:48,128 --> 00:03:51,098 +or a preview, +and no matter which side it is, + +69 +00:03:51,131 --> 00:03:54,968 +it will await for the other +to send a complete tuple. + +70 +00:03:55,002 --> 00:03:58,505 +We can await the pairs +such that they can be uploaded together + +71 +00:03:58,539 --> 00:04:04,044 +because Zip awaits each side concurrently +to construct a tuple of the values. + +72 +00:04:04,077 --> 00:04:07,714 +We came to the conclusion +that modeling our incoming messages + +73 +00:04:07,748 --> 00:04:10,717 +as an AsyncSequence +made a lot of sense. + +74 +00:04:10,751 --> 00:04:14,421 +So we decided to use AsyncStream +to handle those messages + +75 +00:04:14,454 --> 00:04:17,157 +since it preserves order +and turns our callbacks + +76 +00:04:17,191 --> 00:04:20,093 +into an AsyncSequence of messages. + +77 +00:04:20,127 --> 00:04:23,130 +One of the requested features +we needed to tackle is + +78 +00:04:23,163 --> 00:04:25,532 +that we wanted +to support multiple accounts. + +79 +00:04:25,566 --> 00:04:30,304 +So each account creates an AsyncStream +of incoming messages, + +80 +00:04:30,337 --> 00:04:31,939 +but when implementing this, + +81 +00:04:31,972 --> 00:04:36,844 +we need to handle them all together +as one singular AsyncSequence. + +82 +00:04:36,877 --> 00:04:41,715 +This means we needed an algorithm +for merging those AsyncSequences together. + +83 +00:04:41,748 --> 00:04:47,421 +Thankfully the Swift Async Algorithms +package has an algorithm for exactly that, + +84 +00:04:47,454 --> 00:04:49,957 +aptly named "Merge." + +85 +00:04:49,990 --> 00:04:54,194 +It works similarly to Zip in the regards +that it concurrently iterates + +86 +00:04:54,228 --> 00:04:58,565 +multiple AsyncSequences. +But instead of creating paired tuples, + +87 +00:04:58,599 --> 00:05:01,735 +it requires the bases +to share the same element type + +88 +00:05:01,768 --> 00:05:04,137 +and merges the base AsyncSequences + +89 +00:05:04,171 --> 00:05:08,141 +into one singular AsyncSequence +of those elements. + +90 +00:05:08,175 --> 00:05:12,346 +Merge works by taking +the first element produced by any + +91 +00:05:12,379 --> 00:05:13,914 +of the sides when iterated. + +92 +00:05:13,947 --> 00:05:18,318 +It keeps iterating until there +are no more values that could be produced, + +93 +00:05:18,352 --> 00:05:23,357 +specifically when all base AsyncSequences +return nil from their iterator. + +94 +00:05:23,390 --> 00:05:27,861 +If any of the bases produces an error, +the other iterations are cancelled. + +95 +00:05:27,895 --> 00:05:33,400 +This lets us take the AsyncSequences +of messages and merge them. + +96 +00:05:33,433 --> 00:05:38,338 +These combining algorithms work +concurrently on when values are produced, + +97 +00:05:38,372 --> 00:05:43,076 +but sometimes it is useful to +actually interact with time itself. + +98 +00:05:43,110 --> 00:05:47,481 +The Swift Async Algorithms package +brings in a family of algorithms + +99 +00:05:47,514 --> 00:05:51,919 +to work with time by leveraging +the new Clock API in Swift. + +100 +00:05:51,952 --> 00:05:55,455 +Time itself can be +a really complex subject, + +101 +00:05:55,489 --> 00:05:58,959 +and new in Swift (5.7) are a set of APIs + +102 +00:05:58,992 --> 00:06:01,495 +to make that safe and consistent: + +103 +00:06:01,528 --> 00:06:04,565 +Clock, Instant, and Duration. + +104 +00:06:06,433 --> 00:06:09,236 +The Clock protocol defines two primitives, + +105 +00:06:09,269 --> 00:06:11,605 +a way to wake up after a given instant + +106 +00:06:11,638 --> 00:06:13,740 +and a way to produce a concept of now. + +107 +00:06:13,774 --> 00:06:15,876 +There are a few built in clocks. + +108 +00:06:15,909 --> 00:06:17,811 +Two of the more common ones are + +109 +00:06:17,845 --> 00:06:20,280 +the ContinuousClock +and the SuspendingClock. + +110 +00:06:20,314 --> 00:06:24,885 +You can use the ContinuousClock +to measure time just like a stopwatch, + +111 +00:06:24,918 --> 00:06:28,655 +where time progresses no matter +the state of the thing being measured. + +112 +00:06:28,689 --> 00:06:32,593 +The SuspendingClock, on the other hand, +does what its name implies; + +113 +00:06:32,626 --> 00:06:35,462 +it suspends +when the machine is put to sleep. + +114 +00:06:35,495 --> 00:06:40,400 +We used the new clock API in our app +to migrate from existing callback events + +115 +00:06:40,434 --> 00:06:45,672 +to clock sleep function to handle +dismissing alerts after a deadline. + +116 +00:06:45,706 --> 00:06:49,943 +We were able to create the deadline +by adding a duration value + +117 +00:06:49,977 --> 00:06:55,249 +that indicated specifically the number +of seconds we wanted to delay. + +118 +00:06:55,282 --> 00:06:58,785 +Clock also has some handy methods +to measure the elapsed duration + +119 +00:06:58,819 --> 00:07:00,454 +of execution of work. + +120 +00:07:00,487 --> 00:07:03,891 +Here we have those two common clocks +I mentioned earlier, + +121 +00:07:03,924 --> 00:07:06,493 +the SuspendingClock +and ContinuousClock. + +122 +00:07:07,494 --> 00:07:09,363 +Below are displays showing + +123 +00:07:09,396 --> 00:07:11,031 +the potential elapsed duration + +124 +00:07:11,064 --> 00:07:12,699 +of work being measured. + +125 +00:07:12,733 --> 00:07:16,170 +The key difference +between these two clocks + +126 +00:07:16,203 --> 00:07:19,373 +comes from its behavior +when the machine is asleep. + +127 +00:07:20,440 --> 00:07:24,244 +For long running work like these, +the work can be paused, + +128 +00:07:24,278 --> 00:07:25,812 +just as we did here, + +129 +00:07:25,846 --> 00:07:30,083 +but when we resume the execution, + +130 +00:07:30,117 --> 00:07:32,286 +the ContinuousClock has progressed + +131 +00:07:32,319 --> 00:07:33,887 +while the machine was asleep, + +132 +00:07:33,921 --> 00:07:36,390 +but the SuspendingClock did not. + +133 +00:07:36,423 --> 00:07:39,426 +Commonly, this difference +can be the key detail + +134 +00:07:39,459 --> 00:07:42,829 +to make sure things like animations +work as expected + +135 +00:07:42,863 --> 00:07:45,999 +by suspending the timing of the execution. + +136 +00:07:46,033 --> 00:07:49,536 +If you need to interact with time +in relation to the machine, + +137 +00:07:49,570 --> 00:07:52,873 +like for animations, +use the SuspendingClock. + +138 +00:07:53,907 --> 00:07:57,110 +Measuring tasks in relation +to the human in front of the device + +139 +00:07:57,144 --> 00:07:59,813 +is better suited for the ContinuousClock. + +140 +00:07:59,847 --> 00:08:03,116 +So if you need to delay +by an absolute duration, + +141 +00:08:03,150 --> 00:08:07,788 +something relative to humans, +use the ContinuousClock. + +142 +00:08:07,821 --> 00:08:12,292 +The Swift Async Algorithms package +uses these new Clock, Instant, + +143 +00:08:12,326 --> 00:08:16,263 +and Duration types to build +generic algorithms for dealing + +144 +00:08:16,296 --> 00:08:21,001 +with many of the concepts of how events +are processed with regards to time. + +145 +00:08:21,034 --> 00:08:24,404 +In our messaging app, +we found these really helpful + +146 +00:08:24,438 --> 00:08:27,508 +for providing precise control over events. + +147 +00:08:27,541 --> 00:08:32,412 +It let us rate limit interactions +and efficiently buffer messages. + +148 +00:08:33,347 --> 00:08:36,483 +Perhaps the most prominent area +that we utilized time + +149 +00:08:36,517 --> 00:08:38,352 +was searching messages. + +150 +00:08:38,385 --> 00:08:42,289 +We created a controller +that manages a channel of results. + +151 +00:08:42,322 --> 00:08:47,661 +The channel marshals search results +from the search task back to our UI. + +152 +00:08:47,694 --> 00:08:51,598 +The search task itself needed +to have some specific characteristics + +153 +00:08:51,632 --> 00:08:53,333 +with regards to time. + +154 +00:08:53,367 --> 00:08:58,105 +We wanted to make sure to rate limit +searching sent messages on the server. + +155 +00:08:59,740 --> 00:09:03,177 +The algorithm Debounce +awaits a quiescence period + +156 +00:09:03,210 --> 00:09:06,547 +before it emits the next values +when iterated. + +157 +00:09:06,580 --> 00:09:10,784 +It means that events can come in fast, +but we want to make sure to wait + +158 +00:09:10,817 --> 00:09:14,588 +for a quiet period +before dealing with values. + +159 +00:09:14,621 --> 00:09:18,025 +When user input from a search field +is changed rapidly, + +160 +00:09:18,058 --> 00:09:19,793 +we don't want the search controller + +161 +00:09:19,826 --> 00:09:22,796 +to fire off a search request +for each change. + +162 +00:09:22,829 --> 00:09:26,133 +Instead, we want to make sure +to wait for a quiet period + +163 +00:09:26,166 --> 00:09:29,670 +when we're certain typing was likely +to be done. + +164 +00:09:29,703 --> 00:09:33,740 +By default, the Debounce algorithm +will use the ContinuousClock. + +165 +00:09:33,774 --> 00:09:37,077 +In this case, we can debounce +the input such that it awaits + +166 +00:09:37,110 --> 00:09:40,581 +a specified duration +while nothing has occurred. + +167 +00:09:40,614 --> 00:09:43,951 +Clocks and durations are +not just used for debouncing, + +168 +00:09:43,984 --> 00:09:46,420 +but they're used +for other algorithms too. + +169 +00:09:46,453 --> 00:09:49,122 +One area that we found +that was really useful + +170 +00:09:49,156 --> 00:09:52,626 +was sending batches +of messages to the server. + +171 +00:09:52,659 --> 00:09:54,795 +In the Swift algorithms package, + +172 +00:09:54,828 --> 00:09:57,998 +there's a set +of algorithms to chunk values. + +173 +00:09:58,031 --> 00:10:01,034 +The Swift Async Algorithms package +offers those, + +174 +00:10:01,068 --> 00:10:03,003 +but also adds a set of versions + +175 +00:10:03,036 --> 00:10:06,406 +that interoperate +with clocks and durations. + +176 +00:10:06,440 --> 00:10:10,244 +The family of chunking algorithms allow +for control over chunks by count, + +177 +00:10:10,277 --> 00:10:12,713 +by time, or by content. + +178 +00:10:12,746 --> 00:10:16,517 +If an error occurs in any of these, +that error is rethrown, + +179 +00:10:16,550 --> 00:10:19,853 +so our code is safe +when it comes to failures. + +180 +00:10:20,821 --> 00:10:24,691 +We used the "chunked(by:)" API +to ensure that chunks of messages + +181 +00:10:24,725 --> 00:10:28,529 +are serialized and sent off +by a certain elapsed duration. + +182 +00:10:28,562 --> 00:10:33,667 +That way, our server gets +efficient packets sent from the clients. + +183 +00:10:33,700 --> 00:10:37,037 +We were able to use this API +to build batches of messages + +184 +00:10:37,070 --> 00:10:39,506 +every 500 milliseconds. + +185 +00:10:39,540 --> 00:10:43,777 +That way, if someone's really excited +and typing really fast, + +186 +00:10:43,810 --> 00:10:47,014 +the requests sent +to the server are grouped up. + +187 +00:10:47,047 --> 00:10:49,783 +When working with collections +and sequence, + +188 +00:10:49,816 --> 00:10:54,621 +it's often useful and performant +to lazily process elements. + +189 +00:10:54,655 --> 00:10:58,559 +AsyncSequence works much +like how the lazy algorithms work + +190 +00:10:58,592 --> 00:11:00,794 +in the Swift standard library. + +191 +00:11:00,827 --> 00:11:04,831 +But just like those lazy algorithms, +there are often times where you need + +192 +00:11:04,865 --> 00:11:08,202 +to move back +into the world of collections. + +193 +00:11:08,235 --> 00:11:12,005 +The Swift Async Algorithms package +offers a set of initializers + +194 +00:11:12,039 --> 00:11:15,342 +for constructing collections +using AsyncSequence. + +195 +00:11:15,375 --> 00:11:19,246 +These let you build up dictionaries, +sets, or arrays + +196 +00:11:19,279 --> 00:11:23,183 +with input AsyncSequences +that are known to be finite. + +197 +00:11:23,217 --> 00:11:26,820 +The collection initializers +let us build in conversions + +198 +00:11:26,854 --> 00:11:31,825 +right into our initialization of messages +and keep our data types as Array. + +199 +00:11:31,859 --> 00:11:34,962 +This was really useful +since we had numerous features + +200 +00:11:34,995 --> 00:11:38,398 +that really could use some updating +to use Swift concurrency. + +201 +00:11:38,432 --> 00:11:41,268 +And by keeping +our existing data structures, + +202 +00:11:41,301 --> 00:11:44,304 +we can migrate parts +of our app incrementally + +203 +00:11:44,338 --> 00:11:46,974 +and where it makes sense. + +204 +00:11:47,007 --> 00:11:49,910 +So far, +we've just gone over just a handful + +205 +00:11:49,943 --> 00:11:52,579 +of the highlights +of Swift Async Algorithms package. + +206 +00:11:52,613 --> 00:11:54,047 +There are a whole lot more + +207 +00:11:54,081 --> 00:11:55,849 +than just what we've covered today. + +208 +00:11:55,883 --> 00:12:01,855 +We have algorithms ranging +from combining multiple AsyncSequences, + +209 +00:12:01,889 --> 00:12:05,425 +rate limiting by time, + +210 +00:12:05,459 --> 00:12:08,061 +breaking things into chunks, + +211 +00:12:08,095 --> 00:12:10,764 +but those were just the highlights +that we ended up + +212 +00:12:10,797 --> 00:12:13,233 +using extensively in our app. + +213 +00:12:13,267 --> 00:12:16,436 +This package has a lot more +than just those. + +214 +00:12:16,470 --> 00:12:19,139 +It ranges from buffering, + +215 +00:12:19,173 --> 00:12:23,610 +reducing, joining, + +216 +00:12:23,644 --> 00:12:27,848 +to injecting values intermittently, +and more. + +217 +00:12:27,881 --> 00:12:31,818 +The Swift Async Algorithms package +takes the set of algorithms + +218 +00:12:31,852 --> 00:12:35,889 +for dealing with things over time +and expands it to a wide range + +219 +00:12:35,923 --> 00:12:39,526 +of advanced functionality +that can help you in your apps. + +220 +00:12:39,560 --> 00:12:40,928 +Try it out. + +221 +00:12:40,961 --> 00:12:44,198 +We're really excited +to discover what you build with these, + +222 +00:12:44,231 --> 00:12:46,400 +and that excitement is shared. + +223 +00:12:46,433 --> 00:12:50,437 +This package is being developed +in the open with you. + +224 +00:12:50,470 --> 00:12:53,173 +Thanks for watching, +and enjoy the rest of the conference. + +225 +00:12:53,207 --> 00:12:58,345 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 110356 Meet distributed actors in Swift en.srt b/eng/2022 Session 110356 Meet distributed actors in Swift en.srt new file mode 100644 index 0000000..c9de4aa --- /dev/null +++ b/eng/2022 Session 110356 Meet distributed actors in Swift en.srt @@ -0,0 +1,2839 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ instrumental hip hop music ♪ + +2 +00:00:03,003 --> 00:00:09,843 +♪ + +3 +00:00:09,843 --> 00:00:11,478 +Hello! My name is Konrad + +4 +00:00:11,478 --> 00:00:13,080 +and I'm an engineer +on the Swift team. + +5 +00:00:13,080 --> 00:00:15,482 +Welcome to "Meet distributed +actors in Swift." + +6 +00:00:15,482 --> 00:00:17,317 +In this session, +we'll learn how you can take + +7 +00:00:17,317 --> 00:00:21,588 +your Swift concurrency-based +apps beyond a single process. + +8 +00:00:21,588 --> 00:00:23,624 +Swift actors were designed +to protect you + +9 +00:00:23,624 --> 00:00:26,360 +from low-level data races +in the same process. + +10 +00:00:26,360 --> 00:00:30,864 +They do this by compile time +enforced actor isolation checks. + +11 +00:00:30,864 --> 00:00:33,934 +Distributed actors refine +the same conceptual actor model + +12 +00:00:33,934 --> 00:00:36,036 +and extend it +to multiple processes, + +13 +00:00:36,036 --> 00:00:39,506 +such as multiple devices +or servers in a cluster. + +14 +00:00:39,506 --> 00:00:42,509 +So just in case you are not yet +familiar with Swift actors, + +15 +00:00:42,509 --> 00:00:43,877 +we recommend you first watch + +16 +00:00:43,877 --> 00:00:46,446 +the "Protect mutable state +with Swift actors" session + +17 +00:00:46,446 --> 00:00:49,716 +from WWDC 2021. + +18 +00:00:49,716 --> 00:00:51,351 +The app we'll work on +during this session + +19 +00:00:51,351 --> 00:00:54,688 +is a tic-tac-toe-style game +I've been developing recently: + +20 +00:00:54,688 --> 00:00:56,790 +Tic Tac Fish! + +21 +00:00:56,790 --> 00:00:59,026 +The fun idea here +is that you can select + +22 +00:00:59,026 --> 00:01:01,995 +a team you're playing for, +which then corresponds to emojis + +23 +00:01:01,995 --> 00:01:05,132 +that will be used to mark +your moves as you play the game. + +24 +00:01:05,132 --> 00:01:07,935 +Then, as you mark +your moves on the field, + +25 +00:01:07,935 --> 00:01:10,637 +emojis from your team +will be placed on the field + +26 +00:01:10,637 --> 00:01:12,639 +until one of the players wins. + +27 +00:01:12,639 --> 00:01:15,676 +Right now, I have only +implemented an offline mode, + +28 +00:01:15,676 --> 00:01:18,445 +where I can play against +a bot opponent, + +29 +00:01:18,445 --> 00:01:21,014 +but I'd like to introduce +a few multiplayer modes + +30 +00:01:21,014 --> 00:01:23,450 +to take my app +to the next level. + +31 +00:01:23,450 --> 00:01:26,253 +I'm already using actors in +this app to manage concurrency + +32 +00:01:26,253 --> 00:01:28,655 +and model players +involved in the game. + +33 +00:01:28,655 --> 00:01:31,458 +Let's see what it takes +to migrate those player actors + +34 +00:01:31,458 --> 00:01:34,461 +to different processes, +and how distributed actors + +35 +00:01:34,461 --> 00:01:37,030 +can help me do this. + +36 +00:01:37,030 --> 00:01:40,300 +Before we jump to the code, +let us take a step back + +37 +00:01:40,300 --> 00:01:42,836 +and visualize why actors +are so well-suited + +38 +00:01:42,836 --> 00:01:46,740 +for building concurrent +and distributed applications. + +39 +00:01:46,740 --> 00:01:49,910 +Throughout WWDC sessions, +you may hear us use the term + +40 +00:01:49,910 --> 00:01:52,913 +"sea of concurrency" +when talking about actors. + +41 +00:01:52,913 --> 00:01:54,915 +This is because +it's a great mental model + +42 +00:01:54,915 --> 00:01:56,717 +to think about them. + +43 +00:01:56,717 --> 00:02:00,187 +Each actor is its own island +in the sea of concurrency, + +44 +00:02:00,187 --> 00:02:02,823 +and instead of accessing +each other's islands directly, + +45 +00:02:02,823 --> 00:02:05,525 +they exchange messages +between them. + +46 +00:02:05,525 --> 00:02:08,295 +In Swift, sending those +messages across islands + +47 +00:02:08,295 --> 00:02:12,165 +is implemented as asynchronous +method calls and async/await. + +48 +00:02:12,165 --> 00:02:16,169 +This, in combination +with actor state isolation, + +49 +00:02:16,169 --> 00:02:18,071 +allows the compiler to guarantee + +50 +00:02:18,071 --> 00:02:20,540 +that once an actor-based +program compiles, + +51 +00:02:20,540 --> 00:02:23,643 +it is free from low-level +data races. + +52 +00:02:23,643 --> 00:02:26,613 +Now let us now take +this same conceptual model + +53 +00:02:26,613 --> 00:02:28,248 +and apply it to our game, + +54 +00:02:28,248 --> 00:02:30,884 +reimagined as +a distributed system. + +55 +00:02:30,884 --> 00:02:33,153 +We can think of each device, +node in a cluster, + +56 +00:02:33,153 --> 00:02:34,821 +or process +of an operating system + +57 +00:02:34,821 --> 00:02:37,391 +as if it were an independent +sea of concurrency, + +58 +00:02:37,391 --> 00:02:39,860 +here marked as +the smaller dark rectangles. + +59 +00:02:39,860 --> 00:02:42,262 +Within those, we were able +to synchronize information + +60 +00:02:42,262 --> 00:02:44,231 +rather easily +since they were still + +61 +00:02:44,231 --> 00:02:46,400 +sharing the same memory space. + +62 +00:02:46,400 --> 00:02:48,602 +And while the same concept +of message passing + +63 +00:02:48,602 --> 00:02:50,504 +works perfectly well +for concurrency + +64 +00:02:50,504 --> 00:02:52,005 +as well as distribution, + +65 +00:02:52,005 --> 00:02:53,673 +there are a few more +restrictions + +66 +00:02:53,673 --> 00:02:57,577 +distribution needs to put +into place for it all to work. + +67 +00:02:57,577 --> 00:03:00,580 +This is where distributed actors +come into the picture. + +68 +00:03:00,580 --> 00:03:03,717 +By using distributed actors, +we're able to establish + +69 +00:03:03,717 --> 00:03:07,921 +a channel between two processes +and send messages between them. + +70 +00:03:07,921 --> 00:03:11,291 +In other words, +if Swift actors were islands + +71 +00:03:11,291 --> 00:03:15,062 +in the sea of concurrency, +distributed actors are the same + +72 +00:03:15,062 --> 00:03:17,998 +in the vast sea +of distributed systems. + +73 +00:03:17,998 --> 00:03:21,535 +From a programming model, +not much has really changed -- + +74 +00:03:21,535 --> 00:03:23,670 +actors still isolate their state + +75 +00:03:23,670 --> 00:03:27,507 +and still can only communicate +using asynchronous messages. + +76 +00:03:27,507 --> 00:03:29,476 +We could even have +more distributed actors + +77 +00:03:29,476 --> 00:03:31,144 +in the same process, + +78 +00:03:31,144 --> 00:03:33,313 +and for all intents +and purposes, + +79 +00:03:33,313 --> 00:03:35,449 +they are as useful +as local actors, + +80 +00:03:35,449 --> 00:03:37,217 +with the difference +that they're also ready + +81 +00:03:37,217 --> 00:03:41,455 +to participate in remote +interactions whenever necessary. + +82 +00:03:41,455 --> 00:03:44,724 +This ability to be potentially +remote without having to change + +83 +00:03:44,724 --> 00:03:47,260 +how we interact +with such distributed actor + +84 +00:03:47,260 --> 00:03:51,198 +is called +"location transparency." + +85 +00:03:51,198 --> 00:03:52,899 +This means that regardless + +86 +00:03:52,899 --> 00:03:55,168 +where a distributed actor +is located, + +87 +00:03:55,168 --> 00:03:57,471 +we can interact +with it the same way. + +88 +00:03:57,471 --> 00:03:59,339 +This is not only +fantastic for testing, + +89 +00:03:59,339 --> 00:04:02,909 +as we execute +the same logic in local actors, + +90 +00:04:02,909 --> 00:04:05,879 +but also enables us +to transparently move our actors + +91 +00:04:05,879 --> 00:04:07,981 +to wherever they +should be located, + +92 +00:04:07,981 --> 00:04:10,517 +without having to change +their implementation. + +93 +00:04:10,517 --> 00:04:12,619 +OK, I think we're ready +to look at some code + +94 +00:04:12,619 --> 00:04:15,922 +and convert our first actor +to a distributed actor. + +95 +00:04:15,922 --> 00:04:19,192 +First, let's have a quick look +at the general game UI, + +96 +00:04:19,192 --> 00:04:21,361 +and how it interacts +with my player actors. + +97 +00:04:21,361 --> 00:04:24,231 +The view is a pretty standard +SwiftUI code, + +98 +00:04:24,231 --> 00:04:26,833 +and I have a few +text and button elements + +99 +00:04:26,833 --> 00:04:28,668 +representing the game field. + +100 +00:04:28,668 --> 00:04:30,504 +As a user clicks a GameCell, + +101 +00:04:30,504 --> 00:04:32,973 +we ask the player actor +to generate a move + +102 +00:04:32,973 --> 00:04:36,209 +and update the view models +that power the UI. + +103 +00:04:36,209 --> 00:04:37,644 +Thanks to Swift concurrency, + +104 +00:04:37,644 --> 00:04:40,747 +all those updates are +thread-safe and well-behaved. + +105 +00:04:40,747 --> 00:04:43,350 +Currently, the actor +representing user input + +106 +00:04:43,350 --> 00:04:45,819 +is implemented +as an offline player. + +107 +00:04:45,819 --> 00:04:47,587 +Let's have a look at it next. + +108 +00:04:47,587 --> 00:04:50,090 +This actor encapsulates +some state that allows it + +109 +00:04:50,090 --> 00:04:52,025 +to generate game moves. + +110 +00:04:52,025 --> 00:04:55,729 +Specifically, it needs to track +how many moves it already made + +111 +00:04:55,729 --> 00:04:58,064 +and what team it is playing for. + +112 +00:04:58,064 --> 00:04:59,766 +Because each team +has a number of emojis + +113 +00:04:59,766 --> 00:05:02,802 +to chose from for each move, +use the number of moves made + +114 +00:05:02,802 --> 00:05:05,405 +to select +the emoji character ID. + +115 +00:05:05,405 --> 00:05:09,109 +I also need to update the model +once a move has been created. + +116 +00:05:09,109 --> 00:05:11,578 +The model is +a MainActor isolated class, + +117 +00:05:11,578 --> 00:05:13,613 +so mutations of it +are thread-safe. + +118 +00:05:13,613 --> 00:05:15,182 +I do need to use "await" + +119 +00:05:15,182 --> 00:05:17,817 +as I make +the userMadeMove call, though. + +120 +00:05:17,817 --> 00:05:20,654 +Lastly, the offline player +also declares a method that is + +121 +00:05:20,654 --> 00:05:24,257 +going to be called whenever +the opponent has made a move. + +122 +00:05:24,257 --> 00:05:27,327 +The only thing we need to do +here is update the view model, + +123 +00:05:27,327 --> 00:05:29,596 +which will make +the game field active again + +124 +00:05:29,596 --> 00:05:31,932 +so the human player +can select their move, + +125 +00:05:31,932 --> 00:05:34,768 +and the cycle continues +until the game ends. + +126 +00:05:34,768 --> 00:05:37,437 +Our bot player is also +represented using an actor. + +127 +00:05:37,437 --> 00:05:39,606 +Its implementation +is actually quite a bit simpler + +128 +00:05:39,606 --> 00:05:41,007 +than the offline player + +129 +00:05:41,007 --> 00:05:44,077 +since it does not have to worry +about updating the view model; + +130 +00:05:44,077 --> 00:05:46,012 +it just keeps track +of the GameState + +131 +00:05:46,012 --> 00:05:47,681 +and generates game moves. + +132 +00:05:47,681 --> 00:05:49,749 +Since the bot player +is a bit simpler, + +133 +00:05:49,749 --> 00:05:51,751 +I think it's a good one +to start our conversion + +134 +00:05:51,751 --> 00:05:53,887 +to distributed actors. + +135 +00:05:53,887 --> 00:05:55,789 +OK, I think we're ready +to look at some code + +136 +00:05:55,789 --> 00:05:59,092 +and convert our first actor +to a distributed actor. + +137 +00:05:59,092 --> 00:06:01,761 +The first step towards our +distributed Tic Tac Fish game + +138 +00:06:01,761 --> 00:06:03,897 +will be to convert +the BotPlayer type + +139 +00:06:03,897 --> 00:06:04,998 +to a distributed actor, + +140 +00:06:04,998 --> 00:06:07,200 +while still only +using it locally. + +141 +00:06:07,200 --> 00:06:10,036 +Let's open Xcode +and see how we can do that. + +142 +00:06:10,036 --> 00:06:12,539 +In order to declare +a distributed actor, + +143 +00:06:12,539 --> 00:06:14,908 +I'll need to import +the new distributed module, + +144 +00:06:14,908 --> 00:06:17,277 +which we introduced +in Swift 5.7. + +145 +00:06:17,277 --> 00:06:19,479 +This module contains +all the necessary types + +146 +00:06:19,479 --> 00:06:23,283 +I'm going to need to declare +and use distributed actors. + +147 +00:06:23,283 --> 00:06:25,318 +I can now add +the distributed keyword + +148 +00:06:25,318 --> 00:06:28,989 +in front of the BotPlayer actor +declaration, like this. + +149 +00:06:28,989 --> 00:06:31,424 +This will cause the actor +to automatically conform + +150 +00:06:31,424 --> 00:06:33,093 +to the DistributedActor +protocol, + +151 +00:06:33,093 --> 00:06:36,029 +and enable a number of +additional compile time checks. + +152 +00:06:36,029 --> 00:06:37,230 +Let's see what kind of errors + +153 +00:06:37,230 --> 00:06:39,466 +the compiler will ask us +to fix now. + +154 +00:06:39,466 --> 00:06:42,369 +The compiler helpfully tells us +that the BotPlayer does not + +155 +00:06:42,369 --> 00:06:44,738 +declare an ActorSystem +it can be used with. + +156 +00:06:44,738 --> 00:06:45,872 +As distributed actors + +157 +00:06:45,872 --> 00:06:48,441 +always belong to some +distributed actor system, + +158 +00:06:48,441 --> 00:06:50,510 +which handles all +the serialization and networking + +159 +00:06:50,510 --> 00:06:52,579 +necessary to perform +remote calls; + +160 +00:06:52,579 --> 00:06:55,215 +we need to declare +what type of actor system + +161 +00:06:55,215 --> 00:06:57,784 +this actor is intended +to be used with. + +162 +00:06:57,784 --> 00:07:00,720 +Since, for now, my only goal +is to have the bot player + +163 +00:07:00,720 --> 00:07:03,056 +pass all the distributed +isolation checks, + +164 +00:07:03,056 --> 00:07:06,393 +without actually running it on +a remote host, I can use the + +165 +00:07:06,393 --> 00:07:08,561 +LocalTestingDistributedActor +System + +166 +00:07:08,561 --> 00:07:10,797 +that comes +with the Distributed module. + +167 +00:07:10,797 --> 00:07:11,931 +I can tell the compiler + +168 +00:07:11,931 --> 00:07:14,067 +about the actor system +we're going to use + +169 +00:07:14,067 --> 00:07:15,802 +by either declaring +a module-wide + +170 +00:07:15,802 --> 00:07:18,405 +DefaultDistributedActorSystem +typealias, + +171 +00:07:18,405 --> 00:07:22,809 +or an ActorSystem typealias in +the body of the specific actor. + +172 +00:07:22,809 --> 00:07:27,314 +The latter bit is a bit more +specific, so let's go with that. + +173 +00:07:32,319 --> 00:07:34,554 +The next error is about +the "id" property, + +174 +00:07:34,554 --> 00:07:36,456 +that I had previously +implemented manually + +175 +00:07:36,456 --> 00:07:39,092 +in order to conform +to the Identifiable protocol + +176 +00:07:39,092 --> 00:07:41,895 +that both my player actors +need to conform to. + +177 +00:07:41,895 --> 00:07:44,631 +The error now says +that the ID property + +178 +00:07:44,631 --> 00:07:47,701 +cannot be defined explicitly +as it conflicts + +179 +00:07:47,701 --> 00:07:50,870 +with a distributed actor +synthesized property. + +180 +00:07:50,870 --> 00:07:53,707 +IDs are a crucial piece +of distributed actors. + +181 +00:07:53,707 --> 00:07:56,076 +They are used +to uniquely identify an actor + +182 +00:07:56,076 --> 00:08:00,113 +in the entire distributed actor +system that it is part of. + +183 +00:08:00,113 --> 00:08:02,649 +They are assigned by +the distributed actor system + +184 +00:08:02,649 --> 00:08:04,050 +as the actor is initialized, + +185 +00:08:04,050 --> 00:08:06,286 +and later managed +by that system. + +186 +00:08:06,286 --> 00:08:08,488 +As such, +we cannot declare or assign + +187 +00:08:08,488 --> 00:08:10,323 +the ID property manually -- + +188 +00:08:10,323 --> 00:08:12,592 +the actor system +will be doing this for us. + +189 +00:08:12,592 --> 00:08:15,428 +In other words, I can simply +leave it to the actor system + +190 +00:08:15,428 --> 00:08:18,998 +and remove my manually +declared ID property. + +191 +00:08:18,998 --> 00:08:20,934 +The last error +we need to deal with here + +192 +00:08:20,934 --> 00:08:23,670 +is the distributed +actor's initializer. + +193 +00:08:23,670 --> 00:08:26,206 +The compiler says +that the actorSystem property + +194 +00:08:26,206 --> 00:08:28,742 +has not been +initialized before use. + +195 +00:08:28,742 --> 00:08:31,077 +This is another compiler +synthesized property + +196 +00:08:31,077 --> 00:08:33,413 +that is part +of every distributed actor. + +197 +00:08:33,413 --> 00:08:36,282 +Not only do we need to declare +the type of actor system + +198 +00:08:36,282 --> 00:08:38,718 +we want to use, +but we also need to initialize + +199 +00:08:38,718 --> 00:08:40,887 +the synthesized +actorSystem property + +200 +00:08:40,887 --> 00:08:43,156 +with some concrete actor system. + +201 +00:08:43,156 --> 00:08:45,024 +Generally, +the right thing to do here + +202 +00:08:45,024 --> 00:08:47,360 +is to accept an actor system +in the initializer, + +203 +00:08:47,360 --> 00:08:49,462 +and pass it through +to the property. + +204 +00:08:49,462 --> 00:08:52,198 +This way, I could pass in +a different actor system + +205 +00:08:52,198 --> 00:08:56,469 +implementation in my tests +to facilitate easy unit testing. + +206 +00:08:56,469 --> 00:08:59,205 +We'll also have to pass +an instance whenever we create + +207 +00:08:59,205 --> 00:09:03,109 +a new bot player, +so let's do this now. + +208 +00:09:11,117 --> 00:09:14,387 +Awesome! We're done with +all the declaration side errors. + +209 +00:09:14,387 --> 00:09:15,889 +But there's still +some call-site errors + +210 +00:09:15,889 --> 00:09:18,858 +we need to address though. + +211 +00:09:18,858 --> 00:09:20,693 +It seems that +only distributed methods + +212 +00:09:20,693 --> 00:09:23,963 +can be called on potentially +remote distributed actors. + +213 +00:09:23,963 --> 00:09:27,434 +This is similar to annotating +only some actors in your system + +214 +00:09:27,434 --> 00:09:29,402 +as distributed actors. + +215 +00:09:29,402 --> 00:09:31,337 +Not every method +on a distributed actor + +216 +00:09:31,337 --> 00:09:34,307 +is necessarily designed +to be called remotely. + +217 +00:09:34,307 --> 00:09:36,342 +They can have +small helper functions, + +218 +00:09:36,342 --> 00:09:38,244 +or functions +which assume the caller + +219 +00:09:38,244 --> 00:09:40,380 +has already been authenticated. + +220 +00:09:40,380 --> 00:09:43,116 +This is why Swift asks you +to be explicit about + +221 +00:09:43,116 --> 00:09:45,251 +the distributed API surface, + +222 +00:09:45,251 --> 00:09:47,720 +you want to expose +to remote callers. + +223 +00:09:47,720 --> 00:09:50,457 +Thankfully, this is also +easily fixed by just adding + +224 +00:09:50,457 --> 00:09:53,393 +the distributed keyword +to those functions. + +225 +00:09:53,393 --> 00:09:55,895 +As both makeMove +and opponentMoved methods + +226 +00:09:55,895 --> 00:09:57,831 +are intended +to be called remotely, + +227 +00:09:57,831 --> 00:10:01,935 +let me add the distributed +keyword to them to both of them. + +228 +00:10:06,439 --> 00:10:09,375 +OK! With that, +there's only one last thing + +229 +00:10:09,375 --> 00:10:11,311 +we need to take care of. + +230 +00:10:11,311 --> 00:10:14,214 +As distributed method calls +can cross network boundaries, + +231 +00:10:14,214 --> 00:10:16,583 +we need to ensure +that all of their parameters + +232 +00:10:16,583 --> 00:10:19,352 +and return values conform +to the serialization requirement + +233 +00:10:19,352 --> 00:10:21,321 +of the actor system. + +234 +00:10:21,321 --> 00:10:24,123 +In our case, the actor system +is using Codable, + +235 +00:10:24,123 --> 00:10:26,459 +Swift's native +serialization mechanism. + +236 +00:10:26,459 --> 00:10:28,995 +Specifically, the compiler +tells us that, + +237 +00:10:28,995 --> 00:10:31,664 +"Result type GameMove +does not conform + +238 +00:10:31,664 --> 00:10:34,634 +to the serialization +requirement Codable." + +239 +00:10:34,634 --> 00:10:37,737 +Let me have a quick look +at the GameMove type. + +240 +00:10:37,737 --> 00:10:40,640 +Luckily, it seems that +it's a clean little data type + +241 +00:10:40,640 --> 00:10:42,375 +that I can easily make Codable + +242 +00:10:42,375 --> 00:10:44,944 +by just adding +the necessary conformance. + +243 +00:10:44,944 --> 00:10:46,546 +The Swift compiler +will synthesize + +244 +00:10:46,546 --> 00:10:49,115 +the necessary Codable +implementation for me. + +245 +00:10:49,115 --> 00:10:50,450 +And with that, we're done! + +246 +00:10:50,450 --> 00:10:53,486 +I can check +the game runs as expected. + +247 +00:10:55,488 --> 00:10:57,690 +OK, a point for team fish! + +248 +00:10:57,690 --> 00:10:59,792 +And although +the bot player still executing + +249 +00:10:59,792 --> 00:11:02,529 +on the same local device, +we have already paved the way + +250 +00:11:02,529 --> 00:11:04,864 +for the exciting next step. + +251 +00:11:04,864 --> 00:11:06,566 +In this step, we'll actually +reap the benefits + +252 +00:11:06,566 --> 00:11:10,770 +of the bot player's newly gained +location transparency powers. + +253 +00:11:10,770 --> 00:11:12,639 +I have already prepared +a WebSocket-based + +254 +00:11:12,639 --> 00:11:15,608 +sample actor system +that we can use for this. + +255 +00:11:15,608 --> 00:11:17,343 +By making use +of this actor system, + +256 +00:11:17,343 --> 00:11:18,945 +we'll be able +to move our bot player + +257 +00:11:18,945 --> 00:11:20,880 +to a server-side +Swift application, + +258 +00:11:20,880 --> 00:11:24,350 +and resolve a remote reference +to it from our mobile game. + +259 +00:11:24,350 --> 00:11:26,185 +As far as the actor +is concerned, + +260 +00:11:26,185 --> 00:11:28,988 +we only need to change +the declared ActorSystem + +261 +00:11:28,988 --> 00:11:31,658 +from the LocalTesting +DistributedActor System + +262 +00:11:31,658 --> 00:11:34,928 +to the +SampleWebSocketActorSystem + +263 +00:11:34,928 --> 00:11:37,230 +that I prepared +for the sample app. + +264 +00:11:37,230 --> 00:11:40,366 +The rest of the actor code +remains the same. + +265 +00:11:40,366 --> 00:11:43,570 +Next, let us resolve +a remote bot player reference, + +266 +00:11:43,570 --> 00:11:45,672 +rather than +creating one locally. + +267 +00:11:45,672 --> 00:11:49,442 +It is worth keeping in mind that +the terms "local" and "remote" + +268 +00:11:49,442 --> 00:11:52,645 +are a matter of perspective when +it comes to distributed actors. + +269 +00:11:52,645 --> 00:11:55,748 +For every remote reference, +there is some corresponding + +270 +00:11:55,748 --> 00:11:57,784 +local instance +on some other node + +271 +00:11:57,784 --> 00:11:59,485 +in the distributed actor system. + +272 +00:11:59,485 --> 00:12:01,888 +Creating a local instance +of a distributed actor + +273 +00:12:01,888 --> 00:12:05,091 +is performed much the same way +as any other Swift object: + +274 +00:12:05,091 --> 00:12:06,726 +by calling its initializer. + +275 +00:12:06,726 --> 00:12:10,029 +Obtaining a remote reference +to a distributed actor, however, + +276 +00:12:10,029 --> 00:12:12,565 +follows a slightly +different pattern. + +277 +00:12:12,565 --> 00:12:14,400 +Instead of creating an actor, + +278 +00:12:14,400 --> 00:12:17,036 +we will attempt to resolve +an actor ID + +279 +00:12:17,036 --> 00:12:19,072 +using a concrete actor system. + +280 +00:12:19,072 --> 00:12:22,609 +The static resolve method allows +us to ask an actor system + +281 +00:12:22,609 --> 00:12:25,111 +to attempt to give us +an existing actor instance + +282 +00:12:25,111 --> 00:12:27,113 +for an actor with that ID + +283 +00:12:27,113 --> 00:12:31,017 +or, return a remote reference +to an actor identified by it. + +284 +00:12:31,017 --> 00:12:33,519 +Actor systems should not +perform actual remote lookups + +285 +00:12:33,519 --> 00:12:36,389 +when resolving identifiers +because as you can see, + +286 +00:12:36,389 --> 00:12:38,958 +the resolve method +is not asynchronous, + +287 +00:12:38,958 --> 00:12:40,793 +and therefore +should return quickly + +288 +00:12:40,793 --> 00:12:42,328 +and not perform any networking + +289 +00:12:42,328 --> 00:12:44,263 +or otherwise +blocking operations. + +290 +00:12:44,263 --> 00:12:47,033 +If an identity looks valid, +and seems to be pointing + +291 +00:12:47,033 --> 00:12:50,036 +at a valid remote location, +systems shall assume + +292 +00:12:50,036 --> 00:12:53,973 +that such actor exists and +return a remote reference to it. + +293 +00:12:53,973 --> 00:12:57,343 +Keep in mind that +at the time of resolving an ID, + +294 +00:12:57,343 --> 00:13:00,079 +the actual instance +on the remote system + +295 +00:13:00,079 --> 00:13:01,948 +may not even exist yet! + +296 +00:13:01,948 --> 00:13:04,517 +For example, here we're +making up a random identifier + +297 +00:13:04,517 --> 00:13:05,985 +for an opponent bot player + +298 +00:13:05,985 --> 00:13:08,821 +that should be dedicated +to playing a game with us. + +299 +00:13:08,821 --> 00:13:11,124 +This bot does not exist yet, +but it will be created + +300 +00:13:11,124 --> 00:13:13,426 +on the server-side system +as the first message + +301 +00:13:13,426 --> 00:13:16,062 +designated to this ID +is received. + +302 +00:13:16,062 --> 00:13:19,198 +Now moving on to +a server-side Swift application. + +303 +00:13:19,198 --> 00:13:22,001 +Thanks to the sample WebSocket +actor system I prepared, + +304 +00:13:22,001 --> 00:13:24,704 +implementing that +will be a breeze. + +305 +00:13:24,704 --> 00:13:28,608 +First, we create the WebSocket +actor system in server mode, + +306 +00:13:28,608 --> 00:13:30,309 +which makes it bind +and listen to the port + +307 +00:13:30,309 --> 00:13:32,912 +rather than connect to it. + +308 +00:13:32,912 --> 00:13:36,716 +And we have the app wait +until the system is terminated. + +309 +00:13:36,716 --> 00:13:38,985 +Next, we'll somehow need +to handle the pattern + +310 +00:13:38,985 --> 00:13:40,520 +of creating actors on demand + +311 +00:13:40,520 --> 00:13:43,322 +as we receive messages +addressed to IDs + +312 +00:13:43,322 --> 00:13:46,759 +that are not yet assigned +any actor instances. + +313 +00:13:46,759 --> 00:13:49,696 +Generally, the actor system +will receive incoming messages, + +314 +00:13:49,696 --> 00:13:52,432 +attempt to resolve +their recipient IDs + +315 +00:13:52,432 --> 00:13:55,268 +in order locate a local +distributed actor instance, + +316 +00:13:55,268 --> 00:13:59,672 +and then execute a remote call +on the located actor. + +317 +00:13:59,672 --> 00:14:01,107 +As we just discussed though, + +318 +00:14:01,107 --> 00:14:03,943 +our bot player IDs +are literally made up, + +319 +00:14:03,943 --> 00:14:07,647 +so the system can't possibly +know about them + +320 +00:14:07,647 --> 00:14:11,684 +and even less so create the +right type of actor by itself. + +321 +00:14:11,684 --> 00:14:14,253 +Thankfully, our sample actor +system implementation + +322 +00:14:14,253 --> 00:14:16,756 +has just the right pattern +prepared for us: + +323 +00:14:16,756 --> 00:14:18,725 +on-demand actor creation. + +324 +00:14:18,725 --> 00:14:21,160 +Please note here +that this is only a pattern, + +325 +00:14:21,160 --> 00:14:22,995 +and not something built in +or provided by + +326 +00:14:22,995 --> 00:14:25,098 +the distributed actor module. + +327 +00:14:25,098 --> 00:14:27,366 +It is, however, +a great example of how flexible + +328 +00:14:27,366 --> 00:14:30,737 +and powerful actor system +implementations can be. + +329 +00:14:30,737 --> 00:14:32,939 +A system can offer +various patterns + +330 +00:14:32,939 --> 00:14:36,209 +and make complex tasks +simple to deal with. + +331 +00:14:36,209 --> 00:14:38,978 +Using this pattern, +the actor system attempts + +332 +00:14:38,978 --> 00:14:42,815 +to resolve a local actor +for all incoming IDs as usual. + +333 +00:14:42,815 --> 00:14:45,585 +However, when it fails +to find an existing actor, + +334 +00:14:45,585 --> 00:14:47,920 +it attempts to +resolveCreateOnDemand. + +335 +00:14:47,920 --> 00:14:50,089 +Since we are in control +of both our client code + +336 +00:14:50,089 --> 00:14:53,059 +making up the IDs +and the piece of server code, + +337 +00:14:53,059 --> 00:14:55,061 +we can help +the actor system out + +338 +00:14:55,061 --> 00:14:58,064 +by creating +the necessary actors on demand. + +339 +00:14:58,064 --> 00:15:00,933 +Since the bot identifiers +we have been making up + +340 +00:15:00,933 --> 00:15:04,103 +on the client are using some +recognizable naming scheme -- + +341 +00:15:04,103 --> 00:15:06,906 +like adding tags +to the ActorIdentity + +342 +00:15:06,906 --> 00:15:09,542 +or just using +some recognizable names -- + +343 +00:15:09,542 --> 00:15:12,211 +we can detect those IDs +and create a new bot opponent + +344 +00:15:12,211 --> 00:15:15,681 +for every message that does not +have one active yet. + +345 +00:15:15,681 --> 00:15:17,583 +We'll only create +a new bot player + +346 +00:15:17,583 --> 00:15:19,685 +for the first message +designated to it, + +347 +00:15:19,685 --> 00:15:21,154 +as subsequent remote calls + +348 +00:15:21,154 --> 00:15:24,157 +will simply resolve +the existing instance. + +349 +00:15:24,157 --> 00:15:25,992 +And that's all there is to it! + +350 +00:15:25,992 --> 00:15:27,693 +Our server implementation +is complete + +351 +00:15:27,693 --> 00:15:30,963 +and we can now play a game +with our remote bot player. + +352 +00:15:30,963 --> 00:15:34,467 +We can run the server from the +command line using Swift run, + +353 +00:15:34,467 --> 00:15:37,403 +or using Xcode +and selecting the server scheme + +354 +00:15:37,403 --> 00:15:39,472 +and clicking Run as usual. + +355 +00:15:39,472 --> 00:15:41,207 +As we're done +making our first move, + +356 +00:15:41,207 --> 00:15:44,343 +we ask the bot player to do +the same by calling makeMove + +357 +00:15:44,343 --> 00:15:46,846 +on the remote player reference +we have created. + +358 +00:15:46,846 --> 00:15:49,615 +This triggers a resolve +in the server-side system. + +359 +00:15:49,615 --> 00:15:52,285 +It can't find an existing bot +for this ID, + +360 +00:15:52,285 --> 00:15:55,788 +so it attempts and succeeds, +creating a bot on demand. + +361 +00:15:55,788 --> 00:15:58,291 +The bot receives +a makeMove call, + +362 +00:15:58,291 --> 00:16:01,327 +and replies with the GameMove +it generated. + +363 +00:16:01,327 --> 00:16:03,162 +That was pretty great already! + +364 +00:16:03,162 --> 00:16:05,064 +While we did have to do +some up-front work + +365 +00:16:05,064 --> 00:16:07,567 +to convert our bot player +to a distributed actor, + +366 +00:16:07,567 --> 00:16:09,368 +actually moving it +to the remote system + +367 +00:16:09,368 --> 00:16:11,370 +was pretty straightforward. + +368 +00:16:11,370 --> 00:16:13,172 +And we didn't have +to deal with any networking + +369 +00:16:13,172 --> 00:16:16,576 +or serialization implementation +details at all! + +370 +00:16:16,576 --> 00:16:18,077 +All the heavy lifting +was done for us + +371 +00:16:18,077 --> 00:16:19,846 +by the distributed actor system. + +372 +00:16:19,846 --> 00:16:21,414 +And while there aren't +many hardened + +373 +00:16:21,414 --> 00:16:24,050 +feature-complete implementations +available just yet, + +374 +00:16:24,050 --> 00:16:25,818 +this ease of going distributed + +375 +00:16:25,818 --> 00:16:28,287 +is something we're striving +for with this feature. + +376 +00:16:28,287 --> 00:16:29,755 +Next, +let's see how we can build + +377 +00:16:29,755 --> 00:16:32,725 +a true multiplayer experience +for our game. + +378 +00:16:32,725 --> 00:16:34,961 +Our previous example +used distributed actors + +379 +00:16:34,961 --> 00:16:37,096 +in a client/server scenario, +which you may be + +380 +00:16:37,096 --> 00:16:39,932 +familiar with already +from other apps you worked on. + +381 +00:16:39,932 --> 00:16:42,101 +However, distributed actors +can also be used + +382 +00:16:42,101 --> 00:16:43,836 +in peer-to-peer systems, + +383 +00:16:43,836 --> 00:16:46,772 +where there isn't a dedicated +server component at all. + +384 +00:16:46,772 --> 00:16:48,774 +This matches another idea +I had for our game. + +385 +00:16:48,774 --> 00:16:51,010 +Sometimes when traveling, +you end up in these locations + +386 +00:16:51,010 --> 00:16:53,179 +that don't really have +great internet, + +387 +00:16:53,179 --> 00:16:55,581 +but the local Wi-Fi works great. + +388 +00:16:55,581 --> 00:16:57,216 +I'd like to still +be able to challenge + +389 +00:16:57,216 --> 00:16:58,251 +and play with my friends -- + +390 +00:16:58,251 --> 00:16:59,952 +which are connected +to the same network -- + +391 +00:16:59,952 --> 00:17:02,321 +as I end up in such a situation. + +392 +00:17:02,321 --> 00:17:03,756 +I went ahead and implemented + +393 +00:17:03,756 --> 00:17:06,058 +another actor system +implementation, + +394 +00:17:06,058 --> 00:17:07,693 +this time using +local networking features + +395 +00:17:07,693 --> 00:17:10,029 +offered by Network framework. + +396 +00:17:10,029 --> 00:17:11,864 +While we don't dive into +the implementation + +397 +00:17:11,864 --> 00:17:14,367 +of that actor system +in this talk, you can watch + +398 +00:17:14,367 --> 00:17:18,571 +"Advances in Networking, Part 2" +from WWDC 2019 + +399 +00:17:18,571 --> 00:17:21,440 +to learn how you would implement +such custom protocol. + +400 +00:17:21,440 --> 00:17:24,310 +It is also worth pointing out +that access to local network + +401 +00:17:24,310 --> 00:17:26,946 +can expose very +privacy-sensitive information, + +402 +00:17:26,946 --> 00:17:29,548 +so please take care +to use it respectfully. + +403 +00:17:29,548 --> 00:17:31,083 +Since this time +we'll be dealing with + +404 +00:17:31,083 --> 00:17:33,920 +already existing distributed +actors on other devices, + +405 +00:17:33,920 --> 00:17:36,122 +we can no longer +just make up the IDs + +406 +00:17:36,122 --> 00:17:38,424 +like we did in the previous +example. + +407 +00:17:38,424 --> 00:17:40,226 +We have to discover +the specific actor + +408 +00:17:40,226 --> 00:17:42,962 +on the other device +we'd like to play a game with. + +409 +00:17:42,962 --> 00:17:45,331 +This problem isn't unique +to distributed actors, + +410 +00:17:45,331 --> 00:17:48,467 +and is generally solved using +service discovery mechanisms. + +411 +00:17:48,467 --> 00:17:50,970 +However, in the domain +of distributed actors, + +412 +00:17:50,970 --> 00:17:53,272 +there is a common pattern +and style of API + +413 +00:17:53,272 --> 00:17:55,775 +actor systems are expected +to offer that allows you + +414 +00:17:55,775 --> 00:17:59,278 +to stick to strongly-typed APIs +throughout all your code. + +415 +00:17:59,278 --> 00:18:01,414 +We call it +the receptionist pattern, + +416 +00:18:01,414 --> 00:18:04,684 +because similar to a hotel, +actors need to check in with it + +417 +00:18:04,684 --> 00:18:08,321 +in order to become known and +available for others to meet. + +418 +00:18:08,321 --> 00:18:10,690 +Every actor system +has its own receptionist + +419 +00:18:10,690 --> 00:18:13,659 +and they can use whatever means +most suitable for the underlying + +420 +00:18:13,659 --> 00:18:16,996 +transport mechanisms +to implement actor discovery. + +421 +00:18:16,996 --> 00:18:20,533 +Sometimes this may rely on +existing service discovery APIs, + +422 +00:18:20,533 --> 00:18:23,703 +and only layer a type-safe API +on top of them, + +423 +00:18:23,703 --> 00:18:26,405 +or it may implement +a gossip-based mechanism, + +424 +00:18:26,405 --> 00:18:28,240 +or something else entirely. + +425 +00:18:28,240 --> 00:18:30,209 +This, however, +is an implementation detail + +426 +00:18:30,209 --> 00:18:33,145 +from the perspective +of the user of the actor system; + +427 +00:18:33,145 --> 00:18:35,815 +all we need to care about +is checking in our actor + +428 +00:18:35,815 --> 00:18:37,350 +to make it discoverable + +429 +00:18:37,350 --> 00:18:39,352 +and look up actors +by some tag or type + +430 +00:18:39,352 --> 00:18:41,487 +when we need to discover them. + +431 +00:18:41,487 --> 00:18:44,290 +Let's have a look at a simple +receptionist I have implemented + +432 +00:18:44,290 --> 00:18:46,926 +for our +SampleLocalNetworkActorSystem. + +433 +00:18:46,926 --> 00:18:50,863 +It allows an actor to check in, +which enables all receptionists + +434 +00:18:50,863 --> 00:18:53,499 +in the distributed actor system +to discover it. + +435 +00:18:53,499 --> 00:18:57,403 +We can then get a listing +of all actors of a specific type + +436 +00:18:57,403 --> 00:19:00,806 +and tag as they become +available in that system. + +437 +00:19:00,806 --> 00:19:03,009 +Let's use this receptionist +to discover + +438 +00:19:03,009 --> 00:19:06,679 +a concrete opponent actor +we'd like to play a game with. + +439 +00:19:06,679 --> 00:19:09,448 +Previously, our GameView +directly created -- + +440 +00:19:09,448 --> 00:19:12,852 +or resolved -- an opponent +in its view initializer. + +441 +00:19:12,852 --> 00:19:15,354 +We can no longer do this, +as we need to asynchronously + +442 +00:19:15,354 --> 00:19:18,157 +wait for an opponent +to appear on the network. + +443 +00:19:18,157 --> 00:19:21,460 +To do this, let me introduce +a matchmaking view + +444 +00:19:21,460 --> 00:19:23,596 +that will show a "Looking for +opponent..." message + +445 +00:19:23,596 --> 00:19:25,731 +while we're trying +to discover one. + +446 +00:19:25,731 --> 00:19:28,701 +As this view appears, +we'll kick off the matchmaking. + +447 +00:19:28,701 --> 00:19:29,835 +The matchmaking will be done + +448 +00:19:29,835 --> 00:19:32,571 +in a new unstructured task +in which we'll ask + +449 +00:19:32,571 --> 00:19:34,807 +the local actor system's +receptionist + +450 +00:19:34,807 --> 00:19:36,909 +for a listing +of all actors tagged + +451 +00:19:36,909 --> 00:19:39,545 +using the opposing team's tag. + +452 +00:19:39,545 --> 00:19:41,580 +So if we're playing +for team fish, + +453 +00:19:41,580 --> 00:19:44,750 +we'll be looking for players +from the team rodents, + +454 +00:19:44,750 --> 00:19:45,785 +and vice versa. + +455 +00:19:45,785 --> 00:19:47,953 +Next, +we'll use an async for loop + +456 +00:19:47,953 --> 00:19:50,156 +to await incoming +opponent actors. + +457 +00:19:50,156 --> 00:19:52,992 +As the system discovers +a nearby device with an opponent + +458 +00:19:52,992 --> 00:19:56,128 +we could play with, +this task loop will be resumed. + +459 +00:19:56,128 --> 00:19:58,998 +Let's assume the opponent +is always ready to play a game + +460 +00:19:58,998 --> 00:20:00,766 +and immediately store it +in our model + +461 +00:20:00,766 --> 00:20:02,435 +and start a game with them. + +462 +00:20:02,435 --> 00:20:03,936 +We use a helper function +to decide + +463 +00:20:03,936 --> 00:20:06,338 +who should make the first move, +and finally, + +464 +00:20:06,338 --> 00:20:09,442 +tell the opponent that we want +to start a game with them. + +465 +00:20:09,442 --> 00:20:11,744 +Be sure to return here, +in order to break out + +466 +00:20:11,744 --> 00:20:14,447 +of the async for loop, +as we only need one opponent + +467 +00:20:14,447 --> 00:20:16,615 +to be done +with our matchmaking task. + +468 +00:20:16,615 --> 00:20:19,285 +For this gameplay mode, +we do have to change + +469 +00:20:19,285 --> 00:20:21,720 +our OfflinePlayer implementation +a little. + +470 +00:20:21,720 --> 00:20:24,056 +Let's call it +LocalNetworkPlayer, + +471 +00:20:24,056 --> 00:20:27,793 +and it'll be using the +SampleLocalNetworkActorSystem. + +472 +00:20:27,793 --> 00:20:31,330 +What's most interesting about it +is that the makeMove method + +473 +00:20:31,330 --> 00:20:33,699 +of the the actor +representing a human player + +474 +00:20:33,699 --> 00:20:35,701 +may now be invoked remotely! + +475 +00:20:35,701 --> 00:20:37,470 +But making the move is actually + +476 +00:20:37,470 --> 00:20:39,705 +the responsibility +of a human player. + +477 +00:20:39,705 --> 00:20:41,340 +In order to solve +this challenge, + +478 +00:20:41,340 --> 00:20:43,442 +we introduce +a humanSelectedField + +479 +00:20:43,442 --> 00:20:46,045 +asynchronous function +to our view model. + +480 +00:20:46,045 --> 00:20:48,380 +It is powered by a @Published +value that is triggered + +481 +00:20:48,380 --> 00:20:51,450 +when the human +user clicks on one of the +fields. + +482 +00:20:51,450 --> 00:20:53,219 +As the human player +clicks a field, + +483 +00:20:53,219 --> 00:20:56,388 +our makeMove function resumes, +and we complete the remote call + +484 +00:20:56,388 --> 00:20:59,825 +by returning the performed +GameMove to the remote caller. + +485 +00:20:59,825 --> 00:21:01,827 +And again, +that's all there is to it! + +486 +00:21:01,827 --> 00:21:03,429 +We had to change +the actor implementation + +487 +00:21:03,429 --> 00:21:06,332 +a little to handle our +true multiplayer game mode, + +488 +00:21:06,332 --> 00:21:07,533 +but nothing really changed + +489 +00:21:07,533 --> 00:21:09,668 +in the overall design +of the system. + +490 +00:21:09,668 --> 00:21:11,170 +And most importantly, + +491 +00:21:11,170 --> 00:21:13,572 +nothing in our game logic +changes was really specific + +492 +00:21:13,572 --> 00:21:16,075 +to the fact we'll be using +local networking. + +493 +00:21:16,075 --> 00:21:18,477 +We discover an opponent +and play a game with them + +494 +00:21:18,477 --> 00:21:21,280 +by invoking distributed methods +on player actors. + +495 +00:21:21,280 --> 00:21:24,783 +To demo this game mode, I'll +need an opponent to play with. + +496 +00:21:24,783 --> 00:21:27,386 +Let's ask my fluffy assistant +Caplin the Capybara. + +497 +00:21:27,386 --> 00:21:29,588 +I heard he's pretty good at it! + +498 +00:21:32,925 --> 00:21:35,961 +OK, he's pretty smart. + +499 +00:21:38,731 --> 00:21:41,567 +He is pretty good at it. + +500 +00:21:41,567 --> 00:21:44,670 +Let me try here. + +501 +00:21:44,670 --> 00:21:46,272 +Oh, he got me! + +502 +00:21:46,272 --> 00:21:47,806 +This time you win, +little critter, + +503 +00:21:47,806 --> 00:21:49,942 +but we'll play another session. + +504 +00:21:49,942 --> 00:21:52,311 +Thanks for your help, Caplin! + +505 +00:21:52,311 --> 00:21:53,412 +Last but not least, + +506 +00:21:53,412 --> 00:21:55,414 +let me give you an idea +of what we can achieve + +507 +00:21:55,414 --> 00:21:57,483 +by combining +different actor systems. + +508 +00:21:57,483 --> 00:22:00,653 +For example, we can use +the WebSocket system to register + +509 +00:22:00,653 --> 00:22:03,055 +device-hosted actor +player actors + +510 +00:22:03,055 --> 00:22:04,957 +in a server-side lobby system + +511 +00:22:04,957 --> 00:22:07,126 +that will pair them up +and act as a proxy + +512 +00:22:07,126 --> 00:22:10,262 +for distributed calls +between them. + +513 +00:22:10,262 --> 00:22:12,398 +We might implement +a GameLobby actor, + +514 +00:22:12,398 --> 00:22:14,466 +with which device-hosted +player actors + +515 +00:22:14,466 --> 00:22:16,468 +are able to register themselves. + +516 +00:22:16,468 --> 00:22:18,637 +As devices enter +the play online mode, + +517 +00:22:18,637 --> 00:22:21,707 +they would discover the +GameLobby using a receptionist, + +518 +00:22:21,707 --> 00:22:23,442 +and call join on it. + +519 +00:22:23,442 --> 00:22:25,844 +The GameLobby keeps track +of available players + +520 +00:22:25,844 --> 00:22:27,613 +and starts a game session + +521 +00:22:27,613 --> 00:22:30,149 +when a pair of players +has been identified. + +522 +00:22:30,149 --> 00:22:32,952 +A game session would act +as the driver of the game, + +523 +00:22:32,952 --> 00:22:34,286 +polling moves and marking them + +524 +00:22:34,286 --> 00:22:36,922 +in the server-stored +representation of the game. + +525 +00:22:36,922 --> 00:22:39,091 +As the game completes, +we can collect results + +526 +00:22:39,091 --> 00:22:40,960 +and report back to the lobby. + +527 +00:22:40,960 --> 00:22:42,795 +More interestingly though, + +528 +00:22:42,795 --> 00:22:45,331 +we can scale +this design horizontally. + +529 +00:22:45,331 --> 00:22:47,233 +We can of course create +more game session actors + +530 +00:22:47,233 --> 00:22:50,369 +to serve more games +concurrently on a single server, + +531 +00:22:50,369 --> 00:22:51,870 +but thanks +to distributed actors, + +532 +00:22:51,870 --> 00:22:54,373 +we could even create +a game session on other nodes + +533 +00:22:54,373 --> 00:22:56,675 +in order to load balance +the number of concurrent games + +534 +00:22:56,675 --> 00:22:58,344 +across a cluster. + +535 +00:22:58,344 --> 00:23:01,614 +That is, if only we had +a cluster actor system. + +536 +00:23:01,614 --> 00:23:03,115 +And, in fact, we do! + +537 +00:23:03,115 --> 00:23:06,118 +We open-sourced a feature-rich +Cluster Actor system library + +538 +00:23:06,118 --> 00:23:08,387 +for you to use +in such scenarios. + +539 +00:23:08,387 --> 00:23:10,856 +It's implemented using SwiftNIO, +and specialized + +540 +00:23:10,856 --> 00:23:13,592 +for server-side +data-center clustering. + +541 +00:23:13,592 --> 00:23:16,161 +It applies advanced techniques +for failure detection, + +542 +00:23:16,161 --> 00:23:17,997 +and comes with it's own +implementation + +543 +00:23:17,997 --> 00:23:20,266 +of a cluster-wide receptionist. + +544 +00:23:20,266 --> 00:23:22,167 +We encourage you +to have a look at it, + +545 +00:23:22,167 --> 00:23:24,770 +as it is both an advanced +reference implementation + +546 +00:23:24,770 --> 00:23:25,971 +of an actor system, + +547 +00:23:25,971 --> 00:23:28,974 +and because of its powerful +server-side applications. + +548 +00:23:28,974 --> 00:23:31,610 +Let's recap what we learned +during this session. + +549 +00:23:31,610 --> 00:23:34,513 +First, we learned about +distributed actors + +550 +00:23:34,513 --> 00:23:36,849 +and how we provide additional +compiler-assisted + +551 +00:23:36,849 --> 00:23:39,318 +actor isolation +and serialization checking. + +552 +00:23:39,318 --> 00:23:42,054 +We learned how they enable +location transparency, + +553 +00:23:42,054 --> 00:23:44,490 +and how we can make use of it +to free our actors + +554 +00:23:44,490 --> 00:23:47,226 +from the necessity of being +located in the same process + +555 +00:23:47,226 --> 00:23:48,794 +as their caller. + +556 +00:23:48,794 --> 00:23:51,697 +We also saw a few actor system +implementations in action + +557 +00:23:51,697 --> 00:23:53,799 +to get you inspired about +what you could build + +558 +00:23:53,799 --> 00:23:55,701 +using distributed actors. + +559 +00:23:55,701 --> 00:23:58,671 +Distributed actors are only +as powerful as the actor systems + +560 +00:23:58,671 --> 00:24:00,139 +they are used with. + +561 +00:24:00,139 --> 00:24:01,507 +So for your reference, + +562 +00:24:01,507 --> 00:24:04,576 +here is a list of actor systems +we saw during this session. + +563 +00:24:04,576 --> 00:24:07,946 +The local testing system, which +ships by default with Swift, + +564 +00:24:07,946 --> 00:24:09,982 +and two sample actor systems: + +565 +00:24:09,982 --> 00:24:12,685 +a client/server style +WebSocket-based one + +566 +00:24:12,685 --> 00:24:15,120 +and a local +networking-based system. + +567 +00:24:15,120 --> 00:24:16,722 +These systems are rather +incomplete, + +568 +00:24:16,722 --> 00:24:18,524 +and served more +as an inspiration + +569 +00:24:18,524 --> 00:24:21,293 +for what you might build +using distributed actors. + +570 +00:24:21,293 --> 00:24:23,195 +You can view them +in the sample code app + +571 +00:24:23,195 --> 00:24:25,531 +associated with this session. + +572 +00:24:25,531 --> 00:24:27,900 +And last but not least, +an open source + +573 +00:24:27,900 --> 00:24:30,836 +fully featured server-side +clustering implementation. + +574 +00:24:30,836 --> 00:24:32,771 +Available as +a beta package now, + +575 +00:24:32,771 --> 00:24:35,774 +and it will be matured +alongside Swift 5.7. + +576 +00:24:35,774 --> 00:24:37,876 +To learn more about +distributed actors, + +577 +00:24:37,876 --> 00:24:40,012 +you can refer +to the following resources: + +578 +00:24:40,012 --> 00:24:42,581 +the sample code associated +with this session, + +579 +00:24:42,581 --> 00:24:45,050 +which includes all the steps +of our Tic Tac Fish game + +580 +00:24:45,050 --> 00:24:47,319 +so you can deep dive +into the code yourself. + +581 +00:24:47,319 --> 00:24:49,054 +The Swift evolution proposals + +582 +00:24:49,054 --> 00:24:52,191 +associated with the distributed +actors language feature, + +583 +00:24:52,191 --> 00:24:55,761 +which explain the mechanisms +powering them in great detail. + +584 +00:24:55,761 --> 00:24:57,563 +You can also reach out +on the Swift forums, + +585 +00:24:57,563 --> 00:25:00,265 +where you can find +a distributed actors category + +586 +00:25:00,265 --> 00:25:04,403 +dedicated to actor system +developers and users alike. + +587 +00:25:04,403 --> 00:25:06,205 +Thanks for listening, and I'm +looking forward to seeing + +588 +00:25:06,205 --> 00:25:08,907 +what you'll use distributed +actors for in your apps! + +589 +00:25:08,907 --> 00:25:14,913 +♪ + diff --git a/eng/2022 Session 110357 Meet Swift Regex en.srt b/eng/2022 Session 110357 Meet Swift Regex en.srt new file mode 100644 index 0000000..b7f45ad --- /dev/null +++ b/eng/2022 Session 110357 Meet Swift Regex en.srt @@ -0,0 +1,1703 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:11,245 +- Hello, I am Michael Ilseman + +3 +00:00:11,278 --> 00:00:13,947 +and I'm an engineer +on the Swift standard library team. + +4 +00:00:13,981 --> 00:00:17,084 +Join me as we meet +and get to know Regex in Swift. + +5 +00:00:17,117 --> 00:00:18,619 +There's a lot to Swift Regex, + +6 +00:00:18,652 --> 00:00:21,922 +and we'll be getting just a taste +of everything it has to offer. + +7 +00:00:21,955 --> 00:00:25,592 +Let's say we're developers collaborating +with some financial investigators + +8 +00:00:25,626 --> 00:00:30,030 +on a tool to analyze +transactions for irregularities. + +9 +00:00:30,063 --> 00:00:33,133 +Now, you'd think that for +a task this important + +10 +00:00:33,166 --> 00:00:36,136 +we'd be processing well-structured data. + +11 +00:00:36,170 --> 00:00:40,307 +But instead, we have a bunch of strings. + +12 +00:00:40,340 --> 00:00:43,744 +Here the first field +has the transaction kind, + +13 +00:00:43,777 --> 00:00:46,280 +the second the transaction date, + +14 +00:00:46,313 --> 00:00:49,283 +the third field +the individual or institution, + +15 +00:00:49,316 --> 00:00:53,120 +the fourth and final field +the amount in US dollars. + +16 +00:00:53,153 --> 00:00:59,126 +Fields are separated +by either 2-or-more spaces or a tab + +17 +00:00:59,159 --> 00:01:02,563 +for a very important technical reason + +18 +00:01:02,596 --> 00:01:06,333 +that no one involved can remember. + +19 +00:01:06,366 --> 00:01:11,338 +And, yes, +that date field is totally ambiguous. + +20 +00:01:11,371 --> 00:01:16,543 +We're just going to hope that it's +month/day/year and see what happens. + +21 +00:01:16,577 --> 00:01:20,480 +Processing these transactions +involves processing strings, + +22 +00:01:20,514 --> 00:01:22,516 +and string is a collection, + +23 +00:01:22,549 --> 00:01:26,220 +which means we get access +to generic collection algorithms. + +24 +00:01:26,253 --> 00:01:29,289 +These algorithms +basically come in two kinds, + +25 +00:01:29,323 --> 00:01:33,627 +those that operate over elements, +and those that operate over indices. + +26 +00:01:34,461 --> 00:01:36,897 +We can try to use +the element-based algorithms + +27 +00:01:36,930 --> 00:01:39,233 +by splitting out the transaction fields, + +28 +00:01:39,266 --> 00:01:43,070 +but the field separator being either tab +or 2-or-more spaces + +29 +00:01:43,103 --> 00:01:45,539 +makes this difficult. + +30 +00:01:45,572 --> 00:01:48,675 +Splitting on whitespace alone +doesn't cut it. + +31 +00:01:48,709 --> 00:01:50,978 +Another approach is to drop down + +32 +00:01:51,011 --> 00:01:53,580 +to low-level index manipulation code. + +33 +00:01:55,282 --> 00:01:56,550 +But it's hard to do right, + +34 +00:01:56,583 --> 00:01:58,485 +and even if you know what you're doing, + +35 +00:01:58,519 --> 00:02:01,188 +it still takes a lot of code. + +36 +00:02:01,221 --> 00:02:02,689 +Let's come back to split. + +37 +00:02:02,723 --> 00:02:06,059 +The reason this approach doesn't work +is because it is element-based + +38 +00:02:06,093 --> 00:02:09,730 +while the field separator +is a more complex pattern. + +39 +00:02:09,763 --> 00:02:12,766 +A solution found in a wide variety +of languages + +40 +00:02:12,799 --> 00:02:15,369 +is to write a regular expression. + +41 +00:02:15,402 --> 00:02:18,939 +Regular expressions emerged +from formal language theory + +42 +00:02:18,972 --> 00:02:21,808 +where they define a regular language. + +43 +00:02:21,842 --> 00:02:25,913 +They entered practical application for +search in editors and command-line tools + +44 +00:02:25,946 --> 00:02:28,949 +as well as lexical analysis in compilers. + +45 +00:02:28,982 --> 00:02:31,218 +These applications +take regular expressions + +46 +00:02:31,251 --> 00:02:33,287 +beyond their theoretical roots, + +47 +00:02:33,320 --> 00:02:36,089 +as they need to extract +portions of the input, + +48 +00:02:36,123 --> 00:02:39,059 +control and direct execution, + +49 +00:02:39,092 --> 00:02:40,694 +and add expressive power. + +50 +00:02:40,727 --> 00:02:42,629 +And Swift is taking them further. + +51 +00:02:42,663 --> 00:02:45,032 +We call this derivative Regex. + +52 +00:02:45,532 --> 00:02:48,836 +Regex is a struct generic +over its Output, + +53 +00:02:48,869 --> 00:02:52,105 +which is the result of applying it, +including captures. + +54 +00:02:52,139 --> 00:02:56,009 +You can create one using a literal +containing regex syntax + +55 +00:02:56,043 --> 00:02:58,245 +in between slash delimiters. + +56 +00:02:58,278 --> 00:03:02,983 +Swift's regex syntax is compatible +with Perl, Python, Ruby, Java, + +57 +00:03:03,016 --> 00:03:06,453 +NSRegularExpression, +and many, many others. + +58 +00:03:07,421 --> 00:03:10,624 +This regex matches one or more digits. + +59 +00:03:10,657 --> 00:03:14,695 +The compiler knows regex syntax, +so you'll get syntax highlighting, + +60 +00:03:14,728 --> 00:03:17,965 +compile-time errors, +and even strongly-typed captures, + +61 +00:03:17,998 --> 00:03:20,767 +which we'll be meeting later. + +62 +00:03:20,801 --> 00:03:22,669 +A regex can be created at run-time + +63 +00:03:22,703 --> 00:03:25,739 +from a string containing +the same regex syntax. + +64 +00:03:25,772 --> 00:03:29,543 +This is useful for search fields +in editors or command-line tools. + +65 +00:03:29,576 --> 00:03:34,681 +This will throw an error at run-time +if the input contains invalid syntax. + +66 +00:03:34,715 --> 00:03:38,452 +The output type +is an existential AnyRegexOutput, + +67 +00:03:38,485 --> 00:03:42,990 +because the types and number of +captures won't be known until run-time. + +68 +00:03:44,157 --> 00:03:47,461 +And the same regex can be +written using a declarative + +69 +00:03:47,494 --> 00:03:51,698 +and well-structured, +albeit more verbose, regex builder. + +70 +00:03:53,100 --> 00:03:57,771 +Let's adapt our split approach +from earlier to use a regex literal. + +71 +00:03:57,804 --> 00:04:03,277 +The first portion matches 2-or-more +occurrences of any whitespace character. + +72 +00:04:03,310 --> 00:04:07,848 +The second portion +matches a single horizontal tab. + +73 +00:04:07,881 --> 00:04:11,685 +And the pipe character +denotes a choice between alternatives, + +74 +00:04:11,718 --> 00:04:17,324 +giving us a field separator of either +2-or-more-spaces or a single tab. + +75 +00:04:17,357 --> 00:04:19,459 +Now that our fields are split, + +76 +00:04:19,493 --> 00:04:23,197 +let's make a contribution +to civilization itself + +77 +00:04:23,230 --> 00:04:29,169 +and normalize that field separator +to a single tab and be done with it. + +78 +00:04:29,203 --> 00:04:32,272 +We could call 'join' +on the result after splitting, + +79 +00:04:32,306 --> 00:04:34,641 +but there's a better algorithm +for that: + +80 +00:04:34,675 --> 00:04:39,213 +'replacing' lets us replace +all field separators with a single tab. + +81 +00:04:40,247 --> 00:04:44,484 +So we go out and evangelize +our clearly superior approach + +82 +00:04:44,518 --> 00:04:46,253 +to anyone who will listen. + +83 +00:04:46,286 --> 00:04:49,990 +Adoption is...slow but promising. + +84 +00:04:50,023 --> 00:04:52,326 +If you are familiar +with regular expressions, + +85 +00:04:52,359 --> 00:04:55,629 +you may also know +of their mixed reputation. + +86 +00:04:55,662 --> 00:05:00,367 +As the old saying goes, "I had a problem, +so I wrote a regular expression. + +87 +00:05:00,400 --> 00:05:02,903 +Now I have two problems." + +88 +00:05:02,936 --> 00:05:03,770 +But Swift regex is different. + +89 +00:05:05,372 --> 00:05:09,142 +Swift advances the art +in four key areas. + +90 +00:05:09,176 --> 00:05:12,446 +Regex syntax is concise and expressive, + +91 +00:05:12,479 --> 00:05:15,048 +but it can become terse +and difficult to read. + +92 +00:05:15,082 --> 00:05:19,019 +And newer features have to use +increasingly cryptic syntax. + +93 +00:05:20,387 --> 00:05:23,090 +Swift regexes can be structured +and organized + +94 +00:05:23,123 --> 00:05:26,760 +the way we structure and organize +source code through Regex builders. + +95 +00:05:26,793 --> 00:05:31,498 +Literals are concise, +builders give structure, + +96 +00:05:31,532 --> 00:05:35,869 +and literals can be used within builders +to find that perfect balance. + +97 +00:05:36,803 --> 00:05:40,307 +Textual representations for data +have become a lot more complicated, + +98 +00:05:40,340 --> 00:05:45,512 +and handling them correctly requires +a standards-conforming parser. + +99 +00:05:45,546 --> 00:05:49,316 +Swift regex lets you +interweave industrial-strength parsers + +100 +00:05:49,349 --> 00:05:52,085 +as individual components of a regex. + +101 +00:05:52,119 --> 00:05:54,755 +This is done +in a library-extensible fashion, + +102 +00:05:54,788 --> 00:05:57,124 +meaning any parsers can participate. + +103 +00:05:58,458 --> 00:06:01,061 +Much of the history of applied +regular expressions + +104 +00:06:01,094 --> 00:06:04,965 +took place in a world +where the entire computer system + +105 +00:06:04,998 --> 00:06:08,602 +only supported +a single language and encoding, + +106 +00:06:08,635 --> 00:06:10,838 +most notably ASCII. + +107 +00:06:10,871 --> 00:06:13,140 +But the modern world is Unicode. + +108 +00:06:13,173 --> 00:06:18,145 +Swift regex does the Unicode +without compromising expressivity. + +109 +00:06:18,178 --> 00:06:21,014 +And finally, +the power of regular expressions + +110 +00:06:21,048 --> 00:06:25,519 +can open up a broad search space +that must be exhaustively explored. + +111 +00:06:25,552 --> 00:06:28,589 +This makes their execution +difficult to reason about. + +112 +00:06:28,622 --> 00:06:30,791 +Some languages support controls, + +113 +00:06:30,824 --> 00:06:33,327 +but because +they're behind cryptic syntax, + +114 +00:06:33,360 --> 00:06:35,462 +they tend to be obscure. + +115 +00:06:35,495 --> 00:06:41,335 +Swift regex provides predictable execution +and surfaces controls prominently. + +116 +00:06:41,368 --> 00:06:44,738 +Let's go back to the financial statements +we've been working with + +117 +00:06:44,771 --> 00:06:48,709 +and fully parse each transaction +using Regex builders, + +118 +00:06:48,742 --> 00:06:52,312 +a declarative approach +to string processing in Swift. + +119 +00:06:52,346 --> 00:06:56,383 +We'll import the RegexBuilder module +to get started. + +120 +00:06:56,416 --> 00:07:01,088 +We can re-use the field separator +regex that we just defined. + +121 +00:07:01,121 --> 00:07:05,459 +The first field is simple; +it's either a CREDIT or a DEBIT. + +122 +00:07:05,492 --> 00:07:10,230 +We can use the regex literal syntax +we've already seen to write that. + +123 +00:07:10,264 --> 00:07:13,534 +After that comes a field separator, +and then the date. + +124 +00:07:13,567 --> 00:07:16,737 +Parsing dates by hand is a bad idea. + +125 +00:07:16,770 --> 00:07:20,040 +Foundation has really good parsers +for types like dates, + +126 +00:07:20,073 --> 00:07:24,511 +numbers, and URLs, and we can +use them directly in a Regex Builder. + +127 +00:07:25,812 --> 00:07:30,951 +We supply an explicit locale which is +our best guess at the author's intent. + +128 +00:07:30,984 --> 00:07:35,255 +We do this instead of implicitly +using the system's current locale. + +129 +00:07:35,289 --> 00:07:37,958 +We can always change it later, +and it's easy to do + +130 +00:07:37,991 --> 00:07:41,895 +because we made our +assumptions _explicit_ in code. + +131 +00:07:43,063 --> 00:07:44,898 +The third field can be "anything," + +132 +00:07:44,932 --> 00:07:48,402 +so it's tempting to just write +"one or more of anything." + +133 +00:07:48,435 --> 00:07:50,871 +And while +that will give us the right answer, + +134 +00:07:50,904 --> 00:07:53,373 +it does a lot of unnecessary work first, + +135 +00:07:53,407 --> 00:07:57,978 +because it starts off by matching +anything else that comes after it. + +136 +00:07:58,011 --> 00:08:02,916 +The regex will back up one character +at a time and try the rest of the pattern. + +137 +00:08:02,950 --> 00:08:08,288 +We want to tell the regex to stop when +it sees the terminating field separator. + +138 +00:08:08,322 --> 00:08:10,858 +There are a quite a few ways +that we could accomplish this. + +139 +00:08:10,891 --> 00:08:14,728 +One good way to do this +is to use NegativeLookahead + +140 +00:08:14,761 --> 00:08:20,000 +which peeks at the next part of the input +without actually consuming it. + +141 +00:08:20,033 --> 00:08:22,269 +Here we peek at the input to make sure + +142 +00:08:22,302 --> 00:08:26,540 +a field separator isn't coming up +before matching any character. + +143 +00:08:26,573 --> 00:08:30,043 +NegativeLookahead +is one of a family of tools + +144 +00:08:30,077 --> 00:08:34,481 +that let you precisely control +how a Regex matches its components. + +145 +00:08:35,816 --> 00:08:40,053 +Finally, we match the amount, +again using one of Foundation's parsers, + +146 +00:08:40,087 --> 00:08:42,122 +this time for currency. + +147 +00:08:42,155 --> 00:08:45,325 +We've been assuming that comma +is a thousands separator + +148 +00:08:45,359 --> 00:08:50,397 +while period is a decimal separator, +and we make this assumption explicit. + +149 +00:08:51,331 --> 00:08:55,903 +We've built a regex that lets us parse +a line from the transaction ledger. + +150 +00:08:55,936 --> 00:09:01,408 +We don't just want to recognize the lines. +We want to extract some of this data out. + +151 +00:09:01,441 --> 00:09:04,611 +To do this, we use captures, + +152 +00:09:04,645 --> 00:09:08,782 +which extract portions +of our input for later processing. + +153 +00:09:08,815 --> 00:09:12,452 +By convention, +the '0th' capture is the part of the input + +154 +00:09:12,486 --> 00:09:17,791 +that the entire regex matched, +and each explicit capture follows. + +155 +00:09:17,824 --> 00:09:23,697 +Our transaction kind is captured as +a Substring that is a slice of our input. + +156 +00:09:23,730 --> 00:09:27,234 +For dates, we actually capture +the strongly-typed value + +157 +00:09:27,267 --> 00:09:31,271 +that was parsed out +without needing to post-process the text. + +158 +00:09:31,305 --> 00:09:37,044 +The individual or institution is again +captured as a portion of our input, + +159 +00:09:37,077 --> 00:09:41,048 +and the decimal capture +is another strongly-typed value. + +160 +00:09:41,081 --> 00:09:44,618 +To use it, we extract date and decimal +values from the match result, + +161 +00:09:44,651 --> 00:09:47,287 +and the investigators take it from here. + +162 +00:09:47,321 --> 00:09:52,192 +It's at this point that we recommend +they dump the data into a real database + +163 +00:09:52,226 --> 00:09:55,996 +for obvious benefits +like structured queries. + +164 +00:09:56,029 --> 00:09:58,799 +They have a...different opinion. + +165 +00:09:58,832 --> 00:10:01,668 +They want to +keep everything as strings. + +166 +00:10:01,702 --> 00:10:07,207 +Which is good news for this talk because +we get to see even more of Swift Regex. + +167 +00:10:07,241 --> 00:10:11,512 +Everything's going well +until suddenly it's not. + +168 +00:10:11,545 --> 00:10:14,681 +We just learned that the date order +in the transaction text, + +169 +00:10:14,715 --> 00:10:20,354 +which we told everyone was +totally ambiguous, is in fact ambiguous. + +170 +00:10:20,387 --> 00:10:22,322 +It's not always the same, + +171 +00:10:22,356 --> 00:10:28,495 +and the leading theory is that it depends +on the currency used in the transaction. + +172 +00:10:28,529 --> 00:10:30,898 +Because of course it does. + +173 +00:10:30,931 --> 00:10:35,869 +This means that US dollars +is month/day/year + +174 +00:10:35,903 --> 00:10:40,407 +and British pounds is day/month/year. + +175 +00:10:40,440 --> 00:10:44,745 +So let's write a sed-like script +to disambiguate this. + +176 +00:10:44,778 --> 00:10:48,215 +For our regex, we're going to use +an extended delimiter. + +177 +00:10:48,248 --> 00:10:53,220 +This allows us to have slashes inside +without having to escape them. + +178 +00:10:53,253 --> 00:10:57,524 +This also gives us access +to an extended syntax mode + +179 +00:10:57,558 --> 00:10:59,660 +where whitespace is ignored, + +180 +00:10:59,693 --> 00:11:05,065 +which means we can use whitespace +for readability, just like in normal code. + +181 +00:11:05,098 --> 00:11:10,871 +We used named captures, which show up +in the Regex's output as tuple labels. + +182 +00:11:11,772 --> 00:11:15,776 +And we use a Unicode Property +to recognize currency symbols. + +183 +00:11:15,809 --> 00:11:18,312 +This makes our regex more adaptable; + +184 +00:11:18,345 --> 00:11:21,582 +we will handle the specific symbols +in application logic. + +185 +00:11:23,450 --> 00:11:27,221 +Rather than try +to cut and splice text manually, + +186 +00:11:27,254 --> 00:11:30,390 +we're going to yet again use +Foundation's date parser. + +187 +00:11:30,424 --> 00:11:33,026 +pickStrategy receives the currency symbol + +188 +00:11:33,060 --> 00:11:35,929 +and will determine +a parse strategy based on it. + +189 +00:11:35,963 --> 00:11:38,699 +All of our assumptions +are explicit in code, + +190 +00:11:38,732 --> 00:11:41,101 +which makes it easier to adapt +and evolve, + +191 +00:11:41,134 --> 00:11:43,871 +something we almost +certainly will end up needing. + +192 +00:11:46,707 --> 00:11:50,644 +Let's use our regex and helper function +with a find-and-replace algorithm + +193 +00:11:50,677 --> 00:11:55,315 +by supplying a closure which uses +the match result, including captures, + +194 +00:11:55,349 --> 00:11:58,218 +to construct the replacement string. + +195 +00:11:58,252 --> 00:12:02,856 +We pick a strategy based on the captured +currency and parse the captured date. + +196 +00:12:02,890 --> 00:12:07,361 +We can access the captures by name, +instead of only by position. + +197 +00:12:07,394 --> 00:12:12,099 +For our output, +we'll format the new date using ISO-8601, + +198 +00:12:12,132 --> 00:12:14,902 +an unambiguous industry standard. + +199 +00:12:14,935 --> 00:12:20,207 +Our tool transforms this ledger Into +an unambiguous one. + +200 +00:12:20,240 --> 00:12:23,610 +Because we're using +a real date parser and formatter, + +201 +00:12:23,644 --> 00:12:27,281 +we're far more adaptable +to changing requirements. + +202 +00:12:27,314 --> 00:12:30,851 +And using a Unicode property +to recognize currency symbols + +203 +00:12:30,884 --> 00:12:33,620 +helps us evolve that much quicker. + +204 +00:12:33,654 --> 00:12:38,825 +A regex declares an algorithm +over some model of String. + +205 +00:12:38,859 --> 00:12:43,497 +Swift's String presents multiple models +for working with Unicode. + +206 +00:12:43,530 --> 00:12:50,137 +This string, representing a love story +for the ages, contains 3 characters. + +207 +00:12:50,170 --> 00:12:52,573 +These characters are complex entities + +208 +00:12:52,606 --> 00:12:56,210 +formally called Unicode +extended grapheme clusters. + +209 +00:12:56,243 --> 00:13:02,549 +A single Character is composed +of one or more Unicode scalar values. + +210 +00:13:02,583 --> 00:13:04,918 +String provides a UnicodeScalarView + +211 +00:13:04,952 --> 00:13:08,989 +to access this lower-level +representation of its contents. + +212 +00:13:09,022 --> 00:13:14,895 +This enables advanced usage as well +as compatibility with other systems. + +213 +00:13:15,963 --> 00:13:19,132 +Our first Character, +who is our story's protagonist, + +214 +00:13:19,166 --> 00:13:22,336 +is composed of 4 Unicode scalars: + +215 +00:13:22,369 --> 00:13:27,875 +ZOMBIE, +Zero Width Joiner, FEMALE SIGN, and uh... + +216 +00:13:27,908 --> 00:13:29,710 +VARIATION SELECTOR-16, + +217 +00:13:29,743 --> 00:13:35,182 +which in this context signals +a preference to be rendered as emoji. + +218 +00:13:35,215 --> 00:13:37,284 +Of course! + +219 +00:13:37,317 --> 00:13:41,421 +These scalars produce +the single emoji we see visually. + +220 +00:13:41,455 --> 00:13:47,227 +When strings are stored in memory, +they are encoded as UTF-8 bytes. + +221 +00:13:47,261 --> 00:13:50,564 +We can view these bytes +using the UTF-8 view. + +222 +00:13:50,597 --> 00:13:56,069 +UTF-8 is a variable-width encoding, +meaning multiple bytes may be needed + +223 +00:13:56,103 --> 00:13:58,338 +for a single scalar, and as we saw, + +224 +00:13:58,372 --> 00:14:03,010 +multiple scalars may be needed +for a single character. + +225 +00:14:03,043 --> 00:14:07,581 +Our story's protagonist, +represented by 4 Unicode scalars, + +226 +00:14:07,614 --> 00:14:11,251 +is encoded using 13 UTF-8 bytes. + +227 +00:14:11,285 --> 00:14:15,255 +In addition +to being composed of multiple scalars, + +228 +00:14:15,289 --> 00:14:20,427 +the same exact character can sometimes be +represented by different sets of scalars. + +229 +00:14:20,460 --> 00:14:24,898 +This comes up a lot when handling +languages other than English. + +230 +00:14:24,932 --> 00:14:27,701 +In this example, +the 'e' with an acute accent + +231 +00:14:27,734 --> 00:14:33,340 +can be represented as either a single +scalar, precomposed ‘e’ with acute accent, + +232 +00:14:33,373 --> 00:14:38,245 +or as an ASCII 'e' followed +by a combining acute accent. + +233 +00:14:38,278 --> 00:14:43,183 +These are the same characters, +so String comparison will return true. + +234 +00:14:43,217 --> 00:14:45,819 +This is because String obeys +what is formally called + +235 +00:14:45,853 --> 00:14:48,322 +Unicode Canonical Equivalence. + +236 +00:14:49,590 --> 00:14:52,292 +From the perspective +of the UnicodeScalarView, + +237 +00:14:52,326 --> 00:14:55,429 +or the UTF-8 view, +the contents are different, + +238 +00:14:55,462 --> 00:14:59,933 +and we see this difference when we +compare within these lower-level views. + +239 +00:14:59,967 --> 00:15:05,239 +Just like String, Swift regex is +obsessively Unicode correct by default. + +240 +00:15:05,272 --> 00:15:09,009 +But it does this without +compromising expressivity. + +241 +00:15:09,042 --> 00:15:11,545 +Let's switch over a pair of strings. + +242 +00:15:11,578 --> 00:15:16,383 +For the first string, we'll match the +named Unicode Scalar SPARKLING HEART + +243 +00:15:16,416 --> 00:15:19,620 +surrounded by +any characters denoted by dot (.). + +244 +00:15:21,288 --> 00:15:24,291 +The any character class +will match any Swift character; + +245 +00:15:24,324 --> 00:15:27,027 +that is, any Unicode extended +grapheme cluster. + +246 +00:15:29,830 --> 00:15:34,801 +For the second string, characters +that are equal compare as equals... + +247 +00:15:34,835 --> 00:15:37,004 +and we can ignore case. + +248 +00:15:37,037 --> 00:15:41,475 +And now our simple love story +has become a lot more complicated. + +249 +00:15:41,508 --> 00:15:45,779 +Sometimes life, or in this case un-life, + +250 +00:15:45,812 --> 00:15:48,382 +has complexities that we need to process. + +251 +00:15:49,416 --> 00:15:54,555 +Just like String, if you do need to +process Unicode scalar values yourself, + +252 +00:15:54,588 --> 00:15:58,625 +either for compatibility or sub-grapheme +cluster precision, + +253 +00:15:58,659 --> 00:16:02,262 +you can by matching with +'unicodeScalar' semantics. + +254 +00:16:02,296 --> 00:16:04,598 +When we match at the Unicode Scalar level, + +255 +00:16:04,631 --> 00:16:07,501 +the dot matches +a single Unicode Scalar value + +256 +00:16:07,534 --> 00:16:09,870 +instead of a full Swift Character. + +257 +00:16:09,903 --> 00:16:14,508 +Which means we get to see our +friend again: VARIATION-SELECTOR 16. + +258 +00:16:14,541 --> 00:16:18,645 +This friendly little selector gets +matched by the dot, and you can't see it + +259 +00:16:18,679 --> 00:16:23,083 +because when it's all alone, +it renders as empty whitespace. + +260 +00:16:23,116 --> 00:16:25,118 +So helpful. + +261 +00:16:26,920 --> 00:16:30,524 +Now that we've worked with +precision and correctness, + +262 +00:16:30,557 --> 00:16:34,027 +let's do something a little different, +and get back to finance. + +263 +00:16:34,061 --> 00:16:36,296 +The investigators have returned, + +264 +00:16:36,330 --> 00:16:40,234 +and this time they have +an interesting request. + +265 +00:16:40,267 --> 00:16:43,770 +They modified our transaction +matching tool to sniff transactions + +266 +00:16:43,804 --> 00:16:48,909 +live off the wire instead of +processing ledgers after the fact. + +267 +00:16:48,942 --> 00:16:52,513 +Looking at their code, +they actually did a reasonably good job, + +268 +00:16:52,546 --> 00:16:56,116 +but they're facing scaling issues +and need our help. + +269 +00:16:56,149 --> 00:17:01,555 +The transactions they are processing are +very similar, but with minor differences. + +270 +00:17:01,588 --> 00:17:05,792 +Instead of a date, +they have a precise time stamp instead. + +271 +00:17:05,826 --> 00:17:09,162 +This is represented in a clear, +unambiguous, + +272 +00:17:09,196 --> 00:17:12,866 +and shockingly proprietary format. + +273 +00:17:12,900 --> 00:17:16,737 +They have a regular expression +written in a prior century + +274 +00:17:16,770 --> 00:17:19,106 +that matches this just fine. + +275 +00:17:19,139 --> 00:17:22,042 +It's fine. + +276 +00:17:22,075 --> 00:17:23,911 +Next they have a details field + +277 +00:17:23,944 --> 00:17:26,813 +which includes individuals +and identification codes. + +278 +00:17:26,847 --> 00:17:29,416 +They filter transactions +against this field + +279 +00:17:29,449 --> 00:17:33,453 +by using a run-time compiled regex +derived from input. + +280 +00:17:33,487 --> 00:17:36,990 +Because this is live, +and there are more fields later on, + +281 +00:17:37,024 --> 00:17:41,295 +they like to bail early on +any uninteresting transactions. + +282 +00:17:41,328 --> 00:17:44,898 +Then comes an amount +and other fields like checksums, + +283 +00:17:44,932 --> 00:17:47,234 +which they handle just fine on their own. + +284 +00:17:47,267 --> 00:17:54,208 +And of course, fields are still separated +by 2-or-more spaces or a tab. + +285 +00:17:55,375 --> 00:17:58,512 +Their transaction matcher +looks a lot like ours. + +286 +00:17:58,545 --> 00:18:01,114 +They have their own regex +for the timestamp, + +287 +00:18:01,148 --> 00:18:04,551 +their details regex +is compiled from input, + +288 +00:18:04,585 --> 00:18:06,887 +and they handle the rest of the fields. + +289 +00:18:06,920 --> 00:18:10,724 +They did a reasonably good job. +Everything technically works. + +290 +00:18:10,757 --> 00:18:12,793 +It just isn't scaling well. + +291 +00:18:12,826 --> 00:18:15,829 +They notice that their timestamp +and details regexes + +292 +00:18:15,863 --> 00:18:19,433 +often match much more +of the input than their fields. + +293 +00:18:19,466 --> 00:18:22,202 +Ideally, these regexes +would be constrained + +294 +00:18:22,236 --> 00:18:25,539 +to only run over a single field. + +295 +00:18:25,572 --> 00:18:29,910 +We handled a similar issue in +our project by using negative lookahead, + +296 +00:18:29,943 --> 00:18:32,079 +so let's pull that regex in. + +297 +00:18:33,714 --> 00:18:36,083 +'field' will efficiently match +any character + +298 +00:18:36,116 --> 00:18:38,619 +until it encounters a field separator, + +299 +00:18:38,652 --> 00:18:43,090 +and we'd like to use it +to contain their regexes. + +300 +00:18:43,123 --> 00:18:47,661 +We could do this as a post-processing +step, but because this is running live, + +301 +00:18:47,694 --> 00:18:52,132 +we want to bail early if these +regexes don't match their fields. + +302 +00:18:52,165 --> 00:18:55,802 +We can do this using TryCapture. + +303 +00:18:55,836 --> 00:18:59,873 +TryCapture passes the matched field +to our closure, + +304 +00:18:59,907 --> 00:19:04,978 +where we test against the investigator's +timestamp and details regexes. + +305 +00:19:05,012 --> 00:19:08,749 +If they match, +we return the field's value, + +306 +00:19:08,782 --> 00:19:12,486 +meaning that matching succeeded +and the field is captured. + +307 +00:19:12,519 --> 00:19:17,157 +Otherwise we return nil, +which signals that matching failed. + +308 +00:19:18,125 --> 00:19:21,862 +TryCapture's closure +actively participates in matching, + +309 +00:19:21,895 --> 00:19:24,598 +which is exactly what we need. + +310 +00:19:24,631 --> 00:19:27,668 +And with this, +we've solved a major scaling issue. + +311 +00:19:27,701 --> 00:19:29,203 +But there's still one more problem: + +312 +00:19:29,236 --> 00:19:32,606 +when something later on +in the transaction matcher fails, + +313 +00:19:32,639 --> 00:19:35,309 +it can take a long time to exit. + +314 +00:19:37,578 --> 00:19:41,048 +Our fieldSeparator regex +we defined at the very beginning + +315 +00:19:41,081 --> 00:19:45,986 +matches 2-or-more whitespaces or a tab, +which is what we want. + +316 +00:19:46,019 --> 00:19:48,522 +If there are 8 whitespace characters, + +317 +00:19:48,555 --> 00:19:53,160 +it will match all of them +before trying the rest of the regex. + +318 +00:19:53,193 --> 00:19:56,129 +But if the regex later fails, +it will back up + +319 +00:19:56,163 --> 00:20:00,367 +and only match 7 whitespace characters +before trying again. + +320 +00:20:00,400 --> 00:20:05,672 +And if that fails, it will match +only 6 whitespace characters, and so on. + +321 +00:20:10,544 --> 00:20:15,949 +Only after trying all alternatives +does matching fail. + +322 +00:20:15,983 --> 00:20:21,622 +This backing up in order to try +alternatives is called global backtracking + +323 +00:20:21,655 --> 00:20:24,791 +or, in formal logics, the Kleene closure. + +324 +00:20:24,825 --> 00:20:28,996 +It's what gives regular expressions +their characteristic power. + +325 +00:20:29,029 --> 00:20:32,399 +But it opens up +a broad search space to explore, + +326 +00:20:32,432 --> 00:20:36,069 +and here we want +a more linear search space. + +327 +00:20:36,103 --> 00:20:39,973 +We want to match all of the whitespace +and never give any up. + +328 +00:20:40,007 --> 00:20:42,943 +There are a couple tools +that we could use; + +329 +00:20:42,976 --> 00:20:45,679 +the more general tool +is to put fieldSeparator + +330 +00:20:45,712 --> 00:20:50,284 +in a local backtracking scope +instead of a global one. + +331 +00:20:51,318 --> 00:20:54,321 +The Local builder creates a scope where, + +332 +00:20:54,354 --> 00:20:57,891 +if the contained regex +ever successfully matches, + +333 +00:20:57,925 --> 00:21:01,528 +any untried alternatives are discarded. + +334 +00:21:02,896 --> 00:21:06,633 +Even if our transaction matcher +fails later on, + +335 +00:21:06,667 --> 00:21:10,771 +we don't go back +to try consuming fewer spaces. + +336 +00:21:10,804 --> 00:21:14,107 +Global backtracking, +the default for regex, + +337 +00:21:14,141 --> 00:21:17,544 +is great for search and fuzzy matching. + +338 +00:21:17,578 --> 00:21:22,449 +Local is useful for matching +precisely specified tokens. + +339 +00:21:22,482 --> 00:21:28,222 +The field separator, +as vexing as it may be, is precise. + +340 +00:21:29,389 --> 00:21:33,527 +Local is known elsewhere as +an atomic non-capturing group, + +341 +00:21:33,560 --> 00:21:36,864 +which can be a… frightening name. + +342 +00:21:36,897 --> 00:21:40,100 +Makes it seem like +your regex might blow up. + +343 +00:21:40,133 --> 00:21:45,038 +But it actually does the opposite-- +it contains the search space. + +344 +00:21:46,573 --> 00:21:50,677 +And with this, we've helped them +solve their scaling issues. + +345 +00:21:50,711 --> 00:21:53,680 +Today we got to meet Swift Regex, + +346 +00:21:53,714 --> 00:21:56,950 +but there's so much more +that we weren't able to cover. + +347 +00:21:56,984 --> 00:22:02,256 +Be sure to check out Swift Regex: Beyond +the Basics by my colleague Richard. + +348 +00:22:02,289 --> 00:22:05,826 +Before we leave, +I want to highlight a few points. + +349 +00:22:05,859 --> 00:22:11,131 +Regex builders give structure. +Regex literals are concise. + +350 +00:22:11,164 --> 00:22:14,268 +The choice between when to use one +over the other + +351 +00:22:14,301 --> 00:22:16,870 +will ultimately be subjective. + +352 +00:22:17,771 --> 00:22:21,608 +Make sure to use real parsers +whenever possible. + +353 +00:22:21,642 --> 00:22:26,980 +This will save you massive +amounts of time and avoid headaches. + +354 +00:22:27,014 --> 00:22:29,816 +Just by using Swift's defaults, +you're going to get + +355 +00:22:29,850 --> 00:22:33,687 +far more Unicode support +and goodness than anywhere else. + +356 +00:22:33,720 --> 00:22:37,558 +Look for ways to use things +like character properties effectively, + +357 +00:22:37,591 --> 00:22:40,928 +such as when we +matched the currency symbols. + +358 +00:22:40,961 --> 00:22:44,731 +And finally, simplify +your search and processing algorithms + +359 +00:22:44,765 --> 00:22:49,703 +by using controls such as lookahead +and local backtracking scopes. + +360 +00:22:49,736 --> 00:22:51,338 +Thank you for watching. + diff --git a/eng/2022 Session 110358 Swift Regex - Beyond the basics en.srt b/eng/2022 Session 110358 Swift Regex - Beyond the basics en.srt new file mode 100644 index 0000000..4469e3c --- /dev/null +++ b/eng/2022 Session 110358 Swift Regex - Beyond the basics en.srt @@ -0,0 +1,1589 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:10,177 --> 00:00:11,612 +Hi, I'm Richard, + +3 +00:00:11,645 --> 00:00:14,281 +I'm an engineer +on the Swift Standard Library team. + +4 +00:00:14,314 --> 00:00:18,585 +Today, let's embark on a journey +beyond the basics of Swift Regex. + +5 +00:00:18,619 --> 00:00:23,390 +Swift 5.7 is gaining powerful +new capabilities for string processing. + +6 +00:00:23,423 --> 00:00:25,492 +They start with the 'Regex' type, + +7 +00:00:25,526 --> 00:00:28,795 +a new type in the Swift Standard Library. + +8 +00:00:28,829 --> 00:00:31,498 +A language built-in Regex literal syntax, + +9 +00:00:31,532 --> 00:00:36,270 +which makes this powerful and +familiar concept even more first-class. + +10 +00:00:36,303 --> 00:00:40,874 +And finally, a result builder API +called RegexBuilder. + +11 +00:00:40,908 --> 00:00:44,645 +It is a domain-specific language, +or DSL, + +12 +00:00:44,678 --> 00:00:47,981 +that takes advantage +of the syntactic simplicity + +13 +00:00:48,015 --> 00:00:50,484 +and composability of result builders, + +14 +00:00:50,517 --> 00:00:53,954 +and pushes the readability +of Regex to a whole new level. + +15 +00:00:54,922 --> 00:00:59,560 +For background as to why Swift Regex +makes it easier to process strings, + +16 +00:00:59,593 --> 00:01:04,031 +check out the Meet Swift Regex session +by my colleague Michael. + +17 +00:01:04,064 --> 00:01:07,868 +Let's look at a very simple example +of Swift Regex. + +18 +00:01:07,901 --> 00:01:10,337 +Let's say I have a string of data, + +19 +00:01:10,370 --> 00:01:14,908 +and like to match and extract +the user ID from this string. + +20 +00:01:14,942 --> 00:01:18,045 +I can create a regular expression +from text + +21 +00:01:18,078 --> 00:01:21,882 +like I normally do with +'NSRegularExpression'. + +22 +00:01:21,915 --> 00:01:25,018 +It matches "user_id" colon + +23 +00:01:25,052 --> 00:01:27,921 +followed by zero or more whitespaces + +24 +00:01:27,955 --> 00:01:30,791 +followed by one or more digits + +25 +00:01:30,824 --> 00:01:32,192 +What's different this time + +26 +00:01:32,226 --> 00:01:35,495 +is that we are creating +a value of type Regex. + +27 +00:01:35,529 --> 00:01:39,399 +This is a new type +in the Swift Standard Library. + +28 +00:01:39,433 --> 00:01:42,603 +I can then use string's +'firstMatch' algorithm + +29 +00:01:42,636 --> 00:01:46,740 +to find the first occurrence +of the pattern defined by this Regex, + +30 +00:01:46,773 --> 00:01:50,611 +and print the whole match, +just like that. + +31 +00:01:50,644 --> 00:01:54,047 +Because my Regex string +is known at compile-time, + +32 +00:01:54,081 --> 00:01:58,785 +I can switch to using a Regex literal +so that the compiler would check + +33 +00:01:58,819 --> 00:02:03,524 +for syntax errors +and Xcode can show syntax highlighting. + +34 +00:02:03,557 --> 00:02:06,426 +But for ultimate readability +and customizations, + +35 +00:02:06,460 --> 00:02:09,863 +I can use the Regex builder DSL. + +36 +00:02:09,897 --> 00:02:13,166 +With Regex builder, +reading the content of a Regex + +37 +00:02:13,200 --> 00:02:17,204 +is as easy as reading native Swift API. + +38 +00:02:17,237 --> 00:02:20,174 +In this session I will show you +how Regex works + +39 +00:02:20,207 --> 00:02:23,777 +and how you can apply Regex +in your workflow. + +40 +00:02:23,810 --> 00:02:29,383 +A Regex is a program that is to be +executed by its underlying Regex engine. + +41 +00:02:29,416 --> 00:02:33,487 +When executing a Regex, +the Regex engine takes an input string, + +42 +00:02:33,520 --> 00:02:38,025 +and performs matching +from the start to the end of the string. + +43 +00:02:38,058 --> 00:02:40,961 +Let's take a look at a very simple Regex. + +44 +00:02:40,994 --> 00:02:45,766 +This Regex matches a string +that starts with one or more of letter "a" + +45 +00:02:45,799 --> 00:02:48,468 +followed by one or more digits. + +46 +00:02:48,502 --> 00:02:51,839 +I use one of the matching algorithms, +'wholeMatch', + +47 +00:02:51,872 --> 00:02:56,076 +to match input "aaa12". + +48 +00:02:56,109 --> 00:03:00,347 +The Regex engine will start +from the first character of the input. + +49 +00:03:00,380 --> 00:03:04,551 +First, it matches +one or more of character a. + +50 +00:03:04,585 --> 00:03:07,688 +At this point, +it reaches character "1" + +51 +00:03:07,721 --> 00:03:11,558 +and tries to match this character +against character "a". + +52 +00:03:11,592 --> 00:03:13,994 +But it doesn't match. + +53 +00:03:14,027 --> 00:03:17,831 +So the Regex engine moves +to the next pattern in the Regex, + +54 +00:03:17,865 --> 00:03:21,235 +to match one or more digits. + +55 +00:03:21,268 --> 00:03:25,672 +As we reach the end of the string, +matching succeeds. + +56 +00:03:25,706 --> 00:03:27,407 +In the rest of this session, + +57 +00:03:27,441 --> 00:03:31,712 +I will explain a bit more +about this execution model. + +58 +00:03:31,745 --> 00:03:35,249 +With Regex built on its underlying +Regex engine, + +59 +00:03:35,282 --> 00:03:36,917 +the Regex builder DSL + +60 +00:03:36,950 --> 00:03:42,256 +and Regex-powered algorithms expand +the power and expressivity of Regex. + +61 +00:03:43,457 --> 00:03:46,693 +Regex-powered algorithms +are collection-based APIs + +62 +00:03:46,727 --> 00:03:51,031 +that provide some of the most +common operations such as first match, + +63 +00:03:51,064 --> 00:03:55,068 +which finds the first occurrence +of a Regex in a string, + +64 +00:03:55,102 --> 00:04:00,207 +'wholeMatch', which matches +the entire string against a Regex, + +65 +00:04:00,240 --> 00:04:05,579 +'prefixMatch', which matches the prefix +of a string against a Regex. + +66 +00:04:05,612 --> 00:04:09,983 +Besides matching, +the Swift standard library also added APIs + +67 +00:04:10,017 --> 00:04:14,288 +for Regex-based predication, replacement, + +68 +00:04:14,321 --> 00:04:17,624 +trimming, and splitting. + +69 +00:04:17,658 --> 00:04:22,696 +Also, Regex can now be used +in Swift's pattern matching syntax + +70 +00:04:22,729 --> 00:04:24,765 +in control flow statements, + +71 +00:04:24,798 --> 00:04:28,468 +making it easier than ever +to switch on strings. + +72 +00:04:28,502 --> 00:04:32,739 +Finally, on top of Regex builder +and Regex-powered algorithms, + +73 +00:04:32,773 --> 00:04:36,844 +this year, Foundation introduced +its own Regex support + +74 +00:04:36,877 --> 00:04:40,814 +that works seamlessly with Regex builder. + +75 +00:04:40,848 --> 00:04:44,952 +The Regex support in Foundation is +none other than the formatters + +76 +00:04:44,985 --> 00:04:48,288 +and parsers +that you are probably already using, + +77 +00:04:48,322 --> 00:04:51,425 +such as those for Date and Number. + +78 +00:04:51,458 --> 00:04:53,894 +If you want to learn +more about these APIs, + +79 +00:04:53,927 --> 00:04:59,099 +watch the What's new in Foundation +session from WWDC21. + +80 +00:04:59,132 --> 00:05:05,472 +This year, Foundation is adding support +for formatting and parsing URLs as well. + +81 +00:05:05,506 --> 00:05:07,741 +With Regex support in Foundation, + +82 +00:05:07,774 --> 00:05:12,212 +you can embed the Foundation parsers +directly in Regex builder. + +83 +00:05:12,246 --> 00:05:16,116 +For example, +to parse a bank statement like this, + +84 +00:05:16,149 --> 00:05:21,455 +I can use a Foundation-provided +date parser with a custom format + +85 +00:05:21,488 --> 00:05:26,059 +and a currency parser +with a domain-specific parse strategy. + +86 +00:05:26,093 --> 00:05:30,163 +This is a really big deal +because you can create Regexes + +87 +00:05:30,197 --> 00:05:34,568 +out of existing battle-tested parsers +that take care of corner cases + +88 +00:05:34,601 --> 00:05:36,503 +and support localization, + +89 +00:05:36,537 --> 00:05:41,808 +and compose them with the expressivity +of the Regex builder DSL. + +90 +00:05:41,842 --> 00:05:44,778 +To show you how you can apply +Swift Regex to your workflow, + +91 +00:05:44,811 --> 00:05:47,314 +let's work out an example together. + +92 +00:05:47,347 --> 00:05:50,017 +I have been writing a script +to parse the logs + +93 +00:05:50,050 --> 00:05:53,453 +from running XCTest-based unit tests. + +94 +00:05:53,487 --> 00:05:58,559 +A test log starts and ends +with the status of a test suite. + +95 +00:05:58,592 --> 00:06:05,299 +Then XCTest runs every test case +and reports the status of the test case. + +96 +00:06:05,332 --> 00:06:09,403 +Today let's parse the first +and the last lines of the log. + +97 +00:06:09,436 --> 00:06:12,372 +It's information about a test suite. + +98 +00:06:12,406 --> 00:06:16,076 +First, I import RegexBuilder. + +99 +00:06:16,109 --> 00:06:19,746 +RegexBuilder is a new module +in the Swift Standard Library + +100 +00:06:19,780 --> 00:06:23,450 +that provides the RegexBuilder DSL. + +101 +00:06:23,483 --> 00:06:27,221 +Regex can be initialized +with a trailing closure + +102 +00:06:27,254 --> 00:06:30,490 +that represents the body of the Regex. + +103 +00:06:30,524 --> 00:06:33,460 +Let's look at an example log message. + +104 +00:06:33,493 --> 00:06:38,131 +There are three variable substrings +that we care about in this log; + +105 +00:06:38,165 --> 00:06:41,001 +the test suite's name, +the status, + +106 +00:06:41,034 --> 00:06:44,037 +whether it started, passed, or failed, + +107 +00:06:44,071 --> 00:06:46,573 +and the timestamp. + +108 +00:06:46,607 --> 00:06:49,576 +I can parse +other parts of this line verbatim, + +109 +00:06:49,610 --> 00:06:54,715 +while coming up with a pattern +to parse the three variable substrings. + +110 +00:06:54,748 --> 00:06:58,519 +The log message starts +with the word "test suite", + +111 +00:06:58,552 --> 00:07:02,589 +followed by a space and a single quote. + +112 +00:07:02,623 --> 00:07:05,225 +Then we parse the test suite's name. + +113 +00:07:05,259 --> 00:07:07,628 +The name is an identifier, + +114 +00:07:07,661 --> 00:07:12,199 +which can contain lowercase or uppercase +letters or digits, + +115 +00:07:12,232 --> 00:07:15,035 +but the first character +can never be a digit. + +116 +00:07:16,270 --> 00:07:21,942 +So we create a custom character class +to match a letter as the first character. + +117 +00:07:21,975 --> 00:07:24,444 +Then I match zero or more characters + +118 +00:07:24,478 --> 00:07:29,349 +that are either a letter +or a digit from zero to nine. + +119 +00:07:29,383 --> 00:07:32,019 +This is very clear and readable, + +120 +00:07:32,052 --> 00:07:34,221 +but it's a little cumbersome. + +121 +00:07:34,254 --> 00:07:38,825 +Many of you may be familiar +with the textual Regex syntax. + +122 +00:07:38,859 --> 00:07:43,797 +In RegexBuilder, I can actually embed +a concise Regex literal + +123 +00:07:43,830 --> 00:07:46,200 +directly in the body. + +124 +00:07:46,233 --> 00:07:50,137 +A Regex literal +starts and ends with a slash. + +125 +00:07:50,170 --> 00:07:53,841 +Swift infers the correct +strong type for it. + +126 +00:07:53,874 --> 00:07:59,313 +This Regex, for example, +matches the substring, "Hello, WWDC!". + +127 +00:07:59,346 --> 00:08:02,549 +So its output type is substring. + +128 +00:08:02,583 --> 00:08:06,053 +But what's really cool about +a first-class Regex literal + +129 +00:08:06,086 --> 00:08:09,389 +is strongly typed capturing groups. + +130 +00:08:09,423 --> 00:08:15,696 +For example, I can write a capturing group +to capture two digits as the year. + +131 +00:08:15,729 --> 00:08:19,533 +And give a name +to this capturing group, "year". + +132 +00:08:19,566 --> 00:08:24,738 +When I do this, another substring +will appear in the output type. + +133 +00:08:24,771 --> 00:08:28,475 +Later in this talk, I will show you +how you can use captures + +134 +00:08:28,509 --> 00:08:30,944 +to extract information from a string. + +135 +00:08:30,978 --> 00:08:33,514 +Besides standard Regex literals, + +136 +00:08:33,547 --> 00:08:36,884 +Swift also supports +extended Regex literals, + +137 +00:08:36,917 --> 00:08:41,488 +starting with pound slash +and ending with slash pound. + +138 +00:08:41,522 --> 00:08:46,093 +The extended literal allows +non-semantic whitespaces. + +139 +00:08:46,126 --> 00:08:51,298 +In this mode you can split your patterns +into multiple lines. + +140 +00:08:51,331 --> 00:08:54,568 +With a Regex literal embedded +in my RegexBuilder, + +141 +00:08:54,601 --> 00:08:57,804 +it's clean and yet familiar. + +142 +00:08:57,838 --> 00:08:59,907 +After I parse the test name, + +143 +00:08:59,940 --> 00:09:03,443 +I parse a single quote and a whitespace. + +144 +00:09:03,477 --> 00:09:05,913 +Now I reach the test status. + +145 +00:09:05,946 --> 00:09:11,919 +There are multiple types of test status: +started, failed, and passed. + +146 +00:09:11,952 --> 00:09:16,557 +To match one of these options, +we use 'ChoiceOf'. + +147 +00:09:16,590 --> 00:09:19,593 +'ChoiceOf' matches one of +multiple subpatterns + +148 +00:09:19,626 --> 00:09:22,796 +and it's exactly what we need. + +149 +00:09:22,829 --> 00:09:26,834 +Next we parse what comes +immediately after the status, + +150 +00:09:26,867 --> 00:09:32,172 +a space followed by +"at" followed by a space. + +151 +00:09:32,206 --> 00:09:34,942 +The rest of the string is a timestamp. + +152 +00:09:34,975 --> 00:09:39,112 +We can match this +as one or more of any character. + +153 +00:09:39,146 --> 00:09:41,648 +But as I look at some more examples, + +154 +00:09:41,682 --> 00:09:45,419 +a log message sometimes ends +with a period. + +155 +00:09:45,452 --> 00:09:49,523 +We still want to use 'Optionally' +to match the period when it exists. + +156 +00:09:51,158 --> 00:09:53,493 +To match an input against a Regex, + +157 +00:09:53,527 --> 00:09:56,697 +use one of the provided +matching algorithms. + +158 +00:09:56,730 --> 00:10:02,369 +Let's use 'wholeMatch', which matches +the entire string against a Regex. + +159 +00:10:02,402 --> 00:10:05,772 +With 'wholeMatch', +I match each of these log messages, + +160 +00:10:05,806 --> 00:10:08,675 +and print the matched content. + +161 +00:10:08,709 --> 00:10:10,077 +It matched! + +162 +00:10:10,110 --> 00:10:12,980 +But we don't just want to know +whether it matches the strings. + +163 +00:10:13,013 --> 00:10:16,183 +We also want to extract information +that we care about, + +164 +00:10:16,216 --> 00:10:17,651 +such as the test name, + +165 +00:10:17,684 --> 00:10:20,988 +the status, and the timestamp. + +166 +00:10:21,021 --> 00:10:24,691 +So let's go ahead and do this +with one of the coolest features of Regex, + +167 +00:10:24,725 --> 00:10:26,093 +Captures! + +168 +00:10:26,126 --> 00:10:30,397 +A Capture saves a portion of the input +during matching. + +169 +00:10:30,430 --> 00:10:33,667 +It is available as "Capture" +in RegexBuilder + +170 +00:10:33,700 --> 00:10:38,539 +and as a pair of parentheses +in Regex syntax. + +171 +00:10:38,572 --> 00:10:44,044 +Capturing appends the matched substring +to the output tuple type. + +172 +00:10:44,077 --> 00:10:47,548 +An output tuple type starts with +the whole substring + +173 +00:10:47,581 --> 00:10:52,819 +that matched the entire Regex, +followed by the first capture, + +174 +00:10:52,853 --> 00:10:55,956 +the second capture, and so on. + +175 +00:10:55,989 --> 00:10:59,426 +The matching algorithm +returns a Regex Match, + +176 +00:10:59,459 --> 00:11:03,263 +from which you can obtain +the output tuple. + +177 +00:11:03,297 --> 00:11:06,600 +The whole match, the first capture, + +178 +00:11:06,633 --> 00:11:08,435 +and the second capture. + +179 +00:11:09,870 --> 00:11:13,974 +Let me use captures +in my test suite log Regex. + +180 +00:11:14,007 --> 00:11:18,011 +I capture the test suite's name, +the status, + +181 +00:11:18,045 --> 00:11:20,981 +and the timestamp. + +182 +00:11:21,014 --> 00:11:23,917 +Let's again run this Regex +on some inputs, + +183 +00:11:23,951 --> 00:11:27,321 +and print the three things +that we captured. + +184 +00:11:27,354 --> 00:11:30,557 +That looks like a successful match! + +185 +00:11:30,591 --> 00:11:34,862 +It printed the name, +the status, and the timestamp. + +186 +00:11:36,096 --> 00:11:40,667 +But as I look closely, +something in the date is a little off. + +187 +00:11:40,701 --> 00:11:45,472 +It included the period in the input +as part of the capture. + +188 +00:11:45,506 --> 00:11:49,843 +So let me go back and check +the Regex for errors. + +189 +00:11:49,877 --> 00:11:53,647 +I want to focus on the timestamp Regex +and see what's wrong with it. + +190 +00:11:53,680 --> 00:11:57,317 +Then I realize, the pattern +"one or more of any character" + +191 +00:11:57,351 --> 00:12:01,188 +consumes everything from +the first digit of the timestamp, + +192 +00:12:01,221 --> 00:12:04,191 +all the way to the end of the line. + +193 +00:12:04,224 --> 00:12:08,095 +So the "Optionally period" pattern +below it never matched. + +194 +00:12:09,796 --> 00:12:14,268 +I can fix this by making this +OneOrMore reluctant. + +195 +00:12:14,301 --> 00:12:18,172 +"Reluctant" is a case +of repetition behaviors. + +196 +00:12:18,205 --> 00:12:22,442 +One or more, zero or more, +optionally, and repeat + +197 +00:12:22,476 --> 00:12:25,846 +are what Swift Regex calls repetitions. + +198 +00:12:25,879 --> 00:12:28,982 +A repetition is eager by default. + +199 +00:12:29,016 --> 00:12:32,753 +It matches as many occurrences +as possible. + +200 +00:12:32,786 --> 00:12:35,689 +Let me use the example from earlier. + +201 +00:12:35,722 --> 00:12:40,661 +When the Regex engine tries to match +OneOrMore of any character eagerly, + +202 +00:12:40,694 --> 00:12:43,397 +it starts with the first character, + +203 +00:12:43,430 --> 00:12:48,702 +and it accepts any character along the way +till the end of the input. + +204 +00:12:48,735 --> 00:12:53,607 +Then the Regex engine moves on +to match Optionally period. + +205 +00:12:53,640 --> 00:12:59,413 +There's no more period to match, +but it's optional anyway, so it succeeds. + +206 +00:12:59,446 --> 00:13:02,015 +Because we're running +the 'wholeMatch' algorithm, + +207 +00:13:02,049 --> 00:13:05,886 +and both the input +and the Regex pattern reach the end, + +208 +00:13:05,919 --> 00:13:08,922 +matching succeeds. + +209 +00:13:08,956 --> 00:13:13,160 +Although matching succeeded, +the period had already been captured + +210 +00:13:13,193 --> 00:13:16,163 +unexpectedly as part of the OneOrMore. + +211 +00:13:18,632 --> 00:13:22,069 +When we change +the repetition behavior to reluctant, + +212 +00:13:22,102 --> 00:13:26,640 +the Regex engine matches +the repetition a little differently. + +213 +00:13:26,673 --> 00:13:30,711 +It matches as few characters as possible. + +214 +00:13:30,744 --> 00:13:35,048 +So when the Regex engine matches +the input string this time, + +215 +00:13:35,082 --> 00:13:38,652 +It carefully marches forward +by always trying to match + +216 +00:13:38,685 --> 00:13:40,721 +the rest of the Regex first, + +217 +00:13:40,754 --> 00:13:44,024 +before consuming a repetition occurrence. + +218 +00:13:44,057 --> 00:13:46,627 +When the rest of the Regex doesn't match, + +219 +00:13:46,660 --> 00:13:49,663 +the engine backtracks to the repetition + +220 +00:13:49,696 --> 00:13:53,033 +and consumes an additional occurrence. + +221 +00:13:53,066 --> 00:13:57,771 +Let's fast forward to the last character, +the period. + +222 +00:13:57,804 --> 00:13:59,673 +Unlike eager behavior, + +223 +00:13:59,706 --> 00:14:02,476 +the Regex engine did not consume +the period initially + +224 +00:14:02,509 --> 00:14:04,344 +as part of the OneOrMore, + +225 +00:14:04,378 --> 00:14:08,549 +but tries to match +the "Optionally period" pattern instead. + +226 +00:14:08,582 --> 00:14:13,320 +This matches, and the Regex engine +reaches the end of the pattern. + +227 +00:14:13,353 --> 00:14:15,589 +So matching succeeds, + +228 +00:14:15,622 --> 00:14:19,593 +and it produces the correct capture +without a trailing period in it. + +229 +00:14:21,028 --> 00:14:23,797 +Because eager is the default behavior, + +230 +00:14:23,830 --> 00:14:26,800 +as you create your Regex +using a repetition, + +231 +00:14:26,834 --> 00:14:30,671 +you should think about its implications +on your intended match. + +232 +00:14:30,704 --> 00:14:33,807 +You can specify the behavior +at a per-repetition level, + +233 +00:14:33,841 --> 00:14:36,977 +by passing an extra argument, + +234 +00:14:37,010 --> 00:14:40,547 +or, you can use +the 'repetitionBehavior' modifier + +235 +00:14:40,581 --> 00:14:45,919 +to override it for all repetitions +that did not specify a behavior. + +236 +00:14:45,953 --> 00:14:50,424 +As we've modified the repetition behavior +for the timestamp to be reluctant, + +237 +00:14:50,457 --> 00:14:53,760 +Matching now extracts the right timestamp + +238 +00:14:53,794 --> 00:14:55,863 +without including the period. + +239 +00:14:58,932 --> 00:15:01,568 +Let's come back to the Regex. + +240 +00:15:01,602 --> 00:15:05,873 +As I use Capture to extract +the test status from the input, + +241 +00:15:05,906 --> 00:15:08,242 +its type is Substring. + +242 +00:15:08,275 --> 00:15:11,111 +But it would be much better +if I can convert the substring + +243 +00:15:11,144 --> 00:15:14,014 +into something more programming friendly, + +244 +00:15:14,047 --> 00:15:16,283 +like a custom data structure. + +245 +00:15:16,316 --> 00:15:19,853 +To do this, +I can use a transforming capture. + +246 +00:15:19,887 --> 00:15:25,192 +A transforming capture is a Capture +with a transform closure. + +247 +00:15:25,225 --> 00:15:29,229 +Upon matching, the Regex engine calls +the transform closure + +248 +00:15:29,263 --> 00:15:31,465 +on the matched substring, + +249 +00:15:31,498 --> 00:15:36,436 +which produces a result +of the desired type. + +250 +00:15:36,470 --> 00:15:41,942 +The corresponding Regex output type +becomes the closure's return type. + +251 +00:15:41,975 --> 00:15:46,713 +Here, by transforming the capture +with Int's initializer from String, + +252 +00:15:46,747 --> 00:15:50,584 +I get an optional Int +in the output tuple type. + +253 +00:15:50,617 --> 00:15:55,122 +To obtain a non-optional output, +TryCapture can help. + +254 +00:15:55,155 --> 00:15:59,660 +TryCapture is a variant of Capture +which accepts a transform + +255 +00:15:59,693 --> 00:16:02,062 +that returns an optional + +256 +00:16:02,095 --> 00:16:05,499 +and removes the optionality +in the output type. + +257 +00:16:05,532 --> 00:16:10,437 +Returning nil during matching will cause +the Regex engine to backtrack + +258 +00:16:10,470 --> 00:16:13,240 +and try an alternative path. + +259 +00:16:13,273 --> 00:16:17,477 +TryCapture is most useful +when you transform a capture + +260 +00:16:17,511 --> 00:16:20,180 +with a failable initializer. + +261 +00:16:20,214 --> 00:16:25,919 +A natural fit for storing the captured +test status, would be an enumeration. + +262 +00:16:25,953 --> 00:16:28,255 +So let's define one. + +263 +00:16:28,288 --> 00:16:31,725 +I defined a TestStatus enum +with three cases: + +264 +00:16:31,758 --> 00:16:34,428 +started, passed, and failed. + +265 +00:16:34,461 --> 00:16:39,032 +The raw string values makes +this enum initializable from a string. + +266 +00:16:40,434 --> 00:16:44,938 +In the Regex, +I switch to 'TryCapture' with a transform. + +267 +00:16:44,972 --> 00:16:48,942 +In the transform closure, +I call the TestStatus initializer + +268 +00:16:48,976 --> 00:16:53,780 +to convert the matched substring +to a TestStatus value. + +269 +00:16:53,814 --> 00:16:57,417 +Now the corresponding output type +is TestStatus. + +270 +00:16:57,451 --> 00:16:59,686 +Using a custom data structure like this + +271 +00:16:59,720 --> 00:17:03,156 +makes the Regex match output type safe. + +272 +00:17:03,190 --> 00:17:04,658 +Back to the Regex. + +273 +00:17:04,691 --> 00:17:08,061 +There is one additional improvement +I'd like to make. + +274 +00:17:08,095 --> 00:17:12,633 +Currently, I match the timestamp +using a wildcard pattern. + +275 +00:17:12,666 --> 00:17:15,202 +It's going to produce a substring. + +276 +00:17:15,235 --> 00:17:19,106 +This means that if my app wants +to understand the timestamp, + +277 +00:17:19,139 --> 00:17:24,444 +it'd have to parse the substring again +into another data structure. + +278 +00:17:24,478 --> 00:17:27,447 +Earlier in the session, +I mentioned that Foundation + +279 +00:17:27,481 --> 00:17:29,750 +now supports Swift Regex, + +280 +00:17:29,783 --> 00:17:33,820 +providing industry-strength parsers +as Regexes. + +281 +00:17:33,854 --> 00:17:36,790 +So instead of parsing the date +as a substring, + +282 +00:17:36,823 --> 00:17:41,395 +I can switch to Foundation's ISO 8601 +date parser + +283 +00:17:41,428 --> 00:17:45,098 +to parse the timestamp as a date. + +284 +00:17:45,132 --> 00:17:49,036 +Now the inferred type shows +that this Regex outputs a Date. + +285 +00:17:50,437 --> 00:17:52,773 +As I run 'wholeMatch' on the inputs, + +286 +00:17:52,806 --> 00:17:58,345 +I can see that the date string was parsed +into a Foundation Date value. + +287 +00:17:58,378 --> 00:18:01,615 +Having access to +battle-tested parsers as a Regex, + +288 +00:18:01,648 --> 00:18:03,450 +like the Foundation date parser, + +289 +00:18:03,483 --> 00:18:07,688 +is incredibly handy +in day-to-day string processing tasks. + +290 +00:18:07,721 --> 00:18:10,691 +Next, I will show you an advanced feature, + +291 +00:18:10,724 --> 00:18:16,296 +re-using a pre-existing parser +defined elsewhere in a Swift Regex. + +292 +00:18:16,330 --> 00:18:20,734 +Let's look at an example where we want +to parse the duration of a test case. + +293 +00:18:20,767 --> 00:18:26,039 +Duration is a floating point number, +such as, 0.001. + +294 +00:18:26,073 --> 00:18:28,342 +The best way to do this is, of course, + +295 +00:18:28,375 --> 00:18:31,745 +using the Foundation-provided +floating point parser + +296 +00:18:31,778 --> 00:18:34,481 +with full support for localization. + +297 +00:18:34,515 --> 00:18:37,384 +But today, I want to show you +what's under the hood + +298 +00:18:37,417 --> 00:18:40,454 +and how you can hook +into the Regex engine yourself + +299 +00:18:40,487 --> 00:18:42,856 +to leverage an existing parser + +300 +00:18:42,890 --> 00:18:46,627 +to parse the duration +floating-point number. + +301 +00:18:46,660 --> 00:18:51,532 +'strtod' is a function +from the C standard library. + +302 +00:18:51,565 --> 00:18:55,536 +It takes a string pointer, +parses the underlying string, + +303 +00:18:55,569 --> 00:19:00,007 +and assigns the end position +of the match to the end pointer. + +304 +00:19:00,040 --> 00:19:03,410 +Let's parse the duration, the C way. + +305 +00:19:03,443 --> 00:19:07,080 +To do this, +I can define a parser type on my own, + +306 +00:19:07,114 --> 00:19:11,885 +and make it conform to the +CustomConsumingRegexComponent protocol. + +307 +00:19:12,886 --> 00:19:16,723 +I define a structure named CDoubleParser. + +308 +00:19:16,757 --> 00:19:19,660 +Its 'RegexOutput' is Double, + +309 +00:19:19,693 --> 00:19:22,696 +because we are parsing a Double number. + +310 +00:19:22,729 --> 00:19:25,999 +In the "consuming" method, +we make a call to the double parser + +311 +00:19:26,033 --> 00:19:28,368 +from the C standard library, + +312 +00:19:28,402 --> 00:19:30,504 +passing the string pointers to it, + +313 +00:19:30,537 --> 00:19:33,507 +and getting a number back. + +314 +00:19:33,540 --> 00:19:37,211 +In the method body, +I use the withCString method + +315 +00:19:37,244 --> 00:19:41,048 +to obtain the start address. + +316 +00:19:41,081 --> 00:19:43,951 +Then I call the 'strtod' C function, + +317 +00:19:43,984 --> 00:19:45,485 +passing the start address + +318 +00:19:45,519 --> 00:19:49,523 +and a pointer +to receive the result end address. + +319 +00:19:49,556 --> 00:19:51,658 +I then check for errors. + +320 +00:19:51,692 --> 00:19:53,460 +When parsing succeeds, + +321 +00:19:53,493 --> 00:19:57,364 +the end address is greater +than the start address. + +322 +00:19:57,397 --> 00:20:01,702 +Otherwise, it is a parse failure, +so I return nil. + +323 +00:20:01,735 --> 00:20:07,274 +I compute the upper bound of the match +from the pointer produced by the C API. + +324 +00:20:07,307 --> 00:20:12,646 +And finally, I return the upper bound +of the match, and the number output. + +325 +00:20:12,679 --> 00:20:19,353 +I can come back to the Regex and use +my 'CDoubleParser' directly in the Regex. + +326 +00:20:19,386 --> 00:20:23,090 +The output type is inferred to be Double. + +327 +00:20:23,123 --> 00:20:26,493 +When I call 'wholeMatch' +and print the parsed number, + +328 +00:20:26,527 --> 00:20:30,597 +it outputs 0.001, like I expected. + +329 +00:20:30,631 --> 00:20:33,300 +In summary, +today we talked about some common + +330 +00:20:33,333 --> 00:20:35,469 +and advanced use of Swift Regex, + +331 +00:20:35,502 --> 00:20:38,438 +a new feature in Swift 5.7 +that enables you + +332 +00:20:38,472 --> 00:20:41,975 +to integrate the power +of string processing in your apps. + +333 +00:20:42,009 --> 00:20:46,313 +A good practice when using Swift Regex +is to try to strike a good balance + +334 +00:20:46,346 --> 00:20:48,782 +between concision and readability, + +335 +00:20:48,815 --> 00:20:53,487 +especially when you mix the RegexBuilder +DSL and Regex literals. + +336 +00:20:53,520 --> 00:20:57,291 +When you encounter common patterns +such as date and URL, + +337 +00:20:57,324 --> 00:21:01,728 +always prefer the industry-strength +parsers provided by Foundation, + +338 +00:21:01,762 --> 00:21:05,866 +as parsing these patterns +with custom code can be prone to errors. + +339 +00:21:07,801 --> 00:21:09,937 +For more information about Swift Regex, + +340 +00:21:09,970 --> 00:21:13,173 +check out the series +of declarative string processing proposals + +341 +00:21:13,207 --> 00:21:14,608 +on Swift Evolution. + +342 +00:21:14,641 --> 00:21:18,779 +I hope you'll enjoy +processing strings with Swift. + +343 +00:21:18,812 --> 00:21:22,115 +Thank you, and have a great WWDC. + diff --git a/eng/2022 Session 110359 Meet Swift Package plugins en.srt b/eng/2022 Session 110359 Meet Swift Package plugins en.srt new file mode 100644 index 0000000..051ee01 --- /dev/null +++ b/eng/2022 Session 110359 Meet Swift Package plugins en.srt @@ -0,0 +1,1586 @@ +1 +00:00:00,033 --> 00:00:03,570 +♪ Mellow instrumental +hip hop music ♪ + +2 +00:00:03,570 --> 00:00:09,810 +♪ + +3 +00:00:09,810 --> 00:00:12,179 +Hello, my name is Anders. + +4 +00:00:12,179 --> 00:00:13,213 +In this video, + +5 +00:00:13,213 --> 00:00:15,949 +I'll show you how to get started +with Swift package plugins. + +6 +00:00:15,949 --> 00:00:18,418 +Swift Packages were +introduced in Xcode 11. + +7 +00:00:18,418 --> 00:00:20,387 +They provide a great way +of distributing libraries + +8 +00:00:20,387 --> 00:00:21,655 +as source code. + +9 +00:00:21,655 --> 00:00:25,058 +Xcode 14 extends this approach +to your development workflow, + +10 +00:00:25,058 --> 00:00:26,793 +letting you use plugins +to do things + +11 +00:00:26,793 --> 00:00:28,829 +like generating source code +during a build, + +12 +00:00:28,829 --> 00:00:31,298 +or automating +your release tasks. + +13 +00:00:31,298 --> 00:00:34,201 +We'll start by taking a look +at what package plugins are + +14 +00:00:34,201 --> 00:00:37,137 +and how they work, +and then talk in more detail + +15 +00:00:37,137 --> 00:00:41,275 +about the two kinds of package +plugins that Xcode 14 supports: + +16 +00:00:41,275 --> 00:00:45,512 +command plugins +and build tool plugins. + +17 +00:00:45,512 --> 00:00:48,048 +So first of all, +what is a plugin? + +18 +00:00:48,048 --> 00:00:50,884 +A package plugin is +a Swift script that can perform + +19 +00:00:50,884 --> 00:00:54,254 +actions on a Swift package +or an Xcode project. + +20 +00:00:54,254 --> 00:00:56,690 +A plugin uses API +that Xcode provides + +21 +00:00:56,690 --> 00:00:58,725 +especially for this purpose. + +22 +00:00:58,725 --> 00:01:01,795 +Package plugins are +implemented as Swift packages. + +23 +00:01:01,795 --> 00:01:03,330 +A package can provide plugins + +24 +00:01:03,330 --> 00:01:05,565 +together with libraries +and executables, + +25 +00:01:05,565 --> 00:01:09,703 +or a package could focus +only on providing plugins. + +26 +00:01:09,703 --> 00:01:11,438 +A package plugin +can be implemented + +27 +00:01:11,438 --> 00:01:13,473 +using more than one source file, + +28 +00:01:13,473 --> 00:01:16,243 +and a Swift package can define +more than one plugin. + +29 +00:01:16,243 --> 00:01:18,045 +A highly specialized plugin + +30 +00:01:18,045 --> 00:01:20,580 +can be private to the package +that provides it, + +31 +00:01:20,580 --> 00:01:25,118 +and in that case, it's available +only within that package. + +32 +00:01:25,118 --> 00:01:27,788 +But a general-purpose plugin +can be made available + +33 +00:01:27,788 --> 00:01:32,125 +to other packages by defining it +as a package product. + +34 +00:01:32,125 --> 00:01:34,594 +That lets other packages +use it too, + +35 +00:01:34,594 --> 00:01:36,530 +in a way that's similar +to how a package + +36 +00:01:36,530 --> 00:01:39,900 +can use a library +from another package. + +37 +00:01:39,900 --> 00:01:42,736 +But unlike a library, +a dependency on a plugin + +38 +00:01:42,736 --> 00:01:45,906 +does not bring in runtime +content into your app. + +39 +00:01:45,906 --> 00:01:48,809 +Instead, it lets you +access development tools + +40 +00:01:48,809 --> 00:01:52,846 +that run on your own machine +or in your build automation. + +41 +00:01:52,846 --> 00:01:55,849 +So what can +a package plugin do? + +42 +00:01:55,849 --> 00:01:59,920 +Well, in Xcode 14 there are +two kinds of package plugins: + +43 +00:01:59,920 --> 00:02:03,623 +command plugins +and build tool plugins. + +44 +00:02:03,623 --> 00:02:06,226 +Command plugins +implement custom actions + +45 +00:02:06,226 --> 00:02:08,862 +that you can run +whenever you want to. + +46 +00:02:08,862 --> 00:02:11,631 +They can run source code +formatters or linters, + +47 +00:02:11,631 --> 00:02:13,266 +or they can perform other tasks + +48 +00:02:13,266 --> 00:02:15,702 +as part of your +development workflow. + +49 +00:02:15,702 --> 00:02:17,838 +That might include updating +contributor lists + +50 +00:02:17,838 --> 00:02:21,074 +or copyright dates in source +files based on Git history, + +51 +00:02:21,074 --> 00:02:24,678 +or other things you might have +arbitrary scripts to do today. + +52 +00:02:24,678 --> 00:02:26,646 +If it needs to, +a command plugin + +53 +00:02:26,646 --> 00:02:29,883 +can ask for permission to modify +the files in a package. + +54 +00:02:29,883 --> 00:02:32,719 +And that's especially useful +for code formatting. + +55 +00:02:32,719 --> 00:02:35,956 +Not all command plugins +need write permission. + +56 +00:02:35,956 --> 00:02:38,658 +Some commands could create +reports or calculate metrics + +57 +00:02:38,658 --> 00:02:42,195 +about your code, without +needing to make any changes. + +58 +00:02:42,195 --> 00:02:45,899 +Build tool plugins extend the +build system's dependency graph. + +59 +00:02:45,899 --> 00:02:48,368 +They're particularly useful +for generating source code + +60 +00:02:48,368 --> 00:02:51,038 +or resources +as part of a build. + +61 +00:02:51,038 --> 00:02:53,340 +Unlike command plugins, +which are invoked + +62 +00:02:53,340 --> 00:02:56,143 +for a whole package +or a project at a time, + +63 +00:02:56,143 --> 00:03:00,013 +build tool plugins are applied +to each target that needs them. + +64 +00:03:00,013 --> 00:03:04,918 +Let's take a look at using +a command plugin in Xcode. + +65 +00:03:04,918 --> 00:03:06,686 +Here's a little iOS app + +66 +00:03:06,686 --> 00:03:09,856 +that shows various kinds +of geometric shapes. + +67 +00:03:09,856 --> 00:03:13,660 +It's composed of an app project +and a local package. + +68 +00:03:13,660 --> 00:03:15,796 +The package implements a library + +69 +00:03:15,796 --> 00:03:19,232 +that provides the core data +types and logic for the app. + +70 +00:03:19,232 --> 00:03:21,068 +I'm thinking of splitting out +the package + +71 +00:03:21,068 --> 00:03:24,471 +into its own repository +so that others can use it, + +72 +00:03:24,471 --> 00:03:27,474 +and as part of this, I'd like +to create a contributor file + +73 +00:03:27,474 --> 00:03:31,511 +that lists everyone who has +committed code of this package. + +74 +00:03:31,511 --> 00:03:33,914 +I could write +a custom script to do this. + +75 +00:03:33,914 --> 00:03:35,515 +But I know of a package +that provides + +76 +00:03:35,515 --> 00:03:38,218 +some useful plugins +for working with code, + +77 +00:03:38,218 --> 00:03:41,421 +and I think it has a plugin +that does exactly what I want. + +78 +00:03:41,421 --> 00:03:44,357 +To get access to those plugins, +I'll do the same thing + +79 +00:03:44,357 --> 00:03:47,327 +as if I needed a library +from another package: + +80 +00:03:47,327 --> 00:03:49,296 +I'm going to add +a package dependency + +81 +00:03:49,296 --> 00:03:51,765 +in the manifest +of my local package. + +82 +00:03:51,765 --> 00:03:55,902 +When I save the manifest, +Xcode fetches the remote package + +83 +00:03:55,902 --> 00:03:59,406 +and it appears in the +Package Dependencies section. + +84 +00:03:59,406 --> 00:04:02,676 +I notice that Xcode has +also fetched SwiftFormat, + +85 +00:04:02,676 --> 00:04:05,745 +which is a popular tool +for formatting code. + +86 +00:04:05,745 --> 00:04:07,781 +This is because +one of the command plugins + +87 +00:04:07,781 --> 00:04:12,385 +in the utility package in turn +has a dependency on SwiftFormat. + +88 +00:04:12,385 --> 00:04:14,921 +Now that I've added +this dependency, + +89 +00:04:14,921 --> 00:04:16,556 +I have access +to any plugin commands + +90 +00:04:16,556 --> 00:04:18,825 +that the package provides. + +91 +00:04:18,825 --> 00:04:20,694 +I use the context menu +on the package + +92 +00:04:20,694 --> 00:04:25,699 +I want to apply the command to. + +93 +00:04:25,699 --> 00:04:28,668 +Now there are three new commands +in the menu; + +94 +00:04:28,668 --> 00:04:32,139 +one is for reformatting +source code using SwiftFormat, + +95 +00:04:32,139 --> 00:04:35,041 +and two others provide +specialized actions. + +96 +00:04:35,041 --> 00:04:37,544 +One of them generates +or updates contributor lists + +97 +00:04:37,544 --> 00:04:40,080 +based on +the commit history in Git, + +98 +00:04:40,080 --> 00:04:41,848 +and another updates +the copyright dates + +99 +00:04:41,848 --> 00:04:43,917 +in my source files. + +100 +00:04:43,917 --> 00:04:48,155 +The command in the middle +there does exactly what I want. + +101 +00:04:48,155 --> 00:04:51,291 +When I invoke the plugin +command on my package, + +102 +00:04:51,291 --> 00:04:52,359 +Xcode lets me choose + +103 +00:04:52,359 --> 00:04:55,195 +which of its targets +to pass to the plugin. + +104 +00:04:55,195 --> 00:04:58,365 +In this case, I'm going to +invoke it on the whole package. + +105 +00:04:58,365 --> 00:05:00,500 +And if the plugin +takes custom arguments, + +106 +00:05:00,500 --> 00:05:05,238 +I can pass those here as well. + +107 +00:05:05,238 --> 00:05:07,607 +I click Run, +and because the plugin + +108 +00:05:07,607 --> 00:05:09,676 +is going to modify +the file system, + +109 +00:05:09,676 --> 00:05:12,145 +Xcode warns me about that. + +110 +00:05:12,145 --> 00:05:14,181 +I can see the plugin +author's stated reason + +111 +00:05:14,181 --> 00:05:16,950 +for wanting to modify my code, +but I want to take a peek + +112 +00:05:16,950 --> 00:05:19,653 +at the implementation +of the plugin as well. + +113 +00:05:19,653 --> 00:05:23,523 +So I choose Show Command, +and Xcode takes me to the code. + +114 +00:05:25,358 --> 00:05:27,260 +What this plugin +is doing is safe, + +115 +00:05:27,260 --> 00:05:29,329 +so I'm going to invoke +the command again + +116 +00:05:29,329 --> 00:05:31,331 +and this time, +I will choose Run. + +117 +00:05:35,468 --> 00:05:40,373 +I'll tell Xcode to remember +my choice for this plugin. + +118 +00:05:40,373 --> 00:05:42,842 +This particular plugin uses +Git history to generate + +119 +00:05:42,842 --> 00:05:45,912 +a file listing showing +the names of contributors, + +120 +00:05:45,912 --> 00:05:50,283 +but there's a lot of flexibility +in what command plugins can do. + +121 +00:05:50,283 --> 00:05:53,153 +Now that we've used +a command plugin in Xcode, + +122 +00:05:53,153 --> 00:05:56,523 +let's take a closer look at how +plugins work under the hood. + +123 +00:05:56,523 --> 00:05:58,425 +Package plugins +are Swift scripts + +124 +00:05:58,425 --> 00:06:01,928 +that are compiled and run +when they are needed. + +125 +00:06:01,928 --> 00:06:05,599 +Each plugin runs +as a separate process. + +126 +00:06:05,599 --> 00:06:08,134 +Plugins have access +to a distilled representation + +127 +00:06:08,134 --> 00:06:12,005 +of the input package, +including its source files. + +128 +00:06:12,005 --> 00:06:13,506 +A plugin also gets information + +129 +00:06:13,506 --> 00:06:16,409 +about any dependencies +of the package. + +130 +00:06:16,409 --> 00:06:18,078 +Many plugins call +command-line tools + +131 +00:06:18,078 --> 00:06:20,247 +as part of doing their work. + +132 +00:06:20,247 --> 00:06:22,782 +Plugins can also create +files and directories, + +133 +00:06:22,782 --> 00:06:25,585 +and can perform other actions +using standard libraries + +134 +00:06:25,585 --> 00:06:28,855 +such as Foundation. + +135 +00:06:28,855 --> 00:06:31,625 +A plugin runs in a sandbox +that prevents network access + +136 +00:06:31,625 --> 00:06:34,928 +and that only allows writing to +a few places in the file system, + +137 +00:06:34,928 --> 00:06:37,731 +such as the build outputs +directory. + +138 +00:06:37,731 --> 00:06:39,566 +But command plugins +can ask for permission + +139 +00:06:39,566 --> 00:06:42,902 +to also modify files +in the package source directory. + +140 +00:06:42,902 --> 00:06:45,138 +If the user approves, +the sandbox is configured + +141 +00:06:45,138 --> 00:06:47,807 +to allow writing +to those locations. + +142 +00:06:47,807 --> 00:06:51,378 +The plugin can also send +results back to Xcode. + +143 +00:06:51,378 --> 00:06:53,179 +It can emit +warnings and errors, + +144 +00:06:53,179 --> 00:06:56,049 +and build tool plugins +can define tool invocations + +145 +00:06:56,049 --> 00:06:59,152 +for Xcode to run +during the build. + +146 +00:06:59,152 --> 00:07:02,522 +All package plugins use API +from the PackagePlugin module + +147 +00:07:02,522 --> 00:07:04,724 +provided by Xcode. + +148 +00:07:04,724 --> 00:07:07,527 +This API allows the plugin +to access the input package, + +149 +00:07:07,527 --> 00:07:10,964 +and if appropriate, +to return results to Xcode. + +150 +00:07:10,964 --> 00:07:13,233 +The main source file +that implements the plugin + +151 +00:07:13,233 --> 00:07:16,636 +also defines +the main entry point. + +152 +00:07:16,636 --> 00:07:18,905 +This should be a class +or a struct that conforms + +153 +00:07:18,905 --> 00:07:22,575 +to the protocol that matches +the type of plugin. + +154 +00:07:22,575 --> 00:07:25,378 +The specific entry point +function that Xcode calls + +155 +00:07:25,378 --> 00:07:28,014 +depends on what +kind of plugin it is. + +156 +00:07:28,014 --> 00:07:30,550 +You can learn more about +the PackagePlugin API + +157 +00:07:30,550 --> 00:07:33,420 +in the "Create Swift Package +plugins" video. + +158 +00:07:33,420 --> 00:07:35,789 +Earlier, +we used a command plugin + +159 +00:07:35,789 --> 00:07:37,924 +to make changes to our package. + +160 +00:07:37,924 --> 00:07:41,661 +Let's look at some more of the +specifics of command plugins. + +161 +00:07:41,661 --> 00:07:44,597 +Command plugins extend +the development workflow. + +162 +00:07:44,597 --> 00:07:47,033 +They are applied directly +to a package, + +163 +00:07:47,033 --> 00:07:48,768 +not during a build. + +164 +00:07:48,768 --> 00:07:51,871 +Not all command plugins +modify the file system -- + +165 +00:07:51,871 --> 00:07:53,173 +there are useful actions + +166 +00:07:53,173 --> 00:07:55,408 +that don't involve +changing any files. + +167 +00:07:55,408 --> 00:07:57,744 +But if a command does want +to write to the file system, + +168 +00:07:57,744 --> 00:07:59,979 +it must declare that +in the manifest of the package + +169 +00:07:59,979 --> 00:08:01,948 +that implements the plugin. + +170 +00:08:01,948 --> 00:08:03,383 +This causes Xcode +to ask the user + +171 +00:08:03,383 --> 00:08:06,786 +for permission before +letting the plugin run. + +172 +00:08:06,786 --> 00:08:08,488 +Plugins are usually +quite small, + +173 +00:08:08,488 --> 00:08:12,392 +and often depend on other +tools to do the actual work. + +174 +00:08:12,392 --> 00:08:14,461 +Earlier, we saw +that one of the plugins + +175 +00:08:14,461 --> 00:08:17,964 +uses SwiftFormat +for all the real work. + +176 +00:08:17,964 --> 00:08:19,466 +Dependencies on tool packages + +177 +00:08:19,466 --> 00:08:22,302 +can be either binaries +or source code -- + +178 +00:08:22,302 --> 00:08:24,504 +Xcode will build +any required tools from source + +179 +00:08:24,504 --> 00:08:26,906 +before the command is invoked. + +180 +00:08:26,906 --> 00:08:29,609 +Note that the plugin can be +provided by a different package + +181 +00:08:29,609 --> 00:08:31,745 +than the tool it relies on. + +182 +00:08:31,745 --> 00:08:33,980 +In the implementation +of command plugins, + +183 +00:08:33,980 --> 00:08:37,183 +the main type conforms to +the CommandPlugin protocol, + +184 +00:08:37,183 --> 00:08:40,587 +and the plugin implements +the performCommand entry point. + +185 +00:08:40,587 --> 00:08:42,722 +This entry point takes a context + +186 +00:08:42,722 --> 00:08:45,725 +and any custom arguments +provided by the user. + +187 +00:08:45,725 --> 00:08:49,095 +Let's look at a different way +of invoking command plugins. + +188 +00:08:49,095 --> 00:08:51,731 +I'm going to use +the same project as before, + +189 +00:08:51,731 --> 00:08:53,500 +and because I added +the dependency + +190 +00:08:53,500 --> 00:08:55,769 +on the SourceCodeUtilities +package earlier, + +191 +00:08:55,769 --> 00:08:58,538 +I can invoke the same plugins +in Terminal. + +192 +00:08:58,538 --> 00:08:59,839 +First I'm going +to change directory + +193 +00:08:59,839 --> 00:09:02,842 +into the CoreLibs package, +since that's the package + +194 +00:09:02,842 --> 00:09:05,578 +that I want to apply +the command plugin to. + +195 +00:09:05,578 --> 00:09:10,216 +Swift Package Manager 5.6 has +a new subcommand for plugins. + +196 +00:09:10,216 --> 00:09:12,485 +I'll type +"swift package plugin --List" + +197 +00:09:12,485 --> 00:09:14,921 +to see what plugins +are available. + +198 +00:09:14,921 --> 00:09:18,625 +This shows the same plugins +as in the menu in Xcode. + +199 +00:09:18,625 --> 00:09:21,127 +Here on the command line, +each command also shows + +200 +00:09:21,127 --> 00:09:23,863 +the verb that should be used +to run it. + +201 +00:09:23,863 --> 00:09:25,665 +I'll use the verb +for regenerating + +202 +00:09:25,665 --> 00:09:30,236 +a contributor list, +as I did in Xcode. + +203 +00:09:30,236 --> 00:09:32,972 +This plugin wants permission +to write to the file system, + +204 +00:09:32,972 --> 00:09:34,908 +since it's going +to create a file. + +205 +00:09:34,908 --> 00:09:36,609 +I type "yes" to allow this, + +206 +00:09:36,609 --> 00:09:40,513 +and the plugin can run +and update the contributor list. + +207 +00:09:40,513 --> 00:09:41,548 +I could also have used + +208 +00:09:41,548 --> 00:09:43,583 +a package manager option +that allows the plugin + +209 +00:09:43,583 --> 00:09:46,586 +to write to the file system +without asking. + +210 +00:09:46,586 --> 00:09:49,222 +This is particularly useful +if you're invoking it + +211 +00:09:49,222 --> 00:09:52,192 +from a CI system +or other build automation. + +212 +00:09:52,192 --> 00:09:54,394 +But be sure you know +what the plugin is doing + +213 +00:09:54,394 --> 00:09:57,464 +before using that option. + +214 +00:09:57,464 --> 00:09:59,098 +Just like in Xcode, + +215 +00:09:59,098 --> 00:10:02,135 +I can pass command line +arguments to the plugin. + +216 +00:10:02,135 --> 00:10:04,671 +Any arguments after +the plugin's action verb + +217 +00:10:04,671 --> 00:10:07,240 +will be passed to the plugin. + +218 +00:10:07,240 --> 00:10:09,742 +In this case, +I pass a verbose flag + +219 +00:10:09,742 --> 00:10:13,213 +to see more output +from the plugin as it runs. + +220 +00:10:13,213 --> 00:10:17,817 +Each command plugin defines +what arguments it supports. + +221 +00:10:17,817 --> 00:10:22,121 +Until now, we've been talking +mostly about command plugins. + +222 +00:10:22,121 --> 00:10:23,923 +But there are +a few more things to say + +223 +00:10:23,923 --> 00:10:25,992 +about build tool plugins. + +224 +00:10:25,992 --> 00:10:27,727 +Unlike a command plugin, + +225 +00:10:27,727 --> 00:10:31,097 +a build tool plugin does not +do its work immediately. + +226 +00:10:31,097 --> 00:10:34,467 +Instead, it creates and returns +build tool invocations + +227 +00:10:34,467 --> 00:10:38,471 +for Xcode to run later +when the package is built. + +228 +00:10:38,471 --> 00:10:41,808 +Each of those tool invocations +has a command line to run, + +229 +00:10:41,808 --> 00:10:43,443 +and it also has +inputs and outputs + +230 +00:10:43,443 --> 00:10:46,679 +that tells Xcode when to run it. + +231 +00:10:46,679 --> 00:10:48,748 +Build tool plugins +can define commands + +232 +00:10:48,748 --> 00:10:52,051 +that run during the build +or before the build. + +233 +00:10:52,051 --> 00:10:54,554 +We'll take a look at +the difference in a minute. + +234 +00:10:56,155 --> 00:10:59,158 +Commands returned by build tool +plugins are usually configured + +235 +00:10:59,158 --> 00:11:01,361 +to write their outputs +to the build directory, + +236 +00:11:01,361 --> 00:11:04,364 +so they persist between +incremental builds. + +237 +00:11:04,364 --> 00:11:06,266 +And like the plugins +themselves, + +238 +00:11:06,266 --> 00:11:08,201 +the commands defined +by a build tool plugin + +239 +00:11:08,201 --> 00:11:10,670 +run in a sandbox +that prevents network access + +240 +00:11:10,670 --> 00:11:13,940 +and any changes to the package. + +241 +00:11:13,940 --> 00:11:16,376 +In the implementation +of a build tool plugin, + +242 +00:11:16,376 --> 00:11:19,846 +the main type conforms to +the BuildToolPlugin protocol + +243 +00:11:19,846 --> 00:11:20,947 +and the plugin implements + +244 +00:11:20,947 --> 00:11:24,050 +the createBuildCommands +entry point. + +245 +00:11:24,050 --> 00:11:26,753 +This entry point takes +a context and the target + +246 +00:11:26,753 --> 00:11:30,089 +to create build commands for. + +247 +00:11:30,089 --> 00:11:32,225 +It returns any +custom build commands + +248 +00:11:32,225 --> 00:11:35,261 +that should run +when the package is built. + +249 +00:11:35,261 --> 00:11:37,397 +There are two basic kinds +of build commands + +250 +00:11:37,397 --> 00:11:40,333 +that a build tool plugin +can return. + +251 +00:11:40,333 --> 00:11:43,836 +Ordinary build commands +specify input and output paths, + +252 +00:11:43,836 --> 00:11:45,939 +and only run +when the outputs are missing + +253 +00:11:45,939 --> 00:11:48,675 +or the inputs have changed. + +254 +00:11:48,675 --> 00:11:51,444 +Prebuild commands +run before the build starts, + +255 +00:11:51,444 --> 00:11:53,413 +and can be used when +the names of the outputs + +256 +00:11:53,413 --> 00:11:56,049 +are unknown ahead of time. + +257 +00:11:56,049 --> 00:11:58,384 +Prebuild commands +run before every build, + +258 +00:11:58,384 --> 00:12:00,853 +so they should make sure +to do as little work as possible + +259 +00:12:00,853 --> 00:12:02,855 +when there are no changes. + +260 +00:12:02,855 --> 00:12:04,657 +Build commands +and prebuild commands + +261 +00:12:04,657 --> 00:12:07,727 +are great for generating +source code or resources. + +262 +00:12:07,727 --> 00:12:09,996 +So how does Xcode know +which build tool plugins + +263 +00:12:09,996 --> 00:12:12,599 +to apply to a package target? + +264 +00:12:12,599 --> 00:12:14,834 +In SwiftPM 5.6 and later, + +265 +00:12:14,834 --> 00:12:16,402 +there is a new +plugins parameter + +266 +00:12:16,402 --> 00:12:18,004 +in the package manifest + +267 +00:12:18,004 --> 00:12:21,808 +that lists the build tool +plugins that a target wants. + +268 +00:12:21,808 --> 00:12:24,310 +This parameter specifies +any build tool plugins + +269 +00:12:24,310 --> 00:12:25,812 +needed by the target, + +270 +00:12:25,812 --> 00:12:28,781 +and just as with any +runtime libraries it depends on, + +271 +00:12:28,781 --> 00:12:30,984 +those plugins can be +either in the same package + +272 +00:12:30,984 --> 00:12:33,353 +or in another package. + +273 +00:12:33,353 --> 00:12:34,954 +Let's go back to Xcode. + +274 +00:12:34,954 --> 00:12:36,923 +I'm going to configure +my geometry app + +275 +00:12:36,923 --> 00:12:38,858 +to use a build tool plugin. + +276 +00:12:38,858 --> 00:12:40,093 +In this particular case, + +277 +00:12:40,093 --> 00:12:42,929 +I have a custom command line +tool that generates Swift code + +278 +00:12:42,929 --> 00:12:46,599 +based on some data files +in my Core Library target. + +279 +00:12:46,599 --> 00:12:48,534 +The specific details +aren't important, + +280 +00:12:48,534 --> 00:12:50,103 +but what I want to end up with + +281 +00:12:50,103 --> 00:12:52,205 +are generated type-safe +Swift accessors + +282 +00:12:52,205 --> 00:12:54,874 +for each piece of data. + +283 +00:12:54,874 --> 00:12:57,944 +In addition to my data files, +I've been using a custom tool + +284 +00:12:57,944 --> 00:13:01,247 +to generate source code that +I've checked into my repository. + +285 +00:13:01,247 --> 00:13:03,416 +I have been manually +running this tool + +286 +00:13:03,416 --> 00:13:06,753 +to regenerate Swift wrapper code +and committing the changes + +287 +00:13:06,753 --> 00:13:09,288 +whenever my data files change. + +288 +00:13:09,288 --> 00:13:11,791 +But with the build tool plugin, +I can do better. + +289 +00:13:11,791 --> 00:13:14,293 +I can generate the code +during the build + +290 +00:13:14,293 --> 00:13:17,964 +and avoid having to keep the +generated code in my repository. + +291 +00:13:19,699 --> 00:13:23,703 +To get access to the plugin, +I go to the package manifest + +292 +00:13:23,703 --> 00:13:26,072 +and add a dependency +on the package that provides + +293 +00:13:26,072 --> 00:13:29,509 +the source generator plugin +I want to use. + +294 +00:13:33,980 --> 00:13:36,249 +The targets in my package +now have access + +295 +00:13:36,249 --> 00:13:40,386 +to any build tool plugins +defined in that package. + +296 +00:13:42,388 --> 00:13:45,191 +Now I go to the target +that needs to use the plugin, + +297 +00:13:45,191 --> 00:13:48,961 +and I add a plugins parameter +to its definition. + +298 +00:13:52,899 --> 00:13:56,903 +This tells Xcode that it wants +to apply a particular build tool + +299 +00:13:56,903 --> 00:13:59,806 +from that package to my target. + +300 +00:13:59,806 --> 00:14:02,008 +Now I can go and delete +those generated source files + +301 +00:14:02,008 --> 00:14:03,710 +from my repository. + +302 +00:14:03,710 --> 00:14:07,847 +They'll be created or updated +as needed during the build. + +303 +00:14:11,517 --> 00:14:14,187 +There, that's much cleaner. + +304 +00:14:14,187 --> 00:14:16,322 +And now when I build +and run my app, + +305 +00:14:16,322 --> 00:14:18,057 +my build tool plugin + +306 +00:14:18,057 --> 00:14:20,259 +tells Xcode to invoke +my code-generation tool + +307 +00:14:20,259 --> 00:14:22,228 +whenever my data files change. + +308 +00:14:22,228 --> 00:14:23,896 +The generated code +will be stored + +309 +00:14:23,896 --> 00:14:26,099 +along with the other build files +in my build folder, + +310 +00:14:26,099 --> 00:14:28,501 +keeping my repository clean. + +311 +00:14:28,501 --> 00:14:30,503 +In this video, +we've talked about + +312 +00:14:30,503 --> 00:14:33,940 +what Swift package plugins are +and how they work. + +313 +00:14:33,940 --> 00:14:35,541 +We have discussed some +of the similarities + +314 +00:14:35,541 --> 00:14:37,643 +and differences +between command plugins + +315 +00:14:37,643 --> 00:14:40,012 +and build tool plugins. + +316 +00:14:40,012 --> 00:14:41,280 +Both types of plugins + +317 +00:14:41,280 --> 00:14:43,583 +let you replace a variety +of random scripts + +318 +00:14:43,583 --> 00:14:47,887 +with a more structured kind of +extensibility in your packages. + +319 +00:14:47,887 --> 00:14:50,490 +Build tool plugins let you +extend the build system + +320 +00:14:50,490 --> 00:14:52,792 +to generate sources +and resources, + +321 +00:14:52,792 --> 00:14:56,562 +or to do other custom work +as part of your build. + +322 +00:14:56,562 --> 00:14:58,431 +Command plugins +let you automate + +323 +00:14:58,431 --> 00:15:01,367 +common development +tasks with custom actions. + +324 +00:15:01,367 --> 00:15:03,636 +They might be tailored +to a particular workflow + +325 +00:15:03,636 --> 00:15:08,307 +or could be written to be useful +in a wide variety of cases. + +326 +00:15:08,307 --> 00:15:10,777 +To learn how to create +your own package plugins, + +327 +00:15:10,777 --> 00:15:11,878 +be sure to check out + +328 +00:15:11,878 --> 00:15:15,348 +the "Create Swift +Package plugins" video. + +329 +00:15:15,348 --> 00:15:19,919 +Thanks for watching +and enjoy the rest of WWDC 2022. + +330 +00:15:19,919 --> 00:15:23,923 +♪ + diff --git a/eng/2022 Session 110360 Use Xcode for server-side development en.srt b/eng/2022 Session 110360 Use Xcode for server-side development en.srt new file mode 100644 index 0000000..2a38913 --- /dev/null +++ b/eng/2022 Session 110360 Use Xcode for server-side development en.srt @@ -0,0 +1,1430 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,643 --> 00:00:11,178 +Hello! +My name is Tom, + +3 +00:00:11,211 --> 00:00:13,614 +and I'm part of the Swift team at Apple. + +4 +00:00:13,647 --> 00:00:18,752 +Today I'd like to share what it takes to +extend an iOS application into the cloud. + +5 +00:00:18,785 --> 00:00:22,456 +Many of our applications start out +focusing on a single device, + +6 +00:00:22,489 --> 00:00:24,558 +usually the iPhone. + +7 +00:00:24,591 --> 00:00:27,661 +As usage grows, +we find ourselves wanting to bring it + +8 +00:00:27,694 --> 00:00:30,430 +to additional devices like the Mac, +the watch, + +9 +00:00:30,464 --> 00:00:33,667 +or other Apple platforms and devices. + +10 +00:00:33,700 --> 00:00:38,572 +Xcode helps us organize and build +our application for these platforms. + +11 +00:00:38,605 --> 00:00:40,941 +We can share code using packages + +12 +00:00:40,974 --> 00:00:43,477 +while embracing the unique aspects +of each device + +13 +00:00:43,510 --> 00:00:47,014 +in the platform specific application code. + +14 +00:00:47,047 --> 00:00:49,483 +As systems continue to grow and evolve, + +15 +00:00:49,516 --> 00:00:52,452 +applications often need to complement +the client application + +16 +00:00:52,486 --> 00:00:54,288 +with a server component. + +17 +00:00:54,321 --> 00:00:57,157 +These server components enable +the client application + +18 +00:00:57,191 --> 00:00:59,826 +to extend their functionality +into the cloud. + +19 +00:00:59,860 --> 00:01:01,361 +For example, + +20 +00:01:01,395 --> 00:01:04,097 +offload tasks that can be done +in the background, + +21 +00:01:04,131 --> 00:01:06,600 +offload tasks +that are computational heavy, + +22 +00:01:06,633 --> 00:01:11,672 +or tasks that require access to data +that is not available on the device. + +23 +00:01:11,705 --> 00:01:14,007 +Often, server components need to be built + +24 +00:01:14,041 --> 00:01:18,045 +using different tools and methodologies +from their client counterparts, + +25 +00:01:18,078 --> 00:01:21,748 +creating duplication of effort +and integration challenges. + +26 +00:01:21,782 --> 00:01:24,318 +Using Swift for +building server components + +27 +00:01:24,351 --> 00:01:26,320 +help bridge this technology gap, + +28 +00:01:26,353 --> 00:01:30,324 +providing a familiar environment +across the stack. + +29 +00:01:30,357 --> 00:01:33,193 +Let's see what building a server +application in Swift looks like. + +30 +00:01:34,161 --> 00:01:38,465 +Server applications are modeled +as Swift packages. + +31 +00:01:38,498 --> 00:01:40,901 +The package defines an executable target + +32 +00:01:40,934 --> 00:01:43,837 +that maps the application entry point. + +33 +00:01:43,871 --> 00:01:46,974 +To make the application +into a web application, + +34 +00:01:47,007 --> 00:01:49,610 +we can add a dependency +on a web framework + +35 +00:01:49,643 --> 00:01:54,715 +that helps us structure our code +and provides basic utilities like routing. + +36 +00:01:54,748 --> 00:01:57,851 +In this example, +we use the Vapor web framework, + +37 +00:01:57,885 --> 00:02:01,588 +an open source community project +popular for building web services. + +38 +00:02:03,290 --> 00:02:05,559 +As with other Swift based executables, + +39 +00:02:05,592 --> 00:02:10,364 +the program's entry point is best modeled +using the @main annotation. + +40 +00:02:10,397 --> 00:02:12,165 +To integrate the web framework, + +41 +00:02:12,199 --> 00:02:15,669 +we add the relevant bootstrap code +to the main function. + +42 +00:02:15,702 --> 00:02:21,141 +The Application type used in this example +is provided by the Vapor web framework. + +43 +00:02:21,175 --> 00:02:23,410 +With the basic bootstrapping in place, + +44 +00:02:23,443 --> 00:02:26,213 +we can make our application +do something useful. + +45 +00:02:26,246 --> 00:02:31,285 +For example, let's add code to greet users +making a request to the server. + +46 +00:02:31,318 --> 00:02:35,088 +We use the web framework to define +an HTTP endpoint + +47 +00:02:35,122 --> 00:02:38,959 +and point it to a method +that provides the greeting. + +48 +00:02:38,992 --> 00:02:42,930 +Taking a step further, +we add a second HTTP endpoint, + +49 +00:02:42,963 --> 00:02:46,366 +this one handling an HTTP post request, + +50 +00:02:46,400 --> 00:02:50,571 +and echoing the content +of the request body back to the caller. + +51 +00:02:50,604 --> 00:02:52,573 +Let's see this in action. + +52 +00:02:52,606 --> 00:02:55,642 +Here we have our server application +in Xcode. + +53 +00:02:55,676 --> 00:02:57,244 +Since we're just getting started, + +54 +00:02:57,277 --> 00:03:01,281 +we can run the server locally +on our own machine to test things out. + +55 +00:03:01,315 --> 00:03:03,784 +To run it locally, +we pick the "MyServer" scheme + +56 +00:03:03,817 --> 00:03:07,421 +that was generated for us by Xcode, +use the “My Mac" as the destination, + +57 +00:03:07,454 --> 00:03:08,722 +and hit "run". + +58 +00:03:17,231 --> 00:03:18,699 +Once the application has launched, + +59 +00:03:18,732 --> 00:03:23,036 +we can use Xcode console to examine +log messages emitted by the server. + +60 +00:03:23,070 --> 00:03:25,339 +In this case, +we can see that the server started + +61 +00:03:25,372 --> 00:03:31,712 +and listening on the localhost address +(127.0.0.1) port 8080. + +62 +00:03:31,745 --> 00:03:34,481 +We can use this information +to test our server. + +63 +00:03:34,515 --> 00:03:35,883 +Let's switch over to the terminal, + +64 +00:03:35,916 --> 00:03:39,086 +and make a request +to the advertised server address. + +65 +00:03:39,119 --> 00:03:43,857 +We use a utility called "curl" +to make the request. + +66 +00:03:43,891 --> 00:03:46,660 +Use our first endpoint. + +67 +00:03:48,562 --> 00:03:51,398 +And our second one. + +68 +00:03:51,431 --> 00:03:54,067 +Pass in some data to echo. + +69 +00:03:59,239 --> 00:04:01,008 +Nice! + +70 +00:04:01,041 --> 00:04:03,443 +Using the terminal sure was fun, + +71 +00:04:03,477 --> 00:04:08,182 +but what we really want to know is +how to call our server from an iOS app. + +72 +00:04:08,215 --> 00:04:09,883 +Let's dig into that. + +73 +00:04:09,917 --> 00:04:12,619 +Here is an example +of a Swift data structure + +74 +00:04:12,653 --> 00:04:15,756 +we can use to abstract the interaction +with the server. + +75 +00:04:15,789 --> 00:04:20,194 +We model the server APIs as async methods +on our abstraction, + +76 +00:04:20,227 --> 00:04:23,530 +because networking is +inherently asynchronous. + +77 +00:04:23,564 --> 00:04:25,399 +We use URLSession + +78 +00:04:25,432 --> 00:04:27,701 +to make an asynchronous request + +79 +00:04:27,734 --> 00:04:32,573 +then parse the server response +and finally return it to the caller. + +80 +00:04:32,606 --> 00:04:35,809 +In this case, +the server response is a plain string, + +81 +00:04:35,843 --> 00:04:39,213 +but in reality, +it is likely to be more sophisticated. + +82 +00:04:39,246 --> 00:04:43,083 +For example, +the response may be encoded in JSON, + +83 +00:04:43,116 --> 00:04:47,254 +in which case we can decode it using +Swift's Codable system. + +84 +00:04:47,287 --> 00:04:50,390 +Let's put this all together in Xcode. + +85 +00:04:50,424 --> 00:04:52,926 +We are using an Xcode workspace +to build and test + +86 +00:04:52,960 --> 00:04:55,996 +the iOS and server applications +side by side. + +87 +00:04:56,029 --> 00:05:00,801 +We already have the iOS application +server abstraction ready to go. + +88 +00:05:00,834 --> 00:05:04,605 +Let's change the default SwiftUI +ContentView to fetch the server greeting + +89 +00:05:04,638 --> 00:05:07,074 +using the code we put together. + +90 +00:05:07,107 --> 00:05:10,410 +First we create a state variable +called serverGreeting. + +91 +00:05:13,180 --> 00:05:17,150 +Next, we bind the serverGreeting +to the Text display. + +92 +00:05:21,221 --> 00:05:24,024 +Finally, +we add a task to call the server API, + +93 +00:05:24,057 --> 00:05:25,392 +and set the state. + +94 +00:05:27,794 --> 00:05:31,164 +With the code ready, we can run +the application in the simulator. + +95 +00:05:31,198 --> 00:05:34,201 +We pick the "MyApp" scheme, + +96 +00:05:34,234 --> 00:05:38,172 +a simulator, + +97 +00:05:38,205 --> 00:05:41,175 +and hit "run". + +98 +00:05:42,176 --> 00:05:44,411 +Oh, no! We got an error! + +99 +00:05:44,444 --> 00:05:48,148 +Hmm, this seems to be +some sort of a connection error. + +100 +00:05:48,182 --> 00:05:49,650 +The address seems right, + +101 +00:05:49,683 --> 00:05:52,786 +so we must have forgotten +to start the local server. + +102 +00:05:52,819 --> 00:05:55,189 +Let's switch back to Xcode, +pick the server scheme, + +103 +00:05:55,222 --> 00:05:56,723 +and run the server. + +104 +00:06:07,100 --> 00:06:11,805 +Now, let's restart our application, + +105 +00:06:11,839 --> 00:06:13,040 +cross our fingers... + +106 +00:06:14,975 --> 00:06:16,710 +And whoo-hoo! It worked! + +107 +00:06:16,743 --> 00:06:18,679 +To complete this part of the demo, + +108 +00:06:18,712 --> 00:06:21,381 +let's deploy our application to the cloud. + +109 +00:06:21,415 --> 00:06:24,885 +There are many cloud providers +to choose from, including AWS, + +110 +00:06:24,918 --> 00:06:28,222 +Google Cloud, Azure, Heroku, +and many others. + +111 +00:06:28,255 --> 00:06:31,058 +In this example, we will use Heroku. + +112 +00:06:31,091 --> 00:06:34,294 +Heroku has a convenient +git push to deploy system + +113 +00:06:34,328 --> 00:06:36,864 +for small projects like this +demo application. + +114 +00:06:36,897 --> 00:06:39,700 +Let's switch over to the terminal +to kick off a deployment. + +115 +00:06:39,733 --> 00:06:42,503 +After setting up our account, +and configuring our application + +116 +00:06:42,536 --> 00:06:46,473 +with the Heroku service, we can git push +our code to the Heroku remote. + +117 +00:06:48,542 --> 00:06:56,550 +[silence while typing] + +118 +00:07:01,021 --> 00:07:02,723 +And off it goes! + +119 +00:07:02,756 --> 00:07:07,427 +Heroku uses a technology called buildpacks +to compile the application remotely, + +120 +00:07:07,461 --> 00:07:11,298 +then deploys the binary artifacts +to an ephemeral host. + +121 +00:07:11,331 --> 00:07:13,834 +Heroku swift buildpack was built +by members + +122 +00:07:13,867 --> 00:07:15,836 +of the Swift open source community, + +123 +00:07:15,869 --> 00:07:19,006 +and it is available +for all Swift on Server users. + +124 +00:07:19,039 --> 00:07:21,942 +With our application deployed, +we can test it using curl, + +125 +00:07:21,975 --> 00:07:24,144 +as we have done with our local server. + +126 +00:07:24,178 --> 00:07:26,046 +Let's test the first endpoint. + +127 +00:07:28,248 --> 00:07:30,717 +Copy the address here. + +128 +00:07:37,658 --> 00:07:39,593 +And our second one. + +129 +00:07:42,596 --> 00:07:45,632 +This time, we'll send a different payload. + +130 +00:07:51,939 --> 00:07:55,042 +Sweet, +our application was successfully deployed! + +131 +00:07:55,075 --> 00:07:58,545 +Before we continue, let's pause here +and review the main takeaways + +132 +00:07:58,579 --> 00:08:00,581 +from this part of the talk. + +133 +00:08:00,614 --> 00:08:04,218 +If you're already using Swift +to build iOS or macOS Applications, + +134 +00:08:04,251 --> 00:08:08,121 +you could also be using it for +developing the server side of the system. + +135 +00:08:08,155 --> 00:08:12,125 +Xcode helps us develop and debug +the different components of the system, + +136 +00:08:12,159 --> 00:08:15,829 +both the client and the server, +all in one Workspace. + +137 +00:08:15,863 --> 00:08:18,098 +And finally, +you have a choice of cloud providers + +138 +00:08:18,131 --> 00:08:20,834 +for deploying +Swift based server applications. + +139 +00:08:20,868 --> 00:08:23,804 +Additional information about deploying +to these cloud platforms + +140 +00:08:23,837 --> 00:08:26,240 +can be found +on the Swift Server documentation + +141 +00:08:26,273 --> 00:08:28,075 +at swift.org. + +142 +00:08:28,108 --> 00:08:30,110 +Now that we have seen a basic setup, + +143 +00:08:30,143 --> 00:08:33,247 +let's look at a more real example– +Food Truck! + +144 +00:08:33,280 --> 00:08:36,717 +You've probably seen this application +used in many of our sessions. + +145 +00:08:36,750 --> 00:08:39,720 +Let's peek under the hood +and see how data is managed. + +146 +00:08:39,753 --> 00:08:43,690 +Hmm, looks like the donut list +is hard coded. + +147 +00:08:43,724 --> 00:08:47,294 +This means that users of the application +may see a different menu of donuts + +148 +00:08:47,327 --> 00:08:49,630 +from what is actually available. + +149 +00:08:49,663 --> 00:08:52,599 +While this may be useful +for a small Food Truck operation, + +150 +00:08:52,633 --> 00:08:55,335 +one that can make +any kind of donut on the spot, + +151 +00:08:55,369 --> 00:08:57,871 +we want to build a donuts empire + +152 +00:08:57,905 --> 00:09:01,909 +where the menu is centralized and +the trucks are all about customer service. + +153 +00:09:01,942 --> 00:09:05,045 +Let's design how our centralized +Food Truck system may look like. + +154 +00:09:06,213 --> 00:09:10,417 +We are starting out with our iOS app, +with its in-memory storage. + +155 +00:09:10,450 --> 00:09:14,154 +To centralize the menu, we can extract +the storage from the iOS app + +156 +00:09:14,188 --> 00:09:16,423 +and move it to the server. + +157 +00:09:16,456 --> 00:09:19,626 +This will allow all users of the app +to share the same storage, + +158 +00:09:19,660 --> 00:09:22,396 +and thus, the same donuts menu. + +159 +00:09:22,429 --> 00:09:25,399 +Similar to the example +in the first part of the talk, + +160 +00:09:25,432 --> 00:09:28,869 +our server will expose an HTTP based API. + +161 +00:09:28,902 --> 00:09:32,806 +The iOS app will use an abstraction +for working with these APIs, + +162 +00:09:32,840 --> 00:09:35,209 +then tie them back together +to the presentation tier, + +163 +00:09:35,242 --> 00:09:37,978 +in this example, SwiftUI. + +164 +00:09:38,011 --> 00:09:39,880 +Our design is complete. + +165 +00:09:39,913 --> 00:09:42,482 +Time to write some sweet code. + +166 +00:09:42,516 --> 00:09:45,219 +You can follow along by downloading +the Food Truck sample app + +167 +00:09:45,252 --> 00:09:47,888 +from the developer resource kit. + +168 +00:09:47,921 --> 00:09:51,058 +We start building our Server +with an application skeleton, + +169 +00:09:51,091 --> 00:09:55,229 +then define an HTTP endpoint +for the "donuts" web API, + +170 +00:09:55,262 --> 00:09:59,533 +and point it to the "listDonuts" method +on our server abstraction. + +171 +00:09:59,566 --> 00:10:03,704 +You may have noticed that the API returns +a Response of type Donuts + +172 +00:10:03,737 --> 00:10:08,375 +and that Response.Donuts conforms +to a protocol called Content. + +173 +00:10:08,408 --> 00:10:11,078 +The Content protocol +is defined by the web framework + +174 +00:10:11,111 --> 00:10:15,516 +and helps us encode the response +as JSON on the wire. + +175 +00:10:15,549 --> 00:10:18,185 +You may have also noticed +that the API includes an array + +176 +00:10:18,218 --> 00:10:21,989 +of a mysterious Model.Donut, +which we have yet to define + +177 +00:10:22,022 --> 00:10:25,058 +So here it is, +our data model in all of its glory: + +178 +00:10:25,092 --> 00:10:29,129 +Donut, Dough, Glaze, and Topping. + +179 +00:10:29,162 --> 00:10:30,964 +One interesting point to make here + +180 +00:10:30,998 --> 00:10:34,968 +is that we copied the definition +of this model from our Food Truck iOS app, + +181 +00:10:35,002 --> 00:10:38,972 +as we need the data models of the server +and the client to roughly align. + +182 +00:10:39,006 --> 00:10:43,210 +Another interesting point is the +conformance to the Encodable protocol. + +183 +00:10:43,243 --> 00:10:46,547 +This is required so that our server +can encode the model objects + +184 +00:10:46,580 --> 00:10:49,049 +as JSON over the wire. + +185 +00:10:49,082 --> 00:10:51,485 +With the data model +and basic APIs in place, + +186 +00:10:51,518 --> 00:10:54,855 +we can expand our logic +to include a storage abstraction. + +187 +00:10:54,888 --> 00:10:58,892 +The storage will provide the Application +with the list of available donuts. + +188 +00:10:58,926 --> 00:11:01,929 +At this point, we should have +a fully functional server. + +189 +00:11:01,962 --> 00:11:04,298 +But wait! +Our donuts menu is empty! + +190 +00:11:04,331 --> 00:11:07,467 +Where should we get +the our centralized menu from? + +191 +00:11:07,501 --> 00:11:12,072 +Storage is always an interesting topic +when designing server side applications. + +192 +00:11:12,105 --> 00:11:13,807 +There are several strategies +to choose from, + +193 +00:11:13,841 --> 00:11:15,976 +depending on the use case. + +194 +00:11:16,009 --> 00:11:19,947 +If the application data is static +or changes very slowly and manually, + +195 +00:11:19,980 --> 00:11:22,983 +files on disk may provide +a good enough solution. + +196 +00:11:23,016 --> 00:11:25,819 +For user-centric data or global datasets, + +197 +00:11:25,853 --> 00:11:29,857 +iCloud provides a set of APIs that you can +use directly from the iOS application, + +198 +00:11:29,890 --> 00:11:32,192 +without deploying a dedicated server. + +199 +00:11:32,226 --> 00:11:34,795 +When dealing with dynamic +or transactional data, + +200 +00:11:34,828 --> 00:11:37,965 +databases provide an excellent solution. + +201 +00:11:37,998 --> 00:11:40,200 +There is a variety of database +technologies available + +202 +00:11:40,234 --> 00:11:42,202 +for server-side applications. + +203 +00:11:42,236 --> 00:11:45,072 +Each technology is designed +for specific performance, + +204 +00:11:45,105 --> 00:11:48,041 +data consistency, +and data modeling needs. + +205 +00:11:48,075 --> 00:11:50,611 +Over the years, +the Swift open source community + +206 +00:11:50,644 --> 00:11:53,180 +developed database drivers +that help interact natively + +207 +00:11:53,213 --> 00:11:55,682 +with most databases technologies. + +208 +00:11:55,716 --> 00:11:58,585 +Partial list includes Postgres, MySQL, + +209 +00:11:58,619 --> 00:12:03,156 +MongoDB, Redis, DynamoDB, +and many others. + +210 +00:12:03,190 --> 00:12:05,192 +For the purposes of simplifying this demo, + +211 +00:12:05,225 --> 00:12:09,162 +we will only demonstrate +a static file storage strategy, + +212 +00:12:09,196 --> 00:12:11,365 +but you can learn more +about using databases + +213 +00:12:11,398 --> 00:12:14,601 +on the Swift Server documentation +at swift.org. + +214 +00:12:14,635 --> 00:12:17,504 +Since we are using +a static file storage strategy, + +215 +00:12:17,538 --> 00:12:21,642 +we start off by creating a JSON file +that captures the donut menu. + +216 +00:12:21,675 --> 00:12:23,310 +After creating this file, + +217 +00:12:23,343 --> 00:12:25,445 +we can make it accessible +to the application + +218 +00:12:25,479 --> 00:12:28,382 +using SwiftPM's resources support. + +219 +00:12:28,415 --> 00:12:33,053 +With that in place, it is time to make our +storage abstraction more sophisticated. + +220 +00:12:33,086 --> 00:12:36,056 +Namely, we add a "load" method. + +221 +00:12:36,089 --> 00:12:38,458 +This method finds the resource file path + +222 +00:12:38,492 --> 00:12:41,628 +using SwiftPM's +generated resource accessor, + +223 +00:12:41,662 --> 00:12:46,867 +then uses FileManager APIs to load +the content of the file into memory. + +224 +00:12:46,900 --> 00:12:48,735 +Finally, we use JSONDecoder + +225 +00:12:48,769 --> 00:12:53,574 +to decode the JSON content +into the server application data model. + +226 +00:12:53,607 --> 00:12:57,811 +One interesting change is that +Storage is now defined as an actor. + +227 +00:12:57,845 --> 00:13:02,549 +We chose to use an actor because Storage +now has a mutable "donuts" variable, + +228 +00:13:02,583 --> 00:13:07,421 +and which the "load" and "listDonuts" +methods might access concurrently. + +229 +00:13:07,454 --> 00:13:10,624 +Actors, +which were first introduced in Swift 5.5, + +230 +00:13:10,657 --> 00:13:13,660 +help us avoid data races +and deal with shared mutable state + +231 +00:13:13,694 --> 00:13:16,563 +in a safe but easy way. + +232 +00:13:16,597 --> 00:13:18,465 +Prior to the introduction of actors, + +233 +00:13:18,498 --> 00:13:21,068 +we would have needed to remember +and add synchronization blocks + +234 +00:13:21,101 --> 00:13:25,272 +when accessing mutable state +using APIs such as Locks or Queues. + +235 +00:13:25,305 --> 00:13:28,942 +With the storage updates done, +we can tie it all together. + +236 +00:13:28,976 --> 00:13:31,278 +We add a "bootstrap" method +to our server abstraction + +237 +00:13:31,311 --> 00:13:33,213 +and load the storage from there. + +238 +00:13:33,247 --> 00:13:37,084 +Then we wire up the bootstrap +to the executables entry point. + +239 +00:13:37,117 --> 00:13:42,322 +Note that since storage is now an actor, +we access it in an async context. + +240 +00:13:42,356 --> 00:13:43,423 +Our server is ready. + +241 +00:13:43,457 --> 00:13:45,959 +Let's switch over to the client side. + +242 +00:13:45,993 --> 00:13:48,896 +We start by adding a Server abstraction + +243 +00:13:48,929 --> 00:13:52,032 +that will help us encapsulate +the server APIs. + +244 +00:13:52,065 --> 00:13:55,335 +We use URLSession to make the HTTP request + +245 +00:13:55,369 --> 00:13:58,005 +and a JSONDecoder +to decode the server response + +246 +00:13:58,038 --> 00:14:02,142 +and transform it from JSON into our iOS +application model. + +247 +00:14:02,176 --> 00:14:05,145 +At this point, +we can remove the hard coded menu + +248 +00:14:05,179 --> 00:14:08,415 +and replace it with an asynchronous fetch +from the server. + +249 +00:14:08,448 --> 00:14:12,553 +Finally, we make the call to the server +from the ContentView load task. + +250 +00:14:12,586 --> 00:14:14,121 +Time to test! + +251 +00:14:14,154 --> 00:14:17,090 +This time, +let's not forget to start the server. + +252 +00:14:17,124 --> 00:14:19,326 +We'll select +the "FoodTruckServer" scheme here. + +253 +00:14:19,359 --> 00:14:20,661 +Hit run. + +254 +00:14:24,298 --> 00:14:26,233 +And with the application running, + +255 +00:14:26,266 --> 00:14:30,237 +let's jump on the terminal +and see that we can access the APIs. + +256 +00:14:33,473 --> 00:14:35,309 +Copy the address again. + +257 +00:14:37,110 --> 00:14:39,513 +This time, we're going to use +a utility called jq + +258 +00:14:39,546 --> 00:14:43,517 +to print the JSON output more nicely. + +259 +00:14:43,550 --> 00:14:44,818 +This looks pretty good. + +260 +00:14:46,286 --> 00:14:48,755 +All right, time to test with our App. + +261 +00:14:49,957 --> 00:14:52,359 +Switch to Xcode. + +262 +00:14:52,392 --> 00:14:56,129 +Pick the Food Truck scheme here. + +263 +00:14:56,163 --> 00:14:57,898 +Simulator. + +264 +00:14:57,931 --> 00:14:59,466 +And run it. + +265 +00:15:01,969 --> 00:15:03,237 +And there we have it, + +266 +00:15:03,270 --> 00:15:05,372 +the three donuts +from our centralized menu. + +267 +00:15:05,405 --> 00:15:08,442 +We can cross reference that +with what we see from the server. + +268 +00:15:08,475 --> 00:15:09,877 +Let's switch back to the terminal. + +269 +00:15:09,910 --> 00:15:15,315 +To make the comparison easy, we will use +jq to query just the name of the donuts. + +270 +00:15:26,827 --> 00:15:29,763 +Deep space, Chocolate 2, +Coffee Caramel– + +271 +00:15:29,796 --> 00:15:32,332 +exactly what we expected. + +272 +00:15:32,366 --> 00:15:34,234 +That was amazing! + +273 +00:15:34,268 --> 00:15:36,069 +But we can do even better. + +274 +00:15:36,103 --> 00:15:38,805 +As it stands, +our server and client applications + +275 +00:15:38,839 --> 00:15:41,642 +both have identical copies +of the data model code. + +276 +00:15:41,675 --> 00:15:44,778 +We can avoid repetition +and make serialization safer, + +277 +00:15:44,811 --> 00:15:48,615 +by sharing the model +across the iOS and server applications. + +278 +00:15:48,649 --> 00:15:51,718 +Let's review how to set this up +at a high level. + +279 +00:15:51,752 --> 00:15:55,722 +First, we create another package +for a library named "Shared" + +280 +00:15:55,756 --> 00:15:58,058 +and add it to the Xcode workspace. + +281 +00:15:58,091 --> 00:16:01,395 +We can then move the data model code to +the Shared package, + +282 +00:16:01,428 --> 00:16:05,032 +add Shared as a dependency +of the server application, + +283 +00:16:05,065 --> 00:16:07,467 +and as a dependency of the iOS +application, + +284 +00:16:07,501 --> 00:16:10,304 +using the Target Frameworks +and Libraries settings. + +285 +00:16:10,337 --> 00:16:13,040 +At which point, +we can refactor our client code + +286 +00:16:13,073 --> 00:16:15,309 +to use the shared model + +287 +00:16:15,342 --> 00:16:18,178 +and do the same + +288 +00:16:18,212 --> 00:16:19,580 +to the server code. + +289 +00:16:22,282 --> 00:16:24,751 +Things look much nicer now. + +290 +00:16:24,785 --> 00:16:29,256 +Before we conclude, here are some ideas of +where we can take the application next. + +291 +00:16:29,289 --> 00:16:32,192 +To take full advantage of the fact +that we have a centralized server, + +292 +00:16:32,226 --> 00:16:37,931 +we are likely to want and define APIs for +adding, editing, or deleting donuts from the menu. + +293 +00:16:37,965 --> 00:16:42,369 +This will require that we move our storage +from a static file to a database. + +294 +00:16:42,402 --> 00:16:46,473 +With a database in place, we can also +implement buying and ordering APIs. + +295 +00:16:46,507 --> 00:16:50,110 +Such APIs can help us +monetize our donut business. + +296 +00:16:50,143 --> 00:16:51,645 +They also provide a signal, + +297 +00:16:51,678 --> 00:16:54,314 +which we can use to implement +dynamic pricing, + +298 +00:16:54,348 --> 00:16:58,018 +like sales and discounts +for those donuts that are less popular. + +299 +00:16:58,051 --> 00:17:00,754 +The opportunities are endless. + +300 +00:17:00,787 --> 00:17:03,156 +To wrap up, +in this session we have seen + +301 +00:17:03,190 --> 00:17:05,659 +that Swift is a general purpose language, + +302 +00:17:05,692 --> 00:17:08,362 +useful for both client and server +applications, + +303 +00:17:08,395 --> 00:17:10,931 +sharing code between the server +and client applications + +304 +00:17:10,964 --> 00:17:14,668 +can reduce boilerplate +and make our system serialization safer, + +305 +00:17:14,701 --> 00:17:18,872 +URLSession is a key tool for interacting +with the server asynchronously, + +306 +00:17:18,906 --> 00:17:21,909 +and finally, Xcode is +a powerful development environment + +307 +00:17:21,942 --> 00:17:24,311 +for the entire system. + +308 +00:17:24,344 --> 00:17:25,913 +Thank you so much for watching, + +309 +00:17:25,946 --> 00:17:28,248 +and enjoy the rest of the conference. + diff --git a/eng/2022 Session 110361 Author fast and reliable tests for Xcode Cloud en.srt b/eng/2022 Session 110361 Author fast and reliable tests for Xcode Cloud en.srt new file mode 100644 index 0000000..540f274 --- /dev/null +++ b/eng/2022 Session 110361 Author fast and reliable tests for Xcode Cloud en.srt @@ -0,0 +1,1556 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:10,110 --> 00:00:15,249 +Suzy: Hello and welcome to "Author fast +and reliable tests for Xcode Cloud." + +3 +00:00:15,282 --> 00:00:18,018 +I'm Suzy, and I work on XCTest. + +4 +00:00:18,051 --> 00:00:21,288 +In this session, I'm going to share +the most effective ways + +5 +00:00:21,321 --> 00:00:24,625 +to start testing for Xcode Cloud. + +6 +00:00:24,658 --> 00:00:30,030 +Our teams designed Xcode Cloud +to be a powerful tool for all developers. + +7 +00:00:30,063 --> 00:00:34,168 +In fact, we use it to test Xcode itself, +and I love it. + +8 +00:00:34,201 --> 00:00:36,637 +One of my favorite features of Xcode Cloud + +9 +00:00:36,670 --> 00:00:40,374 +is its ability to substantially broaden +a given test suite. + +10 +00:00:41,575 --> 00:00:44,411 +By configuring most tests +to run in the cloud, + +11 +00:00:44,444 --> 00:00:48,649 +you now have a practical way +to run tests on multiple destinations, + +12 +00:00:48,682 --> 00:00:52,686 +including those running differing +operating system versions + +13 +00:00:52,719 --> 00:00:56,924 +to leverage diverse platforms +such as an iPhone, iPad, + +14 +00:00:56,957 --> 00:01:00,627 +Apple Watch, Apple TV, and Mac, + +15 +00:01:00,661 --> 00:01:03,430 +and to run various test plan +configurations, + +16 +00:01:03,463 --> 00:01:05,966 +allowing for runtime analysis tools + +17 +00:01:05,999 --> 00:01:10,470 +such as address and thread sanitizers. + +18 +00:01:10,504 --> 00:01:13,740 +Once we have passed +such a thorough test suite, + +19 +00:01:13,774 --> 00:01:16,910 +we can be confident +the code is ready to ship. + +20 +00:01:16,944 --> 00:01:21,181 +Offloading tests to Xcode Cloud +allows for running a broader set of tests + +21 +00:01:21,215 --> 00:01:27,754 +without impacting the developers’ +desktop cycle of code, compile, and test. + +22 +00:01:27,788 --> 00:01:30,157 +With this now expanded test suite, + +23 +00:01:30,190 --> 00:01:34,228 +there is a potential for an increased +number of unreliable tests. + +24 +00:01:34,261 --> 00:01:37,898 +This situation can become unmanageable. + +25 +00:01:37,931 --> 00:01:42,736 +As such, +ensuring reliability is essential. + +26 +00:01:42,769 --> 00:01:46,206 +In addition to reliability, +such a large number of tests + +27 +00:01:46,240 --> 00:01:48,141 +also needs to run efficiently + +28 +00:01:48,175 --> 00:01:53,046 +so as to limit their impact +on the continuous integration process. + +29 +00:01:53,080 --> 00:01:55,749 +Let’s address reliability first. + +30 +00:01:55,782 --> 00:01:59,219 +I will demonstrate how to author +more reliable tests for Xcode Cloud + +31 +00:01:59,253 --> 00:02:00,888 +using Food Truck. + +32 +00:02:00,921 --> 00:02:06,393 +Food Truck is an app that converts +taps and swipes into delicious donuts. + +33 +00:02:06,426 --> 00:02:08,929 +By running the test suite in Xcode Cloud, + +34 +00:02:08,962 --> 00:02:11,999 +we are able to validate +that all Apple platforms + +35 +00:02:12,032 --> 00:02:15,936 +support ordering my favorite donut, +chocolate with sprinkles. + +36 +00:02:15,969 --> 00:02:18,405 +Each improvement +to the Xcode Cloud Workflow + +37 +00:02:18,438 --> 00:02:21,742 +will be identified and demonstrated. + +38 +00:02:21,775 --> 00:02:25,712 +For more information about getting +started with Xcode Cloud Workflows, + +39 +00:02:25,746 --> 00:02:27,814 +watch "Meet Xcode Cloud.” + +40 +00:02:29,116 --> 00:02:31,718 +The first step +to author more reliable tests + +41 +00:02:31,752 --> 00:02:36,323 +is to ensure each test’s setup +and tear down is thorough. + +42 +00:02:36,356 --> 00:02:39,593 +Tests run in Xcode Cloud make use +of a fresh simulator + +43 +00:02:39,626 --> 00:02:43,864 +which may not meet +developers’ original assumptions. + +44 +00:02:43,897 --> 00:02:47,668 +Let’s identify a number of +device configuration assumptions + +45 +00:02:47,701 --> 00:02:49,369 +sometimes seen in test code. + +46 +00:02:50,270 --> 00:02:54,508 +Certain tests may rely on +specific dates and times. + +47 +00:02:54,541 --> 00:02:58,745 +For example, a server may be running +in a different time zone. + +48 +00:02:58,779 --> 00:03:02,549 +Tests should avoid +being time zone specific. + +49 +00:03:02,583 --> 00:03:05,619 +Locale-based values, +such as number formatting + +50 +00:03:05,652 --> 00:03:09,823 +and language directionality, +can lead to unexpected results. + +51 +00:03:09,857 --> 00:03:14,895 +Avoid this problem by explicitly +setting your simulator’s locale. + +52 +00:03:14,928 --> 00:03:19,266 +Another problematic assumption is +reliance on certain device permissions + +53 +00:03:19,299 --> 00:03:21,268 +such as internet access. + +54 +00:03:21,301 --> 00:03:24,071 +It’s best to mock device permissions +in a unit test + +55 +00:03:24,104 --> 00:03:28,275 +and use an alert handler in a UI Test. + +56 +00:03:28,308 --> 00:03:32,012 +Finally, some tests depend +on preloaded data. + +57 +00:03:32,045 --> 00:03:37,484 +For example, a test may expect +to have an empty documents directory. + +58 +00:03:37,518 --> 00:03:41,822 +While explicitly configuring the simulator +is sometimes the easiest choice, + +59 +00:03:41,855 --> 00:03:46,660 +enhancing the test’s setup method +is generally more robust. + +60 +00:03:46,693 --> 00:03:50,330 +For example, +the Food Truck depends on a menu file. + +61 +00:03:50,364 --> 00:03:53,934 +As part of instantiating +the truck object in the setup function, + +62 +00:03:53,967 --> 00:03:58,105 +we generate a mock data file +containing the donut menu items. + +63 +00:03:58,138 --> 00:04:03,510 +Note that rather than relying on teardown +methods to prepare for subsequent tests, + +64 +00:04:03,544 --> 00:04:08,949 +we recommend establishing +all state preparation in the setup method. + +65 +00:04:08,982 --> 00:04:13,253 +In many cases, read-only files +can be checked into the repository + +66 +00:04:13,287 --> 00:04:15,355 +and later accessed by tests. + +67 +00:04:15,389 --> 00:04:18,692 +However, when these files +need to be constructed, + +68 +00:04:18,725 --> 00:04:21,628 +Xcode Cloud supports +running a custom build script + +69 +00:04:21,662 --> 00:04:26,600 +where you can generate the file once +for multiple tests to access. + +70 +00:04:26,633 --> 00:04:29,503 +For more details +on how to configure your script, + +71 +00:04:29,536 --> 00:04:33,173 +watch "Customize your advanced +Xcode Cloud workflow." + +72 +00:04:34,708 --> 00:04:38,245 +That wraps up proper simulator setup. + +73 +00:04:38,278 --> 00:04:43,817 +Now, let’s cover how to handle tests +that fail to meet preconditions. + +74 +00:04:43,851 --> 00:04:47,554 +XCTSkip is an error +that instructs the XCTest Runner + +75 +00:04:47,588 --> 00:04:51,992 +to cease executing the current test +and mark it as skipped. + +76 +00:04:52,025 --> 00:04:58,131 +This may be used to bypass a yet-to-be +supported OS version or device type. + +77 +00:04:58,165 --> 00:05:02,202 +You could also leverage XCTSkip +by setting an environment variable + +78 +00:05:02,236 --> 00:05:06,306 +to skip tests specific to staging +or production environments. + +79 +00:05:06,340 --> 00:05:11,111 +Let’s go over how we can control +test flow using an environment variable. + +80 +00:05:12,179 --> 00:05:15,115 +Environment variables +can provide parameters + +81 +00:05:15,148 --> 00:05:18,418 +to both the XCTest test runner app +on your device + +82 +00:05:18,452 --> 00:05:22,022 +and the test host running xcodebuild. + +83 +00:05:22,055 --> 00:05:26,560 +In Xcode Cloud, environment variables +prefixed with TEST_RUNNER_ + +84 +00:05:26,593 --> 00:05:29,663 +get passed into the XCTest test runner. + +85 +00:05:29,696 --> 00:05:31,465 +This prefix will be stripped prior to + +86 +00:05:31,498 --> 00:05:35,869 +the variable being made available +to your code. + +87 +00:05:35,903 --> 00:05:39,606 +So, for example, +a variable in your test code + +88 +00:05:39,640 --> 00:05:43,877 +named BASE_URL would be passed in +as the environment variable + +89 +00:05:43,911 --> 00:05:48,115 +named TEST_RUNNER_BASE_URL. + +90 +00:05:48,148 --> 00:05:51,351 +Test plans require the same format +as test code. + +91 +00:05:51,385 --> 00:05:55,989 +Namely, we do not add +the TEST_RUNNER_ prefix. + +92 +00:05:56,023 --> 00:06:00,127 +Environment variables may be +referenced anywhere in test code. + +93 +00:06:00,160 --> 00:06:03,263 +For example, +they could be used with XCTSkip + +94 +00:06:03,297 --> 00:06:05,699 +to skip the test for actually +ordering donuts + +95 +00:06:05,732 --> 00:06:08,202 +when we are in a production environment. + +96 +00:06:08,235 --> 00:06:11,171 +Unless you are hungry, of course. + +97 +00:06:11,205 --> 00:06:12,773 +It’s important to keep in mind + +98 +00:06:12,806 --> 00:06:16,510 +that redefining an environment variable +in multiple places, + +99 +00:06:16,543 --> 00:06:20,080 +such as both a test plan and the Xcode +Cloud User Interface, + +100 +00:06:20,113 --> 00:06:22,649 +can lead to unexpected results. + +101 +00:06:22,683 --> 00:06:26,453 +In this particular case, +Xcode Cloud’s Environment variables + +102 +00:06:26,486 --> 00:06:31,592 +will take precedence over what’s +specified in your project’s test plans. + +103 +00:06:31,625 --> 00:06:35,295 +Now that we are referencing our +environment variable within our test code, + +104 +00:06:35,329 --> 00:06:38,999 +we can set its value +in the Xcode Cloud User Interface. + +105 +00:06:41,034 --> 00:06:43,637 +To do this, +navigate to your Cloud Reports, + +106 +00:06:43,670 --> 00:06:46,206 +and control-click on Food Truck. + +107 +00:06:48,876 --> 00:06:51,945 +To edit our environment variables +within our Workflows, + +108 +00:06:51,979 --> 00:06:57,050 +we will select +"Manage Workflows" in the context menu. + +109 +00:06:57,084 --> 00:07:00,287 +We are editing +the integration workflow specifically, + +110 +00:07:00,320 --> 00:07:03,390 +so we will double click on it. + +111 +00:07:03,423 --> 00:07:08,395 +Now, in the sidebar, +we can select "Environment," + +112 +00:07:08,428 --> 00:07:11,865 +and in the middle of the sheet, +under “Environment Variables," + +113 +00:07:11,899 --> 00:07:16,336 +we can add the variable’s name and value. + +114 +00:07:19,973 --> 00:07:22,142 +As an alternative to setting +the environment variable + +115 +00:07:22,176 --> 00:07:24,111 +in the Xcode Cloud Workflow, + +116 +00:07:24,144 --> 00:07:27,714 +we could instead set it +within the test plan. + +117 +00:07:28,482 --> 00:07:31,585 +In this example, +we don’t yet have a test plan. + +118 +00:07:31,618 --> 00:07:34,788 +To enable test plans, +open the scheme editor, + +119 +00:07:34,821 --> 00:07:36,857 +select "Test" in the sidebar, + +120 +00:07:36,890 --> 00:07:40,727 +and then click on +“Convert to Use Test Plans." + +121 +00:07:43,664 --> 00:07:47,568 +Okay, now we have a test plan +I called "Food Truck." + +122 +00:07:47,601 --> 00:07:49,336 +To add the environment variable, + +123 +00:07:49,369 --> 00:07:52,773 +we need to click on the test plan +to open its editor. + +124 +00:07:54,775 --> 00:07:58,745 +Near the top, we can select between +"Tests" and "Configurations." + +125 +00:07:58,779 --> 00:08:02,616 +Let’s select "Configurations." + +126 +00:08:02,649 --> 00:08:04,918 +Now, within the "Arguments" section, + +127 +00:08:04,952 --> 00:08:10,090 +we will add the variable +by clicking on "Environment Variables." + +128 +00:08:10,123 --> 00:08:15,796 +A popup will appear where we can enter +the variable’s name and value. + +129 +00:08:18,498 --> 00:08:21,802 +Now our test will be skipped +when in the production environment. + +130 +00:08:21,835 --> 00:08:24,137 +To learn more about skipping tests, + +131 +00:08:24,171 --> 00:08:27,407 +watch "XCTSkip your tests." + +132 +00:08:28,542 --> 00:08:33,480 +Now that we covered utilizing environment +variables to control XCTSkip, + +133 +00:08:33,514 --> 00:08:36,950 +let’s talk about expectation timeouts. + +134 +00:08:36,984 --> 00:08:41,154 +It is possible for a test to fail +due to an unexpected timeout. + +135 +00:08:41,188 --> 00:08:44,725 +For example, this could be the result +of a slow server + +136 +00:08:44,758 --> 00:08:48,729 +or an overly anxious user interface test. + +137 +00:08:48,762 --> 00:08:51,131 +One approach to resolve either issue + +138 +00:08:51,164 --> 00:08:54,635 +would be to increase +the XCTestExpectation timeout + +139 +00:08:54,668 --> 00:08:59,306 +so the interaction would +have enough time to finish. + +140 +00:08:59,339 --> 00:09:02,543 +In this example, +we increase the OrderDonut timeout + +141 +00:09:02,576 --> 00:09:07,514 +from 5 to 10 seconds to allow the server +more time to respond. + +142 +00:09:07,548 --> 00:09:10,551 +It is usually preferable to instead +replace both the app + +143 +00:09:10,584 --> 00:09:16,590 +and test code timeout handling +with async/await. + +144 +00:09:16,623 --> 00:09:18,725 +This approach allows the test to pause + +145 +00:09:18,759 --> 00:09:22,162 +until the await call finishes +without any timeout. + +146 +00:09:24,031 --> 00:09:26,066 +We have resolved time dependent tests, + +147 +00:09:26,099 --> 00:09:29,803 +so let’s handle any test failures +within the test suite. + +148 +00:09:29,837 --> 00:09:33,106 +For example, we have a test +that relies on a service + +149 +00:09:33,140 --> 00:09:37,377 +within the staging environment +that is down for maintenance. + +150 +00:09:37,411 --> 00:09:42,616 +We can use XCTExpectFailure instead +of disabling or skipping this test. + +151 +00:09:42,649 --> 00:09:46,386 +With XCTExpectFailure, +your test executes normally + +152 +00:09:46,420 --> 00:09:49,089 +and the results are transformed as +follows: + +153 +00:09:49,122 --> 00:09:52,759 +a failure in a test will now be reported +as an expected failure, + +154 +00:09:52,793 --> 00:09:56,964 +while that failed test within its suite +will be reported as a pass. + +155 +00:09:56,997 --> 00:10:01,301 +This approach eliminates the noise +generated by expected failures. + +156 +00:10:02,469 --> 00:10:06,473 +For example, testOrderDonut is failing. + +157 +00:10:06,507 --> 00:10:09,076 +I know that the service +that provides ordering donuts + +158 +00:10:09,109 --> 00:10:10,844 +is under maintenance right now, + +159 +00:10:10,878 --> 00:10:15,649 +so I added a call here +to XCTExpectFailure. + +160 +00:10:15,682 --> 00:10:18,886 +To learn more about XCTExpectFailure + +161 +00:10:18,919 --> 00:10:22,656 +watch "Embrace Expected Failures +in XCTest." + +162 +00:10:23,657 --> 00:10:25,826 +Now that we've declared expected failures, + +163 +00:10:25,859 --> 00:10:29,096 +let’s leverage test repetitions +to substantiate code + +164 +00:10:29,129 --> 00:10:31,498 +and diagnose unreliable code. + +165 +00:10:31,532 --> 00:10:35,736 +Test repetitions is a tool that +runs the same test multiple times + +166 +00:10:35,769 --> 00:10:37,237 +waiting for the following: + +167 +00:10:37,271 --> 00:10:42,743 +the first failure, the first success, +or a statistical result. + +168 +00:10:42,776 --> 00:10:44,511 +For example, at our desk, + +169 +00:10:44,545 --> 00:10:48,081 +we run our new code +and test cases multiple times + +170 +00:10:48,115 --> 00:10:52,386 +with repetitions to confirm initial app +and test code reliability + +171 +00:10:52,419 --> 00:10:54,988 +before checking in the code. + +172 +00:10:55,022 --> 00:11:00,260 +We were able to detect that testOrderDonut +had only an 80% success rate. + +173 +00:11:00,294 --> 00:11:01,695 +Uh-oh! + +174 +00:11:02,329 --> 00:11:06,233 +Knowing the failure exists, we now use +the repeat-until-failure mode + +175 +00:11:06,266 --> 00:11:08,502 +to locally diagnose the bug. + +176 +00:11:08,535 --> 00:11:13,540 +This is another way +of utilizing test repetitions. + +177 +00:11:13,574 --> 00:11:17,044 +For tests that rely +on an unreliable external service, + +178 +00:11:17,077 --> 00:11:20,581 +you may want to leverage +the retry-on-failure repetition policy + +179 +00:11:20,614 --> 00:11:23,984 +to confirm a test can succeed. + +180 +00:11:24,017 --> 00:11:26,520 +While retrying tests +is a powerful approach, + +181 +00:11:26,553 --> 00:11:30,824 +it’s preferable to instead mock +external services when possible. + +182 +00:11:30,858 --> 00:11:36,396 +Advantages of a mocked service include +deterministic reliability and speed. + +183 +00:11:36,430 --> 00:11:38,432 +To learn how to mock your dependencies, + +184 +00:11:38,465 --> 00:11:41,168 +watch “Testing Tips & Tricks.” + +185 +00:11:41,201 --> 00:11:45,138 +Let’s explore how test repetitions +can be enabled. + +186 +00:11:46,273 --> 00:11:48,842 +To enable test repetitions +in your test plan, + +187 +00:11:48,876 --> 00:11:53,113 +go back to the test plan editor +and select "Configurations." + +188 +00:11:56,250 --> 00:11:59,086 +Then, under the "Test Execution" section, + +189 +00:11:59,119 --> 00:12:02,623 +there is a popup to select +your test repetition mode. + +190 +00:12:04,258 --> 00:12:07,494 +In this case, +we will select "Retry on Failure," + +191 +00:12:07,528 --> 00:12:13,166 +which is used primarily to work around +unreliable external services. + +192 +00:12:13,200 --> 00:12:16,069 +Now we have +our test repetition mode enabled. + +193 +00:12:16,103 --> 00:12:19,206 +For more information on +leveraging test repetitions, + +194 +00:12:19,239 --> 00:12:24,511 +watch "Diagnose unreliable code +with test repetition." + +195 +00:12:24,545 --> 00:12:26,980 +So we have gone over a variety of tools + +196 +00:12:27,014 --> 00:12:30,784 +that can be used +to improve test reliability. + +197 +00:12:30,817 --> 00:12:33,820 +For more information +about writing quality tests, + +198 +00:12:33,854 --> 00:12:36,557 +watch "Write tests to fail." + +199 +00:12:36,590 --> 00:12:40,761 +Now that our tests are reliable, +let’s make them run fast! + +200 +00:12:40,794 --> 00:12:44,898 +A number of configuration options exist +for achieving faster results. + +201 +00:12:44,932 --> 00:12:49,570 +Let’s do what we can to reduce +the time it takes to run the test suite. + +202 +00:12:51,572 --> 00:12:53,807 +One technique we use +to improve performance + +203 +00:12:53,841 --> 00:12:56,877 +is to split our tests +into multiple test plans. + +204 +00:12:56,910 --> 00:13:00,647 +Sometimes, two is enough. + +205 +00:13:00,681 --> 00:13:04,251 +You can identify a reduced set of tests +to verify + +206 +00:13:04,284 --> 00:13:07,554 +as part of each open or update +to a pull request. + +207 +00:13:10,557 --> 00:13:13,093 +For example, we could run the unit tests + +208 +00:13:13,126 --> 00:13:18,599 +along with just a key subset of user +interface tests for a single platform. + +209 +00:13:23,504 --> 00:13:27,674 +The full set of tests on all supported +platforms can still be run, + +210 +00:13:27,708 --> 00:13:31,778 +but now in the background, +and not blocking pull requests. + +211 +00:13:32,779 --> 00:13:35,849 +This approach allows us +to add tests and new platforms + +212 +00:13:35,883 --> 00:13:39,219 +while keeping our continuous +integration process timely. + +213 +00:13:40,320 --> 00:13:43,457 +Let’s set up a workflow to +run a select set of tests. + +214 +00:13:43,490 --> 00:13:47,094 +In this example, we have already +created a new test plan + +215 +00:13:47,127 --> 00:13:51,665 +called “Pull Requests," +and have it open in the test plan editor. + +216 +00:13:51,698 --> 00:13:55,702 +Near the top we can selectw +between "Tests" and "Configurations." + +217 +00:13:55,736 --> 00:13:58,071 +Let’s select "Tests." + +218 +00:14:01,742 --> 00:14:07,581 +Here we have chosen a subset of tests +to be verified for a pull request. + +219 +00:14:07,614 --> 00:14:11,818 +Now to setup a Workflow +to run our “Pull Requests” test plan, + +220 +00:14:11,852 --> 00:14:14,855 +we will go back to +Xcode Cloud Manage Workflows + +221 +00:14:14,888 --> 00:14:21,261 +just like we did when we added an +environment variable for skipping tests. + +222 +00:14:21,295 --> 00:14:24,231 +To create a new workflow, +we will click the "Add" button + +223 +00:14:24,264 --> 00:14:28,435 +at the bottom left +of the “Manage Workflow” sheet. + +224 +00:14:28,468 --> 00:14:32,406 +For simplicity, let’s also name +our workflow “Pull Requests” + +225 +00:14:32,439 --> 00:14:34,875 +and choose a start condition. + +226 +00:14:34,908 --> 00:14:39,413 +We want this workflow to prevent +any check-ins with failing tests. + +227 +00:14:39,446 --> 00:14:42,416 +In the sidebar, +to the right of "Start Conditions," + +228 +00:14:42,449 --> 00:14:44,284 +we will click the "Add" button. + +229 +00:14:48,055 --> 00:14:51,558 +A menu will appear +showing the start condition options. + +230 +00:14:51,592 --> 00:14:55,696 +In our case, +we will select “Pull Request Changes." + +231 +00:14:56,964 --> 00:15:00,434 +Now, we now have +a pull request start condition. + +232 +00:15:00,467 --> 00:15:05,239 +Running tests require that +the Food Truck app first be built. + +233 +00:15:05,272 --> 00:15:08,642 +To do this, we need to add a build action. + +234 +00:15:08,675 --> 00:15:12,145 +Again in the sidebar, +below the “Start Conditions,” + +235 +00:15:12,179 --> 00:15:14,348 +let’s add an action. + +236 +00:15:14,381 --> 00:15:17,117 +We will click the "Add" button +next to “Actions," + +237 +00:15:17,150 --> 00:15:20,821 +and then select “Build” +from the context menu. + +238 +00:15:24,258 --> 00:15:26,627 +Now that we have an action +that builds our app, + +239 +00:15:26,660 --> 00:15:29,096 +we will add another to run our tests. + +240 +00:15:29,129 --> 00:15:33,700 +Again we will click add action, +but this time we will select "Test." + +241 +00:15:36,837 --> 00:15:39,072 +Great, we have a test action. + +242 +00:15:39,106 --> 00:15:42,743 +Let’s select the test plan to run. + +243 +00:15:42,776 --> 00:15:46,647 +In the middle of the sheet, +there is a drop down for test. + +244 +00:15:46,680 --> 00:15:50,083 +Here we can select +our “Pull Requests” test plan. + +245 +00:15:53,287 --> 00:15:54,388 +Awesome! + +246 +00:15:54,421 --> 00:15:58,525 +Now our Workflow is configured +to run our test plan on pull request. + +247 +00:15:58,559 --> 00:16:03,230 +To create a second workflow that will run +your complete test suite on a schedule, + +248 +00:16:03,263 --> 00:16:05,499 +you can follow a similar set of steps. + +249 +00:16:05,532 --> 00:16:08,602 +However, this time, +select the start condition + +250 +00:16:08,635 --> 00:16:11,171 +to be “On a Schedule for a Branch” + +251 +00:16:11,205 --> 00:16:15,042 +and then set the workflow +to run your full suite test plan. + +252 +00:16:15,742 --> 00:16:18,345 +We have both Workflows +configured in Xcode Cloud + +253 +00:16:18,378 --> 00:16:22,316 +and running their associated test plans. + +254 +00:16:22,349 --> 00:16:26,753 +To learn more about test plans, +check out "Testing in Xcode." + +255 +00:16:28,555 --> 00:16:32,960 +Now we have created pull request +and scheduled workflow test sets. + +256 +00:16:32,993 --> 00:16:37,831 +Another improvement we can make for speed +is to run tests concurrently. + +257 +00:16:37,865 --> 00:16:43,103 +By default, Xcode Cloud +tests your platforms in parallel. + +258 +00:16:43,136 --> 00:16:46,473 +In addition, you can enable Xcode +to run tests in parallel + +259 +00:16:46,507 --> 00:16:49,510 +on a target and test object class level. + +260 +00:16:52,513 --> 00:16:55,749 +To enable parallel test execution +in Xcode, + +261 +00:16:55,782 --> 00:16:59,853 +we will again go to our test plan editor +and select "Tests." + +262 +00:17:04,258 --> 00:17:07,561 +Then, to the right of our +“Food Truck Tests" test bundle, + +263 +00:17:07,594 --> 00:17:09,663 +we click the “Options” button. + +264 +00:17:11,665 --> 00:17:16,470 +One of the options allows us +to “Execute in parallel” when possible. + +265 +00:17:16,503 --> 00:17:18,906 +If the server has enough cores available, + +266 +00:17:18,939 --> 00:17:23,710 +multiple targets and test object classes +can be executed concurrently. + +267 +00:17:23,744 --> 00:17:28,482 +So let’s enable this option to improve +our test suite turnaround time. + +268 +00:17:29,516 --> 00:17:33,253 +Now our tests are configured +to run in parallel. + +269 +00:17:33,287 --> 00:17:36,557 +Note that tests must be designed +to run independently + +270 +00:17:36,590 --> 00:17:39,760 +to take advantage of parallel execution. + +271 +00:17:39,793 --> 00:17:44,965 +Proper setup and teardown are essential +to reliable test case behavior. + +272 +00:17:47,100 --> 00:17:48,802 +With our tests running in parallel, + +273 +00:17:48,836 --> 00:17:51,972 +it’s time to turn our attention +to runaway tests. + +274 +00:17:52,005 --> 00:17:56,310 +Runaway tests are those tests +that don’t end in a timely manner. + +275 +00:17:56,343 --> 00:17:58,745 +Some examples include an infinite loop + +276 +00:17:58,779 --> 00:18:01,715 +or waiting indefinitely +for a failed server. + +277 +00:18:03,050 --> 00:18:07,821 +We can halt these tests by setting an +execution time allowance in our test plan. + +278 +00:18:07,855 --> 00:18:12,359 +The execution time allowance specifies +the number of seconds for a test to run + +279 +00:18:12,392 --> 00:18:15,729 +before it fails with a timeout error. + +280 +00:18:15,762 --> 00:18:20,534 +This prevents a test suite from +getting stuck on an individual test. + +281 +00:18:23,370 --> 00:18:26,840 +In this case, +the fifth test got stuck for some reason. + +282 +00:18:26,874 --> 00:18:28,842 +By setting the execution time allowance, + +283 +00:18:28,876 --> 00:18:33,247 +this runaway test was eventually halted +and marked as a failure. + +284 +00:18:33,280 --> 00:18:38,318 +The XCTest Test Runner then continued +running the next test in the suite. + +285 +00:18:38,352 --> 00:18:42,022 +Let’s configure an execution +time allowance for our test plan. + +286 +00:18:44,024 --> 00:18:46,560 +To set an execution time allowance, + +287 +00:18:46,593 --> 00:18:51,265 +we will go to our test plan editor +and select “Configurations." + +288 +00:18:53,967 --> 00:18:58,071 +Under the “Test Execution” category, +we can enable “Test Timeouts” + +289 +00:18:58,105 --> 00:19:00,774 +and specify the number of seconds to wait. + +290 +00:19:00,807 --> 00:19:03,911 +Note that the default is 600 seconds. + +291 +00:19:05,245 --> 00:19:08,248 +Having configured the maximum +execution time allowance, + +292 +00:19:08,282 --> 00:19:12,653 +a single runaway test will no longer +disrupt our testing workflow. + +293 +00:19:12,686 --> 00:19:16,523 +For example, an overnight test suite can +now complete on time + +294 +00:19:16,557 --> 00:19:19,860 +and provide a full set of useful results. + +295 +00:19:19,893 --> 00:19:23,030 +Yay! +We finally stopped those runaway tests, + +296 +00:19:23,063 --> 00:19:26,166 +so we are able to move on +to the next improvement. + +297 +00:19:26,200 --> 00:19:30,003 +As you may recall, we were able +to leverage test repetitions + +298 +00:19:30,037 --> 00:19:34,875 +to increase reliability +of tests that rely on external services. + +299 +00:19:34,908 --> 00:19:37,678 +We configured our test plan +to retry on failure + +300 +00:19:37,711 --> 00:19:41,081 +and selected +a sufficient repetition value. + +301 +00:19:41,114 --> 00:19:45,919 +However, these repetitions can add to +the time it takes to run the test suite. + +302 +00:19:47,588 --> 00:19:49,756 +Unnecessary repetitions are wasteful + +303 +00:19:49,790 --> 00:19:54,628 +and you may want to optimize +test repetition value to a lower number. + +304 +00:19:54,661 --> 00:19:58,832 +Furthermore, you may consider +removing the problematic test altogether + +305 +00:19:58,866 --> 00:20:01,235 +from the pull request workflow. + +306 +00:20:01,268 --> 00:20:03,670 +Let’s go over how we might do this. + +307 +00:20:05,038 --> 00:20:09,610 +Let’s return to the test repetitions +configuration in the test plan editor. + +308 +00:20:11,812 --> 00:20:16,350 +Earlier we set the test repetition mode +to “Retry on Failure." + +309 +00:20:16,383 --> 00:20:20,320 +Now we can adjust +the "Maximum Test Repetitions” value. + +310 +00:20:20,354 --> 00:20:24,057 +For example, we may have chosen +to allow up to 10 attempts + +311 +00:20:24,091 --> 00:20:26,627 +for a test that relies +on an external server + +312 +00:20:26,660 --> 00:20:29,329 +that fails 5% of the time. + +313 +00:20:29,363 --> 00:20:32,599 +Most of the time, +we will succeed on the first attempt. + +314 +00:20:32,633 --> 00:20:36,036 +However, if that same test +has an unrelated bug, + +315 +00:20:36,069 --> 00:20:39,740 +it will fail every time +and use all 10 attempts. + +316 +00:20:39,773 --> 00:20:43,944 +Maybe 3 attempts would be sufficient +and a better choice. + +317 +00:20:45,612 --> 00:20:48,882 +While we want to reduce retries +to improve performance, + +318 +00:20:48,916 --> 00:20:52,119 +note that earlier we recommended +increasing retries + +319 +00:20:52,152 --> 00:20:55,422 +to improve reliability in some cases. + +320 +00:20:55,455 --> 00:20:59,726 +As such, this minimally chosen value +must continue be sufficient + +321 +00:20:59,760 --> 00:21:02,095 +to run those tests reliably. + +322 +00:21:02,129 --> 00:21:07,201 +That wraps up +configuring for faster results. + +323 +00:21:07,234 --> 00:21:10,504 +To go into more detail +on getting faster test results, + +324 +00:21:10,537 --> 00:21:13,574 +check out "Get your test results faster." + +325 +00:21:14,575 --> 00:21:16,877 +To recap, +we covered the most effective ways + +326 +00:21:16,910 --> 00:21:19,313 +to begin testing for Xcode Cloud. + +327 +00:21:19,346 --> 00:21:23,417 +We focused on configuring tests to be both +reliable and fast + +328 +00:21:23,450 --> 00:21:28,822 +so that you can avoid irrelevant failures +and verify your code changes quickly. + +329 +00:21:28,856 --> 00:21:32,826 +Thank you, and I hope you enjoy +the rest of your WWDC! + +330 +00:21:32,860 --> 00:21:35,863 +[upbeat music] + diff --git a/eng/2022 Session 110362 Link fast - Improve build and launch times en.srt b/eng/2022 Session 110362 Link fast - Improve build and launch times en.srt new file mode 100644 index 0000000..7fd5887 --- /dev/null +++ b/eng/2022 Session 110362 Link fast - Improve build and launch times en.srt @@ -0,0 +1,2486 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,676 --> 00:00:13,180 +Nick Kledzik: Hi, I'm Nick Kledzik, +lead Engineer on Apple's Linker team. + +3 +00:00:13,213 --> 00:00:16,316 +Today, I'd like to share with you +how to link fast. + +4 +00:00:16,350 --> 00:00:18,752 +I'll tell you what Apple has done +to improve linking, + +5 +00:00:18,785 --> 00:00:21,622 +as well as help you understand +what actually happens during linking + +6 +00:00:21,655 --> 00:00:25,325 +so that you can improve +your app's link performance. + +7 +00:00:25,359 --> 00:00:27,394 +So what is linking? + +8 +00:00:27,427 --> 00:00:31,198 +You've written code, but you also use code +that someone else wrote + +9 +00:00:31,231 --> 00:00:33,734 +in the form of a library or a framework. + +10 +00:00:33,767 --> 00:00:38,172 +In order for your code to use +those libraries, a linker is needed. + +11 +00:00:38,205 --> 00:00:41,141 +Now, there are actually +two types of linking. + +12 +00:00:41,175 --> 00:00:44,711 +There is "static linking", +which happens when you build your app. + +13 +00:00:44,745 --> 00:00:47,214 +This can impact how long it takes +for your app to build + +14 +00:00:47,247 --> 00:00:50,350 +and how big your app ends up being. + +15 +00:00:50,384 --> 00:00:52,452 +And there is "dynamic linking". + +16 +00:00:52,486 --> 00:00:54,821 +This happens when your app is launched. + +17 +00:00:54,855 --> 00:00:59,393 +This can impact how long your customers +have to wait for your app to launch. + +18 +00:00:59,426 --> 00:01:04,464 +In this session I'll be talking about +both static and dynamic linking. + +19 +00:01:04,498 --> 00:01:08,101 +First, I'll define what static linking is +and where it came from, + +20 +00:01:08,135 --> 00:01:10,437 +with some examples. + +21 +00:01:10,470 --> 00:01:16,476 +Next, I'll unveil what is new +in ld64, Apple's static linker. + +22 +00:01:16,510 --> 00:01:18,779 +Then, with this background +on static linking, + +23 +00:01:18,812 --> 00:01:22,950 +I'll detail best practices +for static linking. + +24 +00:01:22,983 --> 00:01:26,286 +The second half of this talk +will cover dynamic linking. + +25 +00:01:26,320 --> 00:01:29,489 +I'll show what dynamic linking is +and where it came from, + +26 +00:01:29,523 --> 00:01:32,960 +and what happens during dynamic linking. + +27 +00:01:32,993 --> 00:01:37,998 +Next, I'll reveal what is new +in dyld this year. + +28 +00:01:38,031 --> 00:01:41,235 +Then, I'll talk about what you can do +to improve your app's + +29 +00:01:41,268 --> 00:01:44,738 +dynamic link time performance. + +30 +00:01:44,771 --> 00:01:47,908 +And lastly, +we'll wrap up with two new tools + +31 +00:01:47,941 --> 00:01:50,444 +that will help you +peek behind the curtains. + +32 +00:01:50,477 --> 00:01:53,280 +You'll be able to see +what is in your binaries, + +33 +00:01:53,313 --> 00:01:57,651 +and what is happening during +dynamic linking. + +34 +00:01:57,684 --> 00:01:59,553 +To understand static linking, + +35 +00:01:59,586 --> 00:02:02,589 +let's go way back to when it all started. + +36 +00:02:02,623 --> 00:02:06,927 +In the beginning, programs were simple +and there was just one source file. + +37 +00:02:06,960 --> 00:02:08,228 +Building was easy. + +38 +00:02:08,262 --> 00:02:10,664 +You just ran the compiler +on your one source file + +39 +00:02:10,697 --> 00:02:13,567 +and it produced the executable program. + +40 +00:02:13,600 --> 00:02:18,205 +But having all your source code +in one file did not scale. + +41 +00:02:18,238 --> 00:02:21,141 +How do you build +with multiple source files? + +42 +00:02:21,175 --> 00:02:24,745 +And this is not just because +you don't want to edit a large text file. + +43 +00:02:24,778 --> 00:02:27,581 +The real savings is +not re-compiling every function, + +44 +00:02:27,614 --> 00:02:29,583 +every time you build. + +45 +00:02:29,616 --> 00:02:33,387 +What they did was to split the compiler +into two parts. + +46 +00:02:33,420 --> 00:02:35,489 +The first part compiles source code + +47 +00:02:35,522 --> 00:02:39,793 +to a new intermediate +"relocatable object" file. + +48 +00:02:39,826 --> 00:02:42,863 +The second part reads +the relocatable .o file + +49 +00:02:42,896 --> 00:02:45,499 +and produces an executable program. + +50 +00:02:45,532 --> 00:02:49,369 +We now call the second part 'ld', +the static linker. + +51 +00:02:49,403 --> 00:02:52,873 +So now you know +where static linking came from. + +52 +00:02:52,906 --> 00:02:56,944 +As software evolved, +soon people were passing around .o files. + +53 +00:02:56,977 --> 00:02:58,879 +But that got cumbersome. + +54 +00:02:58,912 --> 00:03:01,248 +Someone thought, "Wouldn't it be great +if we could package up + +55 +00:03:01,281 --> 00:03:04,818 +a set of .o files into a 'library'?" + +56 +00:03:04,852 --> 00:03:07,120 +At the time the standard way +to bundle files together + +57 +00:03:07,154 --> 00:03:09,656 +was with the archiving tool 'ar'. + +58 +00:03:09,690 --> 00:03:12,226 +It was used for backups and distributions. + +59 +00:03:12,259 --> 00:03:15,629 +So the workflow became this. + +60 +00:03:15,662 --> 00:03:19,433 +You could 'ar' up multiple .o files +into an archive, + +61 +00:03:19,466 --> 00:03:22,870 +and the linker was enhanced +to know how to read .o files directly + +62 +00:03:22,903 --> 00:03:25,172 +out of an archive file. + +63 +00:03:25,205 --> 00:03:28,408 +This was a great improvement +for sharing common code. + +64 +00:03:28,442 --> 00:03:32,212 +At the time it was just called a library +or an archive. + +65 +00:03:32,246 --> 00:03:35,382 +Today, we call it a static library. + +66 +00:03:35,415 --> 00:03:39,253 +But now the final program was getting big +because thousands of functions + +67 +00:03:39,286 --> 00:03:41,488 +from these libraries were copied into it, + +68 +00:03:41,522 --> 00:03:44,658 +even if only a few of those functions +were used. + +69 +00:03:44,691 --> 00:03:46,894 +So a clever optimization was added. + +70 +00:03:46,927 --> 00:03:51,131 +Instead of having the linker use +all the .o files from the static library, + +71 +00:03:51,164 --> 00:03:54,468 +the linker would only pull +.o files from a static library + +72 +00:03:54,501 --> 00:03:57,838 +if doing so would resolve +some undefined symbol. + +73 +00:03:57,871 --> 00:04:01,675 +That meant someone could build +a big libc.a static library, + +74 +00:04:01,708 --> 00:04:05,078 +which contained +all the C standard library functions. + +75 +00:04:05,112 --> 00:04:08,215 +Every program +could link with the one libc.a, + +76 +00:04:08,248 --> 00:04:12,819 +but each program only got the parts +of libc that the program actually needed. + +77 +00:04:12,853 --> 00:04:15,022 +And we still have that model today. + +78 +00:04:15,055 --> 00:04:17,991 +But the selective loading from static +libraries is not obvious + +79 +00:04:18,025 --> 00:04:20,761 +and trips up many programmers. + +80 +00:04:20,794 --> 00:04:24,631 +To make the selective loading +of static libraries a little clearer, + +81 +00:04:24,665 --> 00:04:26,567 +I have a simple scenario. + +82 +00:04:26,600 --> 00:04:32,573 +In main.c, there's a function +called "main" that calls a function "foo". + +83 +00:04:32,606 --> 00:04:36,777 +In foo.c, there is foo which calls bar. + +84 +00:04:36,810 --> 00:04:39,713 +In bar.c, +there is the implementation of bar + +85 +00:04:39,746 --> 00:04:45,252 +but also an implementation of another +function which happens to be unused. + +86 +00:04:45,285 --> 00:04:51,592 +Lastly, in baz.c, there is a function baz +which calls a function named undef. + +87 +00:04:51,625 --> 00:04:56,363 +Now we compile each to its own .o file. + +88 +00:04:56,396 --> 00:05:00,501 +You'll see foo, bar, and undef +don't have gray boxes + +89 +00:05:00,534 --> 00:05:02,469 +because they are undefined. + +90 +00:05:02,503 --> 00:05:06,340 +That is, a use of a symbol +and not a definition. + +91 +00:05:06,373 --> 00:05:12,746 +Now, let's say you decide to combine +bar.o and baz.o into a static library. + +92 +00:05:12,779 --> 00:05:16,817 +Next, you link the two .o files +and the static library. + +93 +00:05:16,850 --> 00:05:19,119 +Let's step through what actually happens. + +94 +00:05:20,053 --> 00:05:23,757 +First, the linker works through the files +in command line order. + +95 +00:05:23,790 --> 00:05:26,560 +The first it finds is main.o. + +96 +00:05:26,593 --> 00:05:29,663 +It loads main.o +and finds a definition for "main", + +97 +00:05:29,696 --> 00:05:31,632 +shown here in the symbol table. + +98 +00:05:31,665 --> 00:05:36,370 +But also finds that main +has an undefined "foo". + +99 +00:05:36,403 --> 00:05:41,642 +The linker then parses the next file +on the command line which is foo.o. + +100 +00:05:41,675 --> 00:05:44,211 +This file adds a definition of "foo". + +101 +00:05:44,244 --> 00:05:47,214 +That means foo is no longer undefined. + +102 +00:05:47,247 --> 00:05:52,586 +But loading foo.o also adds +a new undefined symbol for "bar". + +103 +00:05:52,619 --> 00:05:55,989 +Now that all the .o files +on the command line have been loaded, + +104 +00:05:56,023 --> 00:05:59,593 +the linker checks if there are +any remaining undefined symbols. + +105 +00:05:59,626 --> 00:06:02,663 +In this case "bar" remains undefined, + +106 +00:06:02,696 --> 00:06:06,033 +so the linker starts looking at libraries +on the command line + +107 +00:06:06,066 --> 00:06:10,637 +to see if a library will satisfy +that missing undefined symbol "bar". + +108 +00:06:10,671 --> 00:06:16,276 +The linker finds that bar.o in the static +library defines the symbol "bar". + +109 +00:06:16,310 --> 00:06:20,147 +So the linker loads bar.o +out of the archive. + +110 +00:06:20,180 --> 00:06:23,250 +At that point there are no longer +any undefined symbols, + +111 +00:06:23,283 --> 00:06:26,920 +so the linker stops processing libraries. + +112 +00:06:26,954 --> 00:06:29,389 +The linker moves on to its next phase, + +113 +00:06:29,423 --> 00:06:31,625 +and assigns addresses to all the functions +and data + +114 +00:06:31,658 --> 00:06:33,794 +that will be in the program. + +115 +00:06:33,827 --> 00:06:38,198 +Then it copies all the functions +and data to the output file. + +116 +00:06:38,232 --> 00:06:40,801 +Et voila! +You have your output program. + +117 +00:06:40,834 --> 00:06:46,607 +Notice that baz.o was in the static +library but not loaded into the program. + +118 +00:06:46,640 --> 00:06:50,344 +It was not loaded because the way +the linker selectively loads + +119 +00:06:50,377 --> 00:06:52,179 +from static libraries. + +120 +00:06:52,212 --> 00:06:56,450 +This is non-obvious, +but the key aspect of static libraries. + +121 +00:06:56,483 --> 00:07:00,721 +Now you understand the basics +of static linking and static libraries. + +122 +00:07:00,754 --> 00:07:03,790 +Let's move on to recent improvements +on Apple's static linker, + +123 +00:07:03,824 --> 00:07:06,026 +known as ld64. + +124 +00:07:06,059 --> 00:07:10,297 +By popular demand, we spent some time +this year optimizing ld64. + +125 +00:07:10,330 --> 00:07:12,466 +And this year's linker is... + +126 +00:07:12,499 --> 00:07:15,269 +twice as fast for many projects. + +127 +00:07:15,302 --> 00:07:16,603 +How did we do this? + +128 +00:07:16,637 --> 00:07:21,008 +We now make better use of the cores +on your development machine. + +129 +00:07:21,041 --> 00:07:23,544 +We found a number of areas +where we could use multiple cores + +130 +00:07:23,577 --> 00:07:25,913 +to do linker work in parallel. + +131 +00:07:25,946 --> 00:07:30,918 +That includes copying content +from the input to the output file, + +132 +00:07:30,951 --> 00:07:34,454 +building the different parts +of LINKEDIT in parallel, + +133 +00:07:34,488 --> 00:07:37,291 +and changing the UUID computation + +134 +00:07:37,324 --> 00:07:40,527 +and codesigning hashes +to be done in parallel. + +135 +00:07:40,561 --> 00:07:44,064 +Next, we improved a number of algorithms. + +136 +00:07:44,097 --> 00:07:47,034 +Turns out the exports-trie builder +works really well + +137 +00:07:47,067 --> 00:07:49,870 +if you switch to use +C++ string_view objects + +138 +00:07:49,903 --> 00:07:53,273 +to represent the string slices +of each symbol. + +139 +00:07:53,307 --> 00:07:55,843 +We also used the latest crypto libraries + +140 +00:07:55,876 --> 00:07:58,178 +which take advantage +of hardware acceleration + +141 +00:07:58,212 --> 00:08:01,381 +when computing the UUID of a binary, + +142 +00:08:01,415 --> 00:08:03,884 +and we improved other algorithms too. + +143 +00:08:04,952 --> 00:08:07,521 +While working on +improving linker performance, + +144 +00:08:07,554 --> 00:08:12,526 +we noticed configuration issues +in some apps that impacted link time. + +145 +00:08:12,559 --> 00:08:17,331 +Next, I'll talk about what you can do +in your project to improve link time. + +146 +00:08:17,364 --> 00:08:19,333 +I'll cover five topics. + +147 +00:08:19,366 --> 00:08:22,202 +First, +whether you should use static libraries. + +148 +00:08:22,236 --> 00:08:27,040 +And then three little known options +that have a big effect on your link time. + +149 +00:08:27,074 --> 00:08:32,145 +Finally, I'll discuss some static linking +behavior that might surprise you. + +150 +00:08:32,179 --> 00:08:35,516 +The first topic is if you are +actively working on a source file + +151 +00:08:35,549 --> 00:08:37,618 +that builds into a static library, + +152 +00:08:37,651 --> 00:08:40,454 +you've introduced +a slowdown to your build time. + +153 +00:08:40,487 --> 00:08:42,322 +Because after the file is compiled, + +154 +00:08:42,356 --> 00:08:45,058 +the entire static library +has to be rebuilt, + +155 +00:08:45,092 --> 00:08:47,628 +including its table of contents. + +156 +00:08:47,661 --> 00:08:50,097 +This is just a lot of extra I/O. + +157 +00:08:50,130 --> 00:08:53,367 +Static libraries make +the most sense for stable code. + +158 +00:08:53,400 --> 00:08:56,537 +That is, code not being actively changed. + +159 +00:08:56,570 --> 00:08:59,239 +You should consider moving code +under active development + +160 +00:08:59,273 --> 00:09:02,976 +out of a static library +to reduce build time. + +161 +00:09:03,010 --> 00:09:06,914 +Earlier we showed +the selective loading from archives. + +162 +00:09:06,947 --> 00:09:10,450 +But a downside of that +is that it slows down the linker. + +163 +00:09:10,484 --> 00:09:13,587 +That is because +to make builds reproducible + +164 +00:09:13,620 --> 00:09:16,490 +and follow +traditional static library semantics, + +165 +00:09:16,523 --> 00:09:21,028 +the linker has to process static libraries +in a fixed, serial order. + +166 +00:09:21,061 --> 00:09:24,264 +That means some of the parallelization +wins of ld64 + +167 +00:09:24,298 --> 00:09:26,800 +cannot be used with static libraries. + +168 +00:09:26,834 --> 00:09:29,870 +But if you don't really need +this historical behavior, + +169 +00:09:29,903 --> 00:09:33,540 +you can use a linker option +to speed up your build. + +170 +00:09:33,574 --> 00:09:36,977 +That linker option is called "all load". + +171 +00:09:37,010 --> 00:09:42,316 +It tells the linker to blindly load +all .o files from all static libraries. + +172 +00:09:42,349 --> 00:09:45,719 +This is helpful if your app is +going to wind up selectively loading + +173 +00:09:45,752 --> 00:09:49,690 +most of the content +from all the static libraries anyways. + +174 +00:09:49,723 --> 00:09:53,393 +Using -all_load will allow the linker +to parse all the static libraries + +175 +00:09:53,427 --> 00:09:55,729 +and their content in parallel. + +176 +00:09:55,762 --> 00:09:59,933 +But if your app does clever tricks +where it has multiple static libraries + +177 +00:09:59,967 --> 00:10:02,002 +implementing the same symbols, + +178 +00:10:02,035 --> 00:10:05,138 +and depends on the command line order +of the static libraries + +179 +00:10:05,172 --> 00:10:08,075 +to drive which implementation is used, + +180 +00:10:08,108 --> 00:10:10,210 +then this option is not for you. + +181 +00:10:10,244 --> 00:10:12,679 +Because the linker will load +all the implementations + +182 +00:10:12,713 --> 00:10:15,082 +and not necessarily +get the symbol semantics + +183 +00:10:15,115 --> 00:10:18,385 +that were found +in regular static linking mode. + +184 +00:10:18,418 --> 00:10:23,190 +The other downside of -all_load +is that it may make your program bigger + +185 +00:10:23,223 --> 00:10:26,593 +because "unused" code +is now being added in. + +186 +00:10:26,627 --> 00:10:28,629 +To compensate for that, + +187 +00:10:28,662 --> 00:10:30,998 +you can use the linker +option -dead_strip. + +188 +00:10:31,031 --> 00:10:36,236 +That option will cause the linker +to remove unreachable code and data. + +189 +00:10:36,270 --> 00:10:40,541 +Now, the dead stripping algorithm is fast +and usually pays for itself + +190 +00:10:40,574 --> 00:10:42,876 +by reducing the size of the output file. + +191 +00:10:42,910 --> 00:10:47,347 +But if you are interested +in using -all_load and -dead_strip, + +192 +00:10:47,381 --> 00:10:50,517 +you should time the linker +with and without those options + +193 +00:10:50,551 --> 00:10:54,421 +to see if it is a win +for your particular case. + +194 +00:10:54,454 --> 00:10:58,625 +The next linker option +is -no_exported_symbols. + +195 +00:10:58,659 --> 00:11:00,227 +A little background here. + +196 +00:11:00,260 --> 00:11:03,163 +One part of the LINKEDIT segment +that the linker generates + +197 +00:11:03,197 --> 00:11:06,333 +is an exports trie, +which is a prefix tree + +198 +00:11:06,366 --> 00:11:10,571 +that encodes all the exported symbol +names, addresses, and flags. + +199 +00:11:10,604 --> 00:11:13,841 +Whereas all dylibs need +to have exported symbols, + +200 +00:11:13,874 --> 00:11:18,812 +a main app binary usually +does not need any exported symbols. + +201 +00:11:18,846 --> 00:11:23,083 +That is, usually nothing ever looks up +symbols in the main executable. + +202 +00:11:23,116 --> 00:11:26,353 +If that is the case, +you can use -no_exported_symbols + +203 +00:11:26,386 --> 00:11:31,225 +for the app target to skip the creation +of the trie data structure in LINKEDIT, + +204 +00:11:31,258 --> 00:11:33,527 +which will improve link time. + +205 +00:11:33,560 --> 00:11:37,931 +But if your app loads plugins +which link back to the main executable, + +206 +00:11:37,965 --> 00:11:43,670 +or you use xctest with your app as the +host environment to run xctest bundles, + +207 +00:11:43,704 --> 00:11:46,006 +your app must have all its exports, + +208 +00:11:46,039 --> 00:11:48,809 +which means you cannot use +-no_exported_symbols + +209 +00:11:48,842 --> 00:11:50,978 +for that configuration. + +210 +00:11:51,011 --> 00:11:55,649 +Now, it only makes sense to try to +suppress the exports trie if it is large. + +211 +00:11:55,682 --> 00:11:58,118 +You can run the dyld_info command +shown here + +212 +00:11:58,151 --> 00:12:00,687 +to count the number of exported symbols. + +213 +00:12:00,721 --> 00:12:04,358 +One large app we saw had about +one million exported symbols. + +214 +00:12:04,391 --> 00:12:07,494 +And the linker took two to three seconds +to build the exports trie + +215 +00:12:07,528 --> 00:12:09,329 +for that many symbols. + +216 +00:12:09,363 --> 00:12:12,666 +So adding -no_exported_symbols +shaved two to three seconds + +217 +00:12:12,699 --> 00:12:14,701 +off the link time of that app. + +218 +00:12:14,735 --> 00:12:18,739 +I'll tell you more about +the dyld_info tool later in this talk. + +219 +00:12:18,772 --> 00:12:23,010 +The next option is: -no_deduplicate. + +220 +00:12:23,043 --> 00:12:26,413 +A few years back we added a new pass +to the linker to merge functions + +221 +00:12:26,446 --> 00:12:29,583 +that have the same instructions +but different names. + +222 +00:12:29,616 --> 00:12:33,687 +It turns out, with C++ template +expansions, you can get a lot of those. + +223 +00:12:33,720 --> 00:12:36,290 +But this is an expensive algorithm. + +224 +00:12:36,323 --> 00:12:40,160 +The linker has to recursively hash +the instructions of every function, + +225 +00:12:40,194 --> 00:12:42,362 +to help look for duplicates. + +226 +00:12:42,396 --> 00:12:44,932 +Because of the expense, +we limited the algorithm + +227 +00:12:44,965 --> 00:12:48,135 +so the linker only looks +at weak-def symbols. + +228 +00:12:48,168 --> 00:12:51,772 +Those are the ones the C++ compiler emits +for template expansions + +229 +00:12:51,805 --> 00:12:54,541 +that were not inlined. + +230 +00:12:54,575 --> 00:12:57,477 +Now, de-dup is a size optimization, + +231 +00:12:57,511 --> 00:13:01,415 +and Debug builds are about fast builds, +and not about size. + +232 +00:13:01,448 --> 00:13:05,586 +So by default, Xcode disables +the de-dup optimization + +233 +00:13:05,619 --> 00:13:11,258 +by passing -no_deduplicate to the linker +for Debug configurations. + +234 +00:13:11,291 --> 00:13:14,995 +And clang will also pass +the no-dedup option to the linker + +235 +00:13:15,028 --> 00:13:19,466 +if you run clang link line with -O0. + +236 +00:13:19,499 --> 00:13:24,304 +In summary, if you use C++ +and have a custom build, + +237 +00:13:24,338 --> 00:13:28,041 +that is, either you use +a non-standard configuration in Xcode, + +238 +00:13:28,075 --> 00:13:30,210 +or you use some other build system, + +239 +00:13:30,244 --> 00:13:36,049 +you should ensure your debug builds add +-no_deduplicate to improve link time. + +240 +00:13:36,083 --> 00:13:39,987 +The options I just talked about are +the actual command line arguments to ld. + +241 +00:13:40,020 --> 00:13:44,057 +When using Xcode, you need to change +your product build settings. + +242 +00:13:44,091 --> 00:13:48,028 +Inside build settings, +look for "Other Linker Flags". + +243 +00:13:49,796 --> 00:13:52,599 +Here's what you would set for -all_load. + +244 +00:13:52,633 --> 00:13:56,303 +And notice the "Dead Code Stripping" +option is here as well. + +245 +00:13:56,336 --> 00:13:58,939 +And there's -no_exported_symbols. + +246 +00:13:58,972 --> 00:14:01,708 +And here's -no_deduplicate. + +247 +00:14:03,177 --> 00:14:05,546 +Now let's talk about some surprises +you may experience + +248 +00:14:05,579 --> 00:14:07,381 +when using static libraries. + +249 +00:14:07,414 --> 00:14:11,685 +The first surprise is when you have source +code that builds into a static library + +250 +00:14:11,718 --> 00:14:16,290 +which your app links with, and that code +does not end up in the final app. + +251 +00:14:16,323 --> 00:14:19,226 +For instance, you added +"attribute used" to some function, + +252 +00:14:19,259 --> 00:14:22,196 +or you have an Objective-C category. + +253 +00:14:22,229 --> 00:14:24,865 +Because of the selective loading +the linker does, + +254 +00:14:24,898 --> 00:14:27,267 +if those object files +in the static library + +255 +00:14:27,301 --> 00:14:30,871 +don't also define some symbol +that is needed during the link, + +256 +00:14:30,904 --> 00:14:34,975 +those object files won't get loaded +by the linker. + +257 +00:14:35,008 --> 00:14:40,113 +Another interesting interaction +is static libraries and dead stripping. + +258 +00:14:40,147 --> 00:14:44,751 +It turns out dead stripping can hide +many static library issues. + +259 +00:14:44,785 --> 00:14:47,788 +Normally, +missing symbols or duplicate symbols + +260 +00:14:47,821 --> 00:14:50,357 +will cause the linker to error out. + +261 +00:14:50,390 --> 00:14:54,328 +But dead stripping causes the linker +to run a reachability pass + +262 +00:14:54,361 --> 00:14:57,798 +across all the code and data, +starting from main, + +263 +00:14:57,831 --> 00:15:02,102 +and if it turns out the missing symbol +is from an unreachable code, + +264 +00:15:02,135 --> 00:15:05,305 +the linker will suppress +the missing symbol error. + +265 +00:15:05,339 --> 00:15:09,309 +Similarly, if there are +duplicate symbols from static libraries, + +266 +00:15:09,343 --> 00:15:13,046 +the linker will pick the first +and not error. + +267 +00:15:13,080 --> 00:15:16,083 +The last big surprise +with using static libraries, + +268 +00:15:16,116 --> 00:15:20,220 +is when a static library is incorporated +into multiple frameworks. + +269 +00:15:20,254 --> 00:15:23,457 +Each of those frameworks +runs fine in isolation, + +270 +00:15:23,490 --> 00:15:27,394 +but then at some point, +some app uses both frameworks, and boom, + +271 +00:15:27,427 --> 00:15:32,199 +you get weird runtime issues +because of the multiple definitions. + +272 +00:15:32,232 --> 00:15:35,302 +The most common case you will see +is the Objective-C runtime + +273 +00:15:35,335 --> 00:15:39,339 +warning about multiple instances +of the same class name. + +274 +00:15:39,373 --> 00:15:41,608 +Overall, static libraries are powerful, + +275 +00:15:41,642 --> 00:15:45,579 +but you need to understand them +to avoid the pitfalls. + +276 +00:15:45,612 --> 00:15:47,514 +That wraps up static linking. + +277 +00:15:47,548 --> 00:15:51,685 +Now, let's move on to dynamic linking. + +278 +00:15:51,718 --> 00:15:56,890 +First, let's look at the original diagram +for static linking with a static library. + +279 +00:15:56,924 --> 00:15:59,526 +Now think about +how this will scale over time, + +280 +00:15:59,560 --> 00:16:02,396 +as there is more and more source code. + +281 +00:16:02,429 --> 00:16:06,133 +It should be clear that as more and more +libraries are made available, + +282 +00:16:06,166 --> 00:16:08,969 +the end program may grow in size. + +283 +00:16:09,002 --> 00:16:13,340 +That means the static link time to build +that program will also increase over time. + +284 +00:16:14,975 --> 00:16:18,512 +Now let's look at how these libraries +are made. + +285 +00:16:18,545 --> 00:16:21,048 +What if we did this switch? + +286 +00:16:21,081 --> 00:16:24,151 +We change 'ar' to 'ld' + +287 +00:16:24,184 --> 00:16:27,821 +and the output library +is now an executable binary. + +288 +00:16:27,855 --> 00:16:31,191 +This was the start +of dynamic libraries in the '90s. + +289 +00:16:31,225 --> 00:16:34,394 +As a shorthand, +we call dynamic libraries "dylibs". + +290 +00:16:34,428 --> 00:16:38,732 +On other platforms +they are known as DSOs or DLLs. + +291 +00:16:38,765 --> 00:16:41,268 +So what exactly is going on here? + +292 +00:16:41,301 --> 00:16:43,604 +And how does that help the scalability? + +293 +00:16:44,671 --> 00:16:47,107 +The key is that +the static linker treats linking + +294 +00:16:47,140 --> 00:16:49,676 +with a dynamic library differently. + +295 +00:16:49,710 --> 00:16:53,814 +Instead of copying code out of the library +into the final program, + +296 +00:16:53,847 --> 00:16:56,450 +the linker just records a kind of promise. + +297 +00:16:56,483 --> 00:17:00,153 +That is, it records the symbol name +used from the dynamic library + +298 +00:17:00,187 --> 00:17:03,457 +and what the library's path +will be at runtime. + +299 +00:17:03,490 --> 00:17:05,559 +How is this an advantage? + +300 +00:17:05,592 --> 00:17:08,862 +It means your program file size +is under your control. + +301 +00:17:08,896 --> 00:17:10,797 +It just contains your code, + +302 +00:17:10,831 --> 00:17:13,567 +and a list of dynamic libraries +it needs at runtime. + +303 +00:17:13,600 --> 00:17:16,670 +You no longer get copies of library code +in your program. + +304 +00:17:16,703 --> 00:17:20,507 +Your program's static link time is +now proportional to the size of your code, + +305 +00:17:20,541 --> 00:17:24,244 +and independent of the number +of dylibs you link with. + +306 +00:17:24,278 --> 00:17:27,314 +Also, the Virtual Memory system +can now shine. + +307 +00:17:27,347 --> 00:17:31,385 +When it sees the same dynamic library +used in multiple processes, + +308 +00:17:31,418 --> 00:17:34,688 +the Virtual Memory system will re-use +the same physical pages of RAM + +309 +00:17:34,721 --> 00:17:37,958 +for that dylib in all processes +that use that dylib. + +310 +00:17:37,991 --> 00:17:42,596 +I've shown you how dynamic libraries +started and what problem they solve. + +311 +00:17:42,629 --> 00:17:46,099 +But what are the "costs" +for those "benefits"? + +312 +00:17:46,133 --> 00:17:48,902 +First, +a benefit of using dynamic libraries + +313 +00:17:48,936 --> 00:17:51,004 +is that we have sped up build time. + +314 +00:17:51,038 --> 00:17:54,107 +But the cost is that launching your app +is now slower. + +315 +00:17:54,141 --> 00:17:59,079 +This is because launching is no longer +just loading one program file. + +316 +00:17:59,112 --> 00:18:03,050 +Now all the dylibs also need to be loaded +and connected together. + +317 +00:18:03,083 --> 00:18:05,819 +In other words, you just deferred +some of the linking costs + +318 +00:18:05,853 --> 00:18:08,555 +from build time to launch time. + +319 +00:18:08,589 --> 00:18:13,360 +Second, a dynamic library based program +will have more dirty pages. + +320 +00:18:13,393 --> 00:18:17,064 +In the static library case, +the linker would co-locate all globals + +321 +00:18:17,097 --> 00:18:22,135 +from all static libraries into the same +DATA pages in the main executable. + +322 +00:18:22,169 --> 00:18:26,139 +But with dylibs, +each library has its DATA page. + +323 +00:18:26,173 --> 00:18:30,043 +Lastly, another cost of dynamic linking +is that it introduces the need + +324 +00:18:30,077 --> 00:18:33,580 +for something new: a dynamic linker! + +325 +00:18:33,614 --> 00:18:37,851 +Remember that promise that was recorded +in the executable at build time? + +326 +00:18:37,885 --> 00:18:44,024 +Now we need something at runtime that will +fulfill that promise to load our library. + +327 +00:18:44,057 --> 00:18:48,295 +That's what dyld, +the dynamic linker, is for. + +328 +00:18:48,328 --> 00:18:51,999 +Let's dive into how dynamic linking +works at runtime. + +329 +00:18:52,032 --> 00:18:55,202 +An executable binary is divided up +into segments, + +330 +00:18:55,235 --> 00:18:59,106 +usually at least TEXT, DATA, and LINKEDIT. + +331 +00:18:59,139 --> 00:19:03,243 +Segments are always a multiple +of the page size for the OS. + +332 +00:19:03,277 --> 00:19:05,846 +Each segment has a different permission. + +333 +00:19:05,879 --> 00:19:09,683 +For example, the TEXT segment +has "execute" permissions. + +334 +00:19:09,716 --> 00:19:14,855 +That means the CPU may treat the bytes +on the page as machine code instructions. + +335 +00:19:14,888 --> 00:19:19,026 +At runtime, dyld has to mmap() +the executables into memory + +336 +00:19:19,059 --> 00:19:22,629 +with each segments' permissions, +as show here. + +337 +00:19:22,663 --> 00:19:25,933 +Because the segments are page sized +and page aligned, + +338 +00:19:25,966 --> 00:19:28,302 +that makes it straightforward +for the virtual memory system + +339 +00:19:28,335 --> 00:19:33,707 +to just set up the program or dylib file +as backing store for a VM range. + +340 +00:19:33,740 --> 00:19:35,976 +That means nothing is loaded into RAM + +341 +00:19:36,009 --> 00:19:39,213 +until there is some memory access +on those pages, + +342 +00:19:39,246 --> 00:19:42,683 +which triggers a page fault, +which causes the VM system + +343 +00:19:42,716 --> 00:19:44,785 +to read the proper subrange of the file + +344 +00:19:44,818 --> 00:19:48,555 +and fill in the needed RAM page +with its content. + +345 +00:19:48,589 --> 00:19:50,791 +But just mapping is not enough. + +346 +00:19:50,824 --> 00:19:55,128 +Somehow the program needs to +be "wired up" or bound to the dylib. + +347 +00:19:55,162 --> 00:19:57,397 +For that we have a concept +called "fix ups". + +348 +00:19:58,398 --> 00:20:01,568 +In the diagram, +we see the program got pointers set up + +349 +00:20:01,602 --> 00:20:04,438 +that point to the parts of the dylib +it uses. + +350 +00:20:04,471 --> 00:20:07,107 +Let's dive into what fix-ups are. + +351 +00:20:07,140 --> 00:20:10,010 +Here is our friend, the mach-o file. + +352 +00:20:10,043 --> 00:20:11,812 +Now, TEXT is immutable. + +353 +00:20:11,845 --> 00:20:15,115 +And in fact, it has to be +in a system based on code signing. + +354 +00:20:15,148 --> 00:20:18,585 +So what if there is a function +that calls malloc()? + +355 +00:20:18,619 --> 00:20:20,287 +How can that work? + +356 +00:20:20,320 --> 00:20:24,791 +The relative address of _malloc +can't be known when the program was built. + +357 +00:20:24,825 --> 00:20:27,361 +Well, what happens is, +the static linker saw + +358 +00:20:27,394 --> 00:20:31,665 +that malloc was in a dylib +and transformed the call site. + +359 +00:20:31,698 --> 00:20:35,536 +The call site becomes a call to a stub +synthesized by the linker + +360 +00:20:35,569 --> 00:20:37,471 +in the same TEXT segment, + +361 +00:20:37,504 --> 00:20:40,607 +so the relative address is known +at build time, + +362 +00:20:40,641 --> 00:20:45,579 +which means the BL instruction +can be correctly formed. + +363 +00:20:45,612 --> 00:20:49,082 +How that helps +is that the stub loads a pointer from DATA + +364 +00:20:49,116 --> 00:20:51,251 +and jumps to that location. + +365 +00:20:51,285 --> 00:20:54,955 +Now, no changes to TEXT are needed +at runtime– + +366 +00:20:54,988 --> 00:20:57,858 +just DATA is changed by dyld. + +367 +00:20:57,891 --> 00:21:00,994 +In fact, the secret to understanding dyld + +368 +00:21:01,028 --> 00:21:06,266 +is that all fixups done by dyld +are just dyld setting a pointer in DATA. + +369 +00:21:07,267 --> 00:21:10,938 +So let's dig more +into the fixups that dyld does. + +370 +00:21:10,971 --> 00:21:16,643 +Somewhere in LINKEDIT is the information +dyld needs to drive what fixups are done. + +371 +00:21:16,677 --> 00:21:19,012 +There are two kinds of fixups. + +372 +00:21:19,046 --> 00:21:20,948 +The first are called rebases, + +373 +00:21:20,981 --> 00:21:25,586 +and they are when a dylib or app +has a pointer that points within itself. + +374 +00:21:25,619 --> 00:21:29,323 +Now there is a security feature +called ASLR, + +375 +00:21:29,356 --> 00:21:33,460 +which causes dyld to load dylibs +at random addresses. + +376 +00:21:33,493 --> 00:21:38,899 +And that means those interior pointers +cannot just be set at build time. + +377 +00:21:38,932 --> 00:21:44,505 +Instead, dyld needs to adjust +or "rebase" those pointers at launch. + +378 +00:21:44,538 --> 00:21:47,774 +On disk, those pointers contain +their target address, + +379 +00:21:47,808 --> 00:21:51,144 +if the dylib were to be loaded +at address zero. + +380 +00:21:51,178 --> 00:21:56,750 +That way, all the LINKEDIT needs to record +is the location of each rebase location. + +381 +00:21:56,783 --> 00:22:01,054 +Dyld can then just add +the actual load address of the dylib + +382 +00:22:01,088 --> 00:22:04,791 +to each of the rebase locations +to correctly fix them up. + +383 +00:22:06,226 --> 00:22:08,896 +The second kind of fixups are binds. + +384 +00:22:08,929 --> 00:22:11,431 +Binds are symbolic references. + +385 +00:22:11,465 --> 00:22:15,636 +That is, their target is a symbol name +and not a number. + +386 +00:22:15,669 --> 00:22:18,939 +For instance, +a pointer to the function "malloc". + +387 +00:22:18,972 --> 00:22:24,411 +The string "_malloc" +is actually stored in LINKEDIT, + +388 +00:22:24,444 --> 00:22:28,315 +and dyld uses that string +to look up the actual address of malloc + +389 +00:22:28,348 --> 00:22:31,385 +in the exports trie of libSystem.dylib. + +390 +00:22:31,418 --> 00:22:37,124 +Then, dyld stores that value +in the location specified by the bind. + +391 +00:22:37,157 --> 00:22:40,093 +This year we are announcing +a new way to encode fixups, + +392 +00:22:40,127 --> 00:22:42,362 +that we call "chained fixups". + +393 +00:22:44,097 --> 00:22:47,734 +The first advantage is that +is makes LINKEDIT smaller. + +394 +00:22:47,768 --> 00:22:52,105 +The LINKEDIT is smaller because instead +of storing all the fixup locations, + +395 +00:22:52,139 --> 00:22:57,344 +the new format just stores where the first +fixup location is in each DATA page, + +396 +00:22:57,377 --> 00:23:00,214 +as well as a list of the imported symbols. + +397 +00:23:00,247 --> 00:23:04,585 +Then the rest of the information +is encoded in the DATA segment itself, + +398 +00:23:04,618 --> 00:23:08,956 +in the place +where the fixups will ultimately be set. + +399 +00:23:08,989 --> 00:23:12,192 +This new format gets its name, +chained fixups, + +400 +00:23:12,226 --> 00:23:16,396 +from the fact that the fixup locations +are "chained" together. + +401 +00:23:16,430 --> 00:23:19,900 +The LINKEDIT just says +where the first fixup was, + +402 +00:23:19,933 --> 00:23:23,470 +then in the 64-bit pointer location +in DATA, + +403 +00:23:23,504 --> 00:23:28,308 +some of the bits contain the offset +to the next fixup location. + +404 +00:23:28,342 --> 00:23:33,413 +Also packed in there is a bit that says +if the fixup is a bind or a rebase. + +405 +00:23:33,447 --> 00:23:37,851 +If it is a bind, the rest of the bits +are the index of the symbol. + +406 +00:23:37,885 --> 00:23:41,822 +If it's a rebase, the rest of the bits +are the offset of the target + +407 +00:23:41,855 --> 00:23:44,324 +within the image. + +408 +00:23:44,358 --> 00:23:49,796 +Lastly, runtime support for chained fixups +already exists in iOS 13.4 and later. + +409 +00:23:49,830 --> 00:23:52,699 +Which means you can start using +this new format today, + +410 +00:23:52,733 --> 00:23:57,437 +as long as your deployment target +is iOS 13.4 or later. + +411 +00:23:57,471 --> 00:24:00,874 +And the chained fixup format enables +a new OS feature + +412 +00:24:00,908 --> 00:24:02,910 +we are announcing this year. + +413 +00:24:02,943 --> 00:24:06,813 +But to understand that, +I need to talk about how dyld works. + +414 +00:24:08,081 --> 00:24:11,752 +Dyld starts with the main executable– +say your app. + +415 +00:24:11,785 --> 00:24:14,821 +Parses that mach-o to find +the dependent dylibs, + +416 +00:24:14,855 --> 00:24:18,292 +that is, +what promised dynamic libraries it needs. + +417 +00:24:18,325 --> 00:24:21,495 +It finds those dylibs and mmap()s them. + +418 +00:24:21,528 --> 00:24:25,966 +Then for each of those, it recurses +and parses their mach-o structures, + +419 +00:24:25,999 --> 00:24:29,970 +loading any additional dylibs as needed. + +420 +00:24:30,003 --> 00:24:31,839 +Once everything is loaded, + +421 +00:24:31,872 --> 00:24:34,241 +dyld looks up all the bind symbols needed + +422 +00:24:34,274 --> 00:24:37,811 +and uses those addresses +when doing fixups. + +423 +00:24:37,845 --> 00:24:40,647 +Lastly, once all the fixups are done, + +424 +00:24:40,681 --> 00:24:44,251 +dyld runs initializers, bottom up. + +425 +00:24:44,284 --> 00:24:47,921 +Five years ago +we announced a new dyld technology. + +426 +00:24:47,955 --> 00:24:53,260 +We realized the steps in green above were +the same every time your app was launched. + +427 +00:24:53,293 --> 00:24:56,096 +So as long as the program and dylibs +did not change, + +428 +00:24:56,129 --> 00:24:59,099 +all the steps in green +could be cached on first launch + +429 +00:24:59,132 --> 00:25:02,703 +and re-used on subsequent launches. + +430 +00:25:02,736 --> 00:25:07,741 +This year we are announcing +additional dyld performance improvements. + +431 +00:25:07,774 --> 00:25:12,946 +We are announcing a new dyld feature +called "page-in linking". + +432 +00:25:12,980 --> 00:25:17,451 +Instead of dyld applying all the fixups +to all dylibs at launch, + +433 +00:25:17,484 --> 00:25:23,023 +the kernel can now apply fixups +to your DATA pages lazily, on page-in. + +434 +00:25:23,056 --> 00:25:26,026 +It has always been the case +that the first use of some address + +435 +00:25:26,059 --> 00:25:31,765 +in some page of an mmap()ed region +triggered the kernel to read in that page. + +436 +00:25:31,798 --> 00:25:34,268 +But now, if it is a DATA page, + +437 +00:25:34,301 --> 00:25:38,705 +the kernel will also apply the fixup +that page needs. + +438 +00:25:38,739 --> 00:25:42,109 +We have had a special case +of page-in linking for over a decade + +439 +00:25:42,142 --> 00:25:45,412 +for OS dylibs in the dyld shared cache. + +440 +00:25:45,445 --> 00:25:49,616 +This year we generalized it +and made it available to everyone. + +441 +00:25:49,650 --> 00:25:53,687 +This mechanism reduces +dirty memory and launch time. + +442 +00:25:53,720 --> 00:25:56,523 +It also means DATA_CONST pages are clean, + +443 +00:25:56,557 --> 00:26:00,694 +which means they can be evicted +and recreated just like TEXT pages, + +444 +00:26:00,727 --> 00:26:02,896 +which reduces memory pressure. + +445 +00:26:02,930 --> 00:26:06,166 +This page-in linking feature +will be in the upcoming release of iOS, + +446 +00:26:06,200 --> 00:26:09,002 +macOS, and watchOS. + +447 +00:26:09,036 --> 00:26:13,373 +But page-in linking only works +for binaries built with chained fixups. + +448 +00:26:13,407 --> 00:26:15,409 +That is because with chained fixups, + +449 +00:26:15,442 --> 00:26:20,047 +most of the fixup information will be +encoded in the DATA segment on disk, + +450 +00:26:20,080 --> 00:26:23,717 +which means it is available +to the kernel during page-in. + +451 +00:26:23,750 --> 00:26:28,088 +One caveat is that dyld only +uses this mechanism during launch. + +452 +00:26:28,121 --> 00:26:32,192 +Any dylibs dlopen()ed later +do not get page-in linking. + +453 +00:26:32,226 --> 00:26:34,995 +In that case, +dyld takes the traditional path + +454 +00:26:35,028 --> 00:26:38,932 +and applies the fixups +during the dlopen call. + +455 +00:26:38,966 --> 00:26:43,003 +With that in mind, let's go back +to the dyld workflow diagram. + +456 +00:26:43,036 --> 00:26:46,940 +For five years now, dyld has been +optimizing the steps above in green + +457 +00:26:46,974 --> 00:26:52,446 +by caching that work on first launch +and reusing it on later launches. + +458 +00:26:52,479 --> 00:26:57,050 +Now, dyld can optimize +the "apply fixup" step + +459 +00:26:57,084 --> 00:26:59,253 +by not actually doing the fixups, + +460 +00:26:59,286 --> 00:27:03,857 +and letting the kernel do them lazily +on page-in. + +461 +00:27:03,891 --> 00:27:06,660 +Now that you have seen +what's new in dyld, + +462 +00:27:06,693 --> 00:27:11,164 +let's talk about best practices +for dynamic linking. + +463 +00:27:11,198 --> 00:27:14,668 +What can you do to help +improve dynamic link performance? + +464 +00:27:14,701 --> 00:27:17,237 +As I just showed, +dyld has already accelerated + +465 +00:27:17,271 --> 00:27:19,973 +most of the steps in dynamic linking. + +466 +00:27:20,007 --> 00:27:24,211 +One thing you can control +is how many dylibs you have. + +467 +00:27:24,244 --> 00:27:28,515 +The more dylibs there are, +the more work dyld has to do to load them. + +468 +00:27:28,549 --> 00:27:33,253 +Conversely, the fewer dylibs, +the less work dyld has to perform. + +469 +00:27:33,287 --> 00:27:36,356 +The next thing you can look at +are static initializers, + +470 +00:27:36,390 --> 00:27:39,459 +which is code that always runs, pre-main. + +471 +00:27:39,493 --> 00:27:44,131 +For instance, don't do I/O or networking +in a static initializer. + +472 +00:27:44,164 --> 00:27:46,700 +Anything that can take more +than a few milliseconds + +473 +00:27:46,733 --> 00:27:49,803 +should never be done in an initializer. + +474 +00:27:49,837 --> 00:27:52,272 +As we know, +the world is getting more complicated, + +475 +00:27:52,306 --> 00:27:54,708 +and your users want more functionality. + +476 +00:27:54,741 --> 00:27:59,413 +So it makes sense to use libraries +to manage all that functionality. + +477 +00:27:59,446 --> 00:28:04,551 +Your goal is to find your sweet spot +between dynamic and static libraries. + +478 +00:28:04,585 --> 00:28:06,086 +Too many static libraries + +479 +00:28:06,119 --> 00:28:09,756 +and your iterative build/debug cycle +is slowed down. + +480 +00:28:09,790 --> 00:28:12,292 +On the other hand, +too many dynamic libraries + +481 +00:28:12,326 --> 00:28:15,796 +and your launch time is slow +and your customers notice. + +482 +00:28:15,829 --> 00:28:18,432 +But we sped up ld64 this year, + +483 +00:28:18,465 --> 00:28:20,601 +so your sweet spot may have changed, + +484 +00:28:20,634 --> 00:28:23,203 +as you can now use more static libraries, + +485 +00:28:23,237 --> 00:28:25,672 +or more source files directly in your app, + +486 +00:28:25,706 --> 00:28:28,675 +and still build +in the same amount of time. + +487 +00:28:28,709 --> 00:28:31,678 +Lastly, +if it works for your installed base, + +488 +00:28:31,712 --> 00:28:34,081 +updating to a newer deployment target + +489 +00:28:34,114 --> 00:28:36,683 +can enable the tools +to generate chained fixups, + +490 +00:28:36,717 --> 00:28:39,920 +making your binaries smaller, +and improving launch time. + +491 +00:28:39,953 --> 00:28:42,689 +The last thing +I'd like you all to be aware of + +492 +00:28:42,723 --> 00:28:47,394 +is two new tools that will help you peek +inside the linking process. + +493 +00:28:47,427 --> 00:28:49,696 +The first tool is dyld_usage. + +494 +00:28:49,730 --> 00:28:53,333 +You can use it +to get a trace of what dyld is doing. + +495 +00:28:53,367 --> 00:28:55,736 +The tool is only on macOS, + +496 +00:28:55,769 --> 00:28:59,806 +but you can use it to trace your app +launching in the simulator, + +497 +00:28:59,840 --> 00:29:03,410 +or if your app built for Mac Catalyst. + +498 +00:29:03,443 --> 00:29:06,847 +Here is an example run +against TextEdit on macOS. + +499 +00:29:08,849 --> 00:29:13,820 +As you can tell by the top few lines, +the launch took 15ms overall, + +500 +00:29:13,854 --> 00:29:18,025 +but only 1ms for fixups, +thanks to page-in linking. + +501 +00:29:18,058 --> 00:29:22,129 +The vast majority of time is now spent +in static initializers. + +502 +00:29:23,697 --> 00:29:26,400 +The next tool is dyld_info. + +503 +00:29:26,433 --> 00:29:28,635 +You can use it to inspect binaries, + +504 +00:29:28,669 --> 00:29:31,772 +both on disk +and in the current dyld cache. + +505 +00:29:31,805 --> 00:29:36,877 +The tool has many options, but I'll show +you how to view exports and fixups. + +506 +00:29:36,910 --> 00:29:41,648 +Here the -fixup option shows +all the fixup locations dyld will process + +507 +00:29:41,682 --> 00:29:43,884 +and their targets. + +508 +00:29:43,917 --> 00:29:47,688 +The output is the same regardless +of if the file is old style fixups + +509 +00:29:47,721 --> 00:29:50,824 +or new chained fixups. + +510 +00:29:50,858 --> 00:29:56,730 +Here the -exports option will show +all the exported symbols in the dylib, + +511 +00:29:56,763 --> 00:30:00,567 +and the offset of each symbol +from the start of the dylib. + +512 +00:30:00,601 --> 00:30:04,238 +In this case, it is showing information +about Foundation.framework + +513 +00:30:04,271 --> 00:30:07,040 +which is the dylib in the dyld cache. + +514 +00:30:07,074 --> 00:30:08,775 +There is no file on disk, + +515 +00:30:08,809 --> 00:30:13,680 +but the dyld_info tool uses the same code +as dyld and can thus find it. + +516 +00:30:15,148 --> 00:30:17,484 +Now that you understand +the history and tradeoffs + +517 +00:30:17,518 --> 00:30:19,786 +of static versus dynamic libraries, + +518 +00:30:19,820 --> 00:30:22,089 +you should review what you app does + +519 +00:30:22,122 --> 00:30:25,225 +and determine +if you have found your sweet spot. + +520 +00:30:25,259 --> 00:30:27,628 +Next, if you have a large app + +521 +00:30:27,661 --> 00:30:30,831 +and have noticed +the build takes a while to link, + +522 +00:30:30,864 --> 00:30:35,435 +try out Xcode 14 +which has the new faster linker. + +523 +00:30:35,469 --> 00:30:37,971 +If you still want to speed up +your static link more, + +524 +00:30:38,005 --> 00:30:40,674 +look into the three linker options +I detailed + +525 +00:30:40,707 --> 00:30:45,045 +and see if they make sense in your build, +and improve your link time. + +526 +00:30:45,078 --> 00:30:49,917 +Lastly, you can also try building +your app, and any embedded frameworks, + +527 +00:30:49,950 --> 00:30:54,021 +for iOS 13.4 or later +to enable chained fixups. + +528 +00:30:54,054 --> 00:30:58,759 +Then see if your app is smaller +and launches faster on iOS 16. + +529 +00:30:58,792 --> 00:31:01,895 +Thanks for watching, +and have a great WWDC. + diff --git a/eng/2022 Session 110363 Improve app size and runtime performance en.srt b/eng/2022 Session 110363 Improve app size and runtime performance en.srt new file mode 100644 index 0000000..7354040 --- /dev/null +++ b/eng/2022 Session 110363 Improve app size and runtime performance en.srt @@ -0,0 +1,1334 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,810 --> 00:00:13,914 +Ahmed: Hi, my name is Ahmed, and +I work on the Clang and Swift compilers. + +3 +00:00:13,947 --> 00:00:17,684 +In this session we're going to dive deep +into changes we've made + +4 +00:00:17,718 --> 00:00:21,788 +to make common Swift and Objective-C +operations faster and more efficient, + +5 +00:00:21,822 --> 00:00:24,558 +so that we can improve your app's size +and runtime performance. + +6 +00:00:25,259 --> 00:00:27,494 +When you write code +in Swift or Objective-C, + +7 +00:00:27,528 --> 00:00:30,764 +you're always really interacting +with two major components. + +8 +00:00:30,797 --> 00:00:32,733 +First, you build using Xcode, + +9 +00:00:32,766 --> 00:00:35,435 +and that uses the Swift +and Clang compilers. + +10 +00:00:35,469 --> 00:00:38,138 +But when you run your app, +a lot of the heavy lifting is done + +11 +00:00:38,172 --> 00:00:41,441 +in the Swift and Objective-C Runtime. + +12 +00:00:41,475 --> 00:00:45,345 +The runtime is embedded in the operating +systems for all of our platforms. + +13 +00:00:45,379 --> 00:00:50,584 +What the compiler cannot do at build time, +the runtime does, well, at run time. + +14 +00:00:50,617 --> 00:00:52,386 +We're going to look at several +improvements we've made + +15 +00:00:52,419 --> 00:00:54,488 +in both compilers and runtimes. + +16 +00:00:54,521 --> 00:00:58,192 +Now, this session is a bit unusual; +there are no new APIs, + +17 +00:00:58,225 --> 00:01:00,727 +language changes, or new build settings. + +18 +00:01:00,761 --> 00:01:03,430 +You don't have to change your code, +so all these improvements are transparent + +19 +00:01:03,463 --> 00:01:05,632 +to you, the developer. + +20 +00:01:05,666 --> 00:01:06,700 +Let's dive in. + +21 +00:01:06,733 --> 00:01:08,902 +We're going to look at four improvements. + +22 +00:01:08,936 --> 00:01:11,538 +We've made protocol checks in Swift +more efficient, + +23 +00:01:11,572 --> 00:01:14,708 +we've also made Objective-C message send +calls smaller, + +24 +00:01:14,741 --> 00:01:16,944 +as we did retain and release calls, + +25 +00:01:16,977 --> 00:01:21,248 +and finally, we've made autorelease +elision faster and smaller. + +26 +00:01:21,281 --> 00:01:22,783 +Let's take a closer look. + +27 +00:01:23,917 --> 00:01:25,953 +Let's start with protocol checks in Swift. + +28 +00:01:27,421 --> 00:01:30,090 +Here we have a CustomLoggable protocol. + +29 +00:01:30,123 --> 00:01:33,527 +It has a read-only computed property +customLogString, + +30 +00:01:33,560 --> 00:01:35,262 +and we can use it in our log function, + +31 +00:01:35,295 --> 00:01:38,832 +that has special handling +for CustomLoggable objects. + +32 +00:01:38,866 --> 00:01:42,636 +Later, we're defining an Event type +with name and date fields. + +33 +00:01:42,669 --> 00:01:45,272 +And we're conforming +to the CustomLoggable protocol, + +34 +00:01:45,305 --> 00:01:48,809 +by defining the getter +for the customLogString property. + +35 +00:01:49,910 --> 00:01:53,247 +And this lets us pass +Event objects to our 'log' function. + +36 +00:01:53,280 --> 00:01:56,450 +When we execute this code, +the 'log' function needs to check + +37 +00:01:56,483 --> 00:01:59,553 +whether the value we passed +conforms to the protocol. + +38 +00:01:59,586 --> 00:02:04,024 +And it does that using the 'as' operator. +You may also have seen the 'is' operator. + +39 +00:02:05,425 --> 00:02:09,930 +Whenever possible, this check is optimized +away at build time, in the compiler. + +40 +00:02:09,963 --> 00:02:13,767 +However, we don't always +have enough information yet. + +41 +00:02:13,800 --> 00:02:15,802 +So this often needs to happen +in the runtime, + +42 +00:02:15,836 --> 00:02:20,340 +with the help of protocol check metadata +we compute earlier. + +43 +00:02:20,374 --> 00:02:23,810 +With this metadata, the runtime knows +whether this particular object + +44 +00:02:23,844 --> 00:02:25,846 +really does conform to the protocol, + +45 +00:02:25,879 --> 00:02:27,581 +and the check succeeds. + +46 +00:02:29,016 --> 00:02:31,485 +Part of the metadata is built +at compile time, + +47 +00:02:31,518 --> 00:02:33,587 +but a lot can only be built +at launch time, + +48 +00:02:33,620 --> 00:02:35,656 +particularly when using Generics. + +49 +00:02:36,390 --> 00:02:40,761 +When you use a lot of protocols, this can +add up to hundreds of milliseconds. + +50 +00:02:40,794 --> 00:02:45,599 +On real-world apps, we've seen this take +up to half of the launch time. + +51 +00:02:45,632 --> 00:02:48,869 +With the new Swift runtime, +we now precompute these ahead of time, + +52 +00:02:48,902 --> 00:02:51,538 +as part of the dyld closure +for the app executable + +53 +00:02:51,572 --> 00:02:54,074 +and any dylib it uses at launch. + +54 +00:02:54,107 --> 00:02:56,877 +Best of all, +this is enabled even for existing apps + +55 +00:02:56,910 --> 00:03:01,048 +when running on iOS 16, tvOS 16, +or watchOS 9. + +56 +00:03:01,081 --> 00:03:03,684 +If you'd like to learn more +about dyld and launch closures, + +57 +00:03:03,717 --> 00:03:07,521 +watch the talk "App Startup Time: +Past, Present, and Future." + +58 +00:03:07,554 --> 00:03:09,389 +That was protocol checks in Swift. + +59 +00:03:10,791 --> 00:03:13,427 +Let's move on to message send. + +60 +00:03:14,361 --> 00:03:17,197 +With the new compilers +and linker in Xcode 14, + +61 +00:03:17,231 --> 00:03:19,833 +we've made message send calls +up to 8 bytes smaller, + +62 +00:03:19,867 --> 00:03:22,636 +down from 12, on ARM64. + +63 +00:03:22,669 --> 00:03:25,205 +As we'll see in just a moment, +message send is really everywhere, + +64 +00:03:25,239 --> 00:03:26,607 +so this adds up, + +65 +00:03:26,640 --> 00:03:30,811 +and we've seen up to 2% code size +improvements on binaries. + +66 +00:03:30,844 --> 00:03:33,714 +This is enabled automatically +when building with Xcode 14, + +67 +00:03:33,747 --> 00:03:38,051 +even if you use an older OS release +as deployment target. + +68 +00:03:38,085 --> 00:03:40,854 +It defaults to a balance +of size win and performance, + +69 +00:03:40,888 --> 00:03:43,223 +but you can opt into optimizing +for size only, + +70 +00:03:43,257 --> 00:03:46,593 +using the objc_stubs_small linker flag. + +71 +00:03:46,627 --> 00:03:49,496 +Now let's look into what changed. + +72 +00:03:49,530 --> 00:03:50,831 +So let's start with an example. + +73 +00:03:50,864 --> 00:03:54,168 +Here we're trying to make an NSDate +for the start day of the conference. + +74 +00:03:54,201 --> 00:03:56,303 +We start by making an NSCalendar, + +75 +00:03:56,336 --> 00:03:58,205 +then we fill out NSDateComponents, + +76 +00:03:58,238 --> 00:04:01,742 +make a date out of that, +and finally return it. + +77 +00:04:01,775 --> 00:04:05,012 +Now let's look at the assembly +the compiler generates. + +78 +00:04:05,045 --> 00:04:07,314 +Now, the details of the assembly +aren't super important. + +79 +00:04:07,347 --> 00:04:11,018 +Us compiler folks stare at it all day +so that you don't have to. + +80 +00:04:11,051 --> 00:04:13,820 +What's important is that +almost every line here + +81 +00:04:13,854 --> 00:04:16,690 +ends up needing an instruction +to call objc_msgSend, + +82 +00:04:16,723 --> 00:04:21,628 +even when doing property accesses +like we do for the date components. + +83 +00:04:21,662 --> 00:04:24,464 +This is because at compile time, +we don't know which method to call, + +84 +00:04:24,498 --> 00:04:26,834 +and it's only the objc runtime that does. + +85 +00:04:26,867 --> 00:04:29,002 +So we call into the runtime +using objc_msgSend + +86 +00:04:29,036 --> 00:04:32,039 +to ask it to find the right method. + +87 +00:04:32,072 --> 00:04:34,608 +Let's focus on one of these calls. + +88 +00:04:34,641 --> 00:04:37,411 +We already mentioned +the instruction to call objc_msgSend. + +89 +00:04:37,444 --> 00:04:38,745 +But there's more. + +90 +00:04:38,779 --> 00:04:40,614 +To tell the runtime which method to call, + +91 +00:04:40,647 --> 00:04:44,952 +we have to pass a selector +to these objc_msgSend calls. + +92 +00:04:44,985 --> 00:04:48,422 +That needs a couple more instructions +to prepare the selector. + +93 +00:04:48,455 --> 00:04:52,292 +When we look at the binary, each of these +instructions takes a little bit of space. + +94 +00:04:52,326 --> 00:04:55,529 +On ARM64, that's 4 bytes each. + +95 +00:04:55,562 --> 00:04:59,566 +So for each of these objc_msgSend calls, +we're using 12 bytes, + +96 +00:04:59,600 --> 00:05:01,602 +and we need that for every single one +of these calls; + +97 +00:05:01,635 --> 00:05:04,505 +that really adds up. + +98 +00:05:04,538 --> 00:05:06,473 +Let's see what we can do to improve that. + +99 +00:05:08,509 --> 00:05:09,843 +Now, as we've seen before, + +100 +00:05:09,877 --> 00:05:13,447 +8 of those bytes are dedicated +to preparing the selector. + +101 +00:05:13,480 --> 00:05:17,885 +Interesting thing is, for any given +selector, it's always the same code. + +102 +00:05:17,918 --> 00:05:21,088 +And this is where +our optimization comes in. + +103 +00:05:21,121 --> 00:05:23,190 +Since this is always the same code, +we can share it + +104 +00:05:23,223 --> 00:05:25,292 +and only emit it once per selector + +105 +00:05:25,325 --> 00:05:27,961 +instead of every time +we do a message send. + +106 +00:05:27,995 --> 00:05:30,797 +We can take it out and put it +into a little helper function, + +107 +00:05:30,831 --> 00:05:33,534 +and call that function instead. + +108 +00:05:33,567 --> 00:05:35,702 +Over many calls using the same selector, + +109 +00:05:35,736 --> 00:05:38,338 +we can save all these instruction bytes. + +110 +00:05:38,372 --> 00:05:40,941 +We call this helper function +a "selector stub." + +111 +00:05:42,442 --> 00:05:45,245 +We still need to call the real +objc_msgSend function, though, + +112 +00:05:45,279 --> 00:05:47,681 +so we continue onto that. + +113 +00:05:47,714 --> 00:05:51,084 +And again, that has another, +different, indirection to load the address + +114 +00:05:51,118 --> 00:05:53,520 +of the function itself and call it. + +115 +00:05:53,554 --> 00:05:55,989 +The details aren't important, +but what's important is that we need + +116 +00:05:56,023 --> 00:05:58,192 +another several bytes of code to do that. + +117 +00:05:59,860 --> 00:06:01,695 +And this is where you can choose +which mode you want, + +118 +00:06:01,728 --> 00:06:03,630 +as I mentioned earlier. + +119 +00:06:03,664 --> 00:06:07,434 +We can either keep these two little stub +functions separate, like we've done here. + +120 +00:06:07,467 --> 00:06:09,636 +We get to share the most code, +and make these functions + +121 +00:06:09,670 --> 00:06:11,905 +as small as possible. + +122 +00:06:11,939 --> 00:06:14,174 +But unfortunately, +this would do two calls back to back, + +123 +00:06:14,208 --> 00:06:17,444 +which is not ideal for performance. + +124 +00:06:17,477 --> 00:06:20,781 +So we can further improve this +with an alternative version. + +125 +00:06:20,814 --> 00:06:25,652 +We can take these two stub functions +we've created, combine them into one. + +126 +00:06:25,686 --> 00:06:29,156 +That way, we keep the code closer together +and we don't need as many calls. + +127 +00:06:29,189 --> 00:06:31,124 +And that's on the right here. + +128 +00:06:32,226 --> 00:06:33,894 +So these are the two options. + +129 +00:06:33,927 --> 00:06:36,096 +You can choose whether +to optimize for size alone, + +130 +00:06:36,129 --> 00:06:38,765 +and get the maximum +size savings available. + +131 +00:06:38,799 --> 00:06:42,636 +You can enable that using +the -objc_stubs_small linker flag, + +132 +00:06:42,669 --> 00:06:45,906 +or you can use the code generation +that provides size benefits + +133 +00:06:45,939 --> 00:06:48,375 +while keeping the best performance. + +134 +00:06:48,408 --> 00:06:51,345 +And unless you're severely +size-constrained, we recommend using this, + +135 +00:06:51,378 --> 00:06:54,181 +and that's why it's the default. + +136 +00:06:54,214 --> 00:06:57,050 +And that was smaller message send +using stubs. + +137 +00:06:57,084 --> 00:07:01,488 +Another improvement we've made +is making retain/release cheaper. + +138 +00:07:01,522 --> 00:07:03,357 +With the new compilers in Xcode 14, + +139 +00:07:03,390 --> 00:07:05,759 +retain/release calls are now +up to 4 bytes smaller, + +140 +00:07:05,792 --> 00:07:08,629 +down from 8 on ARM64. + +141 +00:07:08,662 --> 00:07:11,131 +As we'll see in just a moment, +just like message send, + +142 +00:07:11,164 --> 00:07:13,000 +retain/release is also everywhere. + +143 +00:07:13,033 --> 00:07:18,539 +So this adds up, and we've seen up to 2% +more code size improvements on binaries. + +144 +00:07:18,572 --> 00:07:21,775 +Now, unlike message send stubs, +this does need runtime support, + +145 +00:07:21,808 --> 00:07:24,545 +so you'll get this automatically +as you migrate to a deployment target + +146 +00:07:24,578 --> 00:07:28,248 +of iOS 16, tvOS 16, or watchOS 9. + +147 +00:07:28,282 --> 00:07:31,018 +Now let's look into what changed. + +148 +00:07:31,051 --> 00:07:32,853 +Let's go back to our example. + +149 +00:07:32,886 --> 00:07:35,055 +We talked about msgSend calls, + +150 +00:07:35,088 --> 00:07:37,591 +but with automatic reference counting, +or ARC, + +151 +00:07:37,624 --> 00:07:39,726 +we also end up with a lot +of retain/release calls + +152 +00:07:39,760 --> 00:07:42,462 +inserted by the compiler. + +153 +00:07:42,496 --> 00:07:45,999 +At a very high level, whenever we make +a copy of a pointer to an object, + +154 +00:07:46,033 --> 00:07:49,102 +we need to increment +its retain count to keep it live. + +155 +00:07:49,136 --> 00:07:53,707 +And here, this happens with our variables +cal, dateComponent, and theDate. + +156 +00:07:53,740 --> 00:07:57,644 +We do that by calling into the runtime, +using objc_retain. + +157 +00:07:57,678 --> 00:07:59,079 +When the variables go out of scope, + +158 +00:07:59,112 --> 00:08:03,383 +we then need to decrement the retain count +using objc_release. + +159 +00:08:03,417 --> 00:08:06,353 +Of course, part of the benefit of ARC +is all the compiler magic + +160 +00:08:06,386 --> 00:08:10,257 +that eliminates a lot of these calls, +to keep them to a minimum. + +161 +00:08:10,290 --> 00:08:13,894 +And we're going to go into one +of these magic tricks a little bit later. + +162 +00:08:13,927 --> 00:08:17,898 +But even with all the magic, +we still often need these calls. + +163 +00:08:17,931 --> 00:08:21,435 +In this example, we end up needing +to release our local copies of calendar + +164 +00:08:21,468 --> 00:08:23,036 +and dateComponents. + +165 +00:08:24,605 --> 00:08:27,474 +Under the hood, +these objc_retain/release functions + +166 +00:08:27,508 --> 00:08:29,276 +are just plain C functions; + +167 +00:08:29,309 --> 00:08:32,613 +take a single argument, +the object to be released. + +168 +00:08:32,646 --> 00:08:36,049 +So with ARC, the compiler inserts calls +to these C functions, + +169 +00:08:36,083 --> 00:08:38,852 +passing the appropriate object pointers. + +170 +00:08:38,886 --> 00:08:42,189 +Because of that, these calls +have to respect the C calling convention, + +171 +00:08:42,222 --> 00:08:46,093 +defined by our platform +Application Binary Interface, or ABI. + +172 +00:08:46,126 --> 00:08:49,496 +Concretely, what that means +is that we need even more code + +173 +00:08:49,530 --> 00:08:53,433 +to do these calls, +to pass the pointer in the right register. + +174 +00:08:53,467 --> 00:08:57,037 +So we end up with a few additional +'move' instructions just for that. + +175 +00:08:57,070 --> 00:09:00,440 +And that's where +our new optimization comes in. + +176 +00:09:00,474 --> 00:09:03,877 +By specializing retain/release +with a custom calling convention, + +177 +00:09:03,911 --> 00:09:06,547 +we can opportunistically use +the right variant + +178 +00:09:06,580 --> 00:09:09,316 +depending on where +the object pointer already is, + +179 +00:09:09,349 --> 00:09:11,919 +so that we don't need to move it. + +180 +00:09:11,952 --> 00:09:15,455 +Concretely, what this means is, +we get rid of a bunch of redundant code + +181 +00:09:15,489 --> 00:09:17,124 +for all these calls. + +182 +00:09:17,157 --> 00:09:18,692 +And again, +while this may not seem like much + +183 +00:09:18,725 --> 00:09:20,494 +for these puny little instructions, + +184 +00:09:20,527 --> 00:09:23,530 +over an entire app, it really adds up. + +185 +00:09:23,564 --> 00:09:26,533 +That's how we made retain/release +operations cheaper. + +186 +00:09:26,567 --> 00:09:29,870 +Finally, +let's talk about autorelease elision. + +187 +00:09:29,903 --> 00:09:32,339 +Now this one is even more interesting. + +188 +00:09:32,372 --> 00:09:36,376 +With objc runtime changes, +we've made autorelease elision faster. + +189 +00:09:36,410 --> 00:09:38,512 +That happens automatically +for existing apps + +190 +00:09:38,545 --> 00:09:41,548 +when you run them on the new OS releases. + +191 +00:09:41,582 --> 00:09:44,718 +Building on top of that, +with additional compiler changes, + +192 +00:09:44,751 --> 00:09:47,321 +we also made the code smaller. + +193 +00:09:47,354 --> 00:09:49,323 +And you'll get this size benefit +automatically + +194 +00:09:49,356 --> 00:09:53,927 +as you migrate to a deployment target +of iOS 16, tvOS 16, or watchOS 9. + +195 +00:09:55,462 --> 00:09:59,600 +Now this is all great, but what's +autorelease elision in the first place? + +196 +00:09:59,633 --> 00:10:02,102 +Let's go back to our example. + +197 +00:10:02,135 --> 00:10:05,072 +I mentioned earlier that ARC +already gives us a lot of compiler magic + +198 +00:10:05,105 --> 00:10:07,040 +to optimize retains and releases. + +199 +00:10:07,074 --> 00:10:08,909 +So let's focus on one case here: + +200 +00:10:08,942 --> 00:10:11,378 +autoreleased return values. + +201 +00:10:11,411 --> 00:10:13,614 +In this example, +we made a temporary object, + +202 +00:10:13,647 --> 00:10:15,349 +and we're returning it to our caller. + +203 +00:10:15,382 --> 00:10:17,951 +So let's look at how that works. + +204 +00:10:17,985 --> 00:10:19,953 +So we have our temporary theDate, + +205 +00:10:19,987 --> 00:10:21,889 +we return it, the call completes, + +206 +00:10:21,922 --> 00:10:24,825 +and the caller saves it +to its own variable. + +207 +00:10:24,858 --> 00:10:28,028 +So let's see how that works with ARC. + +208 +00:10:28,061 --> 00:10:32,232 +ARC inserts a retain in the caller, +and a release in the called function. + +209 +00:10:32,266 --> 00:10:35,969 +Here, when we return our temporary object, +we need to release it first + +210 +00:10:36,003 --> 00:10:38,972 +in the function, +because it's going out of scope. + +211 +00:10:39,006 --> 00:10:43,343 +But we can't do that just yet, because +it doesn't have any other references yet. + +212 +00:10:43,377 --> 00:10:46,547 +If we did release it, it would be +destroyed before we even return, + +213 +00:10:46,580 --> 00:10:48,048 +and that's no good. + +214 +00:10:48,081 --> 00:10:51,952 +So a special convention is used +to be able to return the temporary. + +215 +00:10:51,985 --> 00:10:56,056 +We autorelease it before the return +so that the caller can then retain it. + +216 +00:10:56,089 --> 00:10:58,992 +You've likely seen autorelease +and autoreleasepools before: + +217 +00:10:59,026 --> 00:11:02,729 +it's simply a way to defer a release +until some later point. + +218 +00:11:02,763 --> 00:11:05,899 +Runtime doesn't really make any guarantees +as to when the release happens, + +219 +00:11:05,933 --> 00:11:09,069 +but as long as it's not right here, +right now, it's convenient, + +220 +00:11:09,102 --> 00:11:12,039 +because it lets us +return this temporary object. + +221 +00:11:12,072 --> 00:11:13,473 +Now, this isn't free. + +222 +00:11:13,507 --> 00:11:16,276 +There is some overhead +to doing an autorelease. + +223 +00:11:16,310 --> 00:11:19,613 +This is where +autorelease elision comes in. + +224 +00:11:19,646 --> 00:11:21,014 +So to understand how that works, + +225 +00:11:21,048 --> 00:11:24,518 +let's look at the assembly +and retrace this return. + +226 +00:11:24,551 --> 00:11:27,821 +When we call autorelease, +that goes into the objc runtime, + +227 +00:11:27,855 --> 00:11:30,824 +and that's where the fun begins. + +228 +00:11:30,858 --> 00:11:33,126 +The runtime tries to recognize +what's happening: + +229 +00:11:33,160 --> 00:11:35,863 +that we're returning +an autoreleased value. + +230 +00:11:35,896 --> 00:11:38,332 +To help it out, the compiler emits +a special marker + +231 +00:11:38,365 --> 00:11:40,434 +that we never use otherwise. + +232 +00:11:40,467 --> 00:11:45,405 +It's there to tell the runtime that this +is eligible for autorelease elision. + +233 +00:11:45,439 --> 00:11:48,809 +And it's followed by the retain, +that we will execute later. + +234 +00:11:48,842 --> 00:11:50,644 +But right now, +we're still in the autorelease, + +235 +00:11:50,677 --> 00:11:54,781 +and when we do it, the runtime loads +the special marker instruction, as data, + +236 +00:11:54,815 --> 00:11:59,253 +and compares it to see if it is +the special marker value it expects. + +237 +00:11:59,286 --> 00:12:02,055 +If it is, +that means the compiler told the runtime + +238 +00:12:02,089 --> 00:12:05,192 +that we're returning a temporary +that will immediately be retained. + +239 +00:12:05,225 --> 00:12:09,530 +And this lets us elide, or remove, +the matching autorelease and retain calls. + +240 +00:12:09,563 --> 00:12:11,498 +And that's autorelease elision. + +241 +00:12:12,833 --> 00:12:15,335 +However, this is not free either: + +242 +00:12:15,369 --> 00:12:18,138 +loading code as data isn't something +that's super common otherwise, + +243 +00:12:18,172 --> 00:12:20,174 +so it's not optimal on the CPUs. + +244 +00:12:20,207 --> 00:12:21,909 +We can do better. + +245 +00:12:21,942 --> 00:12:23,977 +So let's retrace +the return sequence again, + +246 +00:12:24,011 --> 00:12:26,747 +this time using the new way. + +247 +00:12:26,780 --> 00:12:28,282 +We started at the autorelease. + +248 +00:12:28,315 --> 00:12:30,817 +That still goes +into the Objective-C runtime. + +249 +00:12:30,851 --> 00:12:35,889 +At this point, we actually already have +valuable information: the return address. + +250 +00:12:35,923 --> 00:12:40,994 +It tells us where we need to return to +after this function completes execution. + +251 +00:12:41,028 --> 00:12:42,963 +So we can keep track of that. + +252 +00:12:42,996 --> 00:12:45,165 +Thankfully, getting the return address +is very cheap. + +253 +00:12:45,199 --> 00:12:48,535 +It's just a pointer, +and we can store it on the side. + +254 +00:12:48,569 --> 00:12:51,371 +We then leave +the runtime autorelease call. + +255 +00:12:51,405 --> 00:12:52,973 +We return to the caller, + +256 +00:12:53,006 --> 00:12:56,376 +and we re-enter the runtime +when doing the retain. + +257 +00:12:56,410 --> 00:12:59,947 +And this is where +the new bit of magic happens. + +258 +00:12:59,980 --> 00:13:01,982 +At that point, we can look at where we are + +259 +00:13:02,015 --> 00:13:04,885 +and get a pointer +to our current return address. + +260 +00:13:04,918 --> 00:13:07,588 +In the runtime, +we can compare this pointer we just got + +261 +00:13:07,621 --> 00:13:09,990 +while doing the retain +with the one we saved earlier + +262 +00:13:10,023 --> 00:13:12,226 +when we were doing the autorelease. + +263 +00:13:12,259 --> 00:13:13,660 +And since we're +just comparing two pointers, + +264 +00:13:13,694 --> 00:13:14,795 +this is super cheap. + +265 +00:13:14,828 --> 00:13:17,764 +We don't need to do +expensive memory accesses. + +266 +00:13:17,798 --> 00:13:20,133 +If the comparison succeeds, +we know we can elide + +267 +00:13:20,167 --> 00:13:21,535 +the autorelease/retain pair, + +268 +00:13:21,568 --> 00:13:24,004 +and we get to improve some performance. + +269 +00:13:25,372 --> 00:13:27,241 +And on top of that, +now that we don't need to compare + +270 +00:13:27,274 --> 00:13:29,510 +this special marker instruction +as data anymore, + +271 +00:13:29,543 --> 00:13:32,212 +we don't need it, so we can remove it. + +272 +00:13:32,246 --> 00:13:34,948 +And that lets us +save some code size as well. + +273 +00:13:34,982 --> 00:13:38,051 +That's how we made autorelease elision +faster and smaller. + +274 +00:13:38,685 --> 00:13:41,989 +We went through several Swift +and Objective-C runtime improvements. + +275 +00:13:42,022 --> 00:13:43,690 +Let's wrap up. + +276 +00:13:43,724 --> 00:13:45,692 +When your app is run on the new OS, + +277 +00:13:45,726 --> 00:13:47,728 +thanks to the improvements +in the runtimes, + +278 +00:13:47,761 --> 00:13:50,597 +Swift protocol checks are more efficient. + +279 +00:13:50,631 --> 00:13:54,968 +Every time we try to do +autorelease elision, that's faster too. + +280 +00:13:55,002 --> 00:13:58,805 +Thanks to the new compilers and linker +in Xcode 14 and message send stubs, + +281 +00:13:58,839 --> 00:14:02,843 +you can save up to 2% of code size +by rebuilding your app. + +282 +00:14:02,876 --> 00:14:06,880 +And finally, when you update +your deployment target to iOS 16, tvOS 16, + +283 +00:14:06,914 --> 00:14:10,384 +or watchOS 9, +you can further save another 2% + +284 +00:14:10,417 --> 00:14:12,286 +by making retain/release calls smaller. + +285 +00:14:12,319 --> 00:14:16,356 +Even more, thanks to the smaller +autorelease elision sequence. + +286 +00:14:16,390 --> 00:14:19,893 +I hope you enjoyed this deep dive +into the Swift and Objective-C runtimes, + +287 +00:14:19,927 --> 00:14:21,361 +and thanks for watching. + diff --git a/eng/2022 Session 110364 Demystify parallelization in Xcode builds en.srt b/eng/2022 Session 110364 Demystify parallelization in Xcode builds en.srt new file mode 100644 index 0000000..274cb74 --- /dev/null +++ b/eng/2022 Session 110364 Demystify parallelization in Xcode builds en.srt @@ -0,0 +1,2245 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:12,579 +Hello, and welcome to WWDC 2022. + +3 +00:00:12,613 --> 00:00:15,749 +My name is Ben, and I'm an engineer +on the Xcode build system team. + +4 +00:00:15,782 --> 00:00:19,152 +Hi, my name is Artem, and I'm +an engineer on the Swift Compiler team. + +5 +00:00:19,186 --> 00:00:22,489 +In this talk we're gonna give you +a deep dive into Xcode's build process + +6 +00:00:22,523 --> 00:00:25,959 +to demystify parallelization +inside builds. + +7 +00:00:25,993 --> 00:00:29,062 +Ben is going to start with an introduction +of the core concepts about builds + +8 +00:00:29,096 --> 00:00:31,565 +and look at the available tools +that Xcode offers + +9 +00:00:31,598 --> 00:00:34,301 +to help investigate +build performance issues. + +10 +00:00:34,334 --> 00:00:39,006 +He will then explain how Xcode increases +parallelization while building a target. + +11 +00:00:39,039 --> 00:00:40,240 +Building on top of that, + +12 +00:00:40,274 --> 00:00:43,610 +I will explain how Xcode +parallelizes a build holistically + +13 +00:00:43,644 --> 00:00:46,280 +while building projects +consisting of many targets + +14 +00:00:46,313 --> 00:00:48,415 +and summarize the takeaways in the end. + +15 +00:00:48,448 --> 00:00:50,551 +Ben? + +16 +00:00:50,584 --> 00:00:55,355 +Let's re-iterate on what happens when +pressing CMD+B in Xcode to build an app. + +17 +00:00:55,389 --> 00:00:57,691 +The build system, as part of Xcode, + +18 +00:00:57,724 --> 00:01:00,661 +gets invoked with a representation +of the whole project, + +19 +00:01:00,694 --> 00:01:02,996 +including all source files, assets, + +20 +00:01:03,030 --> 00:01:07,267 +build settings, and other configurations +like the run destination. + +21 +00:01:07,301 --> 00:01:11,205 +The build system is the single source +of truth about how an app should be built. + +22 +00:01:11,238 --> 00:01:14,141 +It knows which tools to invoke +using which settings + +23 +00:01:14,174 --> 00:01:18,078 +and which intermediate files to produce +to eventually create an app. + +24 +00:01:18,111 --> 00:01:20,881 +In the next step, +the build system invokes the tools + +25 +00:01:20,914 --> 00:01:24,852 +to process the project's input files, +for example, the compilers. + +26 +00:01:26,253 --> 00:01:29,857 +Both compilers, Clang and Swift, +will produce object files + +27 +00:01:29,890 --> 00:01:32,693 +that the linker needs +to link the executable program + +28 +00:01:32,726 --> 00:01:34,728 +that represents the app. + +29 +00:01:34,761 --> 00:01:37,831 +While this order makes sense, +it's not obvious where it comes from. + +30 +00:01:37,865 --> 00:01:40,133 +So let's take a look +at one example of that process + +31 +00:01:40,167 --> 00:01:44,238 +and how the build system decides +in which order to execute all tasks. + +32 +00:01:46,273 --> 00:01:49,343 +Using the input source-files, +the Swift compiler captures + +33 +00:01:49,376 --> 00:01:53,113 +the programmer's intent and translates it +into a machine-executable binary, + +34 +00:01:53,146 --> 00:01:55,949 +checking the source code +for errors along the way. + +35 +00:01:55,983 --> 00:01:58,418 +This process can fail, +which would cancel the build, + +36 +00:01:58,452 --> 00:02:02,789 +but if it succeeds, +it creates an object file for each input. + +37 +00:02:02,823 --> 00:02:05,592 +Those object files are used +to invoke the linker + +38 +00:02:05,626 --> 00:02:08,862 +which combines them and adds references +to externally linked libraries + +39 +00:02:08,896 --> 00:02:11,265 +to produce the executable. + +40 +00:02:11,298 --> 00:02:15,002 +The two tasks have a dependency +based on what they consume and produce. + +41 +00:02:15,035 --> 00:02:19,840 +The object files produced by the compiler +get consumed by the linker. + +42 +00:02:19,873 --> 00:02:22,676 +This creates a dependency +on the build system graph. + +43 +00:02:22,709 --> 00:02:25,879 +The file contents itself +are not of interest to the build system, + +44 +00:02:25,913 --> 00:02:28,749 +but the dependency between the tasks is. + +45 +00:02:28,782 --> 00:02:30,784 +While executing the build, +it needs to make sure + +46 +00:02:30,817 --> 00:02:35,923 +that a task that produces another task's +input finishes before said task can start. + +47 +00:02:35,956 --> 00:02:39,159 +And since this core concept +is valid for all kind of tasks, + +48 +00:02:39,193 --> 00:02:41,328 +let's switch to +a more generic visualization + +49 +00:02:41,361 --> 00:02:44,498 +that shows a dependency +between Task A and Task B. + +50 +00:02:44,531 --> 00:02:48,635 +In this case, +A produces some or all of B's inputs. + +51 +00:02:49,937 --> 00:02:53,106 +Compiling and Linking are only +a few of many different task types + +52 +00:02:53,140 --> 00:02:56,043 +that need to be executed +to build a whole target, + +53 +00:02:56,076 --> 00:02:58,145 +so let's add some more generic tasks +to the graph + +54 +00:02:58,178 --> 00:03:00,914 +that represent other types +like compiling assets, + +55 +00:03:00,948 --> 00:03:03,650 +copying files or codesigning. + +56 +00:03:03,684 --> 00:03:07,454 +Together, they represent +building a Framework target. + +57 +00:03:07,487 --> 00:03:10,224 +Again, +those tasks have defined dependencies + +58 +00:03:10,257 --> 00:03:12,426 +based on their inputs and outputs. + +59 +00:03:12,459 --> 00:03:16,897 +So completing executing task A +unblocks running task B and C, + +60 +00:03:16,930 --> 00:03:21,168 +while finishing task B +unblocks task D and E. + +61 +00:03:21,201 --> 00:03:24,204 +Tasks that get unblocked +are called 'downstream' + +62 +00:03:24,238 --> 00:03:27,274 +and tasks that block 'upstream'. + +63 +00:03:27,307 --> 00:03:30,143 +Many projects contain +more than one framework target, + +64 +00:03:30,177 --> 00:03:34,481 +so let's add two more targets representing +an app and an app extension. + +65 +00:03:34,515 --> 00:03:37,551 +Targets define dependencies +between each other in the project + +66 +00:03:37,584 --> 00:03:39,820 +via explicit or implicit dependencies. + +67 +00:03:39,853 --> 00:03:44,157 +For example, by getting added to the +'Link Binary with Libraries' build phase. + +68 +00:03:45,993 --> 00:03:48,662 +In this case, +the app embeds the app extension + +69 +00:03:48,695 --> 00:03:50,631 +and links against the Framework. + +70 +00:03:50,664 --> 00:03:52,900 +The app extension +is not using the Framework, + +71 +00:03:52,933 --> 00:03:55,068 +so they don't have +a dependency relationship. + +72 +00:03:56,470 --> 00:04:00,741 +When executing the build graph, different +tasks take a different amount of time. + +73 +00:04:00,774 --> 00:04:05,045 +This comes down to the level of complexity +that is necessary to complete the work, + +74 +00:04:05,078 --> 00:04:10,517 +depending on the computation that's needed +as well as the size of the input. + +75 +00:04:10,551 --> 00:04:15,389 +Compiling many files takes usually much +more time than copying a few header files, + +76 +00:04:15,422 --> 00:04:19,526 +and taking this into consideration +will end up with something like this. + +77 +00:04:19,560 --> 00:04:21,762 +When the build system executes this build, + +78 +00:04:21,795 --> 00:04:25,432 +it starts by running tasks +that don't have dependencies. + +79 +00:04:25,465 --> 00:04:29,570 +And once those completed, +they unblock downstream tasks and so on, + +80 +00:04:29,603 --> 00:04:32,906 +following this process +until all planned tasks finished. + +81 +00:04:35,142 --> 00:04:38,312 +On following builds, +the build system is able to skip tasks + +82 +00:04:38,345 --> 00:04:43,050 +for which inputs haven't changed +while the output is still up to date. + +83 +00:04:43,083 --> 00:04:46,019 +If a task needs to re-run +due to a changed input, + +84 +00:04:46,053 --> 00:04:48,355 +like B of the App target in this case, + +85 +00:04:48,388 --> 00:04:52,159 +downstream tasks have to re-run too +if its output changed. + +86 +00:04:52,192 --> 00:04:55,762 +Skipping all other tasks allows +for very fast turnaround times + +87 +00:04:55,796 --> 00:04:58,432 +when iteratively working on the project. + +88 +00:04:58,465 --> 00:05:02,035 +This is called an incremental build, +but let's stick to full builds for now. + +89 +00:05:03,704 --> 00:05:06,640 +The dependencies and duration +of the task execution + +90 +00:05:06,673 --> 00:05:10,177 +defines the first possible time +a downstream task can start. + +91 +00:05:10,210 --> 00:05:13,480 +With this information it's possible +to calculate the critical path + +92 +00:05:13,514 --> 00:05:16,049 +which is the shortest time +the build needs to run + +93 +00:05:16,083 --> 00:05:18,952 +with theoretical unlimited resources. + +94 +00:05:18,986 --> 00:05:22,456 +A common pattern throughout this talk +will be to shorten this path + +95 +00:05:22,489 --> 00:05:25,826 +to create a highly parallelizable +and scalable build graph. + +96 +00:05:25,859 --> 00:05:29,263 +A shorter critical path +does not necessarily result + +97 +00:05:29,296 --> 00:05:30,931 +in a shorter overall build time, + +98 +00:05:30,964 --> 00:05:34,268 +but it ensures that the build scales +with the hardware. + +99 +00:05:34,301 --> 00:05:36,870 +The critical build path +defines the limiting factor + +100 +00:05:36,904 --> 00:05:38,772 +of how fast a build can be– + +101 +00:05:38,805 --> 00:05:43,310 +it cannot complete faster, +even if the hardware would allow it. + +102 +00:05:43,343 --> 00:05:47,881 +Shortening the critical path is done by +breaking up dependencies within it. + +103 +00:05:47,915 --> 00:05:51,652 +When looking at how a build performed and +to understand more about its execution, + +104 +00:05:51,685 --> 00:05:55,589 +the data needs to be plotted +based on the time they executed. + +105 +00:05:55,622 --> 00:05:58,492 +The width still indicates +the length of tasks. + +106 +00:05:58,525 --> 00:06:01,929 +Wide elements like these two +indicate a long running task + +107 +00:06:01,962 --> 00:06:05,899 +while narrow elements like these +represent fast finishing tasks. + +108 +00:06:07,534 --> 00:06:11,004 +The height of the graph shows the number +of parallel executing tasks + +109 +00:06:11,038 --> 00:06:12,439 +at a given time. + +110 +00:06:12,472 --> 00:06:17,144 +Be aware that this does not directly map +to CPU or memory utilization. + +111 +00:06:18,512 --> 00:06:22,082 +Empty space originates by tasks blocking +its downstream tasks + +112 +00:06:22,115 --> 00:06:24,451 +like in those two scenarios. + +113 +00:06:24,484 --> 00:06:29,423 +And finally, the color of the elements +represent their associated target. + +114 +00:06:29,456 --> 00:06:33,527 +I'm very excited to announce that +this visualization is new in Xcode 14 + +115 +00:06:33,560 --> 00:06:37,297 +and will help understand +a build's performance after it finished. + +116 +00:06:37,331 --> 00:06:40,367 +The Xcode Build Timeline is +a great new addition to the build log. + +117 +00:06:40,400 --> 00:06:42,736 +It visualizes based on +parallelization, + +118 +00:06:42,769 --> 00:06:46,139 +rather than hierarchy +to understand the build's performance. + +119 +00:06:46,173 --> 00:06:49,910 +The number of rows at given time +represents the level of parallelism + +120 +00:06:49,943 --> 00:06:51,411 +during that time. + +121 +00:06:51,445 --> 00:06:54,248 +The horizontal length of individual tasks + +122 +00:06:54,281 --> 00:06:57,584 +represent the duration they needed +to finish their work. + +123 +00:06:57,618 --> 00:07:00,387 +Empty space in the graph +shows where unfinished tasks + +124 +00:07:00,420 --> 00:07:04,057 +blocked downstream tasks +from starting to execute. + +125 +00:07:04,091 --> 00:07:06,560 +Different colors applied +to the timeline elements + +126 +00:07:06,593 --> 00:07:10,531 +help distinguish the different targets +that were part of the build. + +127 +00:07:10,564 --> 00:07:13,800 +And on incremental builds, +the timeline will only contain tasks + +128 +00:07:13,834 --> 00:07:15,903 +that got actually executed, + +129 +00:07:15,936 --> 00:07:17,738 +allowing to spot long-running tasks, + +130 +00:07:17,771 --> 00:07:21,842 +especially ones which might not have +been expected to run during this build. + +131 +00:07:22,843 --> 00:07:25,712 +Here's a demo of the Build Timeline +in Xcode 14. + +132 +00:07:25,746 --> 00:07:29,550 +In this window I opened a copy +of the swift-docc project from Github + +133 +00:07:29,583 --> 00:07:31,785 +which builds the documentation compiler. + +134 +00:07:31,818 --> 00:07:34,655 +To get an overview about the targets +that are built for the scheme, + +135 +00:07:34,688 --> 00:07:36,590 +let's check out the scheme editor. + +136 +00:07:36,623 --> 00:07:39,426 +To open that I click on the scheme +and select "Edit scheme". + +137 +00:07:40,427 --> 00:07:43,197 +The 'build' tab contains a list +of all targets. + +138 +00:07:43,230 --> 00:07:45,632 +Targets can explicitly get added +to the scheme + +139 +00:07:45,666 --> 00:07:48,268 +or implicitly +by being a dependency of a target + +140 +00:07:48,302 --> 00:07:50,270 +that is already part of the scheme. + +141 +00:07:50,304 --> 00:07:54,007 +In this case I'm using a Swift package +with an automatically generated scheme + +142 +00:07:54,041 --> 00:07:58,612 +for the package, so all targets +from the manifest are explicitly defined. + +143 +00:08:00,881 --> 00:08:04,451 +This log represents a build of that scheme +that I executed earlier. + +144 +00:08:04,484 --> 00:08:08,188 +In contains entries for all tasks +that the build system executed. + +145 +00:08:08,222 --> 00:08:10,557 +The entries are organized in a hierarchy + +146 +00:08:10,591 --> 00:08:14,761 +based on the targets they belong to, +like the 'docc' target here. + +147 +00:08:14,795 --> 00:08:17,397 +To successfully build the executable +of that target, + +148 +00:08:17,431 --> 00:08:21,568 +Xcode ran all tasks that are represented +by the children of this node. + +149 +00:08:21,602 --> 00:08:23,904 +Since the build log is currently +in its 'All' state, + +150 +00:08:23,937 --> 00:08:26,073 +it also shows tasks from previous builds + +151 +00:08:26,106 --> 00:08:28,709 +that didn't need to re-run +in an incremental build. + +152 +00:08:28,742 --> 00:08:32,246 +Selecting 'Recent' only shows tasks +that got actually executed, + +153 +00:08:32,279 --> 00:08:34,848 +hiding all skipped tasks. + +154 +00:08:34,882 --> 00:08:37,718 +In addition to that, +the build log also supports filters + +155 +00:08:37,751 --> 00:08:40,754 +to only show tasks +that had issues or even failed. + +156 +00:08:43,924 --> 00:08:45,859 +To open the build timeline for this build, + +157 +00:08:45,893 --> 00:08:49,696 +I go to the editor options +and open the assistant. + +158 +00:08:49,730 --> 00:08:52,399 +The build timeline +opens next to the build log. + +159 +00:08:52,432 --> 00:08:55,736 +Like usual, the editor options +provide settings to show the assistant + +160 +00:08:55,769 --> 00:08:57,271 +on the right or bottom. + +161 +00:08:57,304 --> 00:08:59,473 +I'll stay with bottom for now. + +162 +00:08:59,506 --> 00:09:02,576 +The timeline visualizes the same data +as the 'recent' build log + +163 +00:09:02,609 --> 00:09:05,179 +based on the parallelization of the build. + +164 +00:09:05,212 --> 00:09:08,715 +Selecting an element in one +also selects it in the other. + +165 +00:09:08,749 --> 00:09:12,920 +This enables to see +a task's execution in context. + +166 +00:09:12,953 --> 00:09:15,088 +The timeline here gives a sense +about the tasks + +167 +00:09:15,122 --> 00:09:18,091 +that executed in parallel +to the selected task. + +168 +00:09:18,125 --> 00:09:21,662 +I'm using a pinch gesture +on the trackpad to zoom out again. + +169 +00:09:26,233 --> 00:09:30,470 +Selecting an element in the timeline +shows it in the build log. + +170 +00:09:30,504 --> 00:09:34,007 +And since the build log visualizes +based on the hierarchical structure, + +171 +00:09:34,041 --> 00:09:36,476 +it enables to view +which files were compiled + +172 +00:09:36,510 --> 00:09:38,579 +as part of this compiler invocation. + +173 +00:09:38,612 --> 00:09:42,482 +It also enables to view the whole +command line of that invocation. + +174 +00:09:46,954 --> 00:09:49,890 +Holding down Option while selecting +an area in the build timeline + +175 +00:09:49,923 --> 00:09:52,960 +adjusts the view port +to fit this timeframe. + +176 +00:09:52,993 --> 00:09:56,463 +Here we can verify +that linking of the target ArgumentParser + +177 +00:09:56,496 --> 00:09:59,499 +is in fact waiting for compilation +of the same target. + +178 +00:09:59,533 --> 00:10:03,504 +Holding Option while scrolling up +allows me to zoom out quickly. + +179 +00:10:03,537 --> 00:10:06,507 +The number of rows in the timeline +represents the number of tasks + +180 +00:10:06,540 --> 00:10:08,609 +that ran in parallel at that time. + +181 +00:10:08,642 --> 00:10:14,081 +An empty space like this indicates tasks +waiting for un-produced inputs. + +182 +00:10:14,114 --> 00:10:18,418 +Ideally, the timeline is vertically filled +and has as little empty space as possible. + +183 +00:10:18,452 --> 00:10:21,388 +This scales the build graph the best +and makes builds faster, + +184 +00:10:21,421 --> 00:10:23,123 +the faster the hardware. + +185 +00:10:23,156 --> 00:10:26,126 +To achieve this, Xcode comes with +many improvements this year + +186 +00:10:26,159 --> 00:10:28,695 +to shorten the critical path. + +187 +00:10:28,729 --> 00:10:32,633 +Next, let's check out how Xcode defines +and builds individual targets + +188 +00:10:32,666 --> 00:10:35,636 +as well as how +it can increase parallelization. + +189 +00:10:35,669 --> 00:10:38,639 +When configuring a target, +build phases describe the work + +190 +00:10:38,672 --> 00:10:41,808 +that needs to be done +to produce that target's product. + +191 +00:10:41,842 --> 00:10:45,312 +They are defined in the project editor +and can contain a set of source code files + +192 +00:10:45,345 --> 00:10:46,813 +and assets to compile, + +193 +00:10:46,847 --> 00:10:49,583 +files that need to be copied +like headers or resources, + +194 +00:10:49,616 --> 00:10:54,221 +as well as libraries that should be linked +or scripts that should be executed. + +195 +00:10:54,254 --> 00:10:57,090 +Many build phases describe +tasks with inputs or outputs + +196 +00:10:57,124 --> 00:11:00,260 +from other build phases, +creating dependencies between them. + +197 +00:11:00,294 --> 00:11:04,865 +For example, a target's source files +must be compiled before it is linked. + +198 +00:11:04,898 --> 00:11:08,602 +However, +this doesn't apply to all build phases. + +199 +00:11:08,635 --> 00:11:11,672 +Instead of running tasks from +each build phase in a linear order, + +200 +00:11:11,705 --> 00:11:15,042 +the build system will consider the inputs +and outputs of build phases + +201 +00:11:15,075 --> 00:11:17,744 +to determine if they can run in parallel. + +202 +00:11:17,778 --> 00:11:21,548 +For example, Compilation +and Resource copying can run in parallel + +203 +00:11:21,582 --> 00:11:24,785 +because neither depends +on any outputs of the other. + +204 +00:11:24,818 --> 00:11:27,654 +However, +linking must still follow compilation + +205 +00:11:27,688 --> 00:11:30,958 +because it depends on the object files +produced by that phase. + +206 +00:11:30,991 --> 00:11:35,028 +Now, let's consider a different target +which contains 'Run Script' build phases. + +207 +00:11:35,062 --> 00:11:38,732 +Unlike other build phases, +the inputs and outputs of script phases + +208 +00:11:38,765 --> 00:11:41,802 +must be manually configured +in the target editor. + +209 +00:11:41,835 --> 00:11:45,305 +As a result, the build system +will run consecutive script phases + +210 +00:11:45,339 --> 00:11:49,443 +one at a time to avoid introducing +a data race in the build process. + +211 +00:11:49,476 --> 00:11:51,445 +If the scripts in a target +are configured to run + +212 +00:11:51,478 --> 00:11:53,247 +based on dependency analysis + +213 +00:11:53,280 --> 00:11:55,983 +and specify their complete list +of inputs and outputs, + +214 +00:11:56,016 --> 00:11:58,285 +then the build setting +FUSE_BUILD_SCRIPT_PHASES + +215 +00:11:58,318 --> 00:11:59,553 +can be set to YES + +216 +00:11:59,586 --> 00:12:03,457 +to indicate the build system +should attempt to run them in parallel. + +217 +00:12:03,490 --> 00:12:06,293 +However, +when running script phases in parallel, + +218 +00:12:06,326 --> 00:12:09,897 +the build system has to rely +on the specified inputs and outputs. + +219 +00:12:09,930 --> 00:12:14,134 +So be aware that an incomplete list +of the inputs or outputs of a script phase + +220 +00:12:14,168 --> 00:12:17,437 +can lead to data races +which are very hard to debug. + +221 +00:12:17,471 --> 00:12:20,707 +To mitigate this, Xcode supports +user script sandboxing + +222 +00:12:20,741 --> 00:12:24,044 +to precisely declare the dependencies +of each script phase. + +223 +00:12:24,077 --> 00:12:27,114 +Sandboxing is an opt-in feature +that blocks shell scripts + +224 +00:12:27,147 --> 00:12:30,617 +from accidentally accessing source files +and intermediate build objects, + +225 +00:12:30,651 --> 00:12:35,122 +unless those are explicitly declared +as an input or output for the phase. + +226 +00:12:35,155 --> 00:12:38,425 +In this example, +neither input nor output.txt + +227 +00:12:38,458 --> 00:12:41,028 +are declared as a dependency +for that script phase. + +228 +00:12:41,061 --> 00:12:43,630 +The sandbox will block the script +from reading and writing + +229 +00:12:43,664 --> 00:12:46,867 +to both files when building the project. + +230 +00:12:46,900 --> 00:12:48,702 +When the script violates the sandbox, + +231 +00:12:48,735 --> 00:12:52,506 +it will fail with a non-zero exit code +causing the build to fail. + +232 +00:12:52,539 --> 00:12:55,209 +In addition to that, +Xcode will list all the paths + +233 +00:12:55,242 --> 00:13:00,180 +that the script phase was trying to access +without properly declaring them. + +234 +00:13:00,214 --> 00:13:03,383 +Adding both files as dependency +information to this script phase + +235 +00:13:03,417 --> 00:13:04,885 +fixes this issue. + +236 +00:13:04,918 --> 00:13:08,188 +This way the sandbox ensures +that the script is not mistakenly + +237 +00:13:08,222 --> 00:13:12,426 +accessing any file other than +its declared inputs and outputs. + +238 +00:13:12,459 --> 00:13:15,562 +Now, let's explore an example +with more than one script phase + +239 +00:13:15,596 --> 00:13:19,766 +and see how sandboxing prevents +data races and incorrect builds. + +240 +00:13:19,800 --> 00:13:21,635 +There are two script phases. + +241 +00:13:21,668 --> 00:13:25,906 +The first one reads a text file, +calculates a checksum of its content, + +242 +00:13:25,939 --> 00:13:30,277 +and writes that value to an intermediate +file in DERIVED_FILE_DIR. + +243 +00:13:30,310 --> 00:13:32,813 +The other script reads the same text file + +244 +00:13:32,846 --> 00:13:34,648 +as well as the produced checksum + +245 +00:13:34,681 --> 00:13:38,819 +and injects them into an html file +for later display in the app. + +246 +00:13:38,852 --> 00:13:42,256 +If the precise set of input +and output dependencies for these phases + +247 +00:13:42,289 --> 00:13:45,792 +is not declared, +Xcode will run the two scripts in parallel + +248 +00:13:45,826 --> 00:13:48,495 +when FUSE_BUILD_SCRIPT_PHASES +is on. + +249 +00:13:48,529 --> 00:13:51,765 +Let's inspect this problematic scenario +in detail. + +250 +00:13:51,798 --> 00:13:55,369 +Let's assume "Generate HTML" +is missing the input declaration + +251 +00:13:55,402 --> 00:13:59,373 +of "checksum.txt", but all other inputs +and outputs of both scripts + +252 +00:13:59,406 --> 00:14:01,375 +have been correctly declared. + +253 +00:14:01,408 --> 00:14:04,878 +Without sandboxing, this misconfiguration +might stay unnoticed, + +254 +00:14:04,912 --> 00:14:07,514 +causing problems in the build. + +255 +00:14:07,548 --> 00:14:10,551 +It means Xcode will fail to infer +the dependency relation + +256 +00:14:10,584 --> 00:14:13,554 +between both phases, +and schedule to run them in parallel + +257 +00:14:13,587 --> 00:14:16,657 +when FUSE_BUILD_SCRIPT_PHASES +is switched on. + +258 +00:14:16,690 --> 00:14:18,225 +There are a few hazards here. + +259 +00:14:18,258 --> 00:14:23,297 +Since checksum.txt is not listed as an +input dependency for "Generate HTML" + +260 +00:14:23,330 --> 00:14:25,999 +during a clean build +the script will attempt to read the file + +261 +00:14:26,033 --> 00:14:29,036 +without it being available +on the filesystem. + +262 +00:14:29,069 --> 00:14:32,573 +The other hazard is if checksum.txt +is available on disk + +263 +00:14:32,606 --> 00:14:34,942 +because of previous runs +of "Calculate Checksum", + +264 +00:14:34,975 --> 00:14:37,744 +"Generate HTML" +may pick up the outdated file + +265 +00:14:37,778 --> 00:14:40,681 +when the two scripts run in parallel. + +266 +00:14:40,714 --> 00:14:43,550 +This is a user error, +and executing the scripts in a sandbox + +267 +00:14:43,584 --> 00:14:45,652 +helps preventing this issue. + +268 +00:14:45,686 --> 00:14:49,323 +With sandboxing switched on, +"Generate HTML" will fail immediately + +269 +00:14:49,356 --> 00:14:51,658 +when it attempts to read "checksum.txt". + +270 +00:14:51,692 --> 00:14:56,363 +The error message will guide adding +the missing input for that build phase. + +271 +00:14:56,396 --> 00:14:58,699 +Having the inputs and outputs +correctly defined + +272 +00:14:58,732 --> 00:15:02,069 +guides Xcode to respect the dependency +relation between both phases + +273 +00:15:02,102 --> 00:15:05,706 +so that "calculate checksum" +runs before "Generate HTML". + +274 +00:15:05,739 --> 00:15:09,743 +While unrelated build phases +can still execute in parallel. + +275 +00:15:09,776 --> 00:15:12,079 +To enable Sandboxed Shell Scripts +for a target, + +276 +00:15:12,112 --> 00:15:15,649 +set ENABLE_USER_SCRIPT_SANDBOXING +to YES in the build settings editor + +277 +00:15:15,682 --> 00:15:17,918 +or an xcconfig file. + +278 +00:15:17,951 --> 00:15:22,122 +In summary, sandboxed shell scripts allow +having correct dependency information + +279 +00:15:22,155 --> 00:15:25,292 +to enable faster +and more robust incremental builds + +280 +00:15:25,325 --> 00:15:28,362 +since the build system has the confidence +to skip script phases + +281 +00:15:28,395 --> 00:15:31,365 +if the inputs haven't changed +and the outputs are still valid, + +282 +00:15:31,398 --> 00:15:34,134 +while re-running the script otherwise. + +283 +00:15:34,168 --> 00:15:37,704 +Enabling the build setting for +a script's target blocks access to files + +284 +00:15:37,738 --> 00:15:41,141 +inside the source root of the project +as well as the derived data directory + +285 +00:15:41,175 --> 00:15:44,678 +if they are not explicitly defined +as inputs or outputs of the script + +286 +00:15:44,711 --> 00:15:46,146 +in the project. + +287 +00:15:46,180 --> 00:15:49,750 +The sandbox will not prevent unauthorized +access to any other directory, + +288 +00:15:49,783 --> 00:15:52,953 +so don't consider this a security feature. + +289 +00:15:52,986 --> 00:15:55,722 +Using this feature helps to debug +missing inputs or outputs + +290 +00:15:55,756 --> 00:15:59,626 +of existing script phases +to ensure a valid configuration + +291 +00:15:59,660 --> 00:16:02,229 +And in combination with +the previously explained build setting + +292 +00:16:02,262 --> 00:16:03,897 +FUSE_BUILD_SCRIPT_PHASES, + +293 +00:16:03,931 --> 00:16:07,367 +script phases with correctly defined +dependency edges through sandboxing + +294 +00:16:07,401 --> 00:16:11,705 +can execute in parallel +to reduce the critical path of the build. + +295 +00:16:11,738 --> 00:16:14,808 +That's it for parallelizing +the steps of building a target. + +296 +00:16:14,842 --> 00:16:17,144 +Now Artem is going to +demystify parallelization + +297 +00:16:17,177 --> 00:16:18,812 +hen building many targets. + +298 +00:16:18,846 --> 00:16:20,214 +Artem: Thanks, Ben. + +299 +00:16:20,247 --> 00:16:22,649 +Now that we've covered the basics +of build system tasks + +300 +00:16:22,683 --> 00:16:25,452 +and phases that may go into building +a target in your project, + +301 +00:16:25,485 --> 00:16:27,054 +let's take a more global view + +302 +00:16:27,087 --> 00:16:30,424 +and explore how Xcode uses dependencies +between Swift targets + +303 +00:16:30,457 --> 00:16:33,493 +to extract the maximum amount +of parallelism out of your builds + +304 +00:16:33,527 --> 00:16:38,131 +and how the structure and organization +of your project can affect build times. + +305 +00:16:38,165 --> 00:16:41,602 +There are likely to be several levels +of hierarchy composing your project. + +306 +00:16:41,635 --> 00:16:45,172 +For example, an App target depending +on a collection of local libraries + +307 +00:16:45,205 --> 00:16:47,975 +broken up into targets +along semantic boundaries, + +308 +00:16:48,008 --> 00:16:49,943 +and in several frameworks. + +309 +00:16:49,977 --> 00:16:52,713 +Each target containing +many different build phases and steps, + +310 +00:16:52,746 --> 00:16:55,182 +producing and consuming file dependencies + +311 +00:16:55,215 --> 00:16:57,784 +to and from build phases in other targets. + +312 +00:16:57,818 --> 00:16:59,586 +As the size of your project grows, + +313 +00:16:59,620 --> 00:17:04,024 +these task graphs tend to increase in size +and complexity. + +314 +00:17:04,057 --> 00:17:06,360 +While the Xcode Build System +flattens these hierarchies, + +315 +00:17:06,393 --> 00:17:08,762 +breaking down the build +into a sea of tasks + +316 +00:17:08,795 --> 00:17:11,164 +that correspond to build phases +of all targets. + +317 +00:17:11,198 --> 00:17:15,769 +One kind of task that is special +for a Swift target is compilation. + +318 +00:17:15,802 --> 00:17:18,872 +Building a Swift target's source code +into binary product's + +319 +00:17:18,906 --> 00:17:22,409 +is a complex operation +that typically consists of many sub-tasks + +320 +00:17:22,442 --> 00:17:26,180 +for build planning, +compilation, and linking. + +321 +00:17:26,213 --> 00:17:29,216 +Coordination of these +tasks is delegated to a specialized tool + +322 +00:17:29,249 --> 00:17:32,152 +in the Xcode toolchain– +the Swift Driver. + +323 +00:17:32,186 --> 00:17:35,222 +The Driver has specialized knowledge +on when and how to construct + +324 +00:17:35,255 --> 00:17:39,092 +the required compiler and linker +invocations for the target's source code. + +325 +00:17:39,126 --> 00:17:41,328 +Any target that includes Swift code + +326 +00:17:41,361 --> 00:17:45,332 +also corresponds to a unit +of code distribution: a module. + +327 +00:17:45,365 --> 00:17:48,802 +A binary module file capturing +the public interface of this target + +328 +00:17:48,836 --> 00:17:52,873 +is a build product that is required for +downstream targets to begin a compilation. + +329 +00:17:52,906 --> 00:17:55,843 +Let's take a closer look at an example +of what Swift Driver does + +330 +00:17:55,876 --> 00:17:58,846 +to build one of the targets. + +331 +00:17:58,879 --> 00:18:02,249 +Your target probably consists of +a collection of several source files. + +332 +00:18:02,282 --> 00:18:05,919 +In release or optimized builds +the driver will schedule one compiler task + +333 +00:18:05,953 --> 00:18:10,257 +including all source files to maximize +opportunities for optimization. + +334 +00:18:10,290 --> 00:18:15,395 +This single compile task will also produce +the target's Swift module. + +335 +00:18:15,429 --> 00:18:17,698 +In debug or incremental compilation modes, + +336 +00:18:17,731 --> 00:18:20,234 +the Swift Driver breaks down +the required compilation effort + +337 +00:18:20,267 --> 00:18:22,970 +into smaller sub-tasks +which can run in parallel, + +338 +00:18:23,003 --> 00:18:26,673 +some of which may not need to re-run +on an incremental build. + +339 +00:18:26,707 --> 00:18:29,843 +Producing a Swift module then requires +an additional step + +340 +00:18:29,877 --> 00:18:33,947 +to merge together partial intermediate +products of each compile task. + +341 +00:18:33,981 --> 00:18:35,816 +If, like in this example, + +342 +00:18:35,849 --> 00:18:38,385 +the number of source files +in your target is high, + +343 +00:18:38,418 --> 00:18:42,389 +individual files may also be assigned +to batch compilation sub-tasks, + +344 +00:18:42,422 --> 00:18:45,192 +according to +the build-system's heuristics. + +345 +00:18:45,225 --> 00:18:47,828 +The build log highlights +which source files get assigned + +346 +00:18:47,861 --> 00:18:49,596 +to batch compilation jobs, + +347 +00:18:49,630 --> 00:18:52,566 +with a separate entry +for each file's diagnostics. + +348 +00:18:52,599 --> 00:18:56,670 +Being able to parallelize a target's build +across different source files is crucial + +349 +00:18:56,703 --> 00:18:59,339 +for both faster +and smaller incremental builds, + +350 +00:18:59,373 --> 00:19:04,244 +so make sure your Debug builds are using +the Incremental Compilation Mode setting. + +351 +00:19:04,278 --> 00:19:06,713 +Before Xcode 14, +because of the boundary + +352 +00:19:06,747 --> 00:19:09,449 +between the Xcode Build System +and Swift Driver, + +353 +00:19:09,483 --> 00:19:11,552 +orchestration of target build phases, + +354 +00:19:11,585 --> 00:19:15,789 +and compilation sub-tasks spawned by each +target's instance of the Driver + +355 +00:19:15,822 --> 00:19:17,658 +happened independently of each other, + +356 +00:19:17,691 --> 00:19:19,493 +with each component doing its best + +357 +00:19:19,526 --> 00:19:22,496 +to make the most +of available system resources. + +358 +00:19:22,529 --> 00:19:24,998 +Let's take this example build graph +and dive deeper + +359 +00:19:25,032 --> 00:19:27,301 +into what goes into scheduling +its compilation phases + +360 +00:19:27,334 --> 00:19:29,970 +with respect to each other. + +361 +00:19:30,003 --> 00:19:33,307 +As we've learned earlier, +Swift target dependencies are resolved + +362 +00:19:33,340 --> 00:19:36,610 +by having their dependents provide +a binary module file + +363 +00:19:36,643 --> 00:19:39,780 +that captures the dependent's +public interface. + +364 +00:19:39,813 --> 00:19:43,383 +Resolving these dependency relationships +leads us to the following ordering, + +365 +00:19:43,417 --> 00:19:46,386 +captured in a timeline showing +the top-level Swift Driver tasks + +366 +00:19:46,420 --> 00:19:49,990 +for each target, +as well as their individual sub-tasks. + +367 +00:19:50,023 --> 00:19:53,827 +With Xcode 14, thanks to an entirely new +implementation of Swift Driver– + +368 +00:19:53,861 --> 00:19:55,562 +itself now written in Swift– + +369 +00:19:55,596 --> 00:19:58,832 +the build system +and the compiler are fully integrated. + +370 +00:19:58,866 --> 00:20:01,902 +The Xcode Build System acts +as the central scheduler + +371 +00:20:01,935 --> 00:20:05,305 +for all tasks that must be performed +to compile your code. + +372 +00:20:05,339 --> 00:20:07,508 +This central planning mechanism +allows Xcode + +373 +00:20:07,541 --> 00:20:09,610 +to make fine-grained scheduling decisions + +374 +00:20:09,643 --> 00:20:12,246 +providing better guarantees +that building your project + +375 +00:20:12,279 --> 00:20:15,115 +will use only as much resources +as available, + +376 +00:20:15,148 --> 00:20:18,952 +without oversubscribing your CPU +and reducing overall system performance. + +377 +00:20:20,387 --> 00:20:23,090 +And what was previously a collection +of islands of sub-tasks + +378 +00:20:23,123 --> 00:20:25,559 +outside of Xcode Build System's purview + +379 +00:20:25,592 --> 00:20:28,662 +are now fully in a domain +of the build-system's scheduler. + +380 +00:20:30,564 --> 00:20:33,834 +With all the individual sub-tasks +in a central task pool, + +381 +00:20:33,867 --> 00:20:37,871 +it is important to consider the trade-offs +made by the build scheduler. + +382 +00:20:37,905 --> 00:20:40,073 +For example, on an 8-core machine + +383 +00:20:40,107 --> 00:20:43,510 +the scheduler's default +is to assign available tasks– + +384 +00:20:43,544 --> 00:20:46,947 +those tasks whose dependencies have been +satisfied and are ready to go– + +385 +00:20:46,980 --> 00:20:49,750 +to one of eight available execution slots. + +386 +00:20:49,783 --> 00:20:51,685 +As soon as one of the slots frees up, + +387 +00:20:51,718 --> 00:20:55,689 +the Build System attempts to fill it +with more outstanding work. + +388 +00:20:55,722 --> 00:20:57,658 +On a higher-core-count machine, + +389 +00:20:57,691 --> 00:20:59,893 +we're able to perform +more concurrent work. + +390 +00:20:59,927 --> 00:21:02,829 +But that means we're also more likely +to have idle cores + +391 +00:21:02,863 --> 00:21:04,932 +which are available to perform more work, + +392 +00:21:04,965 --> 00:21:08,035 +but all of the outstanding tasks +are still awaiting their inputs, + +393 +00:21:08,068 --> 00:21:11,738 +as produced by other tasks +that are currently in-flight or waiting. + +394 +00:21:11,772 --> 00:21:14,541 +The new integrated build system +allows the scheduler + +395 +00:21:14,575 --> 00:21:17,244 +to significantly reduce this idle time. + +396 +00:21:17,277 --> 00:21:20,848 +To see how, let's revisit how +a target's dependencies for compilation, + +397 +00:21:20,881 --> 00:21:23,183 +binary module files, are resolved. + +398 +00:21:25,485 --> 00:21:26,787 +As we've covered earlier, + +399 +00:21:26,820 --> 00:21:28,989 +the partial results +of compilation sub-tasks + +400 +00:21:29,022 --> 00:21:32,025 +are merged into a target's +final module product. + +401 +00:21:32,059 --> 00:21:36,797 +Once this product is available, +downstream targets may begin compilation. + +402 +00:21:36,830 --> 00:21:41,368 +New in Xcode 14 and Swift 5.7, +construction of a target's module + +403 +00:21:41,401 --> 00:21:43,604 +is done in a separate emit-module task + +404 +00:21:43,637 --> 00:21:46,240 +directly from all program source files. + +405 +00:21:46,273 --> 00:21:48,709 +This means a target's dependencies +can begin compilation + +406 +00:21:48,742 --> 00:21:50,878 +as soon as the emit-module task +is complete + +407 +00:21:50,911 --> 00:21:55,115 +without waiting for all of the other +compiler tasks of the dependency target. + +408 +00:21:55,148 --> 00:21:59,253 +Being able to unblock downstream +target compilation this much sooner + +409 +00:21:59,286 --> 00:22:03,423 +cuts down on the time spent waiting +for available work with idle CPU cores– + +410 +00:22:03,457 --> 00:22:07,094 +that empty space in between +spurs of activity in the build timeline. + +411 +00:22:08,495 --> 00:22:10,564 +Extending this to the rest of our project + +412 +00:22:10,597 --> 00:22:14,268 +shows that although we are performing +a similar amount of overall work, + +413 +00:22:14,301 --> 00:22:18,005 +the build system is able to use +the computer's resources more efficiently, + +414 +00:22:18,038 --> 00:22:21,141 +often completing +the build significantly faster. + +415 +00:22:22,276 --> 00:22:24,811 +Now, let's take a look at +a second cross-target optimization + +416 +00:22:24,845 --> 00:22:27,214 +the build system can perform +when building Swift– + +417 +00:22:27,247 --> 00:22:28,782 +Eager Linking. + +418 +00:22:28,815 --> 00:22:30,350 +Building on the previous example, + +419 +00:22:30,384 --> 00:22:32,553 +we've added the linker tasks +for each target, + +420 +00:22:32,586 --> 00:22:34,955 +which are both +on the build's critical path. + +421 +00:22:34,988 --> 00:22:38,292 +In this case, +because Target B links Target A, + +422 +00:22:38,325 --> 00:22:42,863 +Target B's link task must wait for +Target A's linked output to be produced + +423 +00:22:42,896 --> 00:22:46,900 +and its own compilation tasks +to complete before it can run. + +424 +00:22:46,934 --> 00:22:48,769 +However, with eager linking, + +425 +00:22:48,802 --> 00:22:53,640 +Target B's link task can depend on +Target A's emit-module task instead. + +426 +00:22:53,674 --> 00:22:57,044 +As a result, Target B can begin +linking earlier in the build, + +427 +00:22:57,077 --> 00:23:01,281 +running in parallel with linking Target A +and shortening the critical path. + +428 +00:23:01,315 --> 00:23:03,217 +How does this work? + +429 +00:23:03,250 --> 00:23:05,285 +Normally, +the dependency graph of two targets + +430 +00:23:05,319 --> 00:23:09,289 +with a linked product dependency +looks something like this. + +431 +00:23:09,323 --> 00:23:13,560 +Linking the dependent target requires +the linked product of its dependencies + +432 +00:23:13,594 --> 00:23:16,730 +in addition to the target's +own compilation outputs. + +433 +00:23:16,763 --> 00:23:19,600 +When linking eagerly, +this dependency is broken, + +434 +00:23:19,633 --> 00:23:23,470 +allowing the dependent target +to start linking earlier. + +435 +00:23:23,504 --> 00:23:26,373 +Instead of depending on a linked +product of the dependency, + +436 +00:23:26,406 --> 00:23:29,309 +it now depends on a text-based +dynamic library stub + +437 +00:23:29,343 --> 00:23:32,980 +produced earlier in the build process +by the emit-module task. + +438 +00:23:33,013 --> 00:23:35,282 +This stub contains a list of symbols +which will appear + +439 +00:23:35,315 --> 00:23:38,051 +in the linked product +for use by dependents. + +440 +00:23:38,085 --> 00:23:42,322 +You can enable this optimization using +the Xcode build setting shown on screen. + +441 +00:23:42,356 --> 00:23:44,658 +Eager Linking applies +to all pure Swift targets + +442 +00:23:44,691 --> 00:23:47,694 +that are dynamically linked +by their dependents. + +443 +00:23:47,728 --> 00:23:52,032 +To summarize, the Xcode Build System +is a sophisticated scheduling engine + +444 +00:23:52,065 --> 00:23:54,768 +that seeks to extract +as much parallelism as possible + +445 +00:23:54,801 --> 00:23:57,004 +by running build phases in parallel. + +446 +00:23:57,037 --> 00:23:59,940 +And features like Script Sandboxing +allow you to ensure your builds + +447 +00:23:59,973 --> 00:24:03,110 +are both maximally parallel and reliable. + +448 +00:24:03,143 --> 00:24:06,180 +Xcode and Swift are more integrated +than ever. + +449 +00:24:06,213 --> 00:24:10,083 +And project structure: its modularization, +the overall shape of the graph + +450 +00:24:10,117 --> 00:24:12,252 +made up of dependencies +between target products + +451 +00:24:12,286 --> 00:24:15,789 +and the number and complexity +of build phases within them, + +452 +00:24:15,822 --> 00:24:19,493 +combined with the available computational +resources of your machine– + +453 +00:24:19,526 --> 00:24:23,664 +all these are contributing factors +to the degree Xcode is able to parallelize + +454 +00:24:23,697 --> 00:24:25,766 +and speed up your builds. + +455 +00:24:25,799 --> 00:24:29,036 +With this knowledge, and powerful +new tools like the build timeline, + +456 +00:24:29,069 --> 00:24:33,574 +you are well equipped to examine your +project and gain insight into your builds. + +457 +00:24:33,607 --> 00:24:34,908 +And if you're curious to learn even more + +458 +00:24:34,942 --> 00:24:36,944 +of the behind-the-scenes +technical details, + +459 +00:24:36,977 --> 00:24:39,780 +many of the technologies we described +that are used by Xcode + +460 +00:24:39,813 --> 00:24:41,381 +are developed in the open-source. + +461 +00:24:41,415 --> 00:24:45,986 +You can find the repository for +Swift Driver on GitHub at the link below. + +462 +00:24:46,019 --> 00:24:47,621 +For more great sessions about Xcode, + +463 +00:24:47,654 --> 00:24:50,057 +check out all new features +and improvements from this year + +464 +00:24:50,090 --> 00:24:51,892 +in "What's new in Xcode". + +465 +00:24:51,925 --> 00:24:55,295 +And learn how Xcode 14's linker +improves link times up to two times + +466 +00:24:55,329 --> 00:24:59,166 +in the session "Link fast: +Improve build and launch times". + +467 +00:24:59,199 --> 00:25:00,767 +Thanks for following along. + +468 +00:25:00,801 --> 00:25:03,804 +We hope you learned some new insights +about Xcode builds. + +469 +00:25:03,837 --> 00:25:06,006 +We can't wait to see +what you're going to create. + +470 +00:25:06,039 --> 00:25:07,841 +Have a great rest of the conference. + diff --git a/eng/2022 Session 110367 Simplify C++ templates with concepts en.srt b/eng/2022 Session 110367 Simplify C++ templates with concepts en.srt new file mode 100644 index 0000000..9557e75 --- /dev/null +++ b/eng/2022 Session 110367 Simplify C++ templates with concepts en.srt @@ -0,0 +1,1927 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,676 --> 00:00:13,514 +Alex: Hello, my name is Alex, +and I work on Developer Tools. + +3 +00:00:13,547 --> 00:00:20,587 +Today I want to talk to you about the new +C++ 20 features supported in Xcode 14. + +4 +00:00:20,621 --> 00:00:26,326 +I will specifically focus on +how C++ 20 concepts simplify + +5 +00:00:26,360 --> 00:00:30,931 +and improve the type safety +of generic C++ code. + +6 +00:00:30,964 --> 00:00:33,834 +I will demonstrate how to use concepts + +7 +00:00:33,867 --> 00:00:37,871 +and will explain how to create +your own concepts as well. + +8 +00:00:37,905 --> 00:00:43,243 +I will end the talk by listing several +other new C++20 features supported + +9 +00:00:43,277 --> 00:00:47,414 +in Xcode, and will touch +on how some of them can be used + +10 +00:00:47,447 --> 00:00:51,251 +to improve the performance +of your C++ projects + +11 +00:00:51,285 --> 00:00:54,588 +through the power +of compile time code evaluation. + +12 +00:00:55,689 --> 00:01:00,394 +Before diving into C++ concepts, +let's first go over a quick overview + +13 +00:01:00,427 --> 00:01:03,697 +of how to write generic code in C++. + +14 +00:01:03,730 --> 00:01:08,268 +Let's say I want to write a function +that checks if a number is odd. + +15 +00:01:08,302 --> 00:01:11,638 +I can write a function +that takes in an 'int' parameter, + +16 +00:01:11,672 --> 00:01:16,844 +and it would work with any value +that can be represented by the 'int' type. + +17 +00:01:16,877 --> 00:01:22,850 +What would happen if I pass in +a 64 bit unsigned integer value to it? + +18 +00:01:22,883 --> 00:01:27,821 +A concrete function like this one does +not behave correctly with 64 bit values, + +19 +00:01:27,855 --> 00:01:31,925 +as they get truncated +to fit into the 'int' type. + +20 +00:01:31,959 --> 00:01:36,330 +To fix this, +I can make 'isOdd' a function template. + +21 +00:01:36,363 --> 00:01:38,365 +Now that I have a function template, + +22 +00:01:38,398 --> 00:01:42,903 +I can pass in the 64 bit +unsigned integer value to it. + +23 +00:01:42,936 --> 00:01:47,808 +The compiler will now automatically +generate a specialization of 'isOdd' + +24 +00:01:47,841 --> 00:01:53,113 +that works correctly +with the 'uint64_t' type. + +25 +00:01:53,146 --> 00:01:56,783 +This is really useful as it means +I don't have to write two versions + +26 +00:01:56,817 --> 00:02:00,754 +of 'isOdd' +that operate on two different types. + +27 +00:02:00,787 --> 00:02:05,592 +You can use C++ templates +to write generic functions like 'isOdd', + +28 +00:02:05,626 --> 00:02:08,729 +and generic container classes as well. + +29 +00:02:08,762 --> 00:02:12,099 +Let's take a look at how +'isOdd' can be used. + +30 +00:02:12,132 --> 00:02:18,872 +This function is tested with several +test cases that I added in my test file. + +31 +00:02:18,906 --> 00:02:21,909 +Unfortunately, I have made a mistake +in one of my tests. + +32 +00:02:21,942 --> 00:02:24,178 +The compiler caught the mistake, + +33 +00:02:24,211 --> 00:02:27,047 +but instead of pointing +to where I made the mistake, + +34 +00:02:27,080 --> 00:02:31,852 +the compiler shows an error +inside the 'isOdd' template. + +35 +00:02:31,885 --> 00:02:37,658 +It looks like I made a typo and wrote +out '1.1' in my test instead of '11'. + +36 +00:02:37,691 --> 00:02:41,728 +Because of that, the compiler generates +a specialization of 'isOdd' + +37 +00:02:41,762 --> 00:02:44,131 +that takes in a 'double' type. + +38 +00:02:44,164 --> 00:02:47,034 +Unfortunately, +it took me some time to find this typo + +39 +00:02:47,067 --> 00:02:50,204 +as Xcode didn't point me +to the specific location + +40 +00:02:50,237 --> 00:02:53,473 +where 'isOdd' was invoked +with incorrect type. + +41 +00:02:55,609 --> 00:03:00,480 +Can the language and the compiler +help me find mistakes like this faster? + +42 +00:03:00,514 --> 00:03:02,716 +Well, in the current example, + +43 +00:03:02,749 --> 00:03:06,153 +the requirements for what types +are allowed into 'isOdd' + +44 +00:03:06,186 --> 00:03:09,022 +are not specified explicitly. + +45 +00:03:09,056 --> 00:03:13,560 +There's only a documentation comment +that states that I must call isOdd + +46 +00:03:13,594 --> 00:03:16,630 +using integer types. + +47 +00:03:16,663 --> 00:03:20,634 +Prior to C++20, +C++ programmers did not have a good way + +48 +00:03:20,667 --> 00:03:25,472 +to specify template requirements +when writing generic C++ code. + +49 +00:03:25,506 --> 00:03:28,876 +They often had to resort +to documentation comments, + +50 +00:03:28,909 --> 00:03:30,844 +specific parameter names, + +51 +00:03:30,878 --> 00:03:33,080 +or complicated enable_if checks + +52 +00:03:33,113 --> 00:03:35,883 +when specifying template requirements. + +53 +00:03:35,916 --> 00:03:37,751 +Well, as you might've heard, + +54 +00:03:37,784 --> 00:03:42,856 +C++ 20 introduces +a new C++ feature called concepts. + +55 +00:03:42,890 --> 00:03:49,696 +You can use concepts to validate template +requirements in your generic C++ code. + +56 +00:03:49,730 --> 00:03:53,734 +Let's take a look at how concepts +can help me validate the types + +57 +00:03:53,767 --> 00:03:56,170 +that can be passed into 'isOdd'. + +58 +00:03:56,203 --> 00:04:00,908 +First, let's go back +to the declaration of 'isOdd'. + +59 +00:04:00,941 --> 00:04:03,210 +Currently, I use the 'class' keyword + +60 +00:04:03,243 --> 00:04:09,616 +to specify that type ‘T’ that's used by +this template can be any type. + +61 +00:04:09,650 --> 00:04:15,022 +C++ 20 allows me to use a concept +instead of the 'class' keyword + +62 +00:04:15,055 --> 00:04:19,927 +to restrict the set of types +that this template can be used with. + +63 +00:04:19,960 --> 00:04:24,131 +I can use the 'integral' concept +provided by the standard library + +64 +00:04:24,164 --> 00:04:30,404 +to restrict this 'isOdd' function template +only to the built-in integer types. + +65 +00:04:30,437 --> 00:04:35,075 +The compiler will not even try +to specialize this function template + +66 +00:04:35,108 --> 00:04:39,580 +when T does not satisfy this concept. + +67 +00:04:39,613 --> 00:04:43,951 +The integral concept is declared +in the C++ standard library. + +68 +00:04:43,984 --> 00:04:48,956 +So I need to include the concepts +header to use it in my code. + +69 +00:04:48,989 --> 00:04:52,759 +Now that I added an 'integral' +requirement to the type _T_ + +70 +00:04:52,793 --> 00:04:56,964 +in the 'isOdd' function template, +the compiler is able to provide + +71 +00:04:56,997 --> 00:05:00,501 +a much clearer diagnostic +that points directly + +72 +00:05:00,534 --> 00:05:04,171 +at where I made the mistake in my tests. + +73 +00:05:04,204 --> 00:05:07,541 +Turns out, '1.1' is a double, + +74 +00:05:07,574 --> 00:05:11,912 +and therefore, it does not satisfy +the 'integral' concept. + +75 +00:05:11,945 --> 00:05:17,050 +The compiler is able to explain this to me +with a clear error message + +76 +00:05:17,084 --> 00:05:22,389 +that helps me find and fix this typo +much quicker than before. + +77 +00:05:22,422 --> 00:05:24,892 +In addition to helping me fix the bug, + +78 +00:05:24,925 --> 00:05:27,895 +constraining the type passed to 'isOdd' + +79 +00:05:27,928 --> 00:05:30,931 +gives me the peace of mind +that all the test cases + +80 +00:05:30,964 --> 00:05:35,502 +that I have for is 'isOdd' +work only with integer types, + +81 +00:05:35,536 --> 00:05:41,108 +and that they're actually testing +the intended behavior of the algorithm. + +82 +00:05:41,141 --> 00:05:44,178 +You can use concepts +to declare the intent + +83 +00:05:44,211 --> 00:05:48,649 +for which types your templates +are meant to be used with. + +84 +00:05:48,682 --> 00:05:51,852 +The compiler will then validate +the type requirements + +85 +00:05:51,885 --> 00:05:55,355 +before your templates are specialized. + +86 +00:05:55,389 --> 00:05:58,625 +Let's take a closer look +at how concepts can be used + +87 +00:05:58,659 --> 00:06:04,665 +and which core concepts are provided +by the C++ standard library. + +88 +00:06:04,698 --> 00:06:08,836 +The C++ standard library +provides a concepts library. + +89 +00:06:08,869 --> 00:06:11,905 +It implements a set +of core language concepts + +90 +00:06:11,939 --> 00:06:16,476 +that you can use to validate +the core behavior of a type. + +91 +00:06:16,510 --> 00:06:21,582 +You can access this library by including +the concepts header in your code. + +92 +00:06:22,950 --> 00:06:26,119 +I've already shown +how I can use the 'integral' concept + +93 +00:06:26,153 --> 00:06:27,988 +in my earlier example. + +94 +00:06:28,021 --> 00:06:33,126 +Now, let's take a look at the other +concepts provided by this library. + +95 +00:06:33,160 --> 00:06:37,531 +This library provides a number +of useful core language concepts, + +96 +00:06:37,564 --> 00:06:42,202 +like concepts that test +if a type is one of the built-in types. + +97 +00:06:42,236 --> 00:06:45,706 +For instance, +the 'floating_point' concept is satisfied + +98 +00:06:45,739 --> 00:06:48,809 +by built-in types +like 'float' and 'double'. + +99 +00:06:48,842 --> 00:06:54,314 +The 'static_assert' shown here +validates that this is indeed the case. + +100 +00:06:54,348 --> 00:06:57,885 +It also provides +a lot of other useful core concepts + +101 +00:06:57,918 --> 00:07:00,521 +that check if types are constructible, + +102 +00:07:00,554 --> 00:07:02,923 +destructible, convertible, + +103 +00:07:02,956 --> 00:07:05,959 +or are they the same as another type. + +104 +00:07:05,993 --> 00:07:09,129 +For instance, +the 'convertible_to' concept tests + +105 +00:07:09,162 --> 00:07:12,499 +if a type can be converted +to another type. + +106 +00:07:12,533 --> 00:07:16,537 +and the 'move_constructible' concept +is satisfied by types + +107 +00:07:16,570 --> 00:07:21,742 +that can be constructed directly +from another value of the same type. + +108 +00:07:21,775 --> 00:07:25,479 +This library also provides +several comparison concepts + +109 +00:07:25,512 --> 00:07:30,284 +that test if types can be compared +to other types. + +110 +00:07:30,317 --> 00:07:34,955 +For instance, the 'equality_comparable' +concept is satisfied by types + +111 +00:07:34,988 --> 00:07:41,128 +that have a valid '==' operator +that works with a value of the same type. + +112 +00:07:42,262 --> 00:07:45,399 +In addition to concepts +mentioned on this slide, + +113 +00:07:45,432 --> 00:07:49,636 +this library provides numerous +other core language concepts. + +114 +00:07:49,670 --> 00:07:54,908 +It also provides concepts that test +if a type can be moved or copied. + +115 +00:07:54,942 --> 00:07:57,811 +In addition to that, +it also provides concepts + +116 +00:07:57,845 --> 00:08:01,014 +that check if a type +is some callable object. + +117 +00:08:02,482 --> 00:08:05,452 +Now that we've looked over +the concepts provided to us + +118 +00:08:05,485 --> 00:08:07,888 +by the C++ standard library, + +119 +00:08:07,921 --> 00:08:12,726 +let's take a look at how concepts +can be used to constrain templates. + +120 +00:08:12,759 --> 00:08:17,297 +Like I've shown earlier, you can use +a concept instead of the class keyword + +121 +00:08:17,331 --> 00:08:22,469 +in a template to restrict which types +are allowed for this template. + +122 +00:08:22,503 --> 00:08:25,906 +In addition to that, +you can use a 'requires' clause + +123 +00:08:25,939 --> 00:08:31,945 +in a template declaration if you ever need +to constrain a type to multiple concepts. + +124 +00:08:31,979 --> 00:08:36,450 +Let's take a look at a slightly different +example to see how it can be done. + +125 +00:08:37,484 --> 00:08:41,622 +Here I have +'isDefaultValue' function template. + +126 +00:08:41,655 --> 00:08:47,294 +It returns true if the given value +is equal to the default value of its type. + +127 +00:08:47,327 --> 00:08:51,064 +I can use two concepts from +the standard library to test + +128 +00:08:51,098 --> 00:08:56,470 +that this type supports these operations +before this template is specialized. + +129 +00:08:56,503 --> 00:09:00,040 +I'm going to add the 'requires' clause +to restrict the set of types + +130 +00:09:00,073 --> 00:09:03,043 +which are allowed +for this function template. + +131 +00:09:03,076 --> 00:09:06,647 +Let's see which concepts from +the concepts library can help me + +132 +00:09:06,680 --> 00:09:08,715 +validate the type here. + +133 +00:09:08,749 --> 00:09:14,721 +First, the 'equality_comparable' concept +tests if _T_ can be compared + +134 +00:09:14,755 --> 00:09:18,225 +to another value of the same type. + +135 +00:09:18,258 --> 00:09:23,697 +Then, the 'default_constructible' concept +tests if _T_ is a type + +136 +00:09:23,730 --> 00:09:26,033 +with a default constructor. + +137 +00:09:26,066 --> 00:09:29,369 +The logical and operator between them +instructs the compiler + +138 +00:09:29,403 --> 00:09:32,105 +to validate both concepts. + +139 +00:09:32,139 --> 00:09:36,677 +This ensures that this function template +will only be specialized + +140 +00:09:36,710 --> 00:09:39,179 +with supported types. + +141 +00:09:39,213 --> 00:09:43,350 +Let's go over what we've learned +so far about concepts. + +142 +00:09:43,383 --> 00:09:46,320 +You should use concepts to restrict +the types which are allowed + +143 +00:09:46,353 --> 00:09:48,655 +to be used in your templates. + +144 +00:09:48,689 --> 00:09:51,892 +The compiler will then be able +to show clearer diagnostics + +145 +00:09:51,925 --> 00:09:57,364 +as the template won't have to be +specialized if a type mismatch occurs. + +146 +00:09:57,397 --> 00:10:00,501 +You should reuse the concepts +from the concepts library + +147 +00:10:00,534 --> 00:10:03,871 +if you need to validate +some core behavior of a type. + +148 +00:10:05,172 --> 00:10:08,141 +You should add the 'requires' clause +to your templates + +149 +00:10:08,175 --> 00:10:12,379 +when you need to test if types conform +to multiple requirements. + +150 +00:10:12,412 --> 00:10:17,618 +We've now seen how to use concepts +in C++ programs. + +151 +00:10:17,651 --> 00:10:20,888 +C++ allows us to declare custom concepts + +152 +00:10:20,921 --> 00:10:24,691 +that validate specific behavior of a type. + +153 +00:10:24,725 --> 00:10:27,661 +Let's take a look +at how to create our own concepts + +154 +00:10:27,694 --> 00:10:31,498 +that validate specific type behavior. + +155 +00:10:31,532 --> 00:10:33,867 +Before we do that, though, +we need to take a look + +156 +00:10:33,901 --> 00:10:37,471 +at how to identify +the behavioral requirements + +157 +00:10:37,504 --> 00:10:41,441 +that must be validated +by the concept we want to declare. + +158 +00:10:41,475 --> 00:10:44,645 +I'm going to use a new example +to illustrate + +159 +00:10:44,678 --> 00:10:49,316 +how to validate specific type behavior +using concepts. + +160 +00:10:49,349 --> 00:10:52,219 +Say I am building a C++ library + +161 +00:10:52,252 --> 00:10:56,456 +that can render various two-dimensional +shapes to an image. + +162 +00:10:57,457 --> 00:11:00,827 +I would like to support +various shapes in my library. + +163 +00:11:00,861 --> 00:11:05,766 +I'm starting out with a circle shape, +as it's the simplest to render. + +164 +00:11:05,799 --> 00:11:09,903 +I'm going to use a C++ class +to store its properties, + +165 +00:11:09,937 --> 00:11:12,706 +like position and radius. + +166 +00:11:12,739 --> 00:11:14,675 +In order to render the circle, + +167 +00:11:14,708 --> 00:11:18,745 +I'm going to use a distance-function based +rendering algorithm + +168 +00:11:18,779 --> 00:11:23,016 +that runs on each pixel +in the rendered image. + +169 +00:11:23,050 --> 00:11:27,454 +This algorithm needs to compute +the distance to the shape's surface + +170 +00:11:27,487 --> 00:11:30,324 +in order to render it. + +171 +00:11:30,357 --> 00:11:34,394 +The 'getDistanceFrom' method +in the Circle class computes it. + +172 +00:11:34,428 --> 00:11:37,931 +It returns a negative distance +inside the circle, + +173 +00:11:37,965 --> 00:11:42,069 +and a positive distance +outside the circle. + +174 +00:11:42,102 --> 00:11:45,873 +In addition to the circle, +I would like to render other shapes. + +175 +00:11:45,906 --> 00:11:49,943 +For instance, by geometrically subtracting +one circle shape + +176 +00:11:49,977 --> 00:11:55,182 +from another circle shape, +I can render a crescent shape as well. + +177 +00:11:55,215 --> 00:11:58,151 +I'm going to represent shapes +like Crescent that I would like + +178 +00:11:58,185 --> 00:12:00,787 +to render using classes as well. + +179 +00:12:00,821 --> 00:12:06,460 +Each new shape class includes +the 'getDistanceFrom' method. + +180 +00:12:06,493 --> 00:12:09,296 +After creating several shape classes, + +181 +00:12:09,329 --> 00:12:15,202 +I now would like to try rendering these +shapes to verify their implementation. + +182 +00:12:15,235 --> 00:12:19,473 +I have a couple of options for how +I can create the rendering function + +183 +00:12:19,506 --> 00:12:21,909 +that works with any shape. + +184 +00:12:21,942 --> 00:12:24,578 +I can create a class hierarchy +for the shapes, + +185 +00:12:24,611 --> 00:12:29,917 +and use a virtual method to compute +the distance to the shape's surface. + +186 +00:12:29,950 --> 00:12:35,355 +However, I'm going to use a function +template instead for performance reasons, + +187 +00:12:35,389 --> 00:12:38,025 +as I want to avoid +the virtual call overhead + +188 +00:12:38,058 --> 00:12:43,597 +as this function is going to be called +millions of times during rendering. + +189 +00:12:43,630 --> 00:12:47,935 +This is why I created +this rendering function template. + +190 +00:12:47,968 --> 00:12:51,338 +The computePixelColor function +takes in a shape value, + +191 +00:12:51,371 --> 00:12:55,275 +and checks if the given pixel +is inside the shape. + +192 +00:12:55,309 --> 00:12:59,213 +If it's inside, +it returns a plain white color. + +193 +00:12:59,246 --> 00:13:04,251 +This now allows me to verify +that shapes can be filled in correctly. + +194 +00:13:05,485 --> 00:13:09,590 +This function is a template, +which makes it work with any shape type, + +195 +00:13:09,623 --> 00:13:14,661 +be it a circle, crescent, +or any other matching type. + +196 +00:13:14,695 --> 00:13:16,897 +Even though a template works well here, + +197 +00:13:16,930 --> 00:13:19,800 +I would like to use concepts +to constrain the type + +198 +00:13:19,833 --> 00:13:22,636 +that can be passed to this function. + +199 +00:13:22,669 --> 00:13:25,506 +Constraining the type +that's passed to this function + +200 +00:13:25,539 --> 00:13:32,112 +will allow the compiler to produce clearer +diagnostics when a type mismatch occurs. + +201 +00:13:32,145 --> 00:13:36,283 +In addition to that, constraining the type +that's passed to this function + +202 +00:13:36,316 --> 00:13:40,220 +will also allow me to add +additional overloads of this function. + +203 +00:13:41,755 --> 00:13:46,360 +In order to constrain the type, +I'm going to create a Shape concept. + +204 +00:13:46,393 --> 00:13:49,696 +This concept +will validate the type's behavior, + +205 +00:13:49,730 --> 00:13:52,866 +and will accept classes +like circle, crescent, + +206 +00:13:52,900 --> 00:13:57,137 +and any other shape class +that I might want to add in the future. + +207 +00:13:57,171 --> 00:13:59,373 +In order to create a concept like 'Shape', + +208 +00:13:59,406 --> 00:14:01,909 +I first need to identify the requirements + +209 +00:14:01,942 --> 00:14:04,845 +that must be validated by this concept. + +210 +00:14:04,878 --> 00:14:07,614 +Let's see how this can be done. + +211 +00:14:07,648 --> 00:14:11,518 +This function template uses type 'T' +as the generic type. + +212 +00:14:11,552 --> 00:14:16,957 +An argument named 'shape' of type 'T' +is then passed to this function. + +213 +00:14:16,990 --> 00:14:20,260 +The 'shape' argument is then used +inside the function, + +214 +00:14:20,294 --> 00:14:24,231 +when I call +the 'getDistanceFrom' method on it. + +215 +00:14:24,264 --> 00:14:27,267 +As you can see, +this is the only requirement + +216 +00:14:27,301 --> 00:14:29,770 +I want to validate in my concept, + +217 +00:14:29,803 --> 00:14:34,341 +as no other operations are being performed +on shape in this function. + +218 +00:14:35,242 --> 00:14:39,446 +You can use the 'requires' expression +to test if a type behaves + +219 +00:14:39,479 --> 00:14:41,849 +in a specific manner. + +220 +00:14:41,882 --> 00:14:46,854 +Let's take a look at how I can use +'requires' to create the Shape concept. + +221 +00:14:46,887 --> 00:14:50,224 +I need to provide a set of expressions +that test the behavior + +222 +00:14:50,257 --> 00:14:53,126 +of a type inside the 'requires'. + +223 +00:14:53,160 --> 00:14:56,430 +I already identified the call +to 'getDistanceFrom' + +224 +00:14:56,463 --> 00:14:59,066 +as a single requirement I need to test, + +225 +00:14:59,099 --> 00:15:03,537 +so now I can go ahead +and create the 'Shape' concept. + +226 +00:15:03,570 --> 00:15:07,407 +I declared the shape concept +using the 'concept' keyword. + +227 +00:15:07,441 --> 00:15:13,847 +I then added the 'requires' expression +to this concept to validate the type. + +228 +00:15:13,881 --> 00:15:17,317 +I added an argument list +to the 'requires' expression. + +229 +00:15:17,351 --> 00:15:22,055 +This argument list allows me +to declare a value 'shape' of type 'T' + +230 +00:15:22,089 --> 00:15:26,126 +that I will then be testing +inside the 'requires'. + +231 +00:15:26,159 --> 00:15:31,832 +You can use an argument list in a requires +expression to declare values of any type. + +232 +00:15:31,865 --> 00:15:36,670 +You will then be able to use these values +inside the requires. + +233 +00:15:36,703 --> 00:15:40,541 +The body of the 'requires' expression +contains a set of requirements + +234 +00:15:40,574 --> 00:15:44,811 +that must pass +in order for this concept to be satisfied. + +235 +00:15:44,845 --> 00:15:48,549 +The 'shape' concept has just +one simple expression requirement + +236 +00:15:48,582 --> 00:15:53,687 +that checks whether a method call +to 'getDistanceFrom' is valid. + +237 +00:15:53,720 --> 00:15:57,991 +This expression isn't actually +going to be executed in the program. + +238 +00:15:58,025 --> 00:16:02,829 +It's only needed at compile time +to validate the type's behavior, + +239 +00:16:02,863 --> 00:16:06,600 +and it's discarded after the validation. + +240 +00:16:06,633 --> 00:16:10,737 +You can use expression requirements +to validate the type's behavior + +241 +00:16:10,771 --> 00:16:15,876 +by testing if a particular expression +compiles or not. + +242 +00:16:15,909 --> 00:16:19,012 +This particular expression +is not yet complete though, + +243 +00:16:19,046 --> 00:16:23,450 +as we're missing the arguments +to the 'getDistanceFrom' method call. + +244 +00:16:23,483 --> 00:16:28,055 +I know that I want this method +to take two values of type 'float', + +245 +00:16:28,088 --> 00:16:33,460 +so I can use two floating point literals +to complete this expression. + +246 +00:16:33,493 --> 00:16:37,831 +I am going to add an additional check +to test that 'getDistanceFrom' method + +247 +00:16:37,865 --> 00:16:39,900 +returns a float value, + +248 +00:16:39,933 --> 00:16:45,806 +as that's what is being assumed +by my generic code. + +249 +00:16:45,839 --> 00:16:48,442 +I'm currently using a simple +expression requirement + +250 +00:16:48,475 --> 00:16:53,213 +to test if the type has +the 'getDistanceFrom' method. + +251 +00:16:53,247 --> 00:16:57,851 +However, I can use a compound requirement +instead of the expression requirement + +252 +00:16:57,885 --> 00:17:01,455 +to test that it returns a float value. + +253 +00:17:01,488 --> 00:17:05,259 +The arrow operator can follow +a compound requirement. + +254 +00:17:05,292 --> 00:17:08,695 +The arrow operator expects a constraint +on its right hand side, + +255 +00:17:08,729 --> 00:17:13,400 +so this is where I can use a standard +library concept like 'same_as' + +256 +00:17:13,433 --> 00:17:16,403 +to validate that the call +to 'getDistanceFrom' method + +257 +00:17:16,436 --> 00:17:19,006 +returns a float value. + +258 +00:17:19,039 --> 00:17:21,875 +Now this concept looks ready to me. + +259 +00:17:23,610 --> 00:17:27,047 +I can go ahead and use it +to constrain the types + +260 +00:17:27,080 --> 00:17:30,817 +that can be passed +to my 'computePixelColor' function. + +261 +00:17:30,851 --> 00:17:33,420 +Now my generic +'computePixelColor' function + +262 +00:17:33,453 --> 00:17:38,058 +will only work with types +that satisfy the 'Shape' concept. + +263 +00:17:38,091 --> 00:17:42,095 +This means that classes like +Circle and Crescent will be rendered + +264 +00:17:42,129 --> 00:17:45,666 +using this particular generic +'computePixelColor' function, + +265 +00:17:45,699 --> 00:17:49,603 +as both of these types +satisfy the 'Shape' concept. + +266 +00:17:52,005 --> 00:17:54,675 +After seeing the plain shapes rendered, + +267 +00:17:54,708 --> 00:17:58,178 +I would like to create a different version +of 'computePixelColor' + +268 +00:17:58,212 --> 00:18:01,882 +that adds colors to some of my shapes. + +269 +00:18:01,915 --> 00:18:07,788 +Let's say I want to add a colorful +GradientCircle class to my shape library. + +270 +00:18:07,821 --> 00:18:13,460 +I now need a new function to compute +the pixel color in the image. + +271 +00:18:13,493 --> 00:18:17,064 +C++20 allows me +to create multiple variants + +272 +00:18:17,097 --> 00:18:20,267 +of the 'computePixelColor' +function template. + +273 +00:18:20,300 --> 00:18:24,705 +Each variant must be constrained +using different concepts. + +274 +00:18:24,738 --> 00:18:27,708 +I am going to create +a new GradientShape concept + +275 +00:18:27,741 --> 00:18:31,678 +that will be satisfied +by classes like GradientCircle. + +276 +00:18:31,712 --> 00:18:36,149 +This concept will then constrain +a new variant of 'computePixelColor' + +277 +00:18:36,183 --> 00:18:40,420 +that only works +with shapes that have a gradient. + +278 +00:18:40,454 --> 00:18:44,091 +This concept is implemented +using a 'requires' expression, + +279 +00:18:44,124 --> 00:18:46,493 +just like the Shape concept. + +280 +00:18:46,527 --> 00:18:49,730 +However, since I want GradientShape + +281 +00:18:49,763 --> 00:18:52,599 +to satisfy the original Shape concept +as well, + +282 +00:18:52,633 --> 00:18:56,503 +I include it as the first requirement +in the new concept. + +283 +00:18:56,537 --> 00:19:01,441 +This ensures that a class +that satisfies the GradientShape concept + +284 +00:19:01,475 --> 00:19:03,944 +also satisfies the Shape concept, + +285 +00:19:03,977 --> 00:19:07,381 +which means I can still call +the 'getDistanceFrom' method + +286 +00:19:07,414 --> 00:19:10,384 +for values of such class. + +287 +00:19:10,417 --> 00:19:14,188 +I then use the logical and operator +and the 'requires' expression + +288 +00:19:14,221 --> 00:19:18,292 +to ensure that the GradientShape +concept can only be satisfied + +289 +00:19:18,325 --> 00:19:22,996 +by classes that have +the 'getGradientColor' method. + +290 +00:19:23,030 --> 00:19:25,933 +Now that I have created +the GradientShape concept, + +291 +00:19:25,966 --> 00:19:31,872 +I can go ahead and create +a new variant of 'computePixelColor'. + +292 +00:19:31,905 --> 00:19:36,143 +This function template only works +with shape classes with a gradient, + +293 +00:19:36,176 --> 00:19:38,378 +like the GradientCircle class, + +294 +00:19:38,412 --> 00:19:42,449 +as it is constrained +by the GradientShape concept. + +295 +00:19:42,482 --> 00:19:44,785 +Now that I have all the pieces in place, + +296 +00:19:44,818 --> 00:19:49,389 +I can go ahead and try rendering a circle +with a gradient. + +297 +00:19:49,423 --> 00:19:52,359 +Here I'm rendering a GradientCircle. + +298 +00:19:52,392 --> 00:19:55,629 +Let's see which overload +of 'computePixelColor' + +299 +00:19:55,662 --> 00:19:59,166 +the compiler is going to pick +inside the 'render' function. + +300 +00:20:00,701 --> 00:20:04,304 +Even though GradientCircle can +be safely used with both variants + +301 +00:20:04,338 --> 00:20:09,109 +of computePixelColor, the compiler picks +the overload that is constrained + +302 +00:20:09,142 --> 00:20:11,278 +with the GradientShape concept + +303 +00:20:11,311 --> 00:20:15,215 +as it's more specific +than the first overload. + +304 +00:20:15,249 --> 00:20:19,753 +Because the compiler picks the most +matching overload of 'computePixelColor', + +305 +00:20:19,786 --> 00:20:24,558 +I can see this beautiful gradient circle +rendered when I test my library. + +306 +00:20:24,591 --> 00:20:25,959 +Amazing! + +307 +00:20:26,960 --> 00:20:31,365 +Now let's go over what we've learned +about creating concepts. + +308 +00:20:32,766 --> 00:20:37,037 +You can create concepts by identifying +the behavioral requirements + +309 +00:20:37,070 --> 00:20:40,240 +in your existing generic code. + +310 +00:20:40,274 --> 00:20:43,377 +You should use the requires expression +to create concepts + +311 +00:20:43,410 --> 00:20:46,713 +to validate the behavior of types. + +312 +00:20:46,747 --> 00:20:50,384 +You can also use concepts +to create more specific variants + +313 +00:20:50,417 --> 00:20:53,687 +of generic functions and classes. + +314 +00:20:55,556 --> 00:21:00,961 +We've now seen how to enhance +your generic C++ code with concepts. + +315 +00:21:00,994 --> 00:21:03,096 +In addition to supporting concepts, + +316 +00:21:03,130 --> 00:21:09,336 +Xcode 14 has improved its support +for other C++20 features as well. + +317 +00:21:09,369 --> 00:21:11,672 +More specifically, +I would like to highlight + +318 +00:21:11,705 --> 00:21:18,212 +the improved support for compile-time +C++ code evaluation in Xcode 14. + +319 +00:21:19,046 --> 00:21:21,849 +Compile time code evaluation is useful + +320 +00:21:21,882 --> 00:21:24,985 +as it can reduce the cost +of initialization + +321 +00:21:25,018 --> 00:21:28,355 +for variables in your C++ code. + +322 +00:21:28,388 --> 00:21:31,091 +This could help reduce +your app launch time + +323 +00:21:31,124 --> 00:21:33,560 +if your app has a lot of C++ code + +324 +00:21:33,594 --> 00:21:38,298 +that depends on +complex initialization sequences. + +325 +00:21:38,332 --> 00:21:41,301 +In addition to that, +compile time code evaluation + +326 +00:21:41,335 --> 00:21:46,974 +can help you validate constants +that require validation at compile time. + +327 +00:21:47,007 --> 00:21:52,179 +This could help you catch bugs +before your code even runs. + +328 +00:21:52,212 --> 00:21:53,981 +Let's take a look at an example + +329 +00:21:54,014 --> 00:21:58,318 +to see how I can use compile time code +evaluation in C++. + +330 +00:21:59,353 --> 00:22:01,121 +Here I have a snippet of code + +331 +00:22:01,154 --> 00:22:05,926 +that initializes a color palette +in my shape rendering library. + +332 +00:22:05,959 --> 00:22:11,732 +This library is then used in an iOS app +that renders the shapes to the display. + +333 +00:22:11,765 --> 00:22:15,435 +Each color in the palette is initialized +by parsing a string literal + +334 +00:22:15,469 --> 00:22:18,906 +with the HTML hex code of the color. + +335 +00:22:18,939 --> 00:22:23,810 +Currently, the 'fromHexCode' function +needs to parse three string literals + +336 +00:22:23,844 --> 00:22:26,713 +during the initialization of the array. + +337 +00:22:26,747 --> 00:22:30,584 +Complicated constant initialization +operations like this one + +338 +00:22:30,617 --> 00:22:36,690 +can have a measurable impact on the launch +time of my app if I have a lot of them. + +339 +00:22:36,723 --> 00:22:41,828 +I can use compile-time code evaluation +to ensure that this array is initialized + +340 +00:22:41,862 --> 00:22:44,498 +with constant color values instead. + +341 +00:22:44,531 --> 00:22:47,668 +Let me show you can this can be done. + +342 +00:22:47,701 --> 00:22:53,674 +The 'constexpr' keyword enables +compile-time code evaluation in C++. + +343 +00:22:53,707 --> 00:22:57,311 +I must add it in several places +in my example + +344 +00:22:57,344 --> 00:23:02,282 +in order to ensure +that palette is a constant color array. + +345 +00:23:02,316 --> 00:23:07,554 +First, I need to add the 'constexpr' +keyword to the 'fromHexCode' function. + +346 +00:23:07,588 --> 00:23:10,891 +The compiler will be now be able +to execute the code in this function + +347 +00:23:10,924 --> 00:23:16,463 +at compile time when it's used +in a compile time initialization sequence. + +348 +00:23:16,496 --> 00:23:20,100 +You should make your C++ functions +'constexpr' when you want them + +349 +00:23:20,133 --> 00:23:23,036 +to be evaluatable at compile time. + +350 +00:23:23,070 --> 00:23:26,306 +The compiler will let you know +if the code in such function + +351 +00:23:26,340 --> 00:23:30,277 +cannot be evaluated at compile time +by showing an error + +352 +00:23:30,310 --> 00:23:34,181 +when you use it in a 'constexpr' +initialization sequence. + +353 +00:23:34,214 --> 00:23:38,519 +However, you can also examine +a function before adding 'constexpr' + +354 +00:23:38,552 --> 00:23:42,189 +to see if it can be evaluated +at compile time. + +355 +00:23:42,222 --> 00:23:44,892 +Let's take a peek into fromHexCode + +356 +00:23:44,925 --> 00:23:47,761 +to see how to check +if a function like this one + +357 +00:23:47,794 --> 00:23:51,598 +can be a good candidate +for compile time code evaluation. + +358 +00:23:51,632 --> 00:23:55,102 +This function uses +a number of language constructs + +359 +00:23:55,135 --> 00:23:57,137 +like if statements, + +360 +00:23:57,171 --> 00:24:02,709 +and primitive operations like comparison +operators and arithmetic operators. + +361 +00:24:02,743 --> 00:24:07,347 +All of these operations can be evaluated +at compile time. + +362 +00:24:07,381 --> 00:24:13,287 +Also, this function makes several calls +to another function; hexToInt. + +363 +00:24:13,320 --> 00:24:17,591 +I have already annotated +hexToInt function with 'constexpr', + +364 +00:24:17,624 --> 00:24:22,262 +so calls to this function +can be evaluated at compile time. + +365 +00:24:22,296 --> 00:24:25,365 +Overall, it looks like +fromHexCode contains code + +366 +00:24:25,399 --> 00:24:28,936 +that the compiler should be able +to evaluate at compile time, + +367 +00:24:28,969 --> 00:24:34,808 +so I think it's safe to proceed and use it +in a compile time initialization sequence. + +368 +00:24:34,842 --> 00:24:39,546 +After making sure that fromHexCode +can be evaluated at compile time, + +369 +00:24:39,580 --> 00:24:42,082 +I then need to add the 'constexpr' keyword + +370 +00:24:42,115 --> 00:24:45,552 +to the 'colorPalette' +variable declaration. + +371 +00:24:45,586 --> 00:24:49,189 +The compiler now guarantees +that it will evaluate + +372 +00:24:49,223 --> 00:24:54,428 +the entire initialization sequence +for this array at compile time. + +373 +00:24:54,461 --> 00:24:58,432 +More specifically, +the compiler will evaluate each call + +374 +00:24:58,465 --> 00:25:01,401 +to the fromHexCode function. + +375 +00:25:01,435 --> 00:25:04,705 +The evaluation will produce +a constant color value + +376 +00:25:04,738 --> 00:25:09,843 +that will replace the original call to +the function in the palette's initializer. + +377 +00:25:09,877 --> 00:25:15,082 +Since all the calls to fromHexCode +are now replaced by constant color values, + +378 +00:25:15,115 --> 00:25:19,786 +the 'colorPalette' variable +is now guaranteed to be initialized + +379 +00:25:19,820 --> 00:25:24,491 +by an array literal +that contains constant color values. + +380 +00:25:24,525 --> 00:25:28,295 +This means that now my app +doesn't have to pay additional cost + +381 +00:25:28,328 --> 00:25:33,066 +for parsing the color values +when this palette is initialized. + +382 +00:25:33,100 --> 00:25:35,969 +This is great +for the launch time of my app, + +383 +00:25:36,003 --> 00:25:38,405 +as it reduces the amount of work + +384 +00:25:38,438 --> 00:25:43,744 +this C++ library +inside the app has to do at startup. + +385 +00:25:43,777 --> 00:25:46,780 +You should make +your C++ variables 'constexpr' + +386 +00:25:46,813 --> 00:25:51,318 +when you want to ensure that they are +initialized with constant values. + +387 +00:25:51,351 --> 00:25:56,557 +Xcode 14 has actually greatly improved +its standard library support + +388 +00:25:56,590 --> 00:25:59,059 +for compile time evaluation. + +389 +00:25:59,092 --> 00:26:01,828 +This year we've added +the 'constexpr' support + +390 +00:26:01,862 --> 00:26:05,766 +to several different +standard library types and algorithms, + +391 +00:26:05,799 --> 00:26:08,936 +which can now be used during +compile-time code evaluation. + +392 +00:26:10,537 --> 00:26:14,274 +In addition to that, +Xcode 14 has greatly improved + +393 +00:26:14,308 --> 00:26:17,711 +its C++20 standard support. + +394 +00:26:17,744 --> 00:26:22,516 +All of the features shown here +can now be used in C++ 20 mode. + +395 +00:26:23,584 --> 00:26:29,189 +You should switch to C++ 20 mode today +if you haven't already done so. + +396 +00:26:29,223 --> 00:26:32,192 +You can use +the "C++ Language Dialect" setting + +397 +00:26:32,226 --> 00:26:37,231 +in your Xcode project +to upgrade to C++ 20. + +398 +00:26:37,264 --> 00:26:43,070 +Switching to C++20 will let you use +features like concepts in your code. + +399 +00:26:43,103 --> 00:26:47,241 +C++20 does not require +a minimum deployment target, + +400 +00:26:47,274 --> 00:26:50,544 +so you can still ship your code +for the same OS version + +401 +00:26:50,577 --> 00:26:53,447 +that you're currently targeting. + +402 +00:26:53,480 --> 00:26:56,717 +Try C++20 today. + +403 +00:26:56,750 --> 00:26:57,851 +Thank you! + +404 +00:26:57,885 --> 00:27:00,387 +Enjoy the rest +of the developer's conference. + diff --git a/eng/2022 Session 110368 What's new in Swift-DocC en.srt b/eng/2022 Session 110368 What's new in Swift-DocC en.srt new file mode 100644 index 0000000..b4e18da --- /dev/null +++ b/eng/2022 Session 110368 What's new in Swift-DocC en.srt @@ -0,0 +1,1838 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,877 +♪ + +3 +00:00:09,877 --> 00:00:11,144 +Franklin Schrans: Hello, +my name is Franklin, + +4 +00:00:11,144 --> 00:00:13,513 +and I'm an engineer +on the Swift-DocC team. + +5 +00:00:13,513 --> 00:00:15,115 +Together with +my colleague Ethan, + +6 +00:00:15,115 --> 00:00:19,219 +I'm excited to give you a tour +of Swift-DocC in Xcode 14, + +7 +00:00:19,219 --> 00:00:20,821 +packed with great new tools + +8 +00:00:20,821 --> 00:00:24,024 +to help you create +even better documentation. + +9 +00:00:24,024 --> 00:00:27,494 +We introduced Swift-DocC +last year in Xcode 13, + +10 +00:00:27,494 --> 00:00:31,765 +allowing you to guide developers +through your Swift frameworks. + +11 +00:00:31,765 --> 00:00:35,035 +With Swift-DocC, +you write comprehensive content, + +12 +00:00:35,035 --> 00:00:38,005 +from reference documentation +for individual APIs + +13 +00:00:38,005 --> 00:00:41,308 +to conceptual articles that +provide a higher-level story + +14 +00:00:41,308 --> 00:00:43,343 +for using these APIs together, + +15 +00:00:43,343 --> 00:00:45,545 +all the way +to step-by-step tutorials + +16 +00:00:45,545 --> 00:00:48,882 +that walk the user +through a task. + +17 +00:00:48,882 --> 00:00:50,784 +This year, with Xcode 14, + +18 +00:00:50,784 --> 00:00:54,288 +Swift-DocC unlocks +brand-new, exciting workflows. + +19 +00:00:54,288 --> 00:00:56,590 +In addition to documenting +frameworks, + +20 +00:00:56,590 --> 00:00:59,826 +we're thrilled to now +support app projects as well, + +21 +00:00:59,826 --> 00:01:02,663 +so that you can easily +collaborate with your team. + +22 +00:01:02,663 --> 00:01:06,233 +You can now also document +Objective-C and C APIs + +23 +00:01:06,233 --> 00:01:08,235 +using Swift-DocC, +allowing you + +24 +00:01:08,235 --> 00:01:11,838 +to describe all your project's +APIs together. + +25 +00:01:11,838 --> 00:01:13,473 +Publishing your content +to a website + +26 +00:01:13,473 --> 00:01:17,044 +is now dramatically easier, +with out-of-the-box support + +27 +00:01:17,044 --> 00:01:20,347 +for static hosting environments +like GitHub Pages. + +28 +00:01:20,347 --> 00:01:23,216 +And thanks to the powerful new +navigation sidebar, + +29 +00:01:23,216 --> 00:01:25,852 +developers will be able +to discover your content + +30 +00:01:25,852 --> 00:01:28,355 +easier than ever before. + +31 +00:01:28,355 --> 00:01:31,758 +Also, with Swift-DocC now being +an open source project, + +32 +00:01:31,758 --> 00:01:33,193 +these new features +were developed + +33 +00:01:33,193 --> 00:01:37,130 +in close collaboration +with the open source community. + +34 +00:01:37,130 --> 00:01:39,999 +In this session, +we'll take a tour of Swift-DocC + +35 +00:01:39,999 --> 00:01:44,004 +and the exciting new workflows +it unlocks in Xcode 14. + +36 +00:01:44,004 --> 00:01:47,341 +First, we'll discover how +to document your project's APIs + +37 +00:01:47,341 --> 00:01:49,910 +right inline with +your source code. + +38 +00:01:49,910 --> 00:01:51,678 +Then, we'll take a look +at seamlessly + +39 +00:01:51,678 --> 00:01:54,581 +publishing your content +to a website. + +40 +00:01:54,581 --> 00:01:55,949 +And finally, we'll discover + +41 +00:01:55,949 --> 00:01:59,920 +the brand-new navigation sidebar +on the web. + +42 +00:01:59,920 --> 00:02:03,090 +Let's start with +writing documentation. + +43 +00:02:03,090 --> 00:02:06,927 +Good documentation is essential +to any software project. + +44 +00:02:06,927 --> 00:02:08,061 +As your project evolves, + +45 +00:02:08,061 --> 00:02:11,131 +it's important to describe +its functionality and design + +46 +00:02:11,131 --> 00:02:13,333 +so that developers +have a solid reference point + +47 +00:02:13,333 --> 00:02:15,035 +when contributing. + +48 +00:02:15,035 --> 00:02:17,604 +Swift-DocC in Xcode +gives you the tools you need + +49 +00:02:17,604 --> 00:02:19,840 +to create fantastic +documentation, + +50 +00:02:19,840 --> 00:02:22,976 +using the same tools you use +to develop code. + +51 +00:02:22,976 --> 00:02:24,478 +And this year, +we're excited + +52 +00:02:24,478 --> 00:02:27,748 +to expand Swift-DocC +to app projects. + +53 +00:02:27,748 --> 00:02:29,950 +Let's dive right in. + +54 +00:02:29,950 --> 00:02:32,052 +We'll document an app +I'm working on with my team + +55 +00:02:32,052 --> 00:02:33,320 +called Slothy, + +56 +00:02:33,320 --> 00:02:37,557 +which contains both Swift +and Objective-C source files. + +57 +00:02:37,557 --> 00:02:39,192 +Even with a brand-new project, + +58 +00:02:39,192 --> 00:02:41,561 +where I haven't written +documentation yet, + +59 +00:02:41,561 --> 00:02:46,233 +you can open the Product menu +and select Build Documentation, + +60 +00:02:46,233 --> 00:02:49,136 +and Xcode opens +the documentation window, + +61 +00:02:49,136 --> 00:02:51,405 +where you'll see stubs +that Swift-DocC + +62 +00:02:51,405 --> 00:02:54,141 +automatically generates +for your APIs. + +63 +00:02:54,141 --> 00:02:57,811 +This provides a great starting +point for filling in these pages + +64 +00:02:57,811 --> 00:03:00,847 +to help contributors +navigate the project. + +65 +00:03:00,847 --> 00:03:04,117 +So, let's take it step by step +to turn this content + +66 +00:03:04,117 --> 00:03:09,256 +into a rich and complete guide +for contributing to our app. + +67 +00:03:09,256 --> 00:03:12,259 +A good place to start +is to teach how each API + +68 +00:03:12,259 --> 00:03:14,361 +works individually, +and from there, + +69 +00:03:14,361 --> 00:03:18,698 +to provide higher-level content +using a documentation catalog. + +70 +00:03:18,698 --> 00:03:22,269 +Let's start by describing +a view called SlothView. + +71 +00:03:22,269 --> 00:03:25,539 +For this documentation +to be visible to Swift-DocC, + +72 +00:03:25,539 --> 00:03:28,608 +start your comment +using three slashes. + +73 +00:03:28,608 --> 00:03:31,845 +Then, add a concise summary +for your view. + +74 +00:03:31,845 --> 00:03:35,148 +In the built documentation page, +this translates to text + +75 +00:03:35,148 --> 00:03:38,785 +displayed prominently +right below the view's name. + +76 +00:03:38,785 --> 00:03:42,489 +Then, add more details +using additional paragraphs. + +77 +00:03:42,489 --> 00:03:46,326 +This content appears in the +Overview section of the page. + +78 +00:03:46,326 --> 00:03:50,263 +And use Swift-DocC's link syntax +to turn references to APIs + +79 +00:03:50,263 --> 00:03:52,099 +into active links, + +80 +00:03:52,099 --> 00:03:55,802 +allowing you to quickly jump +to Pages to learn more. + +81 +00:03:55,802 --> 00:03:58,572 +DocC even validates these links +when you build, + +82 +00:03:58,572 --> 00:04:01,808 +so you get warnings +if they go out of date. + +83 +00:04:01,808 --> 00:04:04,311 +Finally, if you wish +to provide an example + +84 +00:04:04,311 --> 00:04:05,779 +of how to use this view, + +85 +00:04:05,779 --> 00:04:09,583 +add a code listing using the +Markdown code block syntax. + +86 +00:04:09,583 --> 00:04:11,785 +Now, contributors know +at a glance + +87 +00:04:11,785 --> 00:04:14,421 +how to use this view. + +88 +00:04:14,421 --> 00:04:17,124 +In just a few steps, +my view's documentation + +89 +00:04:17,124 --> 00:04:21,161 +is now much more helpful +to contributors of this project. + +90 +00:04:21,161 --> 00:04:24,865 +Next up, let's document +an initializer. + +91 +00:04:24,865 --> 00:04:27,734 +Again, start by writing +a summary. + +92 +00:04:27,734 --> 00:04:29,202 +For initializers and methods, + +93 +00:04:29,202 --> 00:04:33,273 +it's a good idea to describe +each parameter individually. + +94 +00:04:33,273 --> 00:04:35,742 +You do so by adding +a parameter list item, + +95 +00:04:35,742 --> 00:04:39,112 +with a short description +of what the parameter is. + +96 +00:04:39,112 --> 00:04:40,447 +Notice how the content appears + +97 +00:04:40,447 --> 00:04:43,884 +in a separate +Parameters section. + +98 +00:04:43,884 --> 00:04:46,887 +Now let's take a look +at the Objective-C APIs + +99 +00:04:46,887 --> 00:04:49,089 +defined in this project. + +100 +00:04:49,089 --> 00:04:51,858 +New in Xcode 14, +we're excited to bring + +101 +00:04:51,858 --> 00:04:53,994 +Swift-DocC's +comprehensive tooling + +102 +00:04:53,994 --> 00:04:56,396 +to document Objective-C code. + +103 +00:04:56,396 --> 00:04:59,599 +Using the same familiar Markdown +syntax you know and love, + +104 +00:04:59,599 --> 00:05:02,035 +and updated support +in Xcode's source editor, + +105 +00:05:02,035 --> 00:05:04,171 +you can now +describe and organize + +106 +00:05:04,171 --> 00:05:07,040 +all your project's APIs +together. + +107 +00:05:07,040 --> 00:05:10,243 +And for code that can be called +from both Swift and Objective-C, + +108 +00:05:10,243 --> 00:05:11,611 +there's a nifty language toggle + +109 +00:05:11,611 --> 00:05:15,182 +to browse the page in +the language you're coding in. + +110 +00:05:15,182 --> 00:05:17,684 +Check out the developer +documentation linked below + +111 +00:05:17,684 --> 00:05:20,020 +for more details. + +112 +00:05:20,020 --> 00:05:23,890 +Now, let's apply this +to our Slothy project. + +113 +00:05:23,890 --> 00:05:27,527 +I'll describe the SLOSound class +and its initializer. + +114 +00:05:27,527 --> 00:05:28,762 +Notice that because this class + +115 +00:05:28,762 --> 00:05:31,932 +can be used from both Swift +and Objective-C code, + +116 +00:05:31,932 --> 00:05:34,401 +Xcode displays a language toggle +allowing you + +117 +00:05:34,401 --> 00:05:37,938 +to browse the content +in the language you're using. + +118 +00:05:37,938 --> 00:05:40,173 +Let's describe the class +and initializer + +119 +00:05:40,173 --> 00:05:45,011 +using the same Markdown syntax +you use for Swift code. + +120 +00:05:45,011 --> 00:05:47,113 +Great, this looks much better. + +121 +00:05:47,113 --> 00:05:49,449 +I've added a summary, +an overview, + +122 +00:05:49,449 --> 00:05:53,019 +and for the initializer, +a parameters section. + +123 +00:05:53,019 --> 00:05:55,922 +And that wraps up +describing individual APIs. + +124 +00:05:55,922 --> 00:05:58,992 +By writing a few documentation +comments in source code, + +125 +00:05:58,992 --> 00:06:00,894 +developers contributing +to my project + +126 +00:06:00,894 --> 00:06:04,764 +have a better understanding +of how to use its APIs. + +127 +00:06:04,764 --> 00:06:07,667 +Now, let's focus on creating +a great top-level page + +128 +00:06:07,667 --> 00:06:08,935 +for our app. + +129 +00:06:08,935 --> 00:06:11,504 +This is the first page +contributors will see, + +130 +00:06:11,504 --> 00:06:13,974 +so I'd like to provide +a great introduction + +131 +00:06:13,974 --> 00:06:17,811 +to what the app does +and how to contribute to it. + +132 +00:06:17,811 --> 00:06:19,646 +To customize the top-level page, + +133 +00:06:19,646 --> 00:06:23,283 +start by adding +a documentation catalog. + +134 +00:06:23,283 --> 00:06:26,953 +To do so, right-click +on your project's source folder + +135 +00:06:26,953 --> 00:06:29,422 +and select New File. + +136 +00:06:29,422 --> 00:06:32,525 +Then select +Documentation Catalog. + +137 +00:06:32,525 --> 00:06:34,427 +A Documentation Catalog + +138 +00:06:34,427 --> 00:06:36,630 +complements your +source code documentation + +139 +00:06:36,630 --> 00:06:40,267 +and contains additional +Markdown files and media. + +140 +00:06:40,267 --> 00:06:42,135 +Xcode automatically adds a file + +141 +00:06:42,135 --> 00:06:44,437 +for the top-level page +of your app. + +142 +00:06:44,437 --> 00:06:48,742 +Let's fill it in with an +overview of what the app does. + +143 +00:06:48,742 --> 00:06:51,845 +Using the same syntax you use +for documentation comments + +144 +00:06:51,845 --> 00:06:54,114 +in source code, +I added a summary, + +145 +00:06:54,114 --> 00:06:58,618 +an overview, and even embedded +rich content like images. + +146 +00:06:58,618 --> 00:07:01,288 +Great, this looks +much more inviting. + +147 +00:07:01,288 --> 00:07:06,059 +Now contributors know at a +glance what my app is about. + +148 +00:07:06,059 --> 00:07:06,926 +And that's it! + +149 +00:07:06,926 --> 00:07:09,462 +I've dramatically improved +documentation for my app + +150 +00:07:09,462 --> 00:07:11,431 +and provided +a great reference point + +151 +00:07:11,431 --> 00:07:13,433 +to my project's contributors. + +152 +00:07:13,433 --> 00:07:15,001 +They can browse +the top-level page + +153 +00:07:15,001 --> 00:07:18,138 +for an overview of the project, +and jump into each page + +154 +00:07:18,138 --> 00:07:23,076 +to get detailed information +about each of its APIs. + +155 +00:07:23,076 --> 00:07:26,112 +Now that we've seen how to write +and build documentation, + +156 +00:07:26,112 --> 00:07:28,381 +it's time to publish it +to a website + +157 +00:07:28,381 --> 00:07:31,484 +so that contributors +can easily browse it. + +158 +00:07:31,484 --> 00:07:33,486 +Over to you, Ethan. + +159 +00:07:33,486 --> 00:07:35,155 +Ethan Kusters: Thank you, +Franklin. + +160 +00:07:35,155 --> 00:07:38,325 +We've been developing +the Slothy app in a modular way + +161 +00:07:38,325 --> 00:07:41,528 +alongside a more generally +useful Swift Package + +162 +00:07:41,528 --> 00:07:43,730 +called SlothCreator. + +163 +00:07:43,730 --> 00:07:45,832 +I think it would be great +to publish SlothCreator + +164 +00:07:45,832 --> 00:07:48,234 +to a wider audience +so that other developers + +165 +00:07:48,234 --> 00:07:51,204 +making sloth-related apps +can make use of it. + +166 +00:07:51,204 --> 00:07:53,573 +As a part of this effort, +I want to be sure + +167 +00:07:53,573 --> 00:07:57,210 +SlothCreator's documentation +is easily shareable on the web, + +168 +00:07:57,210 --> 00:07:59,846 +so let's walk through +Swift-DocC's brand-new, + +169 +00:07:59,846 --> 00:08:03,283 +simplified publishing workflow. + +170 +00:08:03,283 --> 00:08:05,385 +When you build documentation +in Xcode, + +171 +00:08:05,385 --> 00:08:07,721 +Swift-DocC produces +a static bundle + +172 +00:08:07,721 --> 00:08:09,756 +containing your documentation. + +173 +00:08:09,756 --> 00:08:12,225 +This bundle is called +a DocC archive + +174 +00:08:12,225 --> 00:08:15,829 +and is a portable container +for your documentation. + +175 +00:08:15,829 --> 00:08:19,032 +You can export it directly from +Xcode's documentation window + +176 +00:08:19,032 --> 00:08:21,000 +and send it to colleagues. + +177 +00:08:21,000 --> 00:08:23,269 +They'll be able to open +and browse the documentation + +178 +00:08:23,269 --> 00:08:26,106 +by just double-clicking +on the archive. + +179 +00:08:26,106 --> 00:08:28,842 +But a DocC archive isn't just +a portable container + +180 +00:08:28,842 --> 00:08:31,111 +for opening documentation +in Xcode. + +181 +00:08:31,111 --> 00:08:33,580 +It also contains +a fully featured website + +182 +00:08:33,580 --> 00:08:35,048 +right out of the box. + +183 +00:08:35,048 --> 00:08:37,684 +And new in Xcode 14, +that DocC archive + +184 +00:08:37,684 --> 00:08:40,787 +is also directly compatible +with most web servers. + +185 +00:08:40,787 --> 00:08:43,022 +This makes publishing +your documentation to the web + +186 +00:08:43,022 --> 00:08:44,891 +easier than ever. + +187 +00:08:44,891 --> 00:08:47,360 +In most cases, you can +deploy your documentation + +188 +00:08:47,360 --> 00:08:50,563 +by just copying the contents +of your built DocC archive + +189 +00:08:50,563 --> 00:08:53,133 +into the root +of your web server. + +190 +00:08:53,133 --> 00:08:55,068 +This also means +that DocC archives + +191 +00:08:55,068 --> 00:08:58,371 +are now compatible with most +managed hosting services, + +192 +00:08:58,371 --> 00:09:00,840 +including GitHub Pages. + +193 +00:09:00,840 --> 00:09:02,642 +GitHub Pages is a popular way + +194 +00:09:02,642 --> 00:09:04,778 +many developers +host their documentation + +195 +00:09:04,778 --> 00:09:07,714 +that's integrated +right into GitHub.com. + +196 +00:09:07,714 --> 00:09:08,948 +And since we've been +using GitHub + +197 +00:09:08,948 --> 00:09:11,084 +for the source control +of SlothCreator, + +198 +00:09:11,084 --> 00:09:13,486 +it makes sense for us to publish +our documentation there + +199 +00:09:13,486 --> 00:09:14,554 +as well. + +200 +00:09:14,554 --> 00:09:16,856 +If you're familiar +with using GitHub pages, + +201 +00:09:16,856 --> 00:09:18,992 +you'll know that, +unlike a standard server, + +202 +00:09:18,992 --> 00:09:22,162 +your website is not published +at the root path of the URL + +203 +00:09:22,162 --> 00:09:24,497 +but at a specific base path. + +204 +00:09:24,497 --> 00:09:26,232 +In this kind +of hosting scenario, + +205 +00:09:26,232 --> 00:09:28,902 +you'll need to specify +your website's base path + +206 +00:09:28,902 --> 00:09:30,303 +with an additional build setting + +207 +00:09:30,303 --> 00:09:33,406 +to produce a DocC archive +that's compatible. + +208 +00:09:33,406 --> 00:09:34,874 +To fully understand +how this works + +209 +00:09:34,874 --> 00:09:36,443 +and why we only need +this configuration + +210 +00:09:36,443 --> 00:09:40,180 +in certain hosting scenarios -- +like on GitHub Pages -- + +211 +00:09:40,180 --> 00:09:42,415 +let's take a look at how +we would expect the URL + +212 +00:09:42,415 --> 00:09:46,886 +of a DocC archive hosted +on your own domain might look. + +213 +00:09:46,886 --> 00:09:49,689 +Let's assume we already have +a website for the Slothy app -- + +214 +00:09:49,689 --> 00:09:52,192 +slothy.example.com -- + +215 +00:09:52,192 --> 00:09:55,462 +and we'd like to publish the +documentation for SlothCreator + +216 +00:09:55,462 --> 00:09:58,832 +as a part of that +existing website. + +217 +00:09:58,832 --> 00:10:02,035 +If we just take the content of +the SlothCreator DocC archive + +218 +00:10:02,035 --> 00:10:04,204 +and copy it to the root +of our web server, + +219 +00:10:04,204 --> 00:10:07,140 +the reference documentation +for SlothCreator will appear + +220 +00:10:07,140 --> 00:10:14,514 +at slothy.example.com/ +documentation/slothcreator. + +221 +00:10:14,514 --> 00:10:17,016 +Any tutorials for +the SlothCreator package + +222 +00:10:17,016 --> 00:10:20,186 +would be at a neighboring +"tutorials" path. + +223 +00:10:20,186 --> 00:10:22,956 +However, in this case, +we're not going to be publishing + +224 +00:10:22,956 --> 00:10:24,123 +to our own domain. + +225 +00:10:24,123 --> 00:10:26,092 +Instead, to keep +the documentation + +226 +00:10:26,092 --> 00:10:28,261 +with our GitHub repository, + +227 +00:10:28,261 --> 00:10:31,531 +we'll publish to the domain +GitHub Pages provides. + +228 +00:10:31,531 --> 00:10:34,367 +When you create a GitHub +Pages site for a repository, + +229 +00:10:34,367 --> 00:10:37,537 +the URL of that website +is not at the root path; + +230 +00:10:37,537 --> 00:10:39,706 +instead, it's at +a specific base path + +231 +00:10:39,706 --> 00:10:43,409 +corresponding to the name +of the repository. + +232 +00:10:43,409 --> 00:10:46,913 +Generally that will be something +like your username.github.io + +233 +00:10:46,913 --> 00:10:50,984 +forward-slash +your repository name. + +234 +00:10:50,984 --> 00:10:53,686 +Any reference and tutorial +documentation paths + +235 +00:10:53,686 --> 00:10:56,155 +are appended to that base path. + +236 +00:10:56,155 --> 00:10:59,092 +Because this base path +is unique to your repository, + +237 +00:10:59,092 --> 00:11:01,127 +it's important to tell +Swift-DocC what it is + +238 +00:11:01,127 --> 00:11:05,932 +before building a DocC archive +for publishing to GitHub Pages. + +239 +00:11:05,932 --> 00:11:08,368 +There's a new build setting +exposed in Xcode 14 + +240 +00:11:08,368 --> 00:11:10,537 +for just this use case. + +241 +00:11:10,537 --> 00:11:11,371 +After configuring + +242 +00:11:11,371 --> 00:11:14,374 +the DocC Archive Hosting +Base Path setting + +243 +00:11:14,374 --> 00:11:16,543 +to be the name +of your GitHub repository, + +244 +00:11:16,543 --> 00:11:17,544 +you'll be ready to go + +245 +00:11:17,544 --> 00:11:20,113 +for all of your future +documentation builds. + +246 +00:11:20,113 --> 00:11:23,116 +Let's take a look at how +this works in practice. + +247 +00:11:23,116 --> 00:11:25,451 +Here, I've opened the +SlothCreator Swift package + +248 +00:11:25,451 --> 00:11:26,753 +Franklin showed you earlier + +249 +00:11:26,753 --> 00:11:29,789 +as a dependency +of the Slothy app. + +250 +00:11:29,789 --> 00:11:31,491 +Since we're getting close +to publishing this package + +251 +00:11:31,491 --> 00:11:33,293 +for wider use, +I'm going to go ahead + +252 +00:11:33,293 --> 00:11:37,397 +and publish the documentation +we've prepared to GitHub Pages. + +253 +00:11:37,397 --> 00:11:39,999 +I'll begin by opening the +framework's project settings + +254 +00:11:39,999 --> 00:11:42,869 +by moving my mouse +to Xcode's Project navigator + +255 +00:11:42,869 --> 00:11:46,306 +and selecting +the SlothCreator item. + +256 +00:11:46,306 --> 00:11:49,275 +Next, I'll select +the SlothCreator target, + +257 +00:11:49,275 --> 00:11:52,378 +and then open +the Build Settings tab. + +258 +00:11:52,378 --> 00:11:55,415 +In this case, I'm looking for +a Swift-DocC-related setting, + +259 +00:11:55,415 --> 00:12:00,420 +so let's filter for DocC. + +260 +00:12:00,420 --> 00:12:04,591 +Now I'll set the DocC Archive +Hosting Base Path setting + +261 +00:12:04,591 --> 00:12:10,830 +to be the name of our +repository: sloth-creator. + +262 +00:12:10,830 --> 00:12:11,698 +Great! + +263 +00:12:11,698 --> 00:12:14,500 +Next, let's build documentation. + +264 +00:12:14,500 --> 00:12:17,503 +I'll move my mouse +up to the Product menu + +265 +00:12:17,503 --> 00:12:21,374 +and select Build +documentation. + +266 +00:12:21,374 --> 00:12:23,643 +After Xcode finishes +compiling my project + +267 +00:12:23,643 --> 00:12:25,578 +and generating +documentation for it, + +268 +00:12:25,578 --> 00:12:28,047 +the documentation window +will open. + +269 +00:12:28,047 --> 00:12:31,417 +Here's the top-level page for +SlothCreator's documentation. + +270 +00:12:31,417 --> 00:12:34,053 +Let's go ahead and export it. + +271 +00:12:34,053 --> 00:12:36,589 +I'll move my mouse over +to the documentation navigator + +272 +00:12:36,589 --> 00:12:39,359 +and the SlothCreator +technology item. + +273 +00:12:39,359 --> 00:12:44,731 +Next I'll click on the +context menu and select Export. + +274 +00:12:44,731 --> 00:12:47,634 +I'm going to export it +to a directory named "docs" + +275 +00:12:47,634 --> 00:12:49,602 +in the root of my repository + +276 +00:12:49,602 --> 00:12:51,838 +because this is how +I've configured GitHub Pages + +277 +00:12:51,838 --> 00:12:54,841 +to publish +my documentation website. + +278 +00:12:58,611 --> 00:13:02,749 +Now I just need to commit and +push my changes up to GitHub. + +279 +00:13:02,749 --> 00:13:05,485 +Back in the main Xcode window, + +280 +00:13:05,485 --> 00:13:08,187 +I'll move my mouse +up to the Source Control menu + +281 +00:13:08,187 --> 00:13:10,390 +and select Commit. + +282 +00:13:10,390 --> 00:13:12,158 +I'll select the docs directory + +283 +00:13:12,158 --> 00:13:14,761 +that has +my documentation content + +284 +00:13:14,761 --> 00:13:16,963 +and write a commit message. + +285 +00:13:16,963 --> 00:13:19,799 +Let's go ahead +and push the changes. + +286 +00:13:24,804 --> 00:13:26,072 +And that's it. + +287 +00:13:26,072 --> 00:13:27,874 +Let's go check out +the published site. + +288 +00:13:27,874 --> 00:13:30,877 +I'll open up my repository +on GitHub.com. + +289 +00:13:33,680 --> 00:13:35,782 +I'd already placed a link +to the documentation site + +290 +00:13:35,782 --> 00:13:39,819 +in the README, +so I'll just click on it. + +291 +00:13:39,819 --> 00:13:41,988 +And here we are. + +292 +00:13:41,988 --> 00:13:43,923 +I'm so excited to have +the documentation + +293 +00:13:43,923 --> 00:13:47,026 +we worked on for SlothCreator +easily accessible on the web. + +294 +00:13:47,026 --> 00:13:49,128 +I think this is going to be +really helpful for folks + +295 +00:13:49,128 --> 00:13:53,299 +interested in using SlothCreator +in their own projects. + +296 +00:13:53,299 --> 00:13:55,134 +Now that we've deployed +the documentation + +297 +00:13:55,134 --> 00:13:56,736 +for SlothCreator once, + +298 +00:13:56,736 --> 00:13:58,571 +I'd really like +to set up automation + +299 +00:13:58,571 --> 00:13:59,539 +to perform a deployment + +300 +00:13:59,539 --> 00:14:03,843 +any time the documentation +in the repository changes. + +301 +00:14:03,843 --> 00:14:06,813 +Since we're releasing +SlothCreator as a Swift Package, + +302 +00:14:06,813 --> 00:14:09,682 +the new Swift-DocC +Swift Package Manager plug-in + +303 +00:14:09,682 --> 00:14:11,818 +is going to be +a great help here. + +304 +00:14:11,818 --> 00:14:13,619 +You can use +the Swift-DocC plug-in + +305 +00:14:13,619 --> 00:14:16,723 +to really simplify the process +of building documentation + +306 +00:14:16,723 --> 00:14:18,658 +for Swift packages. + +307 +00:14:18,658 --> 00:14:20,460 +The plug-in's documentation +is linked below + +308 +00:14:20,460 --> 00:14:21,894 +and I recommend +checking it out + +309 +00:14:21,894 --> 00:14:24,297 +as a great starting place +for configuring automated + +310 +00:14:24,297 --> 00:14:26,733 +documentation deployments +to GitHub Pages + +311 +00:14:26,733 --> 00:14:29,268 +and other hosting services. + +312 +00:14:29,268 --> 00:14:31,304 +And of course, Swift-DocC +continues to have + +313 +00:14:31,304 --> 00:14:33,740 +great command-line support +for Xcode projects + +314 +00:14:33,740 --> 00:14:36,676 +with the xcodebuild docbuild +command-line interface + +315 +00:14:36,676 --> 00:14:39,212 +that was introduced +in Xcode 13. + +316 +00:14:39,212 --> 00:14:42,048 +Documentation for how +to get started using xcodebuild + +317 +00:14:42,048 --> 00:14:46,385 +to automate GitHub Pages +deployments is linked below. + +318 +00:14:46,385 --> 00:14:49,822 +We're thrilled to be releasing +an all-new enhanced browsing + +319 +00:14:49,822 --> 00:14:53,593 +and navigation experience for +Swift-DocC on the web this year. + +320 +00:14:53,593 --> 00:14:56,629 +Let's take a look at how +the new navigation sidebar + +321 +00:14:56,629 --> 00:14:58,998 +can help readers of the +SlothCreator documentation + +322 +00:14:58,998 --> 00:15:02,135 +explore what the framework +has to offer. + +323 +00:15:02,135 --> 00:15:04,504 +We're back on the GitHub Pages +site for SlothCreator, + +324 +00:15:04,504 --> 00:15:06,239 +and over on the left side +of the page + +325 +00:15:06,239 --> 00:15:08,741 +is the new navigation sidebar. + +326 +00:15:08,741 --> 00:15:11,778 +I'll move my mouse over +to the CareSchedule item + +327 +00:15:11,778 --> 00:15:15,548 +and expand it by clicking +on the disclosure triangle. + +328 +00:15:15,548 --> 00:15:17,283 +Now I can see the pages +that are organized + +329 +00:15:17,283 --> 00:15:19,252 +as children of CareSchedule + +330 +00:15:19,252 --> 00:15:21,687 +without even needing +to fully open the page. + +331 +00:15:21,687 --> 00:15:23,156 +In this case, +I'm interested in + +332 +00:15:23,156 --> 00:15:28,027 +jumping straight +to the type's initializer. + +333 +00:15:28,027 --> 00:15:30,429 +I can continue navigating +through the framework, + +334 +00:15:30,429 --> 00:15:35,168 +expanding other items like +FoodGenerator and SlothFood, + +335 +00:15:35,168 --> 00:15:38,971 +eventually opening +the twig page. + +336 +00:15:38,971 --> 00:15:40,873 +As I'm navigating between pages, + +337 +00:15:40,873 --> 00:15:43,910 +the state of the navigation +sidebar stays constant, + +338 +00:15:43,910 --> 00:15:47,246 +allowing me to keep track of +the pages I've already visited. + +339 +00:15:47,246 --> 00:15:49,615 +This allows for a natural +exploration of the framework + +340 +00:15:49,615 --> 00:15:52,018 +that I really appreciate. + +341 +00:15:52,018 --> 00:15:54,654 +But what if I'm already familiar +with the SlothCreator framework + +342 +00:15:54,654 --> 00:15:57,757 +and am looking for information +about a specific symbol? + +343 +00:15:57,757 --> 00:16:00,626 +The filter field at the bottom +of the new navigation sidebar + +344 +00:16:00,626 --> 00:16:02,195 +is perfect for this. + +345 +00:16:02,195 --> 00:16:04,463 +I'm interested in an API +that will help me + +346 +00:16:04,463 --> 00:16:07,300 +increase the energy level +of my sloths. + +347 +00:16:07,300 --> 00:16:10,303 +I'll move my mouse to the bottom +of the navigator, + +348 +00:16:10,303 --> 00:16:15,474 +select the filter bar, +and insert "energy." + +349 +00:16:15,474 --> 00:16:16,475 +Perfect! + +350 +00:16:16,475 --> 00:16:19,111 +This documentation +on the energyLevel property + +351 +00:16:19,111 --> 00:16:21,881 +is exactly what +I was looking for. + +352 +00:16:21,881 --> 00:16:23,149 +The new browsing experience + +353 +00:16:23,149 --> 00:16:25,785 +offered by Swift-DocC +in Xcode 14 + +354 +00:16:25,785 --> 00:16:27,520 +is going to bring +your documentation site + +355 +00:16:27,520 --> 00:16:29,021 +to the next level. + +356 +00:16:29,021 --> 00:16:31,958 +We're so excited +for you to try it out. + +357 +00:16:31,958 --> 00:16:34,827 +Swift-DocC's integration +in Xcode now offers support + +358 +00:16:34,827 --> 00:16:37,597 +for documenting +all of your projects. + +359 +00:16:37,597 --> 00:16:40,466 +This includes your +Objective-C and Swift code + +360 +00:16:40,466 --> 00:16:42,602 +in application +and framework targets, + +361 +00:16:42,602 --> 00:16:47,540 +whether packaged as Xcode +projects or Swift Packages. + +362 +00:16:47,540 --> 00:16:49,775 +The DocC archive +produced by Xcode 14 + +363 +00:16:49,775 --> 00:16:53,012 +is immediately compatible +with popular hosting services, + +364 +00:16:53,012 --> 00:16:54,947 +including GitHub Pages. + +365 +00:16:54,947 --> 00:16:57,750 +This is a game changer for +distributing your documentation + +366 +00:16:57,750 --> 00:17:00,152 +to an even wider audience. + +367 +00:17:00,152 --> 00:17:02,221 +And finally, Swift-DocC offers + +368 +00:17:02,221 --> 00:17:05,625 +a powerful new navigation +experience on the web. + +369 +00:17:05,625 --> 00:17:08,427 +The navigation sidebar +is going to unlock new ways + +370 +00:17:08,427 --> 00:17:13,466 +of exploring and finding +the documentation on your site. + +371 +00:17:13,466 --> 00:17:16,269 +To learn more about +the new Swift-DocC sidebar + +372 +00:17:16,269 --> 00:17:18,437 +and how to best author +your documentation + +373 +00:17:18,437 --> 00:17:20,806 +to take advantage +of its new features, + +374 +00:17:20,806 --> 00:17:23,109 +check out the +"Improve the discoverability + +375 +00:17:23,109 --> 00:17:26,145 +of your Swift-DocC content" +session. + +376 +00:17:26,145 --> 00:17:28,848 +And to take your documentation +even further, + +377 +00:17:28,848 --> 00:17:32,451 +check out the "Build interactive +tutorials using DocC" session + +378 +00:17:32,451 --> 00:17:35,388 +to learn how to build +step-by-step walk-throughs + +379 +00:17:35,388 --> 00:17:39,191 +to guide developers +through your app or framework. + +380 +00:17:39,191 --> 00:17:41,560 +We are so excited to see +all of the documentation + +381 +00:17:41,560 --> 00:17:44,764 +you write and publish +with Xcode 14. + +382 +00:17:44,764 --> 00:17:46,165 +Thank you for watching. + +383 +00:17:46,165 --> 00:17:50,169 +♪ + diff --git a/eng/2022 Session 110369 Improve the discoverability of your Swift-DocC content en.srt b/eng/2022 Session 110369 Improve the discoverability of your Swift-DocC content en.srt new file mode 100644 index 0000000..ce14565 --- /dev/null +++ b/eng/2022 Session 110369 Improve the discoverability of your Swift-DocC content en.srt @@ -0,0 +1,1053 @@ +1 +00:00:00,000 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,776 +♪ + +3 +00:00:09,776 --> 00:00:12,946 +Hi! My name is Bea, and I'm +a human interface designer + +4 +00:00:12,946 --> 00:00:14,648 +on the Documentation team. + +5 +00:00:14,648 --> 00:00:17,251 +I'm going to share how to +improve the discoverability + +6 +00:00:17,251 --> 00:00:19,920 +of your Swift-DocC content. + +7 +00:00:19,920 --> 00:00:23,390 +In this session, I'll talk about +the new navigation experience + +8 +00:00:23,390 --> 00:00:27,294 +available for Swift-DocC +documentation on the web + +9 +00:00:27,294 --> 00:00:29,730 +and share tips for how +to optimize your content + +10 +00:00:29,730 --> 00:00:32,499 +for discoverability. + +11 +00:00:32,499 --> 00:00:35,836 +If you need some additional +context on what Swift-DocC is + +12 +00:00:35,836 --> 00:00:38,305 +and how you can make +great documentation + +13 +00:00:38,305 --> 00:00:40,774 +for your frameworks +and apps with it, + +14 +00:00:40,774 --> 00:00:42,910 +check out these other +WWDC sessions. + +15 +00:00:44,745 --> 00:00:49,416 +Now, let's jump in and check out +the new navigation on the web. + +16 +00:00:49,416 --> 00:00:52,452 +This year, when you publish +your documentation site, + +17 +00:00:52,452 --> 00:00:54,254 +you get a new +navigation experience + +18 +00:00:54,254 --> 00:00:57,658 +that brings out +the best in your content. + +19 +00:00:57,658 --> 00:01:00,227 +The page contains +two main sections. + +20 +00:01:00,227 --> 00:01:03,764 +To the left is a navigator +and filter bar that allow you + +21 +00:01:03,764 --> 00:01:05,999 +to navigate through +your documentation + +22 +00:01:05,999 --> 00:01:08,502 +and find APIs quickly. + +23 +00:01:08,502 --> 00:01:11,004 +To the right +is the content view. + +24 +00:01:11,004 --> 00:01:13,941 +It's optimized to be flexible +in multiple screen + +25 +00:01:13,941 --> 00:01:16,276 +and navigator sizes. + +26 +00:01:16,276 --> 00:01:18,912 +Since the navigator +is separate from the content, + +27 +00:01:18,912 --> 00:01:22,749 +it's easy to quickly switch +between different pages. + +28 +00:01:22,749 --> 00:01:24,885 +You can also use +the disclosure indicators + +29 +00:01:24,885 --> 00:01:26,687 +to explore deeper +in the tree + +30 +00:01:26,687 --> 00:01:29,623 +and understand +the API hierarchy. + +31 +00:01:29,623 --> 00:01:31,725 +All of these features +make it way easier + +32 +00:01:31,725 --> 00:01:34,361 +to explore documentation. + +33 +00:01:34,361 --> 00:01:36,863 +On the other hand, if you know +what you're looking for + +34 +00:01:36,863 --> 00:01:39,466 +and you want to get there +as quickly as possible, + +35 +00:01:39,466 --> 00:01:42,035 +you can use the Filter bar +to refine the results + +36 +00:01:42,035 --> 00:01:44,204 +in the navigator. + +37 +00:01:44,204 --> 00:01:46,773 +Let's see the filter in action. + +38 +00:01:46,773 --> 00:01:49,910 +Let's say that +you're looking for "Habitat." + +39 +00:01:49,910 --> 00:01:52,145 +When you click on +the Filter bar and type that in, + +40 +00:01:52,145 --> 00:01:54,181 +you'll get a filtered view +of the pages + +41 +00:01:54,181 --> 00:01:57,651 +you actually care about, +right now. + +42 +00:01:57,651 --> 00:02:01,121 +You can clear the filter +to reset the navigator. + +43 +00:02:01,121 --> 00:02:04,191 +And you can also filter +to see articles, tutorials, + +44 +00:02:04,191 --> 00:02:08,829 +and even hide deprecated pages +by selecting tags. + +45 +00:02:08,829 --> 00:02:11,898 +For example, if you select +the Tutorials tag, + +46 +00:02:11,898 --> 00:02:13,967 +it's super convenient to find + +47 +00:02:13,967 --> 00:02:17,671 +the "Meet SlothCreator" +tutorial. + +48 +00:02:17,671 --> 00:02:21,208 +Now that you're up to date +with what's new in navigation, + +49 +00:02:21,208 --> 00:02:23,643 +I'm going to talk about +how to make the most + +50 +00:02:23,643 --> 00:02:25,679 +out of this new experience. + +51 +00:02:25,679 --> 00:02:28,281 +Here are some tips and tricks +for how you can optimize + +52 +00:02:28,281 --> 00:02:31,118 +your content so developers +find the pages + +53 +00:02:31,118 --> 00:02:35,155 +they're looking for +as smoothly as possible. + +54 +00:02:35,155 --> 00:02:38,225 +Let me show you how +I optimized the documentation + +55 +00:02:38,225 --> 00:02:41,828 +for my framework, +SlothCreator, as an example. + +56 +00:02:41,828 --> 00:02:44,197 +You can use SlothCreator +to catalog sloths + +57 +00:02:44,197 --> 00:02:49,136 +you find in nature and to create +new, adorable virtual sloths. + +58 +00:02:49,136 --> 00:02:52,139 +I just finished working +on my framework documentation, + +59 +00:02:52,139 --> 00:02:54,141 +so I haven't written +any markdown + +60 +00:02:54,141 --> 00:02:56,543 +to organize my pages yet. + +61 +00:02:56,543 --> 00:02:58,512 +As a starting point, +I'm taking advantage + +62 +00:02:58,512 --> 00:03:02,682 +of Swift-DocC's +automatic organization. + +63 +00:03:02,682 --> 00:03:05,352 +This means my navigator +is organized by types, + +64 +00:03:05,352 --> 00:03:12,192 +like tutorials, articles, +protocols, and structures. + +65 +00:03:12,192 --> 00:03:16,363 +This is already a great start, +but I can do a better job + +66 +00:03:16,363 --> 00:03:19,299 +guiding developers +through my documentation. + +67 +00:03:19,299 --> 00:03:23,003 +To optimize my content, +I'm going to follow three steps. + +68 +00:03:25,105 --> 00:03:28,341 +First, define the main +high-level themes + +69 +00:03:28,341 --> 00:03:30,944 +of what I can do +with this framework. + +70 +00:03:30,944 --> 00:03:36,049 +Then, I'll organize my pages +by importance and specificity. + +71 +00:03:36,049 --> 00:03:39,753 +Last but not least, +I'll optimize my group titles + +72 +00:03:39,753 --> 00:03:44,091 +to make sure they're as clear +and helpful as possible. + +73 +00:03:44,091 --> 00:03:48,428 +I think of this process +as creating a map. + +74 +00:03:48,428 --> 00:03:50,997 +It helps people +understand the boundaries + +75 +00:03:50,997 --> 00:03:53,366 +and general characteristics +of a region + +76 +00:03:53,366 --> 00:03:57,070 +and figure out how to get +from one place to another. + +77 +00:03:57,070 --> 00:04:00,140 +Likewise, the documentation +navigator helps developers + +78 +00:04:00,140 --> 00:04:02,843 +understand what you can do +with a set of APIs + +79 +00:04:02,843 --> 00:04:06,746 +and how to navigate to the pages +they're looking for. + +80 +00:04:06,746 --> 00:04:09,583 +I'll start by helping developers +understand what they can do + +81 +00:04:09,583 --> 00:04:12,986 +with my APIs by defining +the main high-level themes + +82 +00:04:12,986 --> 00:04:15,422 +of SlothCreator. + +83 +00:04:15,422 --> 00:04:18,058 +These themes will show up +on the navigator + +84 +00:04:18,058 --> 00:04:19,693 +on the SlothCreator page, + +85 +00:04:19,693 --> 00:04:22,395 +the top-level page +of my documentation. + +86 +00:04:22,395 --> 00:04:24,831 +It'll be one of the first things +developers see + +87 +00:04:24,831 --> 00:04:27,901 +when they land +on my documentation website. + +88 +00:04:27,901 --> 00:04:31,138 +This is my opportunity +to make a great first impression + +89 +00:04:31,138 --> 00:04:33,740 +and help developers +have a good understanding + +90 +00:04:33,740 --> 00:04:36,843 +of what this framework does. + +91 +00:04:36,843 --> 00:04:39,412 +Now, let me think +about my first theme. + +92 +00:04:39,412 --> 00:04:41,948 +One of the main functionalities +of SlothCreator + +93 +00:04:41,948 --> 00:04:44,451 +is creating sloths. + +94 +00:04:44,451 --> 00:04:48,722 +They have names, colors, +and even special powers. + +95 +00:04:48,722 --> 00:04:53,660 +To summarize, I'll call this +topic group "Sloth Creation" + +96 +00:04:53,660 --> 00:04:56,096 +Later, I'll decide what pages +to include in this group, + +97 +00:04:56,096 --> 00:04:58,965 +so I'll just leave +a placeholder for now. + +98 +00:04:58,965 --> 00:05:01,902 +After you create a sloth, +you can visualize it + +99 +00:05:01,902 --> 00:05:04,838 +in many different ways, +like on an app screen, + +100 +00:05:04,838 --> 00:05:06,706 +in a map view. + +101 +00:05:06,706 --> 00:05:10,577 +I'll call this "Sloth Views" + +102 +00:05:10,577 --> 00:05:13,713 +And of course, +sloths are a lot of work. + +103 +00:05:13,713 --> 00:05:17,751 +They need to be fed, +entertained, taken care of. + +104 +00:05:17,751 --> 00:05:21,188 +I'll call this "Management" + +105 +00:05:21,188 --> 00:05:23,056 +There's already +a lot of functionalities + +106 +00:05:23,056 --> 00:05:25,192 +you can achieve +in these three groups. + +107 +00:05:25,192 --> 00:05:27,527 +And putting myself +in the shoes of a developer + +108 +00:05:27,527 --> 00:05:29,996 +who's never used +SlothCreator before, + +109 +00:05:29,996 --> 00:05:31,965 +I'd love to have +an easy way + +110 +00:05:31,965 --> 00:05:35,101 +to see how to get started +with this framework. + +111 +00:05:35,101 --> 00:05:37,804 +With this in mind, +I'll create a topic group + +112 +00:05:37,804 --> 00:05:41,274 +with high-level +introductory content. + +113 +00:05:41,274 --> 00:05:44,744 +I'll call it "Essentials." + +114 +00:05:44,744 --> 00:05:48,148 +Awesome! Even though +there are hundreds of things + +115 +00:05:48,148 --> 00:05:51,117 +you can do with SlothCreator, +I'm only highlighting + +116 +00:05:51,117 --> 00:05:55,956 +the four most important, +overarching topic groups. + +117 +00:05:55,956 --> 00:05:58,458 +By reducing the number +of options that are available, + +118 +00:05:58,458 --> 00:06:01,161 +I'm increasing +developers' chances + +119 +00:06:01,161 --> 00:06:04,297 +of successfully taking +the next step. + +120 +00:06:04,297 --> 00:06:07,234 +There's no magic number +for how many topic groups + +121 +00:06:07,234 --> 00:06:09,202 +I should create, +but I generally try + +122 +00:06:09,202 --> 00:06:12,772 +to stick to under +10 per page. + +123 +00:06:12,772 --> 00:06:14,774 +Thinking back +to the idea of a map, + +124 +00:06:14,774 --> 00:06:17,510 +I want to give developers +step-by-step guidance + +125 +00:06:17,510 --> 00:06:19,479 +on where they can go next. + +126 +00:06:19,479 --> 00:06:24,784 +The order of my groups is vital +to create a great experience. + +127 +00:06:24,784 --> 00:06:26,720 +Taking another look +at my topic groups, + +128 +00:06:26,720 --> 00:06:29,022 +they're mostly in a good order. + +129 +00:06:29,022 --> 00:06:31,024 +First, you have +to create a sloth, + +130 +00:06:31,024 --> 00:06:34,861 +then you can visualize it, +then you can manage it. + +131 +00:06:34,861 --> 00:06:37,030 +The only thing +I'd change here for now + +132 +00:06:37,030 --> 00:06:40,600 +is moving Essentials +up to the beginning of the list, + +133 +00:06:40,600 --> 00:06:44,437 +so developers see +that beginner content first. + +134 +00:06:44,437 --> 00:06:47,307 +Now, it's time to decide +the pages and themes + +135 +00:06:47,307 --> 00:06:51,578 +that should be organized under +each one of these categories. + +136 +00:06:51,578 --> 00:06:53,780 +Let me start with Essentials. + +137 +00:06:53,780 --> 00:06:55,815 +This will be at the very top +of the navigator + +138 +00:06:55,815 --> 00:06:59,119 +and probably one of the first +things developers will see. + +139 +00:06:59,119 --> 00:07:01,221 +So I want to make sure +this features + +140 +00:07:01,221 --> 00:07:05,325 +the most important +introductory content. + +141 +00:07:05,325 --> 00:07:07,060 +This is a great spot +to highlight + +142 +00:07:07,060 --> 00:07:09,996 +introductory articles +and tutorials. + +143 +00:07:09,996 --> 00:07:11,064 +This enables developers + +144 +00:07:11,064 --> 00:07:14,000 +to find step-by-step +code examples quickly, + +145 +00:07:14,000 --> 00:07:19,072 +which is, personally, +my favorite way to learn. + +146 +00:07:19,072 --> 00:07:20,740 +With these considerations +in mind, + +147 +00:07:20,740 --> 00:07:24,144 +I decided to organize +three groups under Essentials: + +148 +00:07:24,144 --> 00:07:27,080 +the "Meet SlothCreator" +tutorial, + +149 +00:07:27,080 --> 00:07:29,816 +the "Getting Started +with Sloths" article, + +150 +00:07:29,816 --> 00:07:34,854 +and the Sloth struct, one of +the core APIs in my framework. + +151 +00:07:34,854 --> 00:07:38,591 +I'll repeat this same process +for the three other groups. + +152 +00:07:38,591 --> 00:07:41,828 +This feels approachable +because I see the most important + +153 +00:07:41,828 --> 00:07:43,763 +and broad themes first. + +154 +00:07:43,763 --> 00:07:45,532 +As I explore deeper in the tree, + +155 +00:07:45,532 --> 00:07:49,069 +the groups get more specific +and advanced. + +156 +00:07:49,069 --> 00:07:51,371 +For instance, inside Essentials, + +157 +00:07:51,371 --> 00:07:53,673 +I'll find +Getting Visual Attributes. + +158 +00:07:53,673 --> 00:07:55,942 +And inside +Getting Visual Attributes, + +159 +00:07:55,942 --> 00:07:59,713 +I'll find +Getting the Standard Colors. + +160 +00:07:59,713 --> 00:08:02,382 +Great, my documentation +is organized in a way + +161 +00:08:02,382 --> 00:08:05,885 +to guide developers through +the content successfully. + +162 +00:08:05,885 --> 00:08:09,089 +Next, I want to make sure +the titles of my topic groups + +163 +00:08:09,089 --> 00:08:11,324 +are also high quality. + +164 +00:08:11,324 --> 00:08:14,561 +The first characteristic +to a great topic group title + +165 +00:08:14,561 --> 00:08:18,398 +is that it should be +clear and descriptive. + +166 +00:08:18,398 --> 00:08:21,101 +A good title makes sense +on its own + +167 +00:08:21,101 --> 00:08:24,471 +and doesn't need much +additional context. + +168 +00:08:24,471 --> 00:08:26,206 +Thinking back +to my topic titles, + +169 +00:08:26,206 --> 00:08:28,775 +there's some room +for improvement. + +170 +00:08:28,775 --> 00:08:32,345 +The last topic title I wrote +is "Management." + +171 +00:08:32,345 --> 00:08:35,148 +This group of APIs is all +about what you can do + +172 +00:08:35,148 --> 00:08:38,651 +to manage your sloth's +well-being: Activity, + +173 +00:08:38,651 --> 00:08:42,989 +CareSchedule, FoodGenerator, +and Sloth.Food. + +174 +00:08:42,989 --> 00:08:45,492 +At first glance, +this seems like a good title. + +175 +00:08:45,492 --> 00:08:49,596 +However, +upon further consideration, + +176 +00:08:49,596 --> 00:08:51,631 +"Management" +is such a broad term; + +177 +00:08:51,631 --> 00:08:54,901 +it could mean a ton +of different things. + +178 +00:08:54,901 --> 00:08:56,603 +So it's not ideal. + +179 +00:08:56,603 --> 00:08:59,105 +To make this clearer +and more descriptive, + +180 +00:08:59,105 --> 00:09:02,776 +I'm going to call it +"Care and Feeding." + +181 +00:09:02,776 --> 00:09:04,778 +Now, I understand +this group is all about + +182 +00:09:04,778 --> 00:09:08,248 +taking care of my sloths +and giving them food. + +183 +00:09:08,248 --> 00:09:10,550 +For that reason, +it's also important + +184 +00:09:10,550 --> 00:09:14,387 +that topic group titles +be mutually exclusive. + +185 +00:09:14,387 --> 00:09:16,256 +If I have titles +that are interchangeable, + +186 +00:09:16,256 --> 00:09:20,860 +it's hard to know which one +contains what I'm looking for. + +187 +00:09:20,860 --> 00:09:23,363 +For example, +Fueling Superpowers, + +188 +00:09:23,363 --> 00:09:26,366 +Getting Magical Abilities, +and Casting Enchantments + +189 +00:09:26,366 --> 00:09:28,134 +are very similar themes + +190 +00:09:28,134 --> 00:09:32,605 +and could probably be organized +under a single title. + +191 +00:09:32,605 --> 00:09:36,075 +By keeping the names as +mutually exclusive as possible, + +192 +00:09:36,075 --> 00:09:37,911 +I'm making it simpler +for developers + +193 +00:09:37,911 --> 00:09:40,280 +to figure out +where to go next. + +194 +00:09:40,280 --> 00:09:44,384 +The more work I put into +organization and page titles, + +195 +00:09:44,384 --> 00:09:48,021 +the more likely developers +will have a smooth path + +196 +00:09:48,021 --> 00:09:50,123 +to the page +they're looking for. + +197 +00:09:50,123 --> 00:09:53,092 +Also, the more likely +I'll encourage serendipity; + +198 +00:09:53,092 --> 00:09:56,763 +in other words, +happy accidents. + +199 +00:09:56,763 --> 00:09:59,632 +By organizing well-thought-out +themes next to each other, + +200 +00:09:59,632 --> 00:10:05,004 +I'm enabling developers to find +relevant, related pages. + +201 +00:10:05,004 --> 00:10:08,641 +For example, as I'm learning +about SlothCreator Essentials, + +202 +00:10:08,641 --> 00:10:14,147 +I'm delighted to find Getting +Magical Abilities in the list. + +203 +00:10:14,147 --> 00:10:15,849 +All of these tips and tricks + +204 +00:10:15,849 --> 00:10:18,651 +really brought my documentation +to the next level. + +205 +00:10:18,651 --> 00:10:21,321 +It's so much better! + +206 +00:10:21,321 --> 00:10:24,824 +Now, let's review how you can +improve the discoverability + +207 +00:10:24,824 --> 00:10:29,395 +of your content and make it +approachable for developers. + +208 +00:10:29,395 --> 00:10:34,000 +First, identify the main themes +of your documentation. + +209 +00:10:34,000 --> 00:10:39,138 +Then, organize your content +by importance and specificity. + +210 +00:10:39,138 --> 00:10:43,009 +Next, encourage serendipity, +by organizing related themes + +211 +00:10:43,009 --> 00:10:44,878 +next to each other. + +212 +00:10:44,878 --> 00:10:48,915 +Last but not least, write clear, +mutually exclusive titles + +213 +00:10:48,915 --> 00:10:51,951 +for your pages and groups. + +214 +00:10:51,951 --> 00:10:53,620 +Thank you for taking the time + +215 +00:10:53,620 --> 00:10:56,923 +to learn how to make +your documentation better. + +216 +00:10:56,923 --> 00:11:00,059 +I'm sure developers +will deeply appreciate it. + +217 +00:11:00,059 --> 00:11:01,761 +Have a great WWDC! + +218 +00:11:01,761 --> 00:11:06,132 +♪ + diff --git a/eng/2022 Session 110370 Debug Swift debugging with LLDB en.srt b/eng/2022 Session 110370 Debug Swift debugging with LLDB en.srt new file mode 100644 index 0000000..299014f --- /dev/null +++ b/eng/2022 Session 110370 Debug Swift debugging with LLDB en.srt @@ -0,0 +1,1366 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,977 --> 00:00:11,378 +Hello, everyone. + +3 +00:00:11,411 --> 00:00:15,649 +My name is Adrian, and I'll be talking +to you about how to set up your project + +4 +00:00:15,682 --> 00:00:19,119 +for a great debugging experience +with LLDB. + +5 +00:00:19,152 --> 00:00:23,857 +LLDB is the underlying debugging +technology that ships with Xcode. + +6 +00:00:23,891 --> 00:00:27,728 +LLDB allows you to set breakpoints +in your application, + +7 +00:00:27,761 --> 00:00:29,296 +pause its execution, + +8 +00:00:29,329 --> 00:00:32,266 +inspect the state of variables +and objects, + +9 +00:00:32,299 --> 00:00:35,669 +explore your code, and much more. + +10 +00:00:35,702 --> 00:00:39,473 +LLDB can help you understand +what your code is doing + +11 +00:00:39,506 --> 00:00:43,510 +and it enables you to find the point where +the behavior of your code + +12 +00:00:43,544 --> 00:00:46,513 +diverges from your expectation. + +13 +00:00:46,547 --> 00:00:51,185 +It's a powerful tool for +understanding and exploring code. + +14 +00:00:51,218 --> 00:00:54,922 +If you want to learn more about LLDB, +please check out earlier videos, + +15 +00:00:54,955 --> 00:00:59,726 +for example "Discover breakpoint +improvements" from WWDC21. + +16 +00:00:59,760 --> 00:01:02,729 +Today we are going to look at +some advanced workflows + +17 +00:01:02,763 --> 00:01:06,834 +that have unique implications +on debugging Swift code. + +18 +00:01:06,867 --> 00:01:10,871 +Maybe you are integrating +a third-party framework into your app. + +19 +00:01:10,904 --> 00:01:13,407 +Maybe your app and your team has grown +to the point + +20 +00:01:13,440 --> 00:01:18,245 +where most of your code is being built +by a continuous integration system. + +21 +00:01:18,278 --> 00:01:20,914 +Maybe you are using a custom +build system to integrate + +22 +00:01:20,948 --> 00:01:23,684 +with your company's infrastructure. + +23 +00:01:23,717 --> 00:01:28,088 +Maybe you are building software +for other software developers. + +24 +00:01:28,121 --> 00:01:30,924 +Or you just want to learn more about LLDB. + +25 +00:01:30,958 --> 00:01:35,295 +My goal is to provide a better +understanding of how LLDB works, + +26 +00:01:35,329 --> 00:01:39,967 +and what information it needs from +the build system in order to function. + +27 +00:01:40,000 --> 00:01:44,071 +I have a little project here that we are +going to use as our running example. + +28 +00:01:44,104 --> 00:01:47,407 +I'm a compiler engineer, +and I like games, + +29 +00:01:47,441 --> 00:01:50,677 +so in my spare time +I write parsers for text adventures. + +30 +00:01:50,711 --> 00:01:54,147 +This one I recently started in pure Swift. + +31 +00:01:54,181 --> 00:01:56,149 +Let me show you what I've got so far. + +32 +00:01:56,183 --> 00:02:00,120 +The game uses a text interface +so I'm running it in Terminal. + +33 +00:02:00,153 --> 00:02:04,324 +As in every good adventure, +we'll start by checking our inventory. + +34 +00:02:07,194 --> 00:02:10,030 +This game takes place +in a contemporary setting. + +35 +00:02:10,063 --> 00:02:12,199 +I see that I have an iPhone. + +36 +00:02:12,232 --> 00:02:15,569 +Next, let's have a look +at our surroundings. + +37 +00:02:17,538 --> 00:02:21,608 +Hmm, this sensor looks intriguing. + +38 +00:02:21,642 --> 00:02:24,711 +Maybe we can use the iPhone on the sensor? + +39 +00:02:30,350 --> 00:02:32,186 +I dropped the iPhone? + +40 +00:02:32,219 --> 00:02:35,255 +Uh, that's not what I wanted to show you. + +41 +00:02:35,289 --> 00:02:39,026 +I think that my game has a bug. + +42 +00:02:39,059 --> 00:02:41,028 +Good thing this is a debugger talk. + +43 +00:02:41,061 --> 00:02:45,332 +Let's set a breakpoint in the parser +and run our command again. + +44 +00:02:51,371 --> 00:02:55,375 +[typing sounds] + +45 +00:02:59,413 --> 00:03:03,283 +We should first verify +that the command was read correctly. + +46 +00:03:03,317 --> 00:03:06,854 +The "words" variable contains +the tokenized command. + +47 +00:03:11,859 --> 00:03:15,095 +Ah, this did not go as expected. + +48 +00:03:15,128 --> 00:03:17,231 +I don't know what's going on here. + +49 +00:03:17,264 --> 00:03:19,900 +Yesterday I was using the debugger +with no problems, + +50 +00:03:19,933 --> 00:03:22,803 +and then last night I integrated +this UI framework + +51 +00:03:22,836 --> 00:03:25,272 +for styling text on the terminal. + +52 +00:03:25,305 --> 00:03:28,775 +The developers of that framework +have a continuous integration system + +53 +00:03:28,809 --> 00:03:31,311 +that cranks out nightly builds +of the framework, + +54 +00:03:31,345 --> 00:03:34,381 +and I'm linking +directly against the latest one. + +55 +00:03:34,414 --> 00:03:39,319 +I wonder if this framework has something +to do with my debugging troubles. + +56 +00:03:39,353 --> 00:03:41,255 +Case in point, I already noticed + +57 +00:03:41,288 --> 00:03:44,191 +that I can't step +into the framework's source code, + +58 +00:03:44,224 --> 00:03:47,160 +even though I explicitly downloaded +the debug build. + +59 +00:03:47,194 --> 00:03:48,862 +Look at that. + +60 +00:03:51,732 --> 00:03:53,600 +I only see disassembly. + +61 +00:03:54,768 --> 00:03:56,770 +Let's try to understand +what happened there, + +62 +00:03:56,803 --> 00:04:01,475 +and let's start by figuring out +why I couldn't see any source code. + +63 +00:04:01,508 --> 00:04:05,746 +What does LLDB need +in order to show source code? + +64 +00:04:05,779 --> 00:04:09,416 +When the compiler compiles a function, + +65 +00:04:09,449 --> 00:04:12,119 +it generates machine code. + +66 +00:04:15,556 --> 00:04:18,892 +And it leaves breadcrumbs for the debugger + +67 +00:04:18,926 --> 00:04:22,496 +so an address in the executable +can be mapped to a source file + +68 +00:04:22,529 --> 00:04:24,865 +and line number and vice versa. + +69 +00:04:24,898 --> 00:04:28,502 +These breadcrumbs are called debug info. + +70 +00:04:28,535 --> 00:04:33,340 +On Apple platforms +debug info is stored in object files. + +71 +00:04:33,373 --> 00:04:39,213 +For archiving and distribution, debug info +can be linked into .dSYM bundles. + +72 +00:04:39,246 --> 00:04:43,617 +The debug info linker is called dsymutil. + +73 +00:04:43,650 --> 00:04:46,453 +LLDB uses Spotlight +to locate .dSYM bundles, + +74 +00:04:46,486 --> 00:04:50,090 +so it's quite flexible +in terms of where on disk they are. + +75 +00:04:50,123 --> 00:04:55,762 +Now that we know how debug info works, +let's get back to the example. + +76 +00:04:55,796 --> 00:05:01,502 +First, let's verify that LLDB has actually +found the dSYM for the framework. + +77 +00:05:01,535 --> 00:05:04,938 +We can do this +with the image list command. + +78 +00:05:04,972 --> 00:05:09,276 +The UI framework +is called "TerminalInterface". + +79 +00:05:15,516 --> 00:05:19,453 +Yes, LLDB did find the dSYM +for the framework. + +80 +00:05:19,486 --> 00:05:22,489 +That means it has access +to the debug info. + +81 +00:05:22,523 --> 00:05:26,560 +We can use "image lookup" + +82 +00:05:26,593 --> 00:05:29,530 +to get more info +about the current address. + +83 +00:05:30,697 --> 00:05:34,001 +By the way, if you want to learn more +about the various options, + +84 +00:05:34,034 --> 00:05:36,670 +LLDB has an excellent built-in help. + +85 +00:05:37,738 --> 00:05:40,974 +Ah, I think I see +why there is no source code: + +86 +00:05:41,008 --> 00:05:45,212 +This source path here points to where +the sources were on the build server, + +87 +00:05:45,245 --> 00:05:48,148 +not to where they are on my local machine. + +88 +00:05:48,182 --> 00:05:49,850 +We can fix that. + +89 +00:05:49,883 --> 00:05:55,622 +LLDB has a built-in source map +that we can use to redirect these paths. + +90 +00:06:04,531 --> 00:06:06,633 +We could type in the command right now, + +91 +00:06:06,667 --> 00:06:09,670 +but I'd rather make this change +more permanent. + +92 +00:06:09,703 --> 00:06:14,308 +In the Scheme editor, which you can +bring up by going to Product, Scheme, + +93 +00:06:14,341 --> 00:06:18,011 +Edit scheme, + +94 +00:06:18,045 --> 00:06:21,348 +or by just option-clicking +onto the play button, + +95 +00:06:21,381 --> 00:06:25,118 +you can define +a per-project LLDB init file. + +96 +00:06:25,152 --> 00:06:27,187 +I already added one for this project. + +97 +00:06:38,699 --> 00:06:43,103 +Now that we set up LLDB, +let's run our project again. + +98 +00:06:52,813 --> 00:06:55,883 +And we have source code. + +99 +00:06:58,852 --> 00:07:04,825 +LLDB can remap source paths +using "settings set target.source-map". + +100 +00:07:04,858 --> 00:07:08,028 +You can put this command +into your project's .lldbinit file + +101 +00:07:08,061 --> 00:07:10,764 +to have this run automatically. + +102 +00:07:10,797 --> 00:07:15,469 +Alternatively, each .dSYM bundle contains +a XML .plist file + +103 +00:07:15,502 --> 00:07:18,906 +where you can put a path prefix +remapping dictionary. + +104 +00:07:18,939 --> 00:07:22,876 +If you have a download script that +fetches the latest builds from a server, + +105 +00:07:22,910 --> 00:07:25,479 +you could modify that script +to automatically inject + +106 +00:07:25,512 --> 00:07:29,816 +the appropriate remapping dictionary +into the downloaded .dSYM. + +107 +00:07:29,850 --> 00:07:33,754 +You can learn more about this process +on the LLDB website. + +108 +00:07:35,289 --> 00:07:38,258 +Source paths +are not language-specific at all, + +109 +00:07:38,292 --> 00:07:44,231 +so this method works for Swift, +C++, and Objective-C projects alike. + +110 +00:07:44,264 --> 00:07:46,667 +To learn more about +symbols on Apple platforms, + +111 +00:07:46,700 --> 00:07:52,339 +check out "Symbolication: +Beyond the basics" from WWDC21. + +112 +00:07:52,372 --> 00:07:55,642 +When source code is compiled +on a build server farm, + +113 +00:07:55,676 --> 00:08:00,881 +the remote paths to source files could be +different from machine to machine. + +114 +00:08:00,914 --> 00:08:04,751 +To avoid having to define +one remap prefix per machine, + +115 +00:08:04,785 --> 00:08:07,955 +we can instruct the compiler +to canonicalize source paths + +116 +00:08:07,988 --> 00:08:10,691 +before putting them into the debug info. + +117 +00:08:10,724 --> 00:08:14,795 +This is done using +the -debug-prefix-map option. + +118 +00:08:14,828 --> 00:08:18,465 +This way the machine-specific +path prefix can be replaced + +119 +00:08:18,498 --> 00:08:21,001 +by a unique, canonical placeholder name + +120 +00:08:21,034 --> 00:08:24,905 +that can then be remapped +to the local path in LLDB. + +121 +00:08:24,938 --> 00:08:26,807 +Before we went on the source tangent, + +122 +00:08:26,840 --> 00:08:30,143 +I was trying to print +the object description of "words". + +123 +00:08:33,981 --> 00:08:35,449 +That did not work. + +124 +00:08:35,482 --> 00:08:39,553 +In fact, even just evaluating +the expression "words" did not work. + +125 +00:08:45,559 --> 00:08:49,029 +At least we can see the variables +in the variables view. + +126 +00:08:51,932 --> 00:08:55,235 +The console equivalent +of the Xcode variable view + +127 +00:08:55,269 --> 00:08:58,572 +is the "frame variable" or "v" command. + +128 +00:09:05,712 --> 00:09:09,650 +If you want to learn more about +the nuances between these commands, + +129 +00:09:09,683 --> 00:09:14,288 +check out "LLDB: Beyond 'po'" +from WWDC19. + +130 +00:09:14,321 --> 00:09:18,258 +So what is po +and why is it still not working? + +131 +00:09:18,292 --> 00:09:21,962 +To understand what this means, +we need to learn more about LLDB. + +132 +00:09:21,995 --> 00:09:25,666 +As a reminder, LLDB is a debugger. + +133 +00:09:25,699 --> 00:09:27,901 +But LLDB is not just a debugger. + +134 +00:09:27,935 --> 00:09:30,170 +It is also a compiler! + +135 +00:09:30,204 --> 00:09:32,973 +In addition to the functionality +of a debugger, + +136 +00:09:33,006 --> 00:09:37,945 +LLDB also includes a fully functioning +copy of the Swift and Clang compilers. + +137 +00:09:37,978 --> 00:09:41,748 +These compilers power +LLDB's expression evaluator, + +138 +00:09:41,782 --> 00:09:45,652 +which you may know through +p and po command aliases. + +139 +00:09:45,686 --> 00:09:49,423 +With the expression evaluator +we can go beyond looking at variables, + +140 +00:09:49,456 --> 00:09:52,693 +we can perform computation, +call functions, + +141 +00:09:52,726 --> 00:09:55,596 +and even change the state of the program. + +142 +00:09:55,629 --> 00:10:00,567 +Check out "Advanced Debugging +with Xcode and LLDB" from WWDC18 + +143 +00:10:00,601 --> 00:10:04,671 +to get some ideas for what's possible +with those commands. + +144 +00:10:04,705 --> 00:10:09,042 +How does a debugger format +a local variable? + +145 +00:10:09,076 --> 00:10:12,346 +The debug info provided by the compiler +tells the debugger + +146 +00:10:12,379 --> 00:10:15,282 +where in memory a variable is stored. + +147 +00:10:15,315 --> 00:10:19,653 +But with that information alone, +LLDB would only be able to show us + +148 +00:10:19,686 --> 00:10:22,422 +a random assortment of raw bytes. + +149 +00:10:22,456 --> 00:10:26,460 +So how does LLDB turn that into +nicely formatted output? + +150 +00:10:26,493 --> 00:10:28,395 +The answer is types. + +151 +00:10:28,428 --> 00:10:32,032 +Type information allows LLDB +to understand the structure + +152 +00:10:32,065 --> 00:10:34,868 +and memory layout of a source variable. + +153 +00:10:34,902 --> 00:10:39,339 +With type information, LLDB knows +what fields an aggregate type has + +154 +00:10:39,373 --> 00:10:43,277 +and types allow LLDB to use +the appropriate data formatters + +155 +00:10:43,310 --> 00:10:45,279 +to pretty-print them. + +156 +00:10:45,312 --> 00:10:49,983 +Now let's look at +where type information comes from. + +157 +00:10:50,017 --> 00:10:54,621 +On the debugger side, where the frame +variable and v commands live, + +158 +00:10:54,655 --> 00:10:59,059 +LLDB gets type information +from Debug Info. + +159 +00:10:59,092 --> 00:11:04,298 +And LLDB also gets types +from Swift reflection metadata. + +160 +00:11:04,331 --> 00:11:08,869 +On the compiler side, where the expression +evaluator and po live, + +161 +00:11:08,902 --> 00:11:12,039 +LLDB gets type information from Modules. + +162 +00:11:12,072 --> 00:11:15,576 +This clean separation is new in Xcode 14 + +163 +00:11:15,609 --> 00:11:18,278 +and explains why the variable view +can be fully functional + +164 +00:11:18,312 --> 00:11:21,048 +even if the expression evaluator isn't. + +165 +00:11:21,081 --> 00:11:24,785 +Modules are how the compiler organizes +type declarations. + +166 +00:11:24,818 --> 00:11:28,055 +The Swift compiler knows +many ways of importing modules, + +167 +00:11:28,088 --> 00:11:31,491 +but before we dive into that, +I want to show you a handy new feature. + +168 +00:11:32,826 --> 00:11:37,097 +How do we start diagnosing an issue +that is happening on the compiler side? + +169 +00:11:38,031 --> 00:11:42,603 +This year LLDB has added +a new "swift-healthcheck" command. + +170 +00:11:42,636 --> 00:11:47,241 +It's your first stop for figuring out +if a module import failed. + +171 +00:11:47,274 --> 00:11:49,243 +Let me show you how this works. + +172 +00:11:49,276 --> 00:11:52,980 +By running swift-healthcheck +after a problem occurred, + +173 +00:11:53,013 --> 00:11:57,651 +we can get access to a log of the Swift +expression evaluator configuration. + +174 +00:12:05,292 --> 00:12:08,896 +At the end of the log +we see that LLDB had trouble importing + +175 +00:12:08,929 --> 00:12:11,698 +the "TerminalUI" Swift module. + +176 +00:12:11,732 --> 00:12:14,902 +Based on the name, I assume +that this is an implementation detail + +177 +00:12:14,935 --> 00:12:18,338 +of the TerminalInterface framework. + +178 +00:12:18,372 --> 00:12:23,944 +This missing module is a problem +because the type of self is generic + +179 +00:12:23,977 --> 00:12:28,315 +over the UI implementation and +without the module containing that type, + +180 +00:12:28,348 --> 00:12:33,587 +the expression evaluator cannot realize +the dynamic type of "self". + +181 +00:12:33,620 --> 00:12:35,956 +I'm sending a message +to the developers of the framework + +182 +00:12:35,989 --> 00:12:37,858 +and ask them to investigate. + +183 +00:12:37,891 --> 00:12:41,228 +In my experience, +they have always been very responsive. + +184 +00:12:41,261 --> 00:12:45,832 +Who knows, maybe we can even find +a solution before the end of this video. + +185 +00:12:45,866 --> 00:12:51,405 +In the meantime, let's take a look at +how LLDB's compiler finds Swift modules. + +186 +00:12:52,940 --> 00:12:55,943 +My app has its own Swift module. + +187 +00:12:55,976 --> 00:13:00,180 +It may import a system framework, +such as Foundation. + +188 +00:13:00,214 --> 00:13:06,353 +System frameworks are textual stable Swift +interface files that live in the SDK. + +189 +00:13:06,386 --> 00:13:09,823 +Any Swift module +might import a Clang module, + +190 +00:13:09,857 --> 00:13:13,527 +which is a fancy name for one or more +header files that are grouped together + +191 +00:13:13,560 --> 00:13:16,964 +with the help of a module map file. + +192 +00:13:16,997 --> 00:13:20,100 +Clang modules +can depend on other Clang modules. + +193 +00:13:21,902 --> 00:13:26,507 +My app might also import a Swift module +that belongs to a locally built framework. + +194 +00:13:26,540 --> 00:13:29,977 +It could also import textual +Swift interface files + +195 +00:13:30,010 --> 00:13:32,913 +that are not part of the SDK. + +196 +00:13:32,946 --> 00:13:39,553 +If you want to learn how, check out +"Binary Frameworks in Swift" from WWDC19. + +197 +00:13:39,586 --> 00:13:44,124 +My app might also link against +a static library that contains Swift code, + +198 +00:13:44,157 --> 00:13:47,327 +and then that comes +with a Swift module too. + +199 +00:13:47,361 --> 00:13:48,996 +Hmm, we're not done, though. + +200 +00:13:49,029 --> 00:13:51,532 +I should mention there are also +bridging headers, + +201 +00:13:51,565 --> 00:13:54,635 +which can also import Clang modules. + +202 +00:13:54,668 --> 00:13:58,005 +Finally, as a special feature +in LLDB only, + +203 +00:13:58,038 --> 00:14:02,476 +some module contents can be reconstructed +from debug info alone. + +204 +00:14:02,509 --> 00:14:04,077 +That's a lot of sources! + +205 +00:14:04,111 --> 00:14:05,879 +How does LLDB find them all? + +206 +00:14:07,514 --> 00:14:12,586 +It's the build system's job to package up +the modules so LLDB can find them. + +207 +00:14:12,619 --> 00:14:16,657 +Modules from system frameworks +stay in the SDK. + +208 +00:14:16,690 --> 00:14:22,563 +LLDB will find a matching SDK to read them +from as it's attaching to your program. + +209 +00:14:22,596 --> 00:14:25,265 +When debugging +straight from the object files, + +210 +00:14:25,299 --> 00:14:29,703 +LLDB will find all non-SDK modules +where they were at build time. + +211 +00:14:29,736 --> 00:14:33,707 +Dsymutil can package a debug info archive +called a .DSYM bundle + +212 +00:14:33,740 --> 00:14:38,612 +for every dynamic library, +framework or dylib, and executable. + +213 +00:14:40,013 --> 00:14:43,483 +Each .dSYM bundle can contain +binary Swift modules, + +214 +00:14:43,517 --> 00:14:45,686 +which may contain bridging headers, + +215 +00:14:45,719 --> 00:14:47,788 +textual Swift interface files, + +216 +00:14:47,821 --> 00:14:49,923 +and most importantly, debug info. + +217 +00:14:49,957 --> 00:14:51,892 +That covers everything. + +218 +00:14:51,925 --> 00:14:53,527 +Everything? + +219 +00:14:53,560 --> 00:14:57,497 +Everything except Swift modules +that belong to static archives. + +220 +00:14:59,032 --> 00:15:02,202 +In order for a Swift module +to be picked up by dsymutil, + +221 +00:15:02,236 --> 00:15:05,005 +it needs to be registered with the linker. + +222 +00:15:05,038 --> 00:15:07,207 +For dynamic libraries and executables, + +223 +00:15:07,241 --> 00:15:10,444 +the build system will do this +automatically for you. + +224 +00:15:10,477 --> 00:15:14,014 +But static archives are +not produced by the linker, + +225 +00:15:14,047 --> 00:15:18,385 +they are just collections of object files, +like a zip file. + +226 +00:15:18,418 --> 00:15:22,489 +That means that the responsibility +for registering any Swift modules + +227 +00:15:22,523 --> 00:15:25,559 +with the linker +falls onto every executable + +228 +00:15:25,592 --> 00:15:29,763 +or dynamic library +that links the static archive. + +229 +00:15:29,796 --> 00:15:34,168 +In many cases, Xcode's build system +will do this for you. + +230 +00:15:34,201 --> 00:15:37,037 +But if you are maintaining +your own custom build system, + +231 +00:15:37,070 --> 00:15:40,274 +or if you have defined custom build rules, + +232 +00:15:40,307 --> 00:15:42,309 +this is something to be aware of. + +233 +00:15:44,478 --> 00:15:46,580 +When using the Apple linker, + +234 +00:15:46,613 --> 00:15:52,352 +Swift modules need to be registered +with the -add-ast-path option. + +235 +00:15:52,386 --> 00:15:57,124 +Check your build log to verify +that this is the case. + +236 +00:15:57,157 --> 00:16:01,662 +You can also use dsymutil to dump +the symbol table of your executable + +237 +00:16:01,695 --> 00:16:05,132 +and grep for "swiftmodule" +to verify that it worked. + +238 +00:16:08,202 --> 00:16:10,404 +On other platforms like Linux, + +239 +00:16:10,437 --> 00:16:13,507 +the swift driver supports +a -modulewrap action + +240 +00:16:13,540 --> 00:16:16,677 +that converts binary Swift module files +into objects + +241 +00:16:16,710 --> 00:16:20,848 +that you can link into your binary +together with the rest of the debug info. + +242 +00:16:20,881 --> 00:16:23,617 +LLDB will find it there. + +243 +00:16:23,650 --> 00:16:27,054 +The developers of the framework +were incredibly responsive. + +244 +00:16:27,087 --> 00:16:31,692 +As we suspected, it turns out that +as a part of the framework's build system + +245 +00:16:31,725 --> 00:16:33,594 +a static archive is used. + +246 +00:16:33,627 --> 00:16:36,830 +And it was the Swift module +that belongs to that static archive + +247 +00:16:36,864 --> 00:16:38,866 +that was missing from the dSYM bundle. + +248 +00:16:38,899 --> 00:16:41,835 +I have now installed a fixed version +of the framework. + +249 +00:16:41,869 --> 00:16:45,205 +It has registered the missing +static module with the linker + +250 +00:16:45,239 --> 00:16:47,941 +and so dsymutil was able to collect it. + +251 +00:16:49,543 --> 00:16:52,012 +Now self can be resolved. + +252 +00:16:54,815 --> 00:16:58,118 +And we can print the object description +of "words". + +253 +00:17:01,755 --> 00:17:04,958 +Since we're using the console anyway, +I'm using the s alias + +254 +00:17:04,992 --> 00:17:07,728 +to step into the parseFrom function. + +255 +00:17:12,399 --> 00:17:14,401 +And now we can also easily find the bug, + +256 +00:17:14,434 --> 00:17:16,737 +which is just a copy-and-paste error here. + +257 +00:17:26,780 --> 00:17:33,787 +[typing sounds] + +258 +00:17:44,865 --> 00:17:49,036 +And with that, we not only solved +the puzzle of the missing Swift module, + +259 +00:17:49,069 --> 00:17:51,505 +but also the first puzzle of the game. + +260 +00:17:53,173 --> 00:17:57,511 +Before we wrap up, +I have one more detail to watch out for. + +261 +00:17:57,544 --> 00:18:00,981 +The Swift compiler will serialize +Clang header search paths + +262 +00:18:01,014 --> 00:18:04,852 +and other related options into +the binary .swiftmodule files. + +263 +00:18:04,885 --> 00:18:08,589 +This is great because it makes +importing their Clang module dependencies + +264 +00:18:08,622 --> 00:18:11,425 +just work during the build. + +265 +00:18:11,458 --> 00:18:16,330 +But when building on a different machine, +these local paths can be detrimental. + +266 +00:18:16,363 --> 00:18:20,567 +So before shipping a binary .swiftmodule +to another machine, + +267 +00:18:20,601 --> 00:18:22,035 +consider building with the + +268 +00:18:22,069 --> 00:18:25,706 +-no-serialize-debugging-options +Compiler flag. + +269 +00:18:25,739 --> 00:18:27,741 +In Xcode this is controlled via the + +270 +00:18:27,774 --> 00:18:30,644 +SWIFT_SERIALIZE_DEBUGGING_OPTIONS +setting. + +271 +00:18:32,045 --> 00:18:38,252 +You can reintroduce these search paths in +LLDB with one of the following settings. + +272 +00:18:38,285 --> 00:18:40,120 +Let's recap what we learned. + +273 +00:18:40,153 --> 00:18:43,190 +If you want to ship code +from one machine to another, + +274 +00:18:43,223 --> 00:18:47,361 +you should ask yourself what level of +debugging you expect to be doing. + +275 +00:18:47,394 --> 00:18:51,565 +For example, if you ship +a binary framework to another developer + +276 +00:18:51,598 --> 00:18:55,402 +and you don't expect them +to step into your code in the debugger, + +277 +00:18:55,435 --> 00:18:59,873 +it's best to just ship the Swift module +as a textual .swiftinterface file. + +278 +00:18:59,907 --> 00:19:03,877 +But if you are setting up a build server +or a continuous integration system, + +279 +00:19:03,911 --> 00:19:08,582 +where developers are expected to debug +the downloaded build artifacts, + +280 +00:19:08,615 --> 00:19:11,485 +you will want to make sure to build +a binary Swift module + +281 +00:19:11,518 --> 00:19:15,022 +and consider turning off +search path serialization. + +282 +00:19:15,055 --> 00:19:18,292 +You can also canonicalize the source paths +on the server + +283 +00:19:18,325 --> 00:19:21,895 +in the debug info +using the -debug-prefix-map option. + +284 +00:19:21,929 --> 00:19:24,331 +That's all I have for you. + +285 +00:19:24,364 --> 00:19:30,003 +Today we learned about LLDB's dual nature +as a debugger and a compiler. + +286 +00:19:30,037 --> 00:19:34,441 +The debugger needs debug info +and reflection metadata to function + +287 +00:19:34,474 --> 00:19:38,912 +and provides the Xcode variable view, +and the v command. + +288 +00:19:38,946 --> 00:19:43,517 +The compiler needs Modules +and is sensitive to search paths. + +289 +00:19:43,550 --> 00:19:47,654 +It's behind the expr, p, and po commands. + +290 +00:19:47,688 --> 00:19:50,057 +A good way to get at +the compiler diagnostics + +291 +00:19:50,090 --> 00:19:54,695 +is the new swift-healthcheck command +in LLDB. + +292 +00:19:54,728 --> 00:19:56,430 +Thank you for watching! ♪ ♪ + diff --git a/eng/2022 Session 110371 Use Xcode to develop a multiplatform app en.srt b/eng/2022 Session 110371 Use Xcode to develop a multiplatform app en.srt new file mode 100644 index 0000000..e0d81d8 --- /dev/null +++ b/eng/2022 Session 110371 Use Xcode to develop a multiplatform app en.srt @@ -0,0 +1,1303 @@ +1 +00:00:00,334 --> 00:00:06,340 +[upbeat music] + +2 +00:00:09,309 --> 00:00:12,513 +- Howdy. I'm Jake, +a designer on the Xcode team. + +3 +00:00:12,546 --> 00:00:16,917 +Multiplatform app development is being +taken to the next level in Xcode 14. + +4 +00:00:16,950 --> 00:00:20,220 +A single app target can now +support even more destinations + +5 +00:00:20,254 --> 00:00:22,222 +across multiple platforms, + +6 +00:00:22,256 --> 00:00:26,260 +all while maintaining a single common +codebase, sharing settings by default, + +7 +00:00:26,293 --> 00:00:29,730 +and allowing new ways to +conditionalize where needed. + +8 +00:00:29,763 --> 00:00:33,600 +First, we'll cover what +a multiplatform app target is, + +9 +00:00:33,634 --> 00:00:35,269 +and in which cases it works best. + +10 +00:00:36,436 --> 00:00:41,975 +Next, we'll modify our project to support +multiple destinations and platforms, + +11 +00:00:42,009 --> 00:00:45,946 +then we'll update our project to get it +building and running on the new platform. + +12 +00:00:47,314 --> 00:00:50,584 +We'll ensure our app looks great on each +supported platform... + +13 +00:00:52,119 --> 00:00:55,522 +and finally we'll integrate Xcode Cloud +with our project changes. + +14 +00:00:57,224 --> 00:00:59,793 +First, let's understand +which technique we want to use + +15 +00:00:59,826 --> 00:01:02,863 +to allow our app +to support multiple platforms. + +16 +00:01:02,896 --> 00:01:06,767 +Before Xcode 14, +if you wanted your app to support iOS + +17 +00:01:06,800 --> 00:01:10,270 +and macOS, +you would need two separate targets. + +18 +00:01:10,304 --> 00:01:13,574 +This is great if your project +needs significantly different codebases, + +19 +00:01:13,607 --> 00:01:17,244 +shares very few of its settings +between its different platforms, + +20 +00:01:17,277 --> 00:01:21,582 +or if each app target relies heavily +on a different underlying technologies. + +21 +00:01:22,783 --> 00:01:24,785 +If that's still the case +with your project today, + +22 +00:01:24,818 --> 00:01:29,089 +your best bet would be to continue to use +separate targets for each platform. + +23 +00:01:29,122 --> 00:01:33,293 +In Xcode 14, a single app target can +declare support for many destinations + +24 +00:01:33,327 --> 00:01:37,097 +like iPhone, iPad, Mac, and Apple TV. + +25 +00:01:37,130 --> 00:01:39,800 +This is great for an app +that uses a common codebase + +26 +00:01:39,833 --> 00:01:42,369 +and shares most of its settings +across all its destinations + +27 +00:01:42,402 --> 00:01:45,839 +while still allowing for +customization when needed. + +28 +00:01:45,873 --> 00:01:49,610 +Let's take a look at how +multiplatform apps work in Xcode 14. + +29 +00:01:49,643 --> 00:01:52,579 +If we're starting from scratch, +a great way to begin + +30 +00:01:52,613 --> 00:01:55,883 +would be to use the new, +improved multiplatform app template + +31 +00:01:55,916 --> 00:01:57,818 +when making a new project in Xcode. + +32 +00:01:58,886 --> 00:02:03,123 +The multiplatform app template uses +SwiftUI for its lifecycle and interface, + +33 +00:02:03,156 --> 00:02:07,160 +which starts us out with a target +configured by default to support iPhone, + +34 +00:02:07,194 --> 00:02:08,829 +iPad, and Mac. + +35 +00:02:08,862 --> 00:02:11,498 +This is a fantastic configuration +for new projects. + +36 +00:02:11,532 --> 00:02:13,200 +Because we're using SwiftUI, + +37 +00:02:13,233 --> 00:02:16,503 +we have access to the full +feature-set of each platform's SDK, + +38 +00:02:16,537 --> 00:02:18,972 +allowing the creation of amazing new apps + +39 +00:02:19,006 --> 00:02:22,075 +that take advantage of +what each platform has to offer. + +40 +00:02:22,109 --> 00:02:24,978 +Existing projects +can also declare support + +41 +00:02:25,012 --> 00:02:26,580 +for multiple destinations +in their app target + +42 +00:02:27,548 --> 00:02:32,586 +and use SwiftUI to get access to +the full power of each platform's SDK. + +43 +00:02:32,619 --> 00:02:37,591 +Let's take a look at how to add a +Mac destination to an existing iOS app. + +44 +00:02:37,624 --> 00:02:42,196 +I've been building a Food Truck app, +and it works great on iPhone and iPad. + +45 +00:02:42,229 --> 00:02:44,464 +I'm pretty happy with this iOS app, + +46 +00:02:44,498 --> 00:02:48,869 +and now I want to bring it to the Mac +and embrace the platform and its features. + +47 +00:02:48,902 --> 00:02:52,206 +Let's take a look at what our +project looks like in Xcode. + +48 +00:02:54,641 --> 00:02:56,476 +If we take a look at my app target, + +49 +00:02:56,510 --> 00:02:59,346 +we can see a list of all +the destinations my app supports. + +50 +00:03:00,781 --> 00:03:05,018 +You can see I have a Mac destination +already--Designed for iPad. + +51 +00:03:05,052 --> 00:03:09,957 +This allows Mac computers with Apple +silicon to run my unmodified iOS app. + +52 +00:03:09,990 --> 00:03:12,826 +This is a great way to get +started supporting the Mac, + +53 +00:03:12,860 --> 00:03:15,829 +but I want to take my Mac support +to the next level. + +54 +00:03:15,863 --> 00:03:19,399 +Let's add a "Designed for Mac" experience, +so to speak. + +55 +00:03:20,767 --> 00:03:23,337 +We can easily edit our list of +supported destinations + +56 +00:03:23,370 --> 00:03:26,473 +and add a Mac destination to our app. + +57 +00:03:26,507 --> 00:03:28,976 +There are a couple options +for Mac destinations: + +58 +00:03:29,009 --> 00:03:32,479 +Mac, Mac Catalyst, and Designed for iPad, + +59 +00:03:32,513 --> 00:03:35,282 +the last of which is grayed out, +because my app already supports it. + +60 +00:03:36,750 --> 00:03:39,419 +Choosing between Mac and +Mac Catalyst mainly comes down to + +61 +00:03:39,453 --> 00:03:41,655 +which technology +we're most interested in using. + +62 +00:03:41,688 --> 00:03:46,260 +If our app made heavy use of UIKit +or Storyboards at the core of our app, + +63 +00:03:46,293 --> 00:03:49,897 +Mac Catalyst would be a great way +to convert our existing iPad app + +64 +00:03:49,930 --> 00:03:52,366 +into a compatible Mac app. + +65 +00:03:52,399 --> 00:03:56,036 +However, our app uses SwiftUI, +which makes the "Mac option" + +66 +00:03:56,069 --> 00:03:59,506 +the best choice to craft our, +well, Mac app. + +67 +00:03:59,540 --> 00:04:03,210 +We'll get the amazing Mac look and feel +right out of the box, + +68 +00:04:03,243 --> 00:04:07,347 +with the full power of +the macOS SDK without any limits. + +69 +00:04:07,381 --> 00:04:10,851 +Meaning, we'll have the freedom +to use UIKit in our iOS app, + +70 +00:04:10,884 --> 00:04:14,188 +and AppKit in our macOS app, +if we want that flexibility. + +71 +00:04:14,221 --> 00:04:17,324 +With all that in mind, let's choose Mac, + +72 +00:04:17,357 --> 00:04:19,960 +the best option for working with SwiftUI. + +73 +00:04:19,993 --> 00:04:22,362 +Once I make my choice, +Xcode will alert me + +74 +00:04:22,396 --> 00:04:26,200 +to some changes necessary +to prepare my project for Mac support. + +75 +00:04:26,233 --> 00:04:28,502 +In this case, +Xcode will update my target + +76 +00:04:28,535 --> 00:04:32,372 +to only include dependencies and +frameworks that are supported on Mac. + +77 +00:04:32,406 --> 00:04:35,542 +It's important to note Xcode +won't make changes to my code, + +78 +00:04:35,576 --> 00:04:38,478 +so if I'm calling API +that isn't available on Mac, + +79 +00:04:38,512 --> 00:04:40,681 +I'll need to resolve those issues myself. + +80 +00:04:40,714 --> 00:04:45,552 +Once I pick my Mac option, it's added +to my list of supported destinations. + +81 +00:04:45,586 --> 00:04:48,455 +It's totally valid to have more +than one Mac destination + +82 +00:04:48,488 --> 00:04:50,657 +when I'm developing in Xcode. + +83 +00:04:50,691 --> 00:04:53,827 +This is especially useful if I'm +transitioning from "Mac Catalyst" + +84 +00:04:53,861 --> 00:04:56,496 +or "Designed for iPad" to a full Mac app. + +85 +00:04:57,731 --> 00:05:02,669 +This means I can continue testing out +each of my Mac products within Xcode. + +86 +00:05:02,703 --> 00:05:06,974 +And I'm not necessarily restricted to +a single choice when developing my app. + +87 +00:05:07,007 --> 00:05:10,911 +However, if I were to publish +my native Mac app to the App Store, + +88 +00:05:10,944 --> 00:05:15,582 +my Designed for iPad app wouldn't +be available anymore to my customers, + +89 +00:05:15,616 --> 00:05:19,152 +so Xcode offers a quick way +to remove this destination. + +90 +00:05:19,186 --> 00:05:21,388 +But I'll consider removing +this destination + +91 +00:05:21,421 --> 00:05:24,024 +once I'm happy +with my native Mac experience. + +92 +00:05:24,057 --> 00:05:25,926 +Whether I'm starting from scratch + +93 +00:05:25,959 --> 00:05:28,529 +or adding a new destination +to an existing app, + +94 +00:05:28,562 --> 00:05:31,965 +using a single target in Xcode +lets me share code + +95 +00:05:31,999 --> 00:05:33,800 +and build settings by default. + +96 +00:05:33,834 --> 00:05:37,304 +There may be cases where I want +to customize an individual setting, + +97 +00:05:37,337 --> 00:05:40,941 +like my app's display name +or a minimum deployment version. + +98 +00:05:40,974 --> 00:05:45,412 +Let's take a look at how to do that in +the improved target editor in Xcode 14. + +99 +00:05:45,445 --> 00:05:50,450 +Many app target settings now include +a way to conditionalize its value. + +100 +00:05:50,484 --> 00:05:53,020 +On supported settings, +I can reveal an editor + +101 +00:05:53,053 --> 00:05:57,357 +that lets me set the default value for +each build configuration in my project. + +102 +00:05:57,391 --> 00:06:00,027 +I have a custom Beta configuration +that I added, + +103 +00:06:00,060 --> 00:06:02,763 +as well as the standard Debug +and Release configurations + +104 +00:06:02,796 --> 00:06:04,932 +that come with new Xcode projects. + +105 +00:06:04,965 --> 00:06:06,934 +I want to give my app +a different display name + +106 +00:06:06,967 --> 00:06:11,538 +when built with a beta configuration, +so I can just edit the name right here. + +107 +00:06:11,572 --> 00:06:14,208 +As I type, we'll see our app's +display name in Xcode + +108 +00:06:14,241 --> 00:06:16,710 +has been replaced with a readout +of all possible values + +109 +00:06:16,743 --> 00:06:19,246 +the display name can now have. + +110 +00:06:19,279 --> 00:06:21,381 +If needed, I could also add a condition, + +111 +00:06:21,415 --> 00:06:25,252 +letting me specify a value based on +which SDK is being used. + +112 +00:06:25,285 --> 00:06:27,754 +This allows me to say +set a specific name + +113 +00:06:27,788 --> 00:06:30,624 +for the beta configuration +when building for Mac. + +114 +00:06:32,059 --> 00:06:35,562 +Okay, I think we're done with the edits +we want to make in the General tab. + +115 +00:06:35,596 --> 00:06:38,298 +Let's take a look at the +Signing and Capabilities tab + +116 +00:06:38,332 --> 00:06:40,701 +for any other changes we need to make. + +117 +00:06:42,035 --> 00:06:44,605 +The good news is, +with Automatic Signing turned on, + +118 +00:06:44,638 --> 00:06:47,074 +there are no extra steps to take. + +119 +00:06:47,107 --> 00:06:50,344 +When I added my Mac destination, +the necessary Signing Certificate + +120 +00:06:50,377 --> 00:06:53,780 +and Provisioning Profile for the Mac +was generated on my behalf. + +121 +00:06:53,814 --> 00:06:59,353 +Both my iOS and macOS app products +use the same bundle identifier by default, + +122 +00:06:59,386 --> 00:07:02,990 +which is awesome, because that means +when I publish them to the App Store, + +123 +00:07:03,023 --> 00:07:05,792 +they will be made available +for Universal Purchase. + +124 +00:07:05,826 --> 00:07:10,197 +So folks who buy my iOS app +will also get my Mac app automatically. + +125 +00:07:10,230 --> 00:07:14,601 +My app also makes use of +capabilities like push notifications. + +126 +00:07:14,635 --> 00:07:17,638 +Any capabilities +I've been using for my iOS app + +127 +00:07:17,671 --> 00:07:21,441 +that are applicable to my macOS app +get applied with no extra work from me. + +128 +00:07:21,475 --> 00:07:25,078 +They even get combined +into a single entitlements file. + +129 +00:07:25,112 --> 00:07:28,448 +Now that we've added support +for multiple destinations to our app, + +130 +00:07:28,482 --> 00:07:31,185 +our next goal is to get it to build. + +131 +00:07:31,218 --> 00:07:34,621 +It's normal to run into issues +building an app for a new destination, + +132 +00:07:34,655 --> 00:07:38,692 +especially if a new SDK is involved, +like our new Mac support. + +133 +00:07:38,725 --> 00:07:41,195 +So let's take a look +at some of these common issues. + +134 +00:07:41,228 --> 00:07:44,264 +Some frameworks +are not available to all platforms. + +135 +00:07:44,298 --> 00:07:46,800 +We'll need to make sure +we're not importing or linking + +136 +00:07:46,834 --> 00:07:48,969 +any unavailable frameworks. + +137 +00:07:49,002 --> 00:07:53,740 +Remember that Xcode won't change our code +when adding support for a new destination, + +138 +00:07:53,774 --> 00:07:57,277 +so we'll need to conditionalize +our code based on SDK, + +139 +00:07:57,311 --> 00:08:00,380 +similar to how we conditionalized +our app's settings. + +140 +00:08:00,414 --> 00:08:02,616 +This is also true for API. + +141 +00:08:02,649 --> 00:08:06,954 +Some features are marked as unavailable +based on which SDK we're building with. + +142 +00:08:06,987 --> 00:08:10,290 +Swift offers a way to conditionalize +portions of our code + +143 +00:08:10,324 --> 00:08:14,528 +to only include features available +for the SDKs we're building with. + +144 +00:08:14,561 --> 00:08:18,565 +Xcode also lets us specify +if an individual file should be compiled + +145 +00:08:18,599 --> 00:08:20,834 +when building for some SDKs. + +146 +00:08:20,868 --> 00:08:24,838 +If I build my project right now... + +147 +00:08:24,872 --> 00:08:26,740 +I don't see any issues. + +148 +00:08:26,773 --> 00:08:29,510 +That's because I still have +a destination selected in my toolbar + +149 +00:08:29,543 --> 00:08:32,312 +that uses the iOS SDK. + +150 +00:08:32,346 --> 00:08:37,317 +I'll need to pick "My Mac" from the list +to build against the macOS SDK. + +151 +00:08:40,654 --> 00:08:42,556 +Building now reveals some new issues, + +152 +00:08:42,589 --> 00:08:45,859 +and as we expected, +they're mainly related to availability. + +153 +00:08:45,893 --> 00:08:51,765 +In one of my files, I'm importing ARKit, +which is not available on Mac. + +154 +00:08:51,798 --> 00:08:56,904 +I could wrap this import statement +in #if canImport to conditionalize it out. + +155 +00:08:58,405 --> 00:09:01,775 +This is useful if I don't want to manage +a list of known platforms + +156 +00:09:01,808 --> 00:09:03,443 +a framework is available for + +157 +00:09:03,477 --> 00:09:06,613 +and simply say if it's not available, +don't include it. + +158 +00:09:06,647 --> 00:09:10,184 +However, I'm still using ARKit +throughout this file, + +159 +00:09:10,217 --> 00:09:11,718 +so sometimes it makes more sense + +160 +00:09:11,752 --> 00:09:15,222 +to conditionalize out +an entire file for an SDK. + +161 +00:09:15,255 --> 00:09:17,157 +If we navigate back to our target + +162 +00:09:17,191 --> 00:09:21,695 +and go to the Build Phases tab, +I can search for my file... + +163 +00:09:24,331 --> 00:09:27,167 +And specify +it should only be compiled for iOS. + +164 +00:09:35,642 --> 00:09:37,744 +After building, +once I've made those changes, + +165 +00:09:37,778 --> 00:09:39,680 +Xcode reports a new issue-- + +166 +00:09:39,713 --> 00:09:42,416 +a framework that is available on Mac, +SwiftUI, + +167 +00:09:42,449 --> 00:09:45,919 +has a feature that's +been marked as unavailable. + +168 +00:09:45,953 --> 00:09:49,223 +Specifically, +I'm using EditMode on iOS to allow users + +169 +00:09:49,256 --> 00:09:51,792 +to make edits and select content +in Tables and Lists, + +170 +00:09:51,825 --> 00:09:54,928 +but on macOS EditMode doesn't exist! + +171 +00:09:54,962 --> 00:09:58,565 +Users can already freely select +and edit rows of content on Mac, + +172 +00:09:58,599 --> 00:10:01,902 +so let's make sure this code +is only runs on iOS. + +173 +00:10:01,935 --> 00:10:04,037 +I can condition out +my environment property + +174 +00:10:04,071 --> 00:10:06,273 +and any place I was using EditMode below. + +175 +00:10:07,574 --> 00:10:10,444 +Now, I need to make sure any places +I was using this property + +176 +00:10:10,477 --> 00:10:14,882 +are also conditioned out, +like this onChange modifier. + +177 +00:10:14,915 --> 00:10:18,018 +I can wrap the entire modifier +in an "if os" condition. + +178 +00:10:19,419 --> 00:10:23,891 +And finally, I'm using an EditButton view +in the toolbar, which is also iOS-only. + +179 +00:10:28,762 --> 00:10:30,597 +Okay, let's try running our app. + +180 +00:10:33,767 --> 00:10:37,237 +Ah! It lives! +Our app now builds and runs on Mac! + +181 +00:10:37,271 --> 00:10:40,474 +Just because our app now builds +and runs on our new platform + +182 +00:10:40,507 --> 00:10:42,709 +doesn't mean our job is done. + +183 +00:10:42,743 --> 00:10:45,779 +There will be cases where you want to +refine your app experience + +184 +00:10:45,812 --> 00:10:48,482 +for what users on +your new platform will expect. + +185 +00:10:49,416 --> 00:10:53,120 +Also, trimming out our iOS-only +features isn't the end of our journey. + +186 +00:10:53,153 --> 00:10:56,523 +We now have all the features +of the macOS SDK to play with. + +187 +00:10:56,557 --> 00:10:59,126 +Now that I see my app running on Mac, + +188 +00:10:59,159 --> 00:11:00,928 +I'm noticing a quirk about my app + +189 +00:11:00,961 --> 00:11:03,230 +that doesn't feel natural +in its new context. + +190 +00:11:03,263 --> 00:11:07,434 +These donuts in this grid view +seem much too large! + +191 +00:11:07,467 --> 00:11:10,003 +That's because our grid items +were designed for touch. + +192 +00:11:10,037 --> 00:11:15,042 +Situations like this arise when you +declare a point size for a UI element + +193 +00:11:15,075 --> 00:11:19,379 +or otherwise customize a control +with only a single platform in mind. + +194 +00:11:19,413 --> 00:11:23,383 +On the Mac, we don't need to +make our buttons or thumbnails so large, + +195 +00:11:23,417 --> 00:11:27,120 +since we have a much more +precise pointing device. + +196 +00:11:27,154 --> 00:11:30,757 +This is a great case to conditionalize +a constant in our project + +197 +00:11:30,791 --> 00:11:33,694 +to vary based on which SDK +we're building for. + +198 +00:11:33,727 --> 00:11:36,930 +When we bring our app to other +platforms it's important to reconsider + +199 +00:11:36,964 --> 00:11:40,968 +many of these choices with +our new platform's expectations. + +200 +00:11:41,001 --> 00:11:43,136 +Let's take a look at specifying +a different value + +201 +00:11:43,170 --> 00:11:45,339 +based on which SDK we're building for. + +202 +00:11:45,372 --> 00:11:49,510 +One technique I often use is +making a constant a computed property, + +203 +00:11:49,543 --> 00:11:52,779 +and using "#if os" to +conditionalize what is returned. + +204 +00:11:52,813 --> 00:11:55,115 +Let's convert this to a computed property + +205 +00:11:55,148 --> 00:11:58,285 +and return what previously +was a constant... + +206 +00:11:58,318 --> 00:12:00,287 +but only return that value on iOS. + +207 +00:12:11,932 --> 00:12:14,601 +Ah, 80 feels much more naturally sized. + +208 +00:12:21,742 --> 00:12:26,547 +Now, as for making use of the macOS SDK, +there's a cool new feature in SwiftUI + +209 +00:12:26,580 --> 00:12:29,850 +that allows us to add +our own UI element to the Menu Bar. + +210 +00:12:29,883 --> 00:12:32,786 +I have a summary view for my app +that I'd love to let my users + +211 +00:12:32,819 --> 00:12:34,922 +have quick and easy access to. + +212 +00:12:34,955 --> 00:12:36,657 +Let's go to my App declaration, + +213 +00:12:36,690 --> 00:12:41,028 +and here, I can add a new Scene +for my Menu Bar Extra. + +214 +00:12:41,061 --> 00:12:43,764 +Note, though, +because this is a macOS-only feature, + +215 +00:12:43,797 --> 00:12:46,767 +I do need to conditionalize it +for the macOS SDK. + +216 +00:12:48,135 --> 00:12:50,103 +Let's build and run and take a look. + +217 +00:12:54,107 --> 00:12:57,845 +Ah cool, my truck icon +now shows up in the menu bar. + +218 +00:12:57,878 --> 00:13:00,414 +Awesome, now my Mac users +can see a quick glance + +219 +00:13:00,447 --> 00:13:03,383 +at today's information +right from their menu bar. + +220 +00:13:03,417 --> 00:13:07,387 +When we use SwiftUI, we get access +to the full SDK of each platform + +221 +00:13:07,421 --> 00:13:09,656 +and can utilize its awesome features. + +222 +00:13:09,690 --> 00:13:12,526 +It's important to note, +when we bring our app to other platforms, + +223 +00:13:12,559 --> 00:13:15,262 +we'll often need to +reconsider many past choices + +224 +00:13:15,295 --> 00:13:18,065 +when working in the context +of our new platform. + +225 +00:13:18,098 --> 00:13:22,102 +SwiftUI bakes platform expectations +directly into the API. + +226 +00:13:22,135 --> 00:13:25,005 +Many interface elements +will gain an automatic appearance + +227 +00:13:25,038 --> 00:13:27,040 +that looks great on each platform. + +228 +00:13:27,074 --> 00:13:30,177 +Conversely, that means we can lose +that automatic styling + +229 +00:13:30,210 --> 00:13:34,014 +when we heavily customize +our controls and other pieces of our UI, + +230 +00:13:34,047 --> 00:13:38,352 +so we should always double-check +our UI looks great everywhere. + +231 +00:13:38,385 --> 00:13:40,754 +All that said, +as we construct our cool app, + +232 +00:13:40,787 --> 00:13:43,123 +we should ensure we're following +the best practices + +233 +00:13:43,156 --> 00:13:45,726 +laid out by the human interface +guidelines. + +234 +00:13:45,759 --> 00:13:48,428 +Now that we're happy with +our local changes to our app, + +235 +00:13:48,462 --> 00:13:52,165 +it's time to archive our app products +and upload them to App Store Connect, + +236 +00:13:52,199 --> 00:13:56,103 +which we can do from Xcode +or automate it with Xcode Cloud. + +237 +00:13:56,136 --> 00:13:58,138 +Once we're ready, +we can then share the app + +238 +00:13:58,172 --> 00:14:00,474 +with internal and external testers +on TestFlight + +239 +00:14:00,507 --> 00:14:02,409 +and release it to the App Store. + +240 +00:14:02,442 --> 00:14:05,779 +We'll need to archive our products +to upload them to App Store Connect. + +241 +00:14:05,812 --> 00:14:07,915 +Just because we have a single target + +242 +00:14:07,948 --> 00:14:10,317 +doesn't mean we only have +a single product. + +243 +00:14:10,350 --> 00:14:15,255 +We'll need to archive for each platform +and upload those individually. + +244 +00:14:15,289 --> 00:14:18,325 +If you're building and archiving locally, +you'll need to select a destination + +245 +00:14:18,358 --> 00:14:22,029 +that has the SDK +you want to create an archive for. + +246 +00:14:22,062 --> 00:14:25,465 +If I want to produce my macOS app, +I'll need to select "My Mac" + +247 +00:14:25,499 --> 00:14:27,334 +from the list of destinations, + +248 +00:14:27,367 --> 00:14:30,771 +otherwise I'd select an iOS device +to produce my iOS app. + +249 +00:14:32,472 --> 00:14:34,174 +Once I have a destination selected, + +250 +00:14:34,208 --> 00:14:37,511 +I can choose "Product Archive" +to create the archive. + +251 +00:14:39,246 --> 00:14:42,716 +Once my archives are complete, +I can use the Organizer window in Xcode + +252 +00:14:42,749 --> 00:14:44,718 +to upload them to App Store Connect. + +253 +00:14:45,853 --> 00:14:49,690 +If I'm using Xcode Cloud, +I can add actions to my workflow to build, + +254 +00:14:49,723 --> 00:14:51,892 +test, and archive my products. + +255 +00:14:51,925 --> 00:14:55,996 +In my list of actions in my workflow, +I can create new items to build, + +256 +00:14:56,029 --> 00:15:00,200 +test, analyze, +and archive each of my products. + +257 +00:15:00,234 --> 00:15:03,804 +In this case, +I have an iOS app and a macOS app. + +258 +00:15:03,837 --> 00:15:07,641 +I can take it one step further +and include a deployment preparation + +259 +00:15:07,674 --> 00:15:10,777 +to automate uploading my app +to App Store Connect, + +260 +00:15:10,811 --> 00:15:14,114 +and I can even send those builds to +my internal TestFlight team right away + +261 +00:15:14,147 --> 00:15:17,150 +and start getting feedback +on the changes hot off the presses. + +262 +00:15:17,184 --> 00:15:20,888 +To summarize, Xcode 14 +takes multiplatform app development + +263 +00:15:20,921 --> 00:15:24,091 +to the next level +with streamlined app targets + +264 +00:15:24,124 --> 00:15:28,262 +which can now support even more +destinations across multiple platforms. + +265 +00:15:28,295 --> 00:15:31,965 +With a single app target, +you can maintain a common codebase + +266 +00:15:31,999 --> 00:15:34,301 +and shared settings by default. + +267 +00:15:34,334 --> 00:15:38,939 +As demonstrated, we can conditionalize our +settings and code based on our needs, + +268 +00:15:38,972 --> 00:15:43,076 +letting us customize our app to +best match platform expectations. + +269 +00:15:43,110 --> 00:15:44,945 +The rest is up to you. + +270 +00:15:44,978 --> 00:15:47,781 +To learn more about new features +and improvements in Xcode this year, + +271 +00:15:47,814 --> 00:15:50,350 +check out "What's new in Xcode." + +272 +00:15:50,384 --> 00:15:52,986 +I can't wait to see what +incredible ideas you bring to life + +273 +00:15:53,020 --> 00:15:55,122 +with the power of Xcode and SwiftUI. + diff --git a/eng/2022 Session 110373 Bring your driver to iPad with DriverKit en.srt b/eng/2022 Session 110373 Bring your driver to iPad with DriverKit en.srt new file mode 100644 index 0000000..106e90e --- /dev/null +++ b/eng/2022 Session 110373 Bring your driver to iPad with DriverKit en.srt @@ -0,0 +1,1271 @@ +1 +00:00:00,334 --> 00:00:07,341 +♪ ♪ + +2 +00:00:09,843 --> 00:00:10,944 +Souvik Banerjee: Hi, and welcome + +3 +00:00:10,978 --> 00:00:14,081 +to "Bring your driver to iPad +with DriverKit." + +4 +00:00:14,114 --> 00:00:19,419 +I'm Souvik, and today I'll discuss several +exciting new developments in DriverKit. + +5 +00:00:19,453 --> 00:00:22,856 +We're going to discuss three topics today. + +6 +00:00:22,890 --> 00:00:27,294 +First, I'll give a brief overview +of DriverKit. + +7 +00:00:27,327 --> 00:00:31,999 +Then, I'll discuss some updates +to AudioDriverKit. + +8 +00:00:32,032 --> 00:00:37,004 +And I'll end with how to bring your +drivers to the iPad + +9 +00:00:37,037 --> 00:00:39,306 +Let's get started with an overview. + +10 +00:00:39,339 --> 00:00:42,809 +In 2019, we introduced DriverKit, + +11 +00:00:42,843 --> 00:00:45,812 +a replacement for IOKit device drivers. + +12 +00:00:45,846 --> 00:00:48,949 +DriverKit brought a new way +to extend the system + +13 +00:00:48,982 --> 00:00:53,921 +that is more reliable and secure, +running in userspace. + +14 +00:00:53,954 --> 00:00:58,959 +And it's easier to develop +since your process isn't in the kernel. + +15 +00:00:58,992 --> 00:01:03,797 +These driver extensions, +also known as dexts, are bundled in apps, + +16 +00:01:03,830 --> 00:01:08,969 +and you can easily distribute your apps +and drivers on the Mac App Store. + +17 +00:01:09,002 --> 00:01:12,105 +People can easily find your driver +with a search, + +18 +00:01:12,139 --> 00:01:14,441 +and if your driver is no longer needed, + +19 +00:01:14,474 --> 00:01:17,177 +you can just delete the app +to uninstall it. + +20 +00:01:17,211 --> 00:01:22,316 +Since introducing DriverKit, we have added +support for many new driver families. + +21 +00:01:23,283 --> 00:01:27,354 +We now support Networking, +Block Storage, Serial, + +22 +00:01:27,387 --> 00:01:31,425 +Audio, and SCSI controller +and peripheral drivers + +23 +00:01:31,458 --> 00:01:36,063 +in addition to transports +such as USB, PCI, and HID. + +24 +00:01:37,030 --> 00:01:41,134 +To learn more about the kinds of drivers +you can build with DriverKit, + +25 +00:01:41,168 --> 00:01:45,038 +please check out the "Create audio drivers +with DriverKit" session + +26 +00:01:45,072 --> 00:01:47,608 +from WWDC 2021 + +27 +00:01:47,641 --> 00:01:52,045 +and the "Modernize PCI and SCSI drivers +with DriverKit" session + +28 +00:01:52,079 --> 00:01:54,882 +from WWDC 2020. + +29 +00:01:54,915 --> 00:01:59,186 +Next, we added several new features +in AudioDriverKit recently + +30 +00:01:59,219 --> 00:02:01,088 +that I want to highlight. + +31 +00:02:01,121 --> 00:02:04,224 +One of these features +is real-time operations. + +32 +00:02:04,258 --> 00:02:08,095 +We're excited to introduce +a new feature in AudioDriverKit + +33 +00:02:08,128 --> 00:02:11,265 +allowing you to register +a real-time callback. + +34 +00:02:11,298 --> 00:02:16,303 +This callback gets invoked +every time an IO operation happens. + +35 +00:02:16,336 --> 00:02:20,240 +You can use this callback if you need +to modify your audio buffers + +36 +00:02:20,274 --> 00:02:23,443 +on a real-time thread, +such as for signal processing. + +37 +00:02:25,646 --> 00:02:29,283 +To register a real-time callback +in AudioDriverKit, + +38 +00:02:29,316 --> 00:02:32,219 +we declare an IOOperationHandler block + +39 +00:02:32,252 --> 00:02:36,156 +to set on the IOUserAudioDevice. + +40 +00:02:36,190 --> 00:02:39,660 +This block will be called +from a real-time context + +41 +00:02:39,693 --> 00:02:44,431 +when an IO operation occurs +on the IOUserAudioStream buffers + +42 +00:02:44,464 --> 00:02:45,666 +for the device. + +43 +00:02:47,167 --> 00:02:50,304 +Inside the block, +we check what the operation is, + +44 +00:02:50,337 --> 00:02:53,473 +and we can modify the data as necessary. + +45 +00:02:53,507 --> 00:02:59,613 +Finally, we call SetIOOperationHandler +to set the block on the audio device. + +46 +00:02:59,646 --> 00:03:02,816 +Now let's talk about entitlements. + +47 +00:03:02,850 --> 00:03:05,919 +When we introduced AudioDriverKit, +you had to use + +48 +00:03:05,953 --> 00:03:11,058 +the allow-any-userclient-access +entitlement on your driver. + +49 +00:03:11,091 --> 00:03:15,028 +In macOS 12.1, +we introduced a new entitlement + +50 +00:03:15,062 --> 00:03:17,764 +specifically for AudioDriverKit. + +51 +00:03:17,798 --> 00:03:22,870 +Please update your audio drivers +to use the new audio family entitlement + +52 +00:03:22,903 --> 00:03:27,174 +instead of the +allow-any-userclient-access entitlement. + +53 +00:03:27,207 --> 00:03:30,844 +You can keep the +allow-any-userclient-access entitlement + +54 +00:03:30,878 --> 00:03:35,148 +if you want any app to be able +to communicate with your driver. + +55 +00:03:35,182 --> 00:03:38,452 +This new entitlement +is public for development, + +56 +00:03:38,485 --> 00:03:43,290 +so you can get started using this today +without filing a request. + +57 +00:03:43,323 --> 00:03:49,530 +In fact, all DriverKit family entitlements +are now available to use for development. + +58 +00:03:49,563 --> 00:03:52,466 +To request +this entitlement for distribution, + +59 +00:03:52,499 --> 00:03:57,304 +please visit the System Extension page +on developer.apple.com. + +60 +00:03:57,337 --> 00:04:02,142 +Now, I'm really excited to tell you +about DriverKit on iPad. + +61 +00:04:02,176 --> 00:04:06,647 +Professionals are increasingly +using iPad to do their best work. + +62 +00:04:06,680 --> 00:04:11,552 +But many rely on external hardware +that they couldn't use on iPad. + +63 +00:04:11,585 --> 00:04:16,423 +So today, we're excited to announce +that DriverKit is coming to iPad. + +64 +00:04:17,457 --> 00:04:21,395 +DriverKit on macOS has made it possible +to extend the system + +65 +00:04:21,428 --> 00:04:23,497 +in a safe and secure way, + +66 +00:04:23,530 --> 00:04:26,900 +and we're bringing +that same technology to the iPad. + +67 +00:04:26,934 --> 00:04:31,738 +In fact, if you've already created +a driver with DriverKit on the Mac, + +68 +00:04:31,772 --> 00:04:35,576 +you can bring that exact same driver +to the iPad + +69 +00:04:35,609 --> 00:04:39,613 +without any changes to your driver. + +70 +00:04:39,646 --> 00:04:45,752 +USB, PCI, and Audio will be +supported in iPadOS 16. + +71 +00:04:45,786 --> 00:04:50,691 +This will enable Thunderbolt audio +interfaces on iPad for the first time, + +72 +00:04:50,724 --> 00:04:54,027 +and many more devices. + +73 +00:04:54,061 --> 00:04:57,030 +This is made possible +with the power of the M1 chip. + +74 +00:04:57,064 --> 00:05:00,934 +All iPads with M1 will support DriverKit. + +75 +00:05:00,968 --> 00:05:05,572 +DriverKit on iPadOS +is the same as on macOS. + +76 +00:05:05,606 --> 00:05:08,842 +This means that you can build +one DriverKit driver + +77 +00:05:08,876 --> 00:05:13,647 +and have it work on both platforms, +no source changes required. + +78 +00:05:13,680 --> 00:05:17,217 +In addition , +using the new multiplatform apps feature + +79 +00:05:17,251 --> 00:05:21,688 +in Xcode 14, you can easily create +a single app target + +80 +00:05:21,722 --> 00:05:25,726 +to deliver your driver +on both Mac and iPad. + +81 +00:05:25,759 --> 00:05:28,862 +For more information +about multiplatform apps, + +82 +00:05:28,896 --> 00:05:33,066 +please check out the "Use Xcode +to develop a multiplatform app" session. + +83 +00:05:34,368 --> 00:05:39,406 +Xcode also now supports +automatic signing of DriverKit drivers. + +84 +00:05:39,439 --> 00:05:42,676 +It knows how to handle DriverKit on iPadOS + +85 +00:05:42,709 --> 00:05:46,180 +and can provision for both Mac and iPad. + +86 +00:05:46,213 --> 00:05:51,485 +You no longer need to configure +manual signing for DriverKit drivers. + +87 +00:05:51,518 --> 00:05:55,455 +Your iPadOS app and driver +can be distributed on the App Store , + +88 +00:05:55,489 --> 00:05:57,591 +just like on macOS. + +89 +00:05:57,624 --> 00:06:01,461 +This means that you can take advantage +of features like in-app purchases + +90 +00:06:01,495 --> 00:06:05,432 +and have your driver be +easily discoverable by users. + +91 +00:06:05,465 --> 00:06:09,903 +Let's see just how easy it is +to take an existing macOS driver and app + +92 +00:06:09,937 --> 00:06:12,005 +and bring it to iPad. + +93 +00:06:12,039 --> 00:06:15,475 +Here, I have an app +called DriverKitSampleApp. + +94 +00:06:15,509 --> 00:06:19,079 +It has a SwiftUI view +with a label and a button + +95 +00:06:19,112 --> 00:06:22,182 +allowing the user to install the driver. + +96 +00:06:22,216 --> 00:06:25,118 +Our driver is called NullDriver. + +97 +00:06:25,152 --> 00:06:28,989 +It prints a message when the driver starts + +98 +00:06:29,022 --> 00:06:32,459 +and starts a timer that fires every second + +99 +00:06:32,492 --> 00:06:36,697 +and increments a counter +called timerCount. + +100 +00:06:36,730 --> 00:06:40,133 +To make this an iPad app, +all I need to do is select + +101 +00:06:40,167 --> 00:06:43,103 +the DriverKitSampleApp target in Xcode... + +102 +00:06:45,305 --> 00:06:48,542 +And add iPad to Supported Destinations. + +103 +00:06:51,845 --> 00:06:56,884 +Now I can change my run destination +to the iPad I have connected to my Mac. + +104 +00:06:59,152 --> 00:07:02,122 +Let's try running this on the iPad. + +105 +00:07:02,155 --> 00:07:04,324 +Here's our iPad app. + +106 +00:07:04,358 --> 00:07:09,029 +We have the label and the button +from the view we saw in Xcode. + +107 +00:07:09,062 --> 00:07:12,566 +Tapping the Install Dext button +takes us to Settings, + +108 +00:07:12,599 --> 00:07:15,169 +where we see this new Drivers link. + +109 +00:07:15,202 --> 00:07:20,507 +We tap that link, and we see a list +of all drivers bundled in this app. + +110 +00:07:20,541 --> 00:07:24,178 +We can then enable our Null Driver. + +111 +00:07:24,211 --> 00:07:27,881 +So you might have noticed +several things in the demo. + +112 +00:07:27,915 --> 00:07:31,685 +Our Null Driver is bundled +inside our iPadOS app, + +113 +00:07:31,718 --> 00:07:36,323 +and it gets automatically discovered +by the system after installation. + +114 +00:07:36,356 --> 00:07:40,627 +On macOS , you would need to use +the SystemExtension framework + +115 +00:07:40,661 --> 00:07:43,931 +to prompt the user to install the driver. + +116 +00:07:43,964 --> 00:07:47,367 +On iPadOS, there is no +SystemExtensions framework. + +117 +00:07:48,769 --> 00:07:50,838 +Inside Xcode, you can see + +118 +00:07:50,871 --> 00:07:54,908 +that our driver is embedded +within our app. + +119 +00:07:54,942 --> 00:07:58,779 +Since drivers are low-level software +and are privileged, + +120 +00:07:58,812 --> 00:08:02,616 +they need to be approved by the user +before they can run. + +121 +00:08:02,649 --> 00:08:07,387 +On macOS, users need to go +to the Security & Privacy preferences + +122 +00:08:07,421 --> 00:08:10,023 +to allow system extensions. + +123 +00:08:10,057 --> 00:08:14,695 +On iPadOS, the driver approvals +are in the Settings app. + +124 +00:08:14,728 --> 00:08:17,631 +There are two options +for driver approvals. + +125 +00:08:17,664 --> 00:08:20,300 +If there is at least one app +with a driver installed, + +126 +00:08:20,334 --> 00:08:22,936 +there will be a menu +inside General Settings + +127 +00:08:22,970 --> 00:08:25,772 +with a list of all available drivers. + +128 +00:08:25,806 --> 00:08:29,576 +Each driver can be toggled on or off. + +129 +00:08:29,610 --> 00:08:34,047 +If your app contains a Settings bundle, +there will be a Drivers link + +130 +00:08:34,081 --> 00:08:36,083 +inside your app's Settings. + +131 +00:08:36,116 --> 00:08:40,053 +Your app should prompt the user +to enable the driver in Settings. + +132 +00:08:40,087 --> 00:08:43,590 +Let's start again +with our macOS driver project + +133 +00:08:43,624 --> 00:08:45,526 +and see how we can have our app + +134 +00:08:45,559 --> 00:08:48,829 +prompt the user to enable +the driver in Settings. + +135 +00:08:48,862 --> 00:08:53,433 +We start by adding iPad +to our supported destinations. + +136 +00:08:55,602 --> 00:09:00,374 +Our SwiftUI view has a button +to install the driver, + +137 +00:09:00,407 --> 00:09:03,744 +and our view model has a state machine + +138 +00:09:03,777 --> 00:09:07,314 +that interacts +with the SystemExtensions framework. + +139 +00:09:07,347 --> 00:09:11,218 +Since this project is going to build +for both Mac and iPad, + +140 +00:09:11,251 --> 00:09:14,154 +we want to keep our Mac view +and view model + +141 +00:09:14,188 --> 00:09:17,191 +but create a new view +that will be used on iPad. + +142 +00:09:25,566 --> 00:09:29,269 +Then, we can go to Build Phases +and Compile sources + +143 +00:09:29,303 --> 00:09:32,105 +and change the platform filter +for each file + +144 +00:09:32,139 --> 00:09:35,642 +to conditionally compile for iOS or macOS. + +145 +00:09:49,223 --> 00:09:52,593 +Now, let's add +a Settings bundle to our app. + +146 +00:09:52,626 --> 00:09:55,429 +We're going to use the default +example Settings for now, + +147 +00:09:55,462 --> 00:09:59,466 +but we can change these later +to real Settings that the app can use. + +148 +00:10:03,537 --> 00:10:07,140 +Now let's check the iOS view +we just created. + +149 +00:10:07,174 --> 00:10:10,310 +We can copy our macOS view +to our iOS view + +150 +00:10:10,344 --> 00:10:12,246 +to use as a starting point. + +151 +00:10:13,780 --> 00:10:17,851 +Our iOS view doesn't use a view model, +so we can remove that. + +152 +00:10:20,420 --> 00:10:24,691 +We also need to change our button action +to open our Settings bundle. + +153 +00:10:24,725 --> 00:10:28,896 +This will take the user into Settings, +so that they can enable the driver. + +154 +00:10:40,107 --> 00:10:43,010 +Finally, we change the button text +to make it clear + +155 +00:10:43,043 --> 00:10:46,046 +that the user needs to enable the driver +in Settings. + +156 +00:10:52,519 --> 00:10:55,189 +Let's see this in action. + +157 +00:10:55,222 --> 00:10:57,357 +We have the view we designed, + +158 +00:10:57,391 --> 00:11:01,061 +and tapping the button takes us +to our Settings bundle. + +159 +00:11:01,094 --> 00:11:04,631 +Then we go into Drivers +and enable the Null Driver. + +160 +00:11:06,266 --> 00:11:10,070 +It's important to keep in mind +that drivers launch on demand. + +161 +00:11:10,103 --> 00:11:12,506 +Although we've enabled the driver +in Settings, + +162 +00:11:12,539 --> 00:11:14,241 +the driver only starts running + +163 +00:11:14,274 --> 00:11:17,644 +when the hardware device is +plugged in to the iPad. + +164 +00:11:17,678 --> 00:11:21,248 +After the driver starts running, +I can attach a debugger to it + +165 +00:11:21,281 --> 00:11:23,483 +using Xcode wireless debugging. + +166 +00:11:23,517 --> 00:11:27,054 +To do that, +I go to the Debug menu in Xcode, + +167 +00:11:27,087 --> 00:11:31,692 +attach to process, +then select the NullDriver process. + +168 +00:11:31,725 --> 00:11:36,396 +Once attached, I can set breakpoints +or pause execution. + +169 +00:11:36,430 --> 00:11:39,700 +Here, I've set a breakpoint in our timer. + +170 +00:11:39,733 --> 00:11:45,472 +I'm going to print timerCount to see +how many times our timer has been called. + +171 +00:11:46,540 --> 00:11:50,377 +When you're done debugging, +detach from the driver process + +172 +00:11:50,410 --> 00:11:53,046 +using the Debug menu in Xcode. + +173 +00:11:54,848 --> 00:11:57,184 +So now we have a driver. + +174 +00:11:57,217 --> 00:12:00,053 +But a driver isn't very useful by itself. + +175 +00:12:00,087 --> 00:12:03,123 +It needs to communicate +with the rest of the system. + +176 +00:12:03,156 --> 00:12:07,861 +Some DriverKit frameworks +like AudioDriverKit handle this for you. + +177 +00:12:07,895 --> 00:12:10,264 +But if you need to do something +more advanced, + +178 +00:12:10,297 --> 00:12:13,967 +such as creating a custom +control panel app for your hardware, + +179 +00:12:14,001 --> 00:12:17,871 +you need to have your app be able +to communicate with your driver. + +180 +00:12:17,905 --> 00:12:21,341 +This is what user clients allow you to do. + +181 +00:12:21,375 --> 00:12:23,877 +They allow you +to define your own interface, + +182 +00:12:23,911 --> 00:12:27,014 +allowing app and driver communication. + +183 +00:12:27,047 --> 00:12:30,951 +Apps use the IOKit.framework +to open user clients. + +184 +00:12:30,984 --> 00:12:33,020 +For an example of how this works, + +185 +00:12:33,053 --> 00:12:36,390 +please see the sample code +on developer.apple.com. + +186 +00:12:38,192 --> 00:12:41,562 +So we know that apps can +communicate with drivers. + +187 +00:12:41,595 --> 00:12:43,897 +But it's important +to keep security in mind. + +188 +00:12:43,931 --> 00:12:47,201 +Since drivers are privileged, +we don't want to allow every app + +189 +00:12:47,234 --> 00:12:49,203 +to communicate with drivers. + +190 +00:12:50,737 --> 00:12:55,809 +On macOS, the app needs the driverkit +userclient-access entitlement, + +191 +00:12:55,843 --> 00:13:00,113 +and the value is an array +of allowed driver bundle identifiers. + +192 +00:13:01,715 --> 00:13:07,321 +On iPadOS, we added a new entitlement +called Communicates With Drivers. + +193 +00:13:07,354 --> 00:13:11,225 +It replaces +the macOS user client entitlement. + +194 +00:13:11,258 --> 00:13:13,160 +This entitlement grants your app + +195 +00:13:13,193 --> 00:13:16,530 +the ability to open user clients +to your driver. + +196 +00:13:18,498 --> 00:13:21,368 +If you want to add +the Communicates With Drivers entitlement + +197 +00:13:21,401 --> 00:13:25,539 +manually to your app, +here's the XML entitlement string. + +198 +00:13:27,007 --> 00:13:29,810 +We can also add +this entitlement from Xcode. + +199 +00:13:29,843 --> 00:13:33,247 +In Xcode, +we go to Signing and Capabilities, + +200 +00:13:33,280 --> 00:13:35,916 +then add a new capability. + +201 +00:13:35,949 --> 00:13:38,752 +Then, we can search +for "communicates with drivers" + +202 +00:13:38,785 --> 00:13:41,288 +and add the capability to our app. + +203 +00:13:43,390 --> 00:13:47,728 +Another use case for user clients +is to allow apps from other developers + +204 +00:13:47,761 --> 00:13:50,330 +to interact with your driver. + +205 +00:13:50,364 --> 00:13:53,700 +So in this case, +suppose you have an app and driver + +206 +00:13:53,734 --> 00:13:56,270 +and you want to provide a service +to other apps, + +207 +00:13:56,303 --> 00:13:59,106 +including those from other developers. + +208 +00:13:59,139 --> 00:14:02,309 +DriverKit user clients also support this. + +209 +00:14:03,577 --> 00:14:05,979 +Each app that needs to communicate +with drivers + +210 +00:14:06,013 --> 00:14:09,216 +needs the communicates +with drivers entitlement. + +211 +00:14:09,249 --> 00:14:13,587 +The driver needs the Allow Third Party +User Clients entitlement. + +212 +00:14:13,620 --> 00:14:17,191 +This allows apps +built with a different team identifier + +213 +00:14:17,224 --> 00:14:20,260 +to open a user client to the driver. + +214 +00:14:20,294 --> 00:14:23,830 +Without this entitlement, +only apps from the same team + +215 +00:14:23,864 --> 00:14:25,966 +can communicate with the driver. + +216 +00:14:25,999 --> 00:14:29,636 +If you want to add the Allow Third Party +User Clients entitlement + +217 +00:14:29,670 --> 00:14:33,907 +manually to your driver, +here's the XML entitlement string. + +218 +00:14:34,675 --> 00:14:37,511 +Or we can add this capability from Xcode + +219 +00:14:37,544 --> 00:14:40,981 +by going into Signing and Capabilities +for our driver. + +220 +00:14:42,683 --> 00:14:46,720 +These new user client entitlements +are public for development, + +221 +00:14:46,753 --> 00:14:51,158 +which means that you can get started +using this today without any approval. + +222 +00:14:51,191 --> 00:14:53,660 +To request these entitlements +for distribution, + +223 +00:14:53,694 --> 00:14:56,496 +please see our developer website. + +224 +00:14:56,530 --> 00:15:01,935 +DriverKit drivers also have +important implications for app update. + +225 +00:15:01,969 --> 00:15:07,474 +Automatic app update ensures users +always get the latest version of your app. + +226 +00:15:07,508 --> 00:15:09,943 +However, for apps containing drivers, + +227 +00:15:09,977 --> 00:15:12,779 +the update process works +a little differently. + +228 +00:15:12,813 --> 00:15:18,151 +Let's suppose you distribute version 1 +of your app on the app store. + +229 +00:15:18,185 --> 00:15:23,524 +Then, you install that app +along with its bundled driver on your iPad + +230 +00:15:23,557 --> 00:15:26,460 +and enable the driver in Settings. + +231 +00:15:26,493 --> 00:15:29,696 +When you plug in the hardware device +for your driver, + +232 +00:15:29,730 --> 00:15:31,632 +the driver starts running, + +233 +00:15:31,665 --> 00:15:35,135 +and once the driver starts running, +your app can begin communicating + +234 +00:15:35,169 --> 00:15:38,539 +with your driver using user clients. + +235 +00:15:38,572 --> 00:15:41,341 +Now, suppose you find a bug in your app + +236 +00:15:41,375 --> 00:15:44,845 +and you submit version 2 to the App Store. + +237 +00:15:44,878 --> 00:15:46,880 +Because of automatic app update, + +238 +00:15:46,914 --> 00:15:52,119 +the version 2 app is downloaded +and installed on your iPad automatically. + +239 +00:15:52,152 --> 00:15:55,222 +The driver approval state is +maintained through updates, + +240 +00:15:55,255 --> 00:15:58,158 +so you don't need to approve +the driver again. + +241 +00:15:58,192 --> 00:16:01,828 +However, notice that the hardware +is still plugged in, + +242 +00:16:01,862 --> 00:16:05,065 +and our version 1 driver is still running. + +243 +00:16:05,098 --> 00:16:08,402 +Driver version 2 was downloaded +with the app update + +244 +00:16:08,435 --> 00:16:11,705 +but does not start running. + +245 +00:16:11,738 --> 00:16:14,208 +Since the old driver +still continues running, + +246 +00:16:14,241 --> 00:16:19,246 +your version 2 app may have to communicate +with the version 1 driver. + +247 +00:16:20,714 --> 00:16:24,785 +When the hardware device is unplugged, +the driver stops running, + +248 +00:16:24,818 --> 00:16:27,654 +so now driver version 1 is done + +249 +00:16:27,688 --> 00:16:30,290 +and we can update the driver to version 2. + +250 +00:16:31,758 --> 00:16:36,830 +Now, if you plug in the device again, +we start the version 2 driver, + +251 +00:16:36,864 --> 00:16:40,033 +and now your app is communicating +with the new driver. + +252 +00:16:41,568 --> 00:16:47,474 +To recap: Apps are updated anytime +with automatic app update. + +253 +00:16:47,508 --> 00:16:51,879 +Drivers are updated +after the device is unplugged. + +254 +00:16:51,912 --> 00:16:55,782 +And your app may communicate +with an old driver. + +255 +00:16:55,816 --> 00:17:01,321 +When your app and driver are ready, +you can submit them to the App Store. + +256 +00:17:01,355 --> 00:17:05,592 +Your drivers can only run +on devices that support DriverKit. + +257 +00:17:05,626 --> 00:17:08,562 +If you want to restrict +your app to those devices, + +258 +00:17:08,595 --> 00:17:11,798 +such as if your app +only installs a driver, + +259 +00:17:11,832 --> 00:17:16,370 +add DriverKit to your app's +UIRequiredDeviceCapabilities. + +260 +00:17:16,403 --> 00:17:19,106 +This will prevent users +from installing your app + +261 +00:17:19,139 --> 00:17:22,643 +on a device +that does not support DriverKit. + +262 +00:17:22,676 --> 00:17:25,679 +We also suggest submitting a video +to App Review + +263 +00:17:25,712 --> 00:17:29,850 +showing how your app and driver work +with your hardware device. + +264 +00:17:29,883 --> 00:17:32,619 +So that's DriverKit on iPad. + +265 +00:17:32,653 --> 00:17:38,125 +You can now bring USB, PCI, +and Audio drivers to iPad with M1 + +266 +00:17:38,158 --> 00:17:42,029 +and deliver those drivers +inside apps on the App Store. + +267 +00:17:42,062 --> 00:17:46,400 +And if you already have a driver, +it's easy to bring that to iPad. + +268 +00:17:46,433 --> 00:17:50,370 +We encourage developers +to try using DriverKit on iPad + +269 +00:17:50,404 --> 00:17:53,373 +and provide any feedback +using Feedback Assistant. + +270 +00:17:53,407 --> 00:17:55,342 +Thank you for watching. + diff --git a/eng/2022 Session 110374 Get the most out of Xcode Cloud en.srt b/eng/2022 Session 110374 Get the most out of Xcode Cloud en.srt new file mode 100644 index 0000000..8ef6007 --- /dev/null +++ b/eng/2022 Session 110374 Get the most out of Xcode Cloud en.srt @@ -0,0 +1,1060 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:11,144 +Hi, my name is Adam, + +3 +00:00:11,178 --> 00:00:13,614 +and I'm a Manager with the Developer +Experience Team. + +4 +00:00:13,647 --> 00:00:17,551 +And I'm Sasan, +an Engineer with the Xcode Cloud Team. + +5 +00:00:17,584 --> 00:00:19,186 +In this session, +we're gonna show you + +6 +00:00:19,219 --> 00:00:21,788 +how to get the most out of Xcode Cloud + +7 +00:00:21,822 --> 00:00:23,957 +by reviewing an existing workflow + +8 +00:00:23,991 --> 00:00:28,295 +and highlighting the brand-new +Xcode Cloud Usage dashboard. + +9 +00:00:28,328 --> 00:00:30,731 +Then, we'll take a look at how we can use +what we've learned + +10 +00:00:30,764 --> 00:00:34,034 +from viewing our existing projects usage, + +11 +00:00:34,067 --> 00:00:35,202 +to optimize it further, + +12 +00:00:35,235 --> 00:00:40,340 +and begin developing +a new Watch OS App version of our project + +13 +00:00:40,374 --> 00:00:44,645 +Before we get to that, +let's do a quick overview of Xcode Cloud. + +14 +00:00:44,678 --> 00:00:48,749 +At WWDC 2021, +we announced Xcode Cloud, + +15 +00:00:48,782 --> 00:00:52,019 +a continuous integration +and delivery service built into Xcode + +16 +00:00:52,052 --> 00:00:55,589 +and designed expressly +for Apple developers. + +17 +00:00:55,622 --> 00:00:59,326 +Xcode Cloud lets you adopt +continuous integration and delivery, + +18 +00:00:59,359 --> 00:01:02,396 +a standard software development practice +that helps you develop + +19 +00:01:02,429 --> 00:01:05,732 +and maintain your code, +and deliver apps to testers and users. + +20 +00:01:06,633 --> 00:01:10,537 +Xcode Cloud accelerates the development +and delivery of high-quality apps + +21 +00:01:10,571 --> 00:01:14,208 +by bringing together cloud-based tools +that help you build apps, + +22 +00:01:14,241 --> 00:01:16,376 +run automated tests in parallel, + +23 +00:01:16,410 --> 00:01:20,747 +deliver apps to testers, and view. +and manage user feedback, + +24 +00:01:20,781 --> 00:01:23,750 +all while protecting user privacy. + +25 +00:01:23,784 --> 00:01:27,287 +If you'd like to learn more about +setting up Xcode Cloud for the first time, + +26 +00:01:27,321 --> 00:01:31,358 +check out "Meet Xcode Cloud" +from WWDC 2021, + +27 +00:01:31,391 --> 00:01:35,562 +where Holly and Geoff go into more detail +in setting up your first workflow. + +28 +00:01:36,263 --> 00:01:38,999 +Now, let's take a look +at an existing workflow and build + +29 +00:01:39,032 --> 00:01:42,970 +for our Food Truck app in Xcode Cloud. + +30 +00:01:43,003 --> 00:01:45,939 +This is the Xcode Cloud dashboard +in App Store Connect, + +31 +00:01:45,973 --> 00:01:49,676 +it gives us an overview of a recent build +of our Food Truck project. + +32 +00:01:49,710 --> 00:01:52,713 +We recently made the decision +to add a companion watchOS app, + +33 +00:01:52,746 --> 00:01:56,450 +so a food truck operator can accept +incoming orders from their watch quickly, + +34 +00:01:56,483 --> 00:01:59,386 +without having to reach for their phone +every time a new order comes in. + +35 +00:01:59,419 --> 00:02:02,756 +Before we get started building +the new watchOS app in Xcode Cloud, + +36 +00:02:02,789 --> 00:02:06,994 +we'd like to make sure our current +workflows and project are fully optimized, + +37 +00:02:07,027 --> 00:02:11,031 +getting us the build and test results +we want, as quickly as possible. + +38 +00:02:11,064 --> 00:02:13,333 +We think there may be some ways +in which we can save some time + +39 +00:02:13,367 --> 00:02:14,568 +and resources here. + +40 +00:02:17,171 --> 00:02:18,906 +To better understand +where we might be able to start + +41 +00:02:18,939 --> 00:02:20,874 +making some of these optimizations, + +42 +00:02:20,908 --> 00:02:25,179 +let's take a closer look +at the build details overview. + +43 +00:02:25,212 --> 00:02:29,349 +First, we notice that we started the build +at 9:15 a.m. + +44 +00:02:29,383 --> 00:02:33,620 +and that it took 14 minutes to complete +and present us with results. + +45 +00:02:33,654 --> 00:02:36,356 +We also see there is a time +associated with usage, + +46 +00:02:36,390 --> 00:02:38,859 +which in this case is 32 minutes. + +47 +00:02:38,892 --> 00:02:41,228 +This is the total time it took +for all the actions + +48 +00:02:41,261 --> 00:02:43,797 +in our 14 minute build to complete. + +49 +00:02:43,830 --> 00:02:45,432 +Next to the usage, +you'll see an option + +50 +00:02:45,465 --> 00:02:48,235 +to view the distribution of actions +for this build. + +51 +00:02:49,937 --> 00:02:53,674 +Each action is broken out, +along with its respective usage, + +52 +00:02:53,707 --> 00:02:56,977 +with the 32-minute total +indicated at the bottom. + +53 +00:02:57,010 --> 00:02:59,813 +This Usage distribution gives us an idea +of some places + +54 +00:02:59,847 --> 00:03:02,149 +we could possibly make some +optimizations. + +55 +00:03:02,883 --> 00:03:05,853 +But before we get to that, +let's take a moment to look more closely + +56 +00:03:05,886 --> 00:03:08,188 +at how Xcode Cloud performs these actions, + +57 +00:03:08,222 --> 00:03:11,625 +and the difference between +a build duration and usage. + +58 +00:03:13,293 --> 00:03:15,662 +Each build is broken out into +a series of actions + +59 +00:03:15,696 --> 00:03:18,031 +depending on the setup of your workflow. + +60 +00:03:18,065 --> 00:03:22,569 +You'll see how Xcode Cloud breaks each +action out into multiple parallel actions + +61 +00:03:22,603 --> 00:03:26,507 +like Analyze, Archive, Build, and Test. + +62 +00:03:26,540 --> 00:03:28,909 +Because these actions are performed +in parallel, + +63 +00:03:28,942 --> 00:03:32,145 +the duration of the build +is equal to the longest running action; + +64 +00:03:32,179 --> 00:03:34,848 +in this case, the tests +we've configured in our workflow + +65 +00:03:34,882 --> 00:03:38,151 +that took 14 minutes to complete. + +66 +00:03:38,185 --> 00:03:42,256 +Now, when calculating usage, +each one of these actions, + +67 +00:03:42,289 --> 00:03:45,626 +when observed in sequence, +give us the total compute usage + +68 +00:03:45,659 --> 00:03:49,229 +of the build–in this case, 32 minutes. + +69 +00:03:49,263 --> 00:03:51,899 +And that's how Xcode Cloud calculates +the build duration, + +70 +00:03:51,932 --> 00:03:53,901 +and usage for a given build. + +71 +00:03:55,169 --> 00:03:58,005 +Now, let's take a look +at the Xcode Cloud Usage dashboard + +72 +00:03:58,038 --> 00:04:00,841 +in App Store Connect! + +73 +00:04:00,874 --> 00:04:04,878 +At the top is the usage overview, +since the beginning of our monthly cycle + +74 +00:04:04,912 --> 00:04:09,216 +for the Truck to Table Team, +including a total percentage used. + +75 +00:04:09,249 --> 00:04:12,452 +Additionally, we see +total usage expressed in minutes, + +76 +00:04:12,486 --> 00:04:16,423 +along with the remaining compute +available on our team's current cycle. + +77 +00:04:17,824 --> 00:04:21,595 +Beneath this, we see an area +dedicated to usage trends for our team, + +78 +00:04:21,628 --> 00:04:24,431 +broken out by builds +created and overall usage, + +79 +00:04:24,464 --> 00:04:29,136 +along with percentage increases +or decreases during the last 30 days. + +80 +00:04:29,169 --> 00:04:31,872 +If we'd like to see the usage +over a different time period, + +81 +00:04:31,905 --> 00:04:34,041 +we can do so by changing the time period + +82 +00:04:34,074 --> 00:04:37,311 +in the top, right-hand corner +of the trends section. + +83 +00:04:38,979 --> 00:04:42,349 +A little further down the page, +we see the total usage of each one + +84 +00:04:42,382 --> 00:04:44,952 +of our products currently +using Xcode Cloud, + +85 +00:04:44,985 --> 00:04:48,822 +again, during the time period +that we selected above. + +86 +00:04:48,856 --> 00:04:53,193 +All right, let's select Food Truck +so we can see its total usage breakdown. + +87 +00:04:55,128 --> 00:04:58,532 +Here we start by seeing the same trends +from our team view, + +88 +00:04:58,565 --> 00:05:02,402 +but now specific +to our Food Truck project. + +89 +00:05:02,436 --> 00:05:05,239 +A little further down the page, +we see the usage stats + +90 +00:05:05,272 --> 00:05:06,907 +for each one of our workflows. + +91 +00:05:06,940 --> 00:05:09,710 +At a quick glance, I can see +from the Release workflow + +92 +00:05:09,743 --> 00:05:14,081 +this is going to be an excellent place to +start making a couple of optimizations. + +93 +00:05:14,114 --> 00:05:15,983 +Now, I'm gonna hand it over to Sasan + +94 +00:05:16,016 --> 00:05:19,219 +who, after observing some of the build +details and compute usage, + +95 +00:05:19,253 --> 00:05:21,788 +is gonna show us a couple of ways +we can optimize our project. + +96 +00:05:21,822 --> 00:05:23,757 +Show them how it's done, Sasan. + +97 +00:05:23,790 --> 00:05:24,691 +Sasan: Thanks, Adam. + +98 +00:05:24,725 --> 00:05:27,961 +Let's use the Food Truck project +to cover some of the best practices + +99 +00:05:27,995 --> 00:05:29,730 +when using Xcode Cloud. + +100 +00:05:29,763 --> 00:05:33,300 +This should allow us to start iterating +quickly on our new watchOS app. + +101 +00:05:34,168 --> 00:05:38,071 +Workflows define when to start a build +through the use of Start Conditions. + +102 +00:05:38,105 --> 00:05:40,374 +It is important to configure +your Start Conditions + +103 +00:05:40,407 --> 00:05:44,545 +so that builds only start for changes +that are intended for the Workflow. + +104 +00:05:44,578 --> 00:05:47,548 +Let's see how we can apply +this practice to the Release workflow + +105 +00:05:47,581 --> 00:05:50,450 +of the Food Truck project. + +106 +00:05:50,484 --> 00:05:54,721 +But first, I recommend checking out +"Explore Xcode Cloud Workflows" + +107 +00:05:54,755 --> 00:05:57,524 +for more detailed information. + +108 +00:05:59,593 --> 00:06:03,964 +I have the same build +that Adam showed us earlier open in Xcode. + +109 +00:06:03,997 --> 00:06:07,701 +To start, let me open the Release workflow +in the editor window. + +110 +00:06:09,570 --> 00:06:11,772 +I right-click on the Workflow +in the Navigation Panel + +111 +00:06:11,805 --> 00:06:14,208 +and select Edit Workflow. + +112 +00:06:19,379 --> 00:06:22,015 +In the editor window, +I can see all of the configurable sections + +113 +00:06:22,049 --> 00:06:26,386 +that make up a Workflow, +including a section for Start Conditions. + +114 +00:06:26,420 --> 00:06:28,589 +We've discovered +that sometimes the scheduled build + +115 +00:06:28,622 --> 00:06:30,257 +doesn't contain any new changes. + +116 +00:06:30,290 --> 00:06:32,492 +To address that, +let's add a new start condition + +117 +00:06:32,526 --> 00:06:36,430 +for branch changes to replace +the existing scheduled start condition. + +118 +00:06:36,463 --> 00:06:40,200 +This will ensure that we don't +build duplicate commits. + +119 +00:06:40,234 --> 00:06:43,036 +I on the Plus button +and select Branch Changes. + +120 +00:06:48,509 --> 00:06:50,177 +Now to delete +the scheduled start condition, + +121 +00:06:50,210 --> 00:06:52,679 +I will select it +and click on the trash icon. + +122 +00:06:55,115 --> 00:06:56,817 +The Branch Changes Start Condition +will run + +123 +00:06:56,850 --> 00:06:59,419 +whenever a new commit +is pushed to a remote branch. + +124 +00:06:59,453 --> 00:07:02,923 +By default, the Source Branch +is configured to be Any Branch. + +125 +00:07:02,956 --> 00:07:05,692 +This means that any change +made to any branch of your repo + +126 +00:07:05,726 --> 00:07:08,695 +will cause this workflow +to start a build. + +127 +00:07:08,729 --> 00:07:11,365 +Since our release workflow +is configured to be thorough, + +128 +00:07:11,398 --> 00:07:15,135 +I want to restrict this to ensure we only +start builds for our release branches. + +129 +00:07:17,538 --> 00:07:20,240 +I click on Custom Branches, +and I can immediately see + +130 +00:07:20,274 --> 00:07:23,076 +that I need to specify the custom branch. + +131 +00:07:26,180 --> 00:07:29,183 +I click on the Plus button +and enter the branch name. + +132 +00:07:33,420 --> 00:07:37,858 +The editor will allow me to choose from +either the exact branch name or a prefix. + +133 +00:07:37,891 --> 00:07:40,794 +In this case, +we know we have multiple release branches + +134 +00:07:40,827 --> 00:07:43,730 +so I'll select +branches beginning with "release". + +135 +00:07:46,400 --> 00:07:49,903 +Next, I want to specify which files +and folders from the release branch + +136 +00:07:49,937 --> 00:07:52,239 +can start a build. + +137 +00:07:52,272 --> 00:07:55,976 +My goal is to not start builds +when the docs folder is modified. + +138 +00:07:56,009 --> 00:08:00,981 +This folder contains only our development +documentation so it's safe to skip. + +139 +00:08:01,014 --> 00:08:04,251 +For the Files and Folders option, +I select Custom Conditions. + +140 +00:08:05,819 --> 00:08:09,489 +I select the Start a Build dropdown +and select Don't start a build. + +141 +00:08:12,526 --> 00:08:15,262 +I click on the Plus button +to add a new condition. + +142 +00:08:18,365 --> 00:08:22,002 +I will specify which folder to exclude +by selecting Any Folder + +143 +00:08:22,035 --> 00:08:24,438 +and selecting Choose. + +144 +00:08:27,508 --> 00:08:29,610 +Finally, this will open a file picker. + +145 +00:08:29,643 --> 00:08:32,913 +Now I can select the docs folder +and click Open. + +146 +00:08:35,983 --> 00:08:38,952 +To finish up, +I'll click Save to persist my changes. + +147 +00:08:43,290 --> 00:08:45,993 +I have now configured +the Start Condition to be more selective + +148 +00:08:46,026 --> 00:08:48,328 +when starting +by restricting to only branches + +149 +00:08:48,362 --> 00:08:52,332 +with the release prefix +and to ignore changes to the docs folder. + +150 +00:08:53,133 --> 00:08:55,469 +Workflows also define +how to run your builds + +151 +00:08:55,502 --> 00:08:57,871 +through the use of pre-defined Actions. + +152 +00:08:57,905 --> 00:09:03,343 +Actions allow you to analyze, +archive, build, and test your changes. + +153 +00:09:03,377 --> 00:09:08,215 +One important component of the test action +is the selection of test destinations. + +154 +00:09:08,248 --> 00:09:10,484 +To make sure +that results are delivered fast, + +155 +00:09:10,517 --> 00:09:14,021 +each destination will run in parallel +once the test products are built. + +156 +00:09:15,422 --> 00:09:16,790 +I want to make sure that I'm selecting + +157 +00:09:16,823 --> 00:09:20,627 +a concise set of simulator destinations +for my tests. + +158 +00:09:20,661 --> 00:09:22,529 +In addition to speeding up my builds, + +159 +00:09:22,563 --> 00:09:24,464 +this also helps reduce noise +from the tests + +160 +00:09:24,498 --> 00:09:26,733 +that might fail on similar devices. + +161 +00:09:28,402 --> 00:09:31,805 +Xcode Cloud provides an alias +for recommended destinations. + +162 +00:09:31,839 --> 00:09:33,640 +These are curated lists of simulators + +163 +00:09:33,674 --> 00:09:36,743 +that represent a cross section +of screen sizes. + +164 +00:09:38,412 --> 00:09:42,182 +Let's visit the Release workflow again +to see how we can select a reasonable set + +165 +00:09:42,216 --> 00:09:45,853 +of simulator destinations +for the iOS test action. + +166 +00:09:46,553 --> 00:09:48,755 +After selecting the Test iOS action, + +167 +00:09:48,789 --> 00:09:52,659 +we can see there are a wide range +of selected test destinations. + +168 +00:09:52,693 --> 00:09:54,394 +To remove test destinations, + +169 +00:09:54,428 --> 00:09:56,964 +I'll select each one +and click the Minus button. + +170 +00:09:56,997 --> 00:09:59,466 +Then I'll click on the dropdown menu +of the last item + +171 +00:09:59,499 --> 00:10:02,569 +and select Recommended iPhones. + +172 +00:10:12,846 --> 00:10:16,350 +Again, I'll click Save to persist +my changes. + +173 +00:10:22,456 --> 00:10:25,826 +I now have a set of test destinations +that will help provide a clear signal + +174 +00:10:25,859 --> 00:10:27,928 +if we introduce a regression. + +175 +00:10:30,898 --> 00:10:33,967 +As we discussed earlier, +Xcode Cloud will run your workflow + +176 +00:10:34,001 --> 00:10:36,937 +when you push new changes +to your repository. + +177 +00:10:36,970 --> 00:10:39,473 +Sometimes, you may want to skip +building in CI + +178 +00:10:39,506 --> 00:10:42,075 +depending on the type of change +being committed. + +179 +00:10:42,109 --> 00:10:44,645 +We've added in +the ability to do just that. + +180 +00:10:44,678 --> 00:10:46,180 +Let's take a look in Xcode. + +181 +00:10:48,315 --> 00:10:52,252 +To skip your commit in Xcode Cloud, +simply append "ci skip" + +182 +00:10:52,286 --> 00:10:55,122 +to the end of the commit message. + +183 +00:10:55,155 --> 00:10:59,393 +Now, when you push to remote, Xcode Cloud +will know to ignore this event. + +184 +00:11:01,028 --> 00:11:05,465 +Make sure you're using the exact format +of the ci skip tag shown here. + +185 +00:11:07,734 --> 00:11:11,605 +For each action, custom scripts +are executed at multiple points. + +186 +00:11:11,638 --> 00:11:15,409 +Tidying up unused dependencies +and resiliently retrying API requests + +187 +00:11:15,442 --> 00:11:17,044 +that are known to be unreliable + +188 +00:11:17,077 --> 00:11:21,248 +will ensure builds complete fast +and consistently. + +189 +00:11:21,281 --> 00:11:25,085 +For more information on custom scripts +and other advanced customizations, + +190 +00:11:25,118 --> 00:11:28,989 +check out "Customize your advanced +Xcode Cloud workflows". + +191 +00:11:30,490 --> 00:11:33,927 +For testing, you should ensure +that flakey and unreliable tests + +192 +00:11:33,961 --> 00:11:35,562 +are corrected quickly. + +193 +00:11:35,596 --> 00:11:40,767 +When a flakey test fails, the instinct +is to immediately retry the build. + +194 +00:11:40,801 --> 00:11:42,903 +Depending on the reliability +of your test suite, + +195 +00:11:42,936 --> 00:11:45,772 +this can result in many retried builds. + +196 +00:11:45,806 --> 00:11:48,976 +Make sure to spend more time +writing reliable tests. + +197 +00:11:50,444 --> 00:11:52,679 +For more information on +how to do that effectively, + +198 +00:11:52,713 --> 00:11:57,618 +check out our other session "Author fast +and reliable tests for Xcode Cloud". + +199 +00:11:58,352 --> 00:12:00,654 +So far we've discussed some best practices + +200 +00:12:00,687 --> 00:12:02,990 +and applied them +to the Food Truck project. + +201 +00:12:03,023 --> 00:12:05,359 +Let's see what sort of impact +those changes had + +202 +00:12:05,392 --> 00:12:09,663 +by comparing the build from earlier +with one from our updated workflow. + +203 +00:12:10,564 --> 00:12:14,501 +This is a build that was started +after applying the best practices. + +204 +00:12:14,535 --> 00:12:16,870 +Compared to the previous build +that Adam showed us, + +205 +00:12:16,904 --> 00:12:22,042 +the duration decreased by a minute +but the usage reduced by four minutes. + +206 +00:12:22,075 --> 00:12:24,912 +It looks like we've made +some good improvements overall. + +207 +00:12:26,747 --> 00:12:30,317 +Let's return to the usage dashboard +to better understand the impact. + +208 +00:12:31,652 --> 00:12:35,355 +Since it might be difficult to see +the impact from a single build right away, + +209 +00:12:35,389 --> 00:12:38,692 +we've applied the best practices +to another one of our workflows, + +210 +00:12:38,725 --> 00:12:42,229 +the Integration Workflow. + +211 +00:12:42,262 --> 00:12:45,566 +We've been running builds for a while +with best practices applied. + +212 +00:12:45,599 --> 00:12:47,601 +We can tell that our changes +were effective + +213 +00:12:47,634 --> 00:12:49,670 +because usage is trending downward. + +214 +00:12:51,572 --> 00:12:54,608 +This means we're now capable of adding +more workflows + +215 +00:12:54,641 --> 00:12:57,978 +and starting more builds to start +development of the watchOS app. + +216 +00:13:00,514 --> 00:13:03,116 +Using the usage dashboard, +you can continue to apply + +217 +00:13:03,150 --> 00:13:06,653 +the same best practices +to your existing projects and workflows + +218 +00:13:06,687 --> 00:13:09,523 +to get the most out of Xcode Cloud. + +219 +00:13:09,556 --> 00:13:13,126 +For more information on how to manage +Xcode Cloud for large teams, + +220 +00:13:13,160 --> 00:13:16,797 +check out Deep Dive +into Xcode Cloud for teams. + +221 +00:13:16,830 --> 00:13:18,699 +We hope you enjoyed our session. + +222 +00:13:18,732 --> 00:13:20,667 +Adam: Thank you for watching. + +223 +00:13:23,804 --> 00:13:29,643 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 110375 Deep dive into Xcode Cloud for teams en.srt b/eng/2022 Session 110375 Deep dive into Xcode Cloud for teams en.srt new file mode 100644 index 0000000..1b7ccbc --- /dev/null +++ b/eng/2022 Session 110375 Deep dive into Xcode Cloud for teams en.srt @@ -0,0 +1,1327 @@ +1 +00:00:01,535 --> 00:00:07,541 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:12,779 +Hello, my name is John, and I'm +an engineer on the Xcode Cloud team. + +3 +00:00:12,813 --> 00:00:15,616 +And I'm Jo Lynn, +a designer on the Xcode Cloud team. + +4 +00:00:15,649 --> 00:00:18,352 +Today we're going to take a look +at some of the features of Xcode Cloud + +5 +00:00:18,385 --> 00:00:20,153 +that are super helpful when +you're using Xcode Cloud + +6 +00:00:20,187 --> 00:00:23,657 +as part of a team, +whether that's a team of 5 or 500. + +7 +00:00:23,690 --> 00:00:27,027 +And I'm going to share +some other features and best practices + +8 +00:00:27,060 --> 00:00:30,464 +that work well when you use +Xcode Cloud as part of a team. + +9 +00:00:30,497 --> 00:00:32,566 +In our session today, +we're going to go into detail + +10 +00:00:32,599 --> 00:00:35,669 +on some topics which are also covered +in some other sessions. + +11 +00:00:35,702 --> 00:00:39,439 +If you want to learn more about webhooks +and other advanced workflow topics, + +12 +00:00:39,473 --> 00:00:43,043 +check out the "Customize your advanced +Xcode Cloud workflows" session. + +13 +00:00:43,076 --> 00:00:45,879 +In this session, we're going to look +at how we can integrate Xcode Cloud + +14 +00:00:45,913 --> 00:00:49,850 +into your existing tools and technologies +that you use every day, + +15 +00:00:49,883 --> 00:00:52,019 +easily manage your code dependencies, + +16 +00:00:52,052 --> 00:00:55,589 +and showcase Xcode Cloud best practices +to help you set up workflows + +17 +00:00:55,622 --> 00:00:59,293 +to continuously build, test, +and distribute your apps effectively. + +18 +00:01:01,061 --> 00:01:02,529 +Let's get started. + +19 +00:01:02,563 --> 00:01:05,132 +When it comes to integrating Xcode Cloud +into your existing tools, + +20 +00:01:05,165 --> 00:01:06,767 +we have a couple of options. + +21 +00:01:06,800 --> 00:01:10,737 +Webhooks allow easy integration with tools +and services that support them. + +22 +00:01:10,771 --> 00:01:12,439 +You can connect a webhook +in App Store Connect + +23 +00:01:12,472 --> 00:01:16,210 +by telling Xcode Cloud what URL to send +the webhook to, + +24 +00:01:16,243 --> 00:01:18,812 +and you should start +to see those webhooks come in right away. + +25 +00:01:20,380 --> 00:01:23,617 +Our API also allows a great amount +of visibility into your builds, + +26 +00:01:23,650 --> 00:01:26,587 +allowing you to do things +like easily create build dashboards, + +27 +00:01:26,620 --> 00:01:30,257 +extract your build artifacts, +or, in our example today, + +28 +00:01:30,290 --> 00:01:33,026 +integrate build information +into your existing software. + +29 +00:01:34,728 --> 00:01:37,531 +We've been building out a bunch +of new features for our food truck app, + +30 +00:01:37,564 --> 00:01:40,067 +but we've also been finding new bugs, + +31 +00:01:40,100 --> 00:01:42,569 +and now that we have a team of people +working on the app together, + +32 +00:01:42,603 --> 00:01:45,539 +we've found it difficult +to understand who is working on what. + +33 +00:01:46,673 --> 00:01:49,510 +To solve this problem, +we've decided to use an issue tracker + +34 +00:01:49,543 --> 00:01:51,678 +to track all of the work +going on in our app. + +35 +00:01:52,412 --> 00:01:55,816 +If you're using an issue tracker, +you often have a view like this. + +36 +00:01:55,849 --> 00:01:59,553 +A common workflow with issue trackers +is to create a ticket, + +37 +00:01:59,586 --> 00:02:02,389 +have designers and developers +work on that ticket, + +38 +00:02:02,422 --> 00:02:06,126 +and finally, close it once the feature +or bug is complete. + +39 +00:02:06,159 --> 00:02:08,028 +Having the actual build information + +40 +00:02:08,061 --> 00:02:09,363 +shown in our issue tracker + +41 +00:02:09,396 --> 00:02:12,466 +would be a big help in connecting +all the dots together. + +42 +00:02:12,499 --> 00:02:14,635 +Let's take a look +at how we might do that now. + +43 +00:02:14,668 --> 00:02:18,705 +Our plan here is to create a very simple +Swift On Server-based service + +44 +00:02:18,739 --> 00:02:19,873 +that will handle everything we need + +45 +00:02:19,907 --> 00:02:23,010 +to integrate our issue tracker +with Xcode Cloud. + +46 +00:02:23,043 --> 00:02:25,879 +To speed up development, +we'll use the Vapor web framework, + +47 +00:02:25,913 --> 00:02:29,383 +which will let us focus on writing +the webhook handling code. + +48 +00:02:29,416 --> 00:02:32,653 +The basic flow will look something +like this. + +49 +00:02:32,686 --> 00:02:35,956 +A webhook will come +from Xcode Cloud to our server. + +50 +00:02:35,989 --> 00:02:38,358 +We will read that webhook +and check if the commit message + +51 +00:02:38,392 --> 00:02:40,661 +written by the committer +has a certain string in it + +52 +00:02:40,694 --> 00:02:43,297 +which maps to an issue in our tracker. + +53 +00:02:43,330 --> 00:02:45,799 +If it does, we'll hit the Xcode Cloud API + +54 +00:02:45,832 --> 00:02:48,902 +to gather more information +about the build. + +55 +00:02:48,936 --> 00:02:52,005 +Then we'll construct a comment we +can post onto our issue tracker + +56 +00:02:52,039 --> 00:02:54,675 +that contains the information +we're interested in. + +57 +00:02:56,076 --> 00:02:57,878 +We'll then call an API +on our issue tracker, + +58 +00:02:57,911 --> 00:03:00,214 +which will save the message +against our issue. + +59 +00:03:01,181 --> 00:03:04,718 +First, let's take a look +at the Xcode Cloud API documentation. + +60 +00:03:04,751 --> 00:03:07,754 +All of the Xcode Cloud API +lives under the App Store Connect API. + +61 +00:03:08,555 --> 00:03:10,657 +If we have already set up +authentication tokens + +62 +00:03:10,691 --> 00:03:12,459 +for the App Store Connect API, + +63 +00:03:12,492 --> 00:03:17,397 +we already have everything we need +to also access our Xcode Cloud data. + +64 +00:03:17,431 --> 00:03:20,133 +Opening up the Build Runs collection, +we can see some endpoints + +65 +00:03:20,167 --> 00:03:22,336 +that let us interact with our builds. + +66 +00:03:22,369 --> 00:03:24,471 +You can call an endpoint +to create a build, + +67 +00:03:24,505 --> 00:03:29,009 +cancel a build, or get more information +about a particular build. + +68 +00:03:29,042 --> 00:03:31,311 +For example, if you wanted to build +a dashboard + +69 +00:03:31,345 --> 00:03:33,180 +to show the status of your builds, + +70 +00:03:33,213 --> 00:03:36,817 +the Ci_Build_Runs endpoint +should be all we would need to call. + +71 +00:03:36,850 --> 00:03:40,587 +Next, let's look at the Artifacts +collection in the documentation. + +72 +00:03:40,621 --> 00:03:42,656 +For our workflow, +we want to keep our build artifacts + +73 +00:03:42,689 --> 00:03:46,760 +outside of Xcode Cloud, and the API +should have everything we need to do that. + +74 +00:03:46,793 --> 00:03:49,963 +First, we can get the artifact ID +from the build action endpoint, + +75 +00:03:49,997 --> 00:03:52,933 +which will return all the artifacts +produced for a given action. + +76 +00:03:52,966 --> 00:03:56,637 +Then we can call ci_artifact, +which will give us the URL we need + +77 +00:03:56,670 --> 00:04:00,774 +to download our artifact +in the downloadUrl attribute. + +78 +00:04:00,807 --> 00:04:02,776 +Let's start by downloading +the openAPI spec + +79 +00:04:02,809 --> 00:04:05,179 +from the App Store Connect documentation, + +80 +00:04:05,212 --> 00:04:07,781 +and go through what we need +to create our client for the API. + +81 +00:04:09,216 --> 00:04:12,119 +Let's create a new directory +where we can create our client code. + +82 +00:04:12,152 --> 00:04:14,721 +I'm going to call this xcodecloud-client. + +83 +00:04:14,755 --> 00:04:17,391 +Now we need to download +the command line tool for our generator. + +84 +00:04:17,424 --> 00:04:20,727 +There's lots of ways to do this on +the openAPI website, and for our session, + +85 +00:04:20,761 --> 00:04:22,062 +I've already set it up + +86 +00:04:22,095 --> 00:04:24,631 +Let's copy the openAPI spec +that we just got + +87 +00:04:24,665 --> 00:04:27,701 +from the app_store_connect_API +into our directory. + +88 +00:04:27,734 --> 00:04:29,870 +Great, now we can call +the openAPI generator + +89 +00:04:29,903 --> 00:04:31,905 +and tell it to create a swift client +for our API. + +90 +00:04:36,977 --> 00:04:38,145 +While the generator is running, + +91 +00:04:38,178 --> 00:04:39,880 +we can quickly talk about what it's doing. + +92 +00:04:39,913 --> 00:04:42,883 +An openAPI specification +is made up of information + +93 +00:04:42,916 --> 00:04:45,919 +about all the paths and models +that make up our API. + +94 +00:04:45,953 --> 00:04:49,122 +The generator is going +through each of the API paths + +95 +00:04:49,156 --> 00:04:51,892 +and generating Swift code that +can talk to that particular path, + +96 +00:04:51,925 --> 00:04:55,262 +with a strongly typed swift object. + +97 +00:04:55,295 --> 00:04:57,030 +In most cases, +this means you don't have to do + +98 +00:04:57,064 --> 00:05:01,335 +any JSON encoding or decoding. + +99 +00:05:01,368 --> 00:05:03,704 +Great, our generator is finished, + +100 +00:05:03,737 --> 00:05:05,639 +so let's take a quick look +at the files it created. + +101 +00:05:06,607 --> 00:05:09,543 +Conveniently, +the client code is a Swift package. + +102 +00:05:09,576 --> 00:05:12,145 +This makes it really easy to include +in our webhook server + +103 +00:05:12,179 --> 00:05:13,881 +that will update our issue tracker. + +104 +00:05:13,914 --> 00:05:17,217 +Now we can commit our code +to a new git repository, + +105 +00:05:17,251 --> 00:05:19,887 +and we can then reference this +as a Swift package. + +106 +00:05:19,920 --> 00:05:22,022 +Now that we've got everything +we need to talk with our API, + +107 +00:05:22,055 --> 00:05:23,991 +let's set up our webhook. + +108 +00:05:24,024 --> 00:05:25,893 +in App Store Connect, +we can register our webhook + +109 +00:05:25,926 --> 00:05:28,529 +that will let our server know +every time a build completes. + +110 +00:05:29,396 --> 00:05:32,032 +To do that, go to your product +in App Store Connect, + +111 +00:05:32,065 --> 00:05:34,201 +go to settings, +and navigate to webhooks. + +112 +00:05:44,811 --> 00:05:46,780 +And I'll paste in the URL for our server. + +113 +00:05:46,813 --> 00:05:49,383 +This will let Xcode Cloud know +where to send the webhooks. + +114 +00:05:49,416 --> 00:05:51,752 +With our web hook set up, +the only thing left to do + +115 +00:05:51,785 --> 00:05:55,055 +is write the code to process them, +so let's get started on that. + +116 +00:05:55,088 --> 00:05:57,024 +First we have to define a struct + +117 +00:05:57,057 --> 00:06:00,160 +which matches to the fields +we're interested in from our webhook. + +118 +00:06:00,194 --> 00:06:03,564 +The webhook will contain information +about the specific build being executed, + +119 +00:06:03,597 --> 00:06:05,632 +as well as each actions being performed, + +120 +00:06:05,666 --> 00:06:08,235 +so let's keep those +in our webhook payload struct. + +121 +00:06:09,369 --> 00:06:11,872 +Next, we have to create +the code to have Vapour correctly route + +122 +00:06:11,905 --> 00:06:14,074 +incoming webhooks to our function. + +123 +00:06:14,107 --> 00:06:19,079 +We'll define a function that runs +on a post request to the webhook path. + +124 +00:06:19,112 --> 00:06:20,881 +Inside this function, all we need to do + +125 +00:06:20,914 --> 00:06:25,085 +to convert the incoming JSON payload +into a strongly typed Swift object + +126 +00:06:25,118 --> 00:06:27,688 +is to decode it, +so we'll also add that code as well. + +127 +00:06:28,689 --> 00:06:31,525 +Great, now all we need to do is call +the Xcode Cloud API + +128 +00:06:31,558 --> 00:06:34,628 +to get a little bit more information +about the build which just completed + +129 +00:06:34,661 --> 00:06:37,197 +so that we can include it +on our issue tracker page. + +130 +00:06:37,231 --> 00:06:39,566 +The webhook payload contains +a lot of information + +131 +00:06:39,600 --> 00:06:40,868 +about the build which just ran, + +132 +00:06:40,901 --> 00:06:43,337 +and what actions succeeded or failed. + +133 +00:06:43,370 --> 00:06:47,307 +We could probably stop there, +but let's take it to the next level. + +134 +00:06:47,341 --> 00:06:49,810 +I think it would be amazing +if we also include information + +135 +00:06:49,843 --> 00:06:51,845 +about which issues were found +in each action. + +136 +00:06:53,247 --> 00:06:57,184 +We can write an extension function +to CiBuildActionsAPI + +137 +00:06:57,217 --> 00:07:00,454 +that gives us the ability to easily return +the issues we're interested in + +138 +00:07:00,487 --> 00:07:01,989 +for a particular action. + +139 +00:07:03,090 --> 00:07:05,025 +In this extension, we'll write a function + +140 +00:07:05,058 --> 00:07:07,728 +that takes the build action ID +as a parameter + +141 +00:07:07,761 --> 00:07:10,697 +and returns all the issues found +for a particular action. + +142 +00:07:12,132 --> 00:07:15,435 +I already have this in my project, +but let's fill in the rest of our code. + +143 +00:07:16,537 --> 00:07:19,473 +We only want our webhook handler +to run if the build is complete, + +144 +00:07:19,506 --> 00:07:21,775 +so let's guard against that. + +145 +00:07:21,808 --> 00:07:24,077 +Next we can start working +on our comment string. + +146 +00:07:24,111 --> 00:07:26,713 +I think it would be great to have +the build number, commit hash, + +147 +00:07:26,747 --> 00:07:28,749 +as well as the author in this string. + +148 +00:07:32,519 --> 00:07:35,289 +Now let's go through each action and call +our extension function + +149 +00:07:35,322 --> 00:07:37,925 +to get the issues for each action. + +150 +00:07:37,958 --> 00:07:40,427 +Let's pass this all on +to our issue tracker now. + +151 +00:07:47,401 --> 00:07:48,602 +With all of this information, + +152 +00:07:48,635 --> 00:07:50,204 +I think this will help us keep +our issue tracker + +153 +00:07:50,237 --> 00:07:52,139 +up to date with our builds. + +154 +00:07:52,840 --> 00:07:53,974 +And we're done! + +155 +00:07:54,007 --> 00:07:55,576 +At this point, +we want to host our server code + +156 +00:07:55,609 --> 00:07:58,612 +at the URL we provided +to Xcode Cloud for the webhooks, + +157 +00:07:58,645 --> 00:08:02,349 +and we should start to see Xcode Cloud +build information in our issue tracker. + +158 +00:08:03,550 --> 00:08:06,253 +Let's have a quick summary +about what we've learned so far. + +159 +00:08:06,286 --> 00:08:08,288 +Xcode Cloud has a great API you can use + +160 +00:08:08,322 --> 00:08:12,426 +to get an even deeper integration +with your existing development workflows. + +161 +00:08:12,459 --> 00:08:17,030 +You can use that API along with webhooks +to connect all the dots together + +162 +00:08:17,064 --> 00:08:20,267 +and integrate your existing tools +and technologies with Xcode Cloud. + +163 +00:08:20,300 --> 00:08:24,671 +Next, here's Jo to talk +about other features and best practices. + +164 +00:08:24,705 --> 00:08:25,839 +Jo: Thanks, John! + +165 +00:08:25,873 --> 00:08:31,512 +Xcode Cloud is designed to integrate +with Apple developer tools and services + +166 +00:08:31,545 --> 00:08:34,414 +that you use +to create apps and frameworks. + +167 +00:08:34,448 --> 00:08:38,986 +However, your Xcode project +may require additional dependencies + +168 +00:08:39,019 --> 00:08:41,522 +or external tools to compile your code. + +169 +00:08:41,555 --> 00:08:45,392 +Xcode Cloud works great +with Swift Package Manager. + +170 +00:08:45,425 --> 00:08:47,628 +It supports Swift package dependencies + +171 +00:08:47,661 --> 00:08:50,497 +without requiring +any additional configuration, + +172 +00:08:50,531 --> 00:08:53,901 +if the package's repository +is publicly accessible. + +173 +00:08:53,934 --> 00:08:59,006 +You can also make Xcode Cloud work +with third-party dependency managers + +174 +00:08:59,039 --> 00:09:01,175 +like Cocoapods and Carthage, + +175 +00:09:01,208 --> 00:09:04,811 +but you'll have to do a little extra work +by using custom build scripts. + +176 +00:09:05,512 --> 00:09:09,483 +You can refer to the Xcode documentation +for instructions + +177 +00:09:09,516 --> 00:09:12,953 +on how to make dependencies +available to Xcode Cloud. + +178 +00:09:15,022 --> 00:09:17,024 +Once we've added our Swift package + +179 +00:09:17,057 --> 00:09:19,526 +and pushed changes +to kick off the workflow, + +180 +00:09:19,560 --> 00:09:22,863 +we can go to the cloud tab +of the Report Navigator, + +181 +00:09:22,896 --> 00:09:24,865 +and view the status of our latest build. + +182 +00:09:26,900 --> 00:09:30,137 +Select Logs to view +the detailed build logs. + +183 +00:09:31,505 --> 00:09:35,809 +We'll see that Xcode Cloud has +automagically resolved the dependencies. + +184 +00:09:36,610 --> 00:09:40,647 +After Xcode Cloud has +successfully completed the first build, + +185 +00:09:40,681 --> 00:09:42,115 +you should plan next steps + +186 +00:09:42,149 --> 00:09:45,719 +for refining your continuous integration +and delivery practice + +187 +00:09:45,752 --> 00:09:50,390 +to make sure that your app or framework +is always in a shippable state. + +188 +00:09:50,424 --> 00:09:54,962 +In this section, I'm going to talk +about how you can use + +189 +00:09:54,995 --> 00:09:58,565 +Xcode Cloud with SwiftLint +for static code analysis, + +190 +00:09:58,599 --> 00:10:01,468 +how to restrict editing on your workflows, + +191 +00:10:01,502 --> 00:10:04,938 +and the value of configuring +multiple start conditions. + +192 +00:10:05,906 --> 00:10:09,209 +SwiftLint is an open source linter tool +that enforces + +193 +00:10:09,243 --> 00:10:14,081 +style guide rules and conventions +generally accepted by the Swift community. + +194 +00:10:14,114 --> 00:10:17,818 +It performs static code analysis +to improve your code quality + +195 +00:10:17,851 --> 00:10:19,520 +and prevent bad practices. + +196 +00:10:20,254 --> 00:10:22,356 +We have found that SwiftLint +is a great way + +197 +00:10:22,389 --> 00:10:24,791 +to keep your team's codebase consistent, + +198 +00:10:24,825 --> 00:10:27,628 +especially when working with larger teams. + +199 +00:10:27,661 --> 00:10:30,797 +What we're going to show now +is how to integrate SwiftLint + +200 +00:10:30,831 --> 00:10:33,534 +with Xcode Cloud +using a custom build script. + +201 +00:10:33,567 --> 00:10:36,670 +We want Xcode Cloud to run +the SwiftLint tool + +202 +00:10:36,703 --> 00:10:41,708 +after it clones our source code +from the team's primary repository. + +203 +00:10:41,742 --> 00:10:45,879 +In the Project navigator, +I've already added a post_clone script + +204 +00:10:45,913 --> 00:10:48,916 +in my ci_scripts folder +to my Food Truck project. + +205 +00:10:51,018 --> 00:10:54,621 +The Xcode Cloud build environment +includes Homebrew, + +206 +00:10:54,655 --> 00:10:57,224 +and that's what we're using +here to install SwiftLint. + +207 +00:10:59,026 --> 00:11:02,429 +On this line, we are executing SwiftLint. + +208 +00:11:02,462 --> 00:11:07,367 +One thing to remember is that the script +executes within the ci_scripts directory, + +209 +00:11:07,401 --> 00:11:10,871 +so we have to tell SwiftLint +to run within the ci_workspace + +210 +00:11:10,904 --> 00:11:14,241 +environment variable, +which points to our repository. + +211 +00:11:16,310 --> 00:11:19,580 +Let's check out +the result of the code analysis. + +212 +00:11:19,613 --> 00:11:22,449 +Looks like SwiftLint found +a bunch of violations, + +213 +00:11:22,482 --> 00:11:24,451 +15 of them serious. + +214 +00:11:24,484 --> 00:11:27,421 +Since this is the first time +we're integrating our tools, + +215 +00:11:27,454 --> 00:11:30,390 +I'm going to temporarily deactivate +this workflow + +216 +00:11:30,424 --> 00:11:34,394 +so that we can have a discussion +with the rest of our team first, + +217 +00:11:34,428 --> 00:11:38,131 +come up with an agreement +about coding styles and conventions, + +218 +00:11:38,165 --> 00:11:42,135 +and then decide as a team +which issues we want to fix. + +219 +00:11:42,169 --> 00:11:46,840 +You can deactivate a workflow in Xcode +from the Manage Workflows menu. + +220 +00:11:46,874 --> 00:11:50,077 +Doing so will stop the start conditions +from working, + +221 +00:11:50,110 --> 00:11:52,546 +and only manual builds can be started. + +222 +00:11:57,651 --> 00:12:01,255 +Now that our static code analysis workflow +has been deactivated, + +223 +00:12:01,288 --> 00:12:04,424 +we can focus on fixing the issues +we want to fix. + +224 +00:12:04,458 --> 00:12:07,060 +Then we can come back +and enable this workflow. + +225 +00:12:07,094 --> 00:12:09,196 +Our team has been growing rapidly, + +226 +00:12:09,229 --> 00:12:13,166 +and we want to make conscious choices +about workflow configurations. + +227 +00:12:13,200 --> 00:12:15,702 +Because I'm responsible +for maintaining and managing + +228 +00:12:15,736 --> 00:12:18,972 +all of our team's workflows, +I don't want team members to make + +229 +00:12:19,006 --> 00:12:21,308 +unintentional changes to a workflow. + +230 +00:12:21,341 --> 00:12:23,243 +That takes a lot of time to undo. + +231 +00:12:23,277 --> 00:12:25,979 +To prevent unintentional changes, + +232 +00:12:26,013 --> 00:12:29,249 +you can restrict who can make edits +to your workflow. + +233 +00:12:29,283 --> 00:12:34,988 +Select the workflow, then choose +Restrict Editing from the context menu. + +234 +00:12:35,022 --> 00:12:38,158 +Anyone in the team +can use or run this workflow, + +235 +00:12:38,192 --> 00:12:41,762 +but only administrators, +account holders, and app managers + +236 +00:12:41,795 --> 00:12:45,599 +can enforce a restriction, +which makes it editable only by them. + +237 +00:12:47,301 --> 00:12:51,205 +A workflow where editing is +restricted will have a 'key' symbol. + +238 +00:12:51,238 --> 00:12:55,042 +This means that it is locked +and can only be edited by you + +239 +00:12:55,075 --> 00:12:57,044 +and other administrative users. + +240 +00:12:59,179 --> 00:13:03,283 +If you see a 'lock' symbol instead, +this means that it has been locked + +241 +00:13:03,317 --> 00:13:06,653 +by an administrator +and cannot be edited by you. + +242 +00:13:06,687 --> 00:13:11,291 +This feature makes it easy +to manage access for complex workflows, + +243 +00:13:11,325 --> 00:13:13,727 +especially if you're part of a large team. + +244 +00:13:13,760 --> 00:13:18,599 +We've just learned how easy it is +to pause or lock down our workflows. + +245 +00:13:18,632 --> 00:13:21,768 +Now let's talk about workflow management +and maintenance. + +246 +00:13:22,936 --> 00:13:26,273 +I want to run the same tests +and archive actions + +247 +00:13:26,306 --> 00:13:29,977 +whenever I make a pull request +or when I merge my branch. + +248 +00:13:30,010 --> 00:13:33,947 +The best way to do so is +through multiple start conditions. + +249 +00:13:33,981 --> 00:13:36,450 +This improves the maintenance of workflows + +250 +00:13:36,483 --> 00:13:41,355 +because it reduces the number of workflows +that you'll need to create and manage. + +251 +00:13:41,388 --> 00:13:45,025 +To illustrate, in this example, +I want to kick off a build + +252 +00:13:45,058 --> 00:13:49,129 +given the following conditions: +if there are changes in the main branch + +253 +00:13:49,162 --> 00:13:52,065 +or the release branch, +or if there's a scheduled build + +254 +00:13:52,099 --> 00:13:54,168 +for the main branch, then, + +255 +00:13:54,201 --> 00:13:57,671 +run the same set +of archive and test actions + +256 +00:13:57,704 --> 00:14:01,275 +as well as deploy +to TestFlight for internal testing. + +257 +00:14:01,308 --> 00:14:03,844 +Instead of creating 3 workflows, +then managing them + +258 +00:14:03,877 --> 00:14:05,879 +and keeping them in sync, + +259 +00:14:05,913 --> 00:14:10,884 +I'd create a single workflow specifying +all the start conditions in one go. + +260 +00:14:10,918 --> 00:14:14,321 +This improves manageability, +especially when the team has + +261 +00:14:14,354 --> 00:14:16,590 +many workflows to maintain. + +262 +00:14:17,558 --> 00:14:20,861 +Configuring start conditions +in Xcode is easy. + +263 +00:14:20,894 --> 00:14:24,431 +From the workflow editor, pick the type +of start conditions + +264 +00:14:24,464 --> 00:14:26,233 +from the + button menu. + +265 +00:14:28,969 --> 00:14:31,405 +Xcode Cloud in App Store Connect + +266 +00:14:31,438 --> 00:14:34,975 +provides a fully featured +web-based experience. + +267 +00:14:35,008 --> 00:14:39,313 +Everything that I just did +in Xcode to configure our workflow, + +268 +00:14:39,346 --> 00:14:43,884 +from deactivating it and locking it down, +to setting up multiple start conditions, + +269 +00:14:43,917 --> 00:14:46,486 +are also available from AppStoreConnect. + +270 +00:14:46,520 --> 00:14:51,258 +Xcode Cloud is a powerful, +continuous integration & delivery service + +271 +00:14:51,291 --> 00:14:55,429 +built right into Xcode that helps +development teams of any size + +272 +00:14:55,462 --> 00:14:59,566 +and can easily be integrated +with your existing tools and processes + +273 +00:14:59,600 --> 00:15:03,237 +to help you deliver high-quality apps +for your users. + +274 +00:15:03,270 --> 00:15:07,307 +To the Xcode Cloud beta participants, +we really appreciate your feedback + +275 +00:15:07,341 --> 00:15:09,610 +and look forward to hearing more. + +276 +00:15:09,643 --> 00:15:11,245 +We hope these features +can help you refine + +277 +00:15:11,278 --> 00:15:13,881 +your team's continuous integration +& delivery practice. + +278 +00:15:13,914 --> 00:15:16,650 +Thank you for watching, +and have a great WWDC! + +279 +00:15:16,683 --> 00:15:18,585 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 110379 Create a more responsive media app en.srt b/eng/2022 Session 110379 Create a more responsive media app en.srt new file mode 100644 index 0000000..71cf174 --- /dev/null +++ b/eng/2022 Session 110379 Create a more responsive media app en.srt @@ -0,0 +1,880 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:10,844 +Jeremy: +Hi. I'm Jeremy, + +3 +00:00:10,878 --> 00:00:13,714 +and I'm here to show you how to create +a more responsive media app + +4 +00:00:13,747 --> 00:00:15,883 +using AVFoundation. + +5 +00:00:15,916 --> 00:00:17,584 +When using media assets in your app, + +6 +00:00:17,618 --> 00:00:19,553 +you might like to do more +than just play them. + +7 +00:00:19,586 --> 00:00:22,523 +You might like to show thumbnails, + +8 +00:00:22,556 --> 00:00:25,626 +combine media into new compositions, + +9 +00:00:25,659 --> 00:00:28,962 +or get information about your assets. + +10 +00:00:28,996 --> 00:00:31,031 +These tasks require loading data, + +11 +00:00:31,064 --> 00:00:35,269 +and with big files like video, +that might take some time to complete. + +12 +00:00:35,302 --> 00:00:38,472 +Unfortunately, it can be easy +to introduce latency issues in your app + +13 +00:00:38,505 --> 00:00:42,242 +if this work is done synchronously +on the main thread. + +14 +00:00:42,276 --> 00:00:45,479 +A great way to keep your app responsive +is to load data asynchronously, + +15 +00:00:45,512 --> 00:00:48,081 +and update your UI when it's finished. + +16 +00:00:48,115 --> 00:00:51,051 +AVFoundation has tools to make this easy. + +17 +00:00:51,084 --> 00:00:53,520 +So here's what we'll talk about today. + +18 +00:00:53,554 --> 00:00:58,225 +First, I'll introduce you +to some new async APIs in AVFoundation. + +19 +00:00:58,258 --> 00:01:00,561 +Then, I'll give you an update +on asset inspection + +20 +00:01:00,594 --> 00:01:04,164 +using the async load(_:) method +we introduced last year. + +21 +00:01:04,198 --> 00:01:07,000 +And I'll show you how to optimize +custom data loading for local + +22 +00:01:07,034 --> 00:01:10,871 +and cached media +using AVAssetResourceLoader. + +23 +00:01:10,904 --> 00:01:14,741 +But first, +let's jump into the new async API. + +24 +00:01:14,775 --> 00:01:18,312 +Grabbing still images from a video +with AVAssetImageGenerator + +25 +00:01:18,345 --> 00:01:20,547 +is a great way to create thumbnails. + +26 +00:01:20,581 --> 00:01:22,649 +But image generation isn't instantaneous. + +27 +00:01:24,017 --> 00:01:27,421 +To generate an image, +image generator needs to load frame data + +28 +00:01:27,454 --> 00:01:29,723 +from your video file. + +29 +00:01:29,756 --> 00:01:32,960 +And for media stored on a remote server, +or on the internet, + +30 +00:01:32,993 --> 00:01:35,329 +that loading will be much slower. + +31 +00:01:35,362 --> 00:01:39,433 +That's why it's important how +you generate your images. + +32 +00:01:39,466 --> 00:01:41,568 +Using a method +that loads data synchronously, + +33 +00:01:41,602 --> 00:01:44,104 +like copyCGImage, on the main thread + +34 +00:01:44,137 --> 00:01:48,141 +can cause your UI to freeze +as it waits for video to be loaded. + +35 +00:01:48,175 --> 00:01:51,211 +This year, we've added +the image(at: time) async method + +36 +00:01:51,245 --> 00:01:53,780 +which uses async/await +to free up the calling thread + +37 +00:01:53,814 --> 00:01:56,783 +while image generator loads data. + +38 +00:01:56,817 --> 00:01:59,186 +Image generator returns a tuple +with the image + +39 +00:01:59,219 --> 00:02:01,455 +and its actual time in the asset. + +40 +00:02:01,488 --> 00:02:05,025 +There are a few reasons the actual time +may vary from the time you requested, + +41 +00:02:05,058 --> 00:02:06,860 +but if you only want the image, + +42 +00:02:06,894 --> 00:02:10,964 +you can directly access it +with the .image property. + +43 +00:02:10,998 --> 00:02:14,501 +Some frames in compressed video +are easier to load than others. + +44 +00:02:14,535 --> 00:02:16,537 +iFrames can be decoded independently, + +45 +00:02:16,570 --> 00:02:20,174 +while other frames +rely on nearby frames to be decoded. + +46 +00:02:20,207 --> 00:02:23,210 +For your requested time, +image generator by default + +47 +00:02:23,243 --> 00:02:26,847 +will use the nearest iFrame +to generate your image. + +48 +00:02:26,880 --> 00:02:29,183 +It might be tempting +to set the tolerances to zero + +49 +00:02:29,216 --> 00:02:31,852 +to get the exact frame +for your requested time. + +50 +00:02:31,885 --> 00:02:34,788 +But keep in mind that +that frame will likely be dependent + +51 +00:02:34,821 --> 00:02:39,560 +on other nearby frames that +image generator will also need to load. + +52 +00:02:39,593 --> 00:02:42,062 +Instead, consider setting wide tolerances + +53 +00:02:42,095 --> 00:02:44,164 +that will still give you the result +you're looking for. + +54 +00:02:44,198 --> 00:02:47,367 +Wide tolerances help image generator +to minimize data loading + +55 +00:02:47,401 --> 00:02:49,803 +by giving it more frames to pick from. + +56 +00:02:49,837 --> 00:02:53,040 +The fewer frames it needs to load, +the faster it can return an image. + +57 +00:02:54,374 --> 00:02:57,344 +To get a series of images +at several times in an asset, + +58 +00:02:57,377 --> 00:03:02,082 +image generator has had +generateCGImagesAsynchronously(forTimes:). + +59 +00:03:02,115 --> 00:03:06,220 +However in Swift, there is some nuance +to watch out for to use it. + +60 +00:03:06,253 --> 00:03:10,224 +New this year we've added +the images(for: times) method. + +61 +00:03:10,257 --> 00:03:12,526 +It now takes an array of CMTimes, + +62 +00:03:12,559 --> 00:03:15,629 +so you don't need to map them +to NSValues first. + +63 +00:03:15,662 --> 00:03:18,899 +It also provides its results +using an Async Sequence. + +64 +00:03:18,932 --> 00:03:23,871 +In Swift, sequences let you iterate +over their items using a for in loop. + +65 +00:03:23,904 --> 00:03:26,874 +For a sequence of items +that aren't ready all at once, + +66 +00:03:26,907 --> 00:03:31,912 +an async sequence lets you await +the next element after each iteration. + +67 +00:03:31,945 --> 00:03:34,281 +For each successfully generated image, + +68 +00:03:34,314 --> 00:03:36,884 +the result includes +the originally requested time + +69 +00:03:36,917 --> 00:03:39,686 +and the actual time alongside the image. + +70 +00:03:39,720 --> 00:03:43,123 +If it fails, +the result has an error to explain why. + +71 +00:03:44,725 --> 00:03:46,927 +And if you are only interested +in the image, + +72 +00:03:46,960 --> 00:03:50,163 +the result has properties +to directly access its values, + +73 +00:03:50,197 --> 00:03:54,101 +which can also throw errors +if generation fails. + +74 +00:03:54,134 --> 00:03:55,836 +To learn more about async sequences, + +75 +00:03:55,869 --> 00:03:59,306 +I recommend checking out +the "meet async sequence" session. + +76 +00:03:59,339 --> 00:04:01,175 +For a task like image generation, + +77 +00:04:01,208 --> 00:04:03,844 +it's a little easier to see +how it involves loading data. + +78 +00:04:03,877 --> 00:04:06,780 +But there are some other +synchronous areas of AVFoundation + +79 +00:04:06,813 --> 00:04:09,016 +that are harder to pick out +as problem spots. + +80 +00:04:09,917 --> 00:04:13,253 +AVMutableComposition +is one of these areas. + +81 +00:04:13,287 --> 00:04:16,823 +Insert time range for asset +needs information about the asset's tracks + +82 +00:04:16,857 --> 00:04:19,793 +to add references to them +in the composition. + +83 +00:04:19,826 --> 00:04:21,962 +It synchronously inspects the tracks, + +84 +00:04:21,995 --> 00:04:24,198 +so if the tracks aren't already loaded, + +85 +00:04:24,231 --> 00:04:27,968 +they'll be synchronously loaded +to create the new composition tracks. + +86 +00:04:29,603 --> 00:04:33,540 +Previously, the solution would've been +to await loading the asset's tracks + +87 +00:04:33,574 --> 00:04:36,910 +before inserting them +into the composition. + +88 +00:04:36,944 --> 00:04:41,782 +However, this year, we're introducing +an async version of insertTimeRange, + +89 +00:04:41,815 --> 00:04:45,219 +which will async load the tracks for you, +as needed. + +90 +00:04:46,687 --> 00:04:49,556 +Video composition +and mutable video composition + +91 +00:04:49,590 --> 00:04:53,493 +have additional methods that require +loading the asset's properties too. + +92 +00:04:53,527 --> 00:04:56,496 +New this year, +the "propertiesOf asset" constructor, + +93 +00:04:56,530 --> 00:05:01,101 +and isValid(for:timeRange:) method +now also have async counterparts. + +94 +00:05:01,134 --> 00:05:05,472 +These new methods will asynchronously +load the tracks and duration of the asset, + +95 +00:05:05,506 --> 00:05:08,308 +so there's no need +for you to pre-load them either. + +96 +00:05:08,342 --> 00:05:11,778 +These new async methods +make it easier to interact with assets + +97 +00:05:11,812 --> 00:05:14,815 +by loading the properties they need +asynchronously. + +98 +00:05:14,848 --> 00:05:18,352 +But for when you need to load +the properties of an asset yourself, + +99 +00:05:18,385 --> 00:05:21,655 +let's get a refresher +of async asset inspection. + +100 +00:05:21,688 --> 00:05:25,292 +You may have noticed there are two ways +to inspect an asset's properties. + +101 +00:05:25,325 --> 00:05:27,094 +When AVFoundation was introduced, + +102 +00:05:27,127 --> 00:05:31,665 +the best way to inspect properties +was with async key value loading. + +103 +00:05:31,698 --> 00:05:34,468 +Last year, we introduced async load(_:). + +104 +00:05:34,501 --> 00:05:37,704 +It uses type safe keys +to identify the properties to load, + +105 +00:05:37,738 --> 00:05:43,477 +where the old async key value loading +technique used hard coded strings as keys. + +106 +00:05:43,510 --> 00:05:46,346 +Typos in these string keys +are difficult to catch. + +107 +00:05:46,380 --> 00:05:50,017 +Misspelling a key prevents it +from being loaded asynchronously, + +108 +00:05:50,050 --> 00:05:53,487 +and when the property is later used, +it'll block while it loads. + +109 +00:05:54,888 --> 00:05:59,626 +It's also very easy to forget to add +new properties to the keys to load + +110 +00:05:59,660 --> 00:06:03,664 +or to forget async loading them entirely. + +111 +00:06:03,697 --> 00:06:07,534 +For these reasons, this year, +we're deprecating async key value loading + +112 +00:06:07,568 --> 00:06:11,672 +and the synchronous properties in Swift, +in favor for async load. + +113 +00:06:11,705 --> 00:06:15,876 +Async load uses type safe identifiers +to prevent typos. + +114 +00:06:15,909 --> 00:06:18,579 +It directly returns property values +as requested + +115 +00:06:18,612 --> 00:06:21,648 +to avoid accessing unloaded properties. + +116 +00:06:21,682 --> 00:06:24,318 +And since all of this is checked +at compile time, + +117 +00:06:24,351 --> 00:06:28,222 +you'll prevent introducing +any new IO bound performance issues. + +118 +00:06:28,255 --> 00:06:32,526 +Async load is now the only recommended way +to asynchronously inspect properties + +119 +00:06:32,559 --> 00:06:35,262 +on AVAsset, AVAssetTrack, + +120 +00:06:35,295 --> 00:06:38,966 +AVMetadataItem, and their sub classes. + +121 +00:06:38,999 --> 00:06:40,667 +However, a handful of these classes + +122 +00:06:40,701 --> 00:06:43,637 +will still offer +synchronous property inspection. + +123 +00:06:43,670 --> 00:06:48,342 +That's because the data for their +properties is already available in memory. + +124 +00:06:48,375 --> 00:06:51,278 +Let's take another look at +mutable composition to see why. + +125 +00:06:52,613 --> 00:06:55,282 +We'll use a mutable composition +to splice together segments + +126 +00:06:55,315 --> 00:06:58,285 +of two existing video tracks. + +127 +00:06:58,318 --> 00:07:01,321 +We'll start by creating +an empty composition + +128 +00:07:01,355 --> 00:07:03,857 +and adding an empty video track. + +129 +00:07:03,891 --> 00:07:07,127 +Then, we can synchronously insert +part of the first video track + +130 +00:07:07,160 --> 00:07:09,830 +into the composition track. + +131 +00:07:09,863 --> 00:07:12,566 +Behind the scenes, +this step isn't loading any data. + +132 +00:07:12,599 --> 00:07:16,770 +Instead, it adds a new track segment +that points to the desired track. + +133 +00:07:18,572 --> 00:07:21,608 +Then we can append part of +the second track in the same way. + +134 +00:07:23,477 --> 00:07:26,446 +Since the composition itself +is backed by an in memory structure + +135 +00:07:26,480 --> 00:07:30,184 +and not a file, we can safely inspect +its properties synchronously + +136 +00:07:30,217 --> 00:07:33,587 +without needing to load them first. + +137 +00:07:33,620 --> 00:07:35,055 +Again, for this reason, + +138 +00:07:35,088 --> 00:07:39,326 +synchronous property inspection +will remain available on these classes + +139 +00:07:39,359 --> 00:07:43,363 +and all classes will use async load +for asynchronous inspection. + +140 +00:07:44,932 --> 00:07:47,501 +All of these new async methods +in AVFoundation + +141 +00:07:47,534 --> 00:07:51,104 +will make it easier to prevent blocking +while loading media data. + +142 +00:07:51,138 --> 00:07:55,142 +But, introducing concurrency into your app +for the first time can be tricky. + +143 +00:07:55,175 --> 00:07:58,545 +Check out these sessions from +WWDC 21 for help getting started + +144 +00:07:58,579 --> 00:08:04,718 +with Swift concurrency and for migrating +to AVFoundation's async load in your app. + +145 +00:08:04,751 --> 00:08:07,788 +For our last topic, let's talk about +optimizing custom data loading + +146 +00:08:07,821 --> 00:08:09,356 +for your assets. + +147 +00:08:09,389 --> 00:08:13,794 +To start, lets take a look +at how AVAsset loads data by default. + +148 +00:08:13,827 --> 00:08:15,929 +When you create an AVAsset with a URL, + +149 +00:08:15,963 --> 00:08:20,267 +the media can either be on the network, +or stored locally on device. + +150 +00:08:20,300 --> 00:08:23,403 +If it's on the network, +AVAsset will dynamically cache + +151 +00:08:23,437 --> 00:08:27,007 +a certain amount of data +to ensure smooth playback. + +152 +00:08:27,040 --> 00:08:30,143 +If the media is local, +AVAsset can bypass the cache + +153 +00:08:30,177 --> 00:08:32,813 +and load data as needed to play. + +154 +00:08:32,846 --> 00:08:34,815 +In some cases, +you might not be able to give + +155 +00:08:34,848 --> 00:08:37,551 +AVAsset a direct pointer to your media. + +156 +00:08:37,584 --> 00:08:42,289 +Maybe you store the raw bytes of an mp4 +inside of a custom project file. + +157 +00:08:42,322 --> 00:08:47,661 +For situations like this, +AVAsset can use an AVAssetResourceLoader. + +158 +00:08:47,694 --> 00:08:50,864 +Resource loader provides the asset +with a way to request arbitrary bytes + +159 +00:08:50,898 --> 00:08:54,201 +from your media +that you have a special way to load. + +160 +00:08:54,234 --> 00:08:57,204 +But since the asset is no longer +handling reading in data, + +161 +00:08:57,237 --> 00:09:01,008 +it can't predict how long it'll take +each chunk to load. + +162 +00:09:01,041 --> 00:09:04,878 +So it assumes that accessing the media +involves network communication, + +163 +00:09:04,912 --> 00:09:09,183 +and waits until it caches data +before it becomes ready to play. + +164 +00:09:09,216 --> 00:09:11,952 +This year, if your media +is locally available, + +165 +00:09:11,985 --> 00:09:14,521 +you can enable +entireLengthAvailableOnDemand + +166 +00:09:14,555 --> 00:09:16,390 +for your resource loader. + +167 +00:09:16,423 --> 00:09:19,560 +Setting this flag tells the asset +that it can expect to receive data + +168 +00:09:19,593 --> 00:09:22,429 +as it's requested, +so it can skip caching. + +169 +00:09:25,232 --> 00:09:28,001 +For local media, +entireLengthAvailableOnDemand + +170 +00:09:28,035 --> 00:09:30,671 +can help reduce your app's memory usage +during playback, + +171 +00:09:30,704 --> 00:09:33,307 +since it won't need to cache extra data. + +172 +00:09:33,340 --> 00:09:35,943 +It can also decrease the time it takes +to start playback, + +173 +00:09:35,976 --> 00:09:39,847 +since the asset won't have to wait +for the cache to fill up first. + +174 +00:09:39,880 --> 00:09:42,349 +Use caution when enabling this flag, +though. + +175 +00:09:42,382 --> 00:09:44,484 +If loading requires +any network operations, + +176 +00:09:44,518 --> 00:09:46,286 +including network file storage, + +177 +00:09:46,320 --> 00:09:48,822 +it's likely playback will be unreliable. + +178 +00:09:50,390 --> 00:09:52,159 +That's the new enhancement +for resource loader. + +179 +00:09:52,192 --> 00:09:55,662 +Now lets wrap things up +with some next steps for your app. + +180 +00:09:56,797 --> 00:10:00,000 +When working with media, use async/await +to keep your app responsive + +181 +00:10:00,033 --> 00:10:02,503 +while it loads in the background. + +182 +00:10:02,536 --> 00:10:07,407 +Consider increasing tolerances when +using image generator for faster results. + +183 +00:10:07,441 --> 00:10:10,511 +And if you are using resource loader +for locally available media, + +184 +00:10:10,544 --> 00:10:14,314 +enable entire length available on demand +to help increase performance. + +185 +00:10:15,249 --> 00:10:16,717 +That's everything I have for today. + +186 +00:10:16,750 --> 00:10:20,654 +Thanks for watching, +and enjoy WWDC 22. + diff --git a/eng/2022 Session 110380 Display ads and interstitials in SharePlay en.srt b/eng/2022 Session 110380 Display ads and interstitials in SharePlay en.srt new file mode 100644 index 0000000..27ebcbf --- /dev/null +++ b/eng/2022 Session 110380 Display ads and interstitials in SharePlay en.srt @@ -0,0 +1,974 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,378 +Prashant: Hey, everyone. +I'm Prashant. + +3 +00:00:11,411 --> 00:00:16,016 +I'm an AVFoundation Engineer, +and welcome to WWDC2022. + +4 +00:00:16,049 --> 00:00:18,685 +With the introduction of SharePlay, +you can provide people + +5 +00:00:18,719 --> 00:00:20,721 +with a connected viewing experience + +6 +00:00:20,754 --> 00:00:24,024 +that lets them navigate through +and interact with content in real time + +7 +00:00:24,057 --> 00:00:27,928 +all while staying completely in sync +with the SharePlay group. + +8 +00:00:27,961 --> 00:00:31,965 +However, some of you might have +targeted ads or other interstitials + +9 +00:00:31,999 --> 00:00:34,201 +scheduled in the content timeline, + +10 +00:00:34,234 --> 00:00:36,170 +making it a challenge +to coordinate playback + +11 +00:00:36,203 --> 00:00:39,640 +between participants that receive ads +of different durations + +12 +00:00:39,673 --> 00:00:41,642 +or say between a mixed set of participants + +13 +00:00:41,675 --> 00:00:44,411 +where some receive ads and some don't. + +14 +00:00:44,444 --> 00:00:47,214 +So if you're interested in scheduling ads +and other interstitials + +15 +00:00:47,247 --> 00:00:51,084 +for coordinated playback in SharePlay, +you've tuned in to the right session. + +16 +00:00:51,118 --> 00:00:55,589 +In this talk we'll start by going over +how coordinated playback works. + +17 +00:00:55,622 --> 00:00:58,091 +Then we'll go over the challenges +associated with ads + +18 +00:00:58,125 --> 00:01:01,028 +and other interstitials +in coordinated playback. + +19 +00:01:01,061 --> 00:01:03,997 +Following that, we'll discuss +the different viewing experiences + +20 +00:01:04,031 --> 00:01:06,834 +that are possible when watching ads. + +21 +00:01:06,867 --> 00:01:10,804 +We'll then see how one might achieve these +for stitched-in ads. + +22 +00:01:10,838 --> 00:01:15,375 +And we'll also see how HLS interstitials +work with coordinated playback. + +23 +00:01:15,409 --> 00:01:18,045 +And to conclude, +we'll go over some best practices + +24 +00:01:18,078 --> 00:01:22,349 +that you can use to provide people with +a really compelling SharePlay experience. + +25 +00:01:23,584 --> 00:01:27,788 +Lets see how coordinated playback works +in SharePlay. + +26 +00:01:27,821 --> 00:01:29,923 +So while participants are in +a FaceTime call, + +27 +00:01:29,957 --> 00:01:33,060 +the playback control commands originating +on one participant's device + +28 +00:01:33,093 --> 00:01:36,530 +are shared across group +so that they all play in sync. + +29 +00:01:36,563 --> 00:01:38,565 +While this works well +if all the participants + +30 +00:01:38,599 --> 00:01:40,367 +are watching the same content, + +31 +00:01:40,400 --> 00:01:43,170 +what would happen if some of them +have ads? + +32 +00:01:43,203 --> 00:01:44,404 +Now let's take a look at the case + +33 +00:01:44,438 --> 00:01:48,275 +where Alice and Bob are trying +to watch some program content. + +34 +00:01:48,308 --> 00:01:52,713 +Now what we see here are the presentation +timelines at their respective ends. + +35 +00:01:52,746 --> 00:01:55,182 +If it is only the program content +that they're watching, + +36 +00:01:55,215 --> 00:01:58,886 +then it should be pretty straightforward +to achieve coordinated playback. + +37 +00:01:58,919 --> 00:02:01,989 +You'd simply associate the groupSession +with the playbackCoordinator, + +38 +00:02:02,022 --> 00:02:04,258 +and you'd be good to go. + +39 +00:02:04,291 --> 00:02:08,262 +Now lets say each of them have +a program recap segment stitched in, + +40 +00:02:08,295 --> 00:02:10,964 +which is preceded by the studio +or network banner. + +41 +00:02:10,998 --> 00:02:12,199 +This is fairly typical, + +42 +00:02:12,232 --> 00:02:16,170 +and since the recap segments +and the banners match for Alice and Bob, + +43 +00:02:16,203 --> 00:02:19,306 +coordinated playback is still achievable. + +44 +00:02:19,339 --> 00:02:22,576 +But now, +let's say Alice's geography requires + +45 +00:02:22,609 --> 00:02:27,281 +that the service show some statutory +warnings before the start of the program, + +46 +00:02:27,314 --> 00:02:31,752 +and there might also be ads scheduled +at different points within the program. + +47 +00:02:31,785 --> 00:02:36,056 +It could be that these ads +are of different durations. + +48 +00:02:36,089 --> 00:02:38,525 +We can now see +how this might pose a challenge + +49 +00:02:38,559 --> 00:02:41,094 +when trying to get them to play in sync. + +50 +00:02:41,128 --> 00:02:44,331 +For Bob, the network banner might appear +at the very beginning, + +51 +00:02:44,364 --> 00:02:48,702 +but for Alice, it would appear +only after the warnings are shown. + +52 +00:02:48,735 --> 00:02:52,406 +And if we were to look at some arbitrary +time in the presentation timeline, + +53 +00:02:52,439 --> 00:02:56,176 +they would be watching completely +different portions of the content. + +54 +00:02:56,210 --> 00:02:58,912 +Now let's take a moment to think about +the user experiences + +55 +00:02:58,946 --> 00:03:01,682 +that would be possible +in scenarios such as these. + +56 +00:03:01,715 --> 00:03:03,750 +For this let's consider a simpler timeline + +57 +00:03:03,784 --> 00:03:07,254 +where each of them have +just the single ad stitched in. + +58 +00:03:08,188 --> 00:03:10,958 +In the ideal case, +the experience shouldn't be too different + +59 +00:03:10,991 --> 00:03:13,627 +from where they were watching +the program alone. + +60 +00:03:13,660 --> 00:03:17,831 +For that to be possible, we'd expect +the durations of the ads to match. + +61 +00:03:17,865 --> 00:03:20,434 +This would be +our preferred experience as well. + +62 +00:03:20,467 --> 00:03:25,472 +Now, after watching the ad, both +of them would resume playback in sync. + +63 +00:03:25,506 --> 00:03:28,442 +But sometimes they might get ads +of different durations, + +64 +00:03:28,475 --> 00:03:30,711 +or one of them may not have +any ads at all. + +65 +00:03:30,744 --> 00:03:34,348 +So now, if they were to play in sync, +one of two experiences is possible. + +66 +00:03:35,883 --> 00:03:37,651 +Once Alice finishes her ad, + +67 +00:03:37,684 --> 00:03:42,356 +she can then wait for Bob's ad to finish +before resuming together in sync. + +68 +00:03:45,926 --> 00:03:47,928 +Or she can continue with the main program + +69 +00:03:47,961 --> 00:03:53,000 +and Bob would then jump ahead to catch up +with Alice so that they stay in sync. + +70 +00:03:53,033 --> 00:03:56,136 +In this scenario, Bob would end up +missing some program content + +71 +00:03:56,170 --> 00:03:58,939 +when catching up with Alice. + +72 +00:03:58,972 --> 00:04:01,575 +Now how would you +enable these experiences? + +73 +00:04:01,608 --> 00:04:05,646 +You can specify these waiting policies +at the AVPlaybackCoordinator + +74 +00:04:05,679 --> 00:04:09,116 +by how you populate the +suspensionReasonsThatTriggerWaiting array. + +75 +00:04:09,149 --> 00:04:12,219 +The default behavior is for +the group to not wait + +76 +00:04:12,252 --> 00:04:15,255 +while some of the participants +are watching ads. + +77 +00:04:15,289 --> 00:04:17,124 +When these participants finish their ads, + +78 +00:04:17,157 --> 00:04:21,061 +they would miss some content +to catch up with the rest of the group. + +79 +00:04:21,094 --> 00:04:25,432 +Alternately, if you wish for the group +to wait while the participants watch ads, + +80 +00:04:25,465 --> 00:04:29,036 +you can simply include the +"playingInterstitial" suspension reason + +81 +00:04:29,069 --> 00:04:32,739 +as part of the +suspensionReasonsThatTriggerWaiting array. + +82 +00:04:32,773 --> 00:04:34,708 +With this policy, +when a participant is waiting + +83 +00:04:34,741 --> 00:04:36,476 +for the others to complete their ads, + +84 +00:04:36,510 --> 00:04:41,215 +their player's timeControlStatus would be +waitingToPlayAtSpecifiedRate + +85 +00:04:41,248 --> 00:04:45,752 +and the waitingReason would be +waitingForCoordinatedPlayback. + +86 +00:04:45,786 --> 00:04:48,255 +So going back to the content +described earlier, + +87 +00:04:48,288 --> 00:04:50,757 +how do we define these time ranges +that are to be left out + +88 +00:04:50,791 --> 00:04:53,393 +of the coordinated playback timeline? + +89 +00:04:53,427 --> 00:04:55,529 +For instance, +the statutory warning is something + +90 +00:04:55,562 --> 00:04:58,298 +that only Alice has and Bob doesn't. + +91 +00:04:58,332 --> 00:05:01,869 +And it's also possible that the ad breaks +are of different durations. + +92 +00:05:01,902 --> 00:05:06,139 +You might be aware that HLS offers +two ways of scheduling ads. + +93 +00:05:06,173 --> 00:05:09,209 +One of which would be to directly +stitch in the ad segments + +94 +00:05:09,243 --> 00:05:11,979 +into the primary content's playlists. + +95 +00:05:12,012 --> 00:05:15,082 +So for video on demand content +that has ads stitched in + +96 +00:05:15,115 --> 00:05:17,251 +using discontinuity tags, + +97 +00:05:17,284 --> 00:05:21,088 +you can set the ad time ranges +on the AVPlayerPlaybackCoordinator + +98 +00:05:21,121 --> 00:05:23,757 +using an AVFoundation API. + +99 +00:05:23,790 --> 00:05:25,692 +So we've added the following +delegate method to– + +100 +00:05:25,726 --> 00:05:29,329 +using which you can specify an array +of sample accurate time ranges + +101 +00:05:29,363 --> 00:05:33,133 +that represent ads +and interstitials in the content. + +102 +00:05:33,166 --> 00:05:36,670 +The AVPlayerPlaybackCoordinator +would then use this information + +103 +00:05:36,703 --> 00:05:39,406 +to coordinate playback across the group. + +104 +00:05:39,439 --> 00:05:41,508 +When a participant enters this time range, + +105 +00:05:41,542 --> 00:05:42,910 +playback would be coordinated + +106 +00:05:42,943 --> 00:05:45,646 +in accordance with +the waiting policy specified. + +107 +00:05:45,679 --> 00:05:48,315 +Also, +depending on the type of interstitial, + +108 +00:05:48,348 --> 00:05:51,852 +you might allow for someone +to navigate into these time ranges. + +109 +00:05:51,885 --> 00:05:54,688 +So if they were to seek into +these time ranges while in SharePlay, + +110 +00:05:54,721 --> 00:05:58,659 +the entire group would then +snap to the beginning of that time range. + +111 +00:06:00,360 --> 00:06:03,397 +This shows a playlist example +where the ad time ranges + +112 +00:06:03,430 --> 00:06:07,467 +are populated by summing up the times +reported in the EXTINF tags + +113 +00:06:07,501 --> 00:06:08,936 +of the playlists. + +114 +00:06:08,969 --> 00:06:12,940 +This assumes that the durations reported +as part of the EXTINF tags + +115 +00:06:12,973 --> 00:06:17,044 +accurately reflect the media durations +of those segments. + +116 +00:06:17,077 --> 00:06:19,847 +So, now what should the assets look like + +117 +00:06:19,880 --> 00:06:22,082 +for coordinated playback to work? + +118 +00:06:22,115 --> 00:06:24,017 +So if you were to look at this example, + +119 +00:06:24,051 --> 00:06:27,087 +Alice and Bob have different +duration ads stitched in, + +120 +00:06:27,120 --> 00:06:30,757 +resulting in different asset durations. + +121 +00:06:30,791 --> 00:06:33,460 +For these to be considered +SharePlay compatible, + +122 +00:06:33,493 --> 00:06:37,364 +we'd expect the actual content duration +of the assets to match. + +123 +00:06:37,397 --> 00:06:40,534 +Note that this applies only +to video on demand content, + +124 +00:06:40,567 --> 00:06:42,636 +for live content with stitched-in ads, + +125 +00:06:42,669 --> 00:06:46,306 +we'd expect the durations of the ad breaks +to match for all participants. + +126 +00:06:46,340 --> 00:06:50,544 +If you think that it might be a challenge +to obtain the sample accurate time ranges + +127 +00:06:50,577 --> 00:06:54,214 +or if you wish to dynamically schedule +targeted ads in live content, + +128 +00:06:54,248 --> 00:06:57,784 +then you can use +HLS interstitials to schedule ads. + +129 +00:06:57,818 --> 00:07:00,787 +In 2021, we introduced HLS interstitials + +130 +00:07:00,821 --> 00:07:04,558 +which offers a different, +more flexible approach to scheduling ads. + +131 +00:07:04,591 --> 00:07:07,494 +For more details, check out +the Explore dynamic pre-rolls + +132 +00:07:07,528 --> 00:07:11,965 +and mid-rolls in HLS talk from WWDC2021. + +133 +00:07:11,999 --> 00:07:15,836 +In short, ads and interstitials +are treated as separate objects + +134 +00:07:15,869 --> 00:07:18,205 +outside of the content timeline. + +135 +00:07:18,238 --> 00:07:22,142 +These objects can be directly referenced +via their multi variant playlist + +136 +00:07:22,176 --> 00:07:24,945 +and one can perform +Server Side Ad Insertion + +137 +00:07:24,978 --> 00:07:27,881 +simply by including +the multi variant playlist URI + +138 +00:07:27,915 --> 00:07:32,252 +as part of a date range tag +in the primary content's media playlist, + +139 +00:07:32,286 --> 00:07:37,057 +or you even schedule ads on +the client side using AVFoundation APIs. + +140 +00:07:37,090 --> 00:07:39,960 +If you use HLS interstitials +to schedule ads, + +141 +00:07:39,993 --> 00:07:44,231 +AVFoundation automatically takes care +of them during coordinated playback. + +142 +00:07:44,264 --> 00:07:47,034 +All you need to do +is to specify the waiting policy. + +143 +00:07:48,535 --> 00:07:51,171 +When using HLS interstitials +to schedule ads, + +144 +00:07:51,205 --> 00:07:54,341 +once again, we'd expect the primary +content durations to match + +145 +00:07:54,374 --> 00:07:56,143 +across all the participants + +146 +00:07:56,176 --> 00:07:59,479 +for the assets +to be considered SharePlay compatible. + +147 +00:07:59,513 --> 00:08:02,082 +Now let's go over some best practices +that you can use + +148 +00:08:02,115 --> 00:08:05,419 +to provide people with a really good +SharePlay experience. + +149 +00:08:05,452 --> 00:08:08,722 +SharePlay is meant to provide +a seamless connected viewing experience + +150 +00:08:08,755 --> 00:08:10,290 +where people interact with content + +151 +00:08:10,324 --> 00:08:12,860 +as though they were +watching it by themselves. + +152 +00:08:12,893 --> 00:08:16,430 +So with this experience in mind, +one should strive to minimize waits + +153 +00:08:16,463 --> 00:08:19,199 +and content skips as much as possible. + +154 +00:08:19,233 --> 00:08:22,870 +So to this end, it is recommended that +you try to align the ad break durations + +155 +00:08:22,903 --> 00:08:26,173 +such that the wait or skip times +are minimized. + +156 +00:08:26,206 --> 00:08:29,409 +When you have mixed participant groups +where some people have ads + +157 +00:08:29,443 --> 00:08:32,513 +and some don't +and all are watching live content, + +158 +00:08:32,546 --> 00:08:35,415 +you can use HLS interstitials +to schedule the ads + +159 +00:08:35,449 --> 00:08:38,652 +while using the default waiting policy. + +160 +00:08:38,685 --> 00:08:42,022 +Now, if you don't specify +the resume offset while scheduling the ad, + +161 +00:08:42,055 --> 00:08:46,226 +the content would resume at an offset +that equals the ad duration. + +162 +00:08:46,260 --> 00:08:48,662 +In such a scenario +the participant without ads + +163 +00:08:48,695 --> 00:08:51,164 +would continue to watch +the primary content + +164 +00:08:51,198 --> 00:08:55,135 +and the others would join that participant +once they've finished watching their ads. + +165 +00:08:56,236 --> 00:09:00,207 +Here we have Alice, Bob, and Charlie +watching some live content. + +166 +00:09:00,240 --> 00:09:03,710 +Alice being a premium subscriber +to that service does not have any ads, + +167 +00:09:03,744 --> 00:09:07,047 +while Bob and Charlie have ads +of different durations + +168 +00:09:07,080 --> 00:09:10,083 +all scheduled using HLS interstitials. + +169 +00:09:10,117 --> 00:09:12,686 +You can see how Alice continues +to watch the program + +170 +00:09:12,719 --> 00:09:16,323 +while Bob and Charlie sync up with Alice +when they come out of their ad break. + +171 +00:09:17,324 --> 00:09:19,059 +Now if this were a live sports game, + +172 +00:09:19,092 --> 00:09:22,629 +you can imagine how Alice might be +watching the live in-stadium feed + +173 +00:09:22,663 --> 00:09:25,132 +while Bob and Charlie are watching ads. + +174 +00:09:25,165 --> 00:09:26,533 +And when they come out of their ad break, + +175 +00:09:26,567 --> 00:09:30,170 +it would be just in time +for the start of play. + +176 +00:09:30,204 --> 00:09:33,140 +For Video on demand streams, +where participants can't afford + +177 +00:09:33,173 --> 00:09:34,775 +to miss any program content, + +178 +00:09:34,808 --> 00:09:37,678 +you can use the wait - waiting policy. + +179 +00:09:37,711 --> 00:09:40,981 +Now you can share the ad schedule +amongst the participants + +180 +00:09:41,014 --> 00:09:43,517 +using the GroupSessionMessenger. + +181 +00:09:43,550 --> 00:09:46,887 +And when someone is waiting for +the others to finish watching their ads, + +182 +00:09:46,920 --> 00:09:50,457 +you'd now know exactly how long +they would have to wait for + +183 +00:09:50,490 --> 00:09:52,960 +since you've already shared +the schedules. + +184 +00:09:52,993 --> 00:09:55,896 +So now while they're waiting, +you could perhaps use a different player + +185 +00:09:55,929 --> 00:09:57,664 +to show some interesting content, + +186 +00:09:57,698 --> 00:10:01,969 +like the upcoming attractions +in your service to keep them engaged. + +187 +00:10:02,002 --> 00:10:04,905 +To wrap up, in order to schedule ads +and other interstitials + +188 +00:10:04,938 --> 00:10:06,640 +during coordinated playback, + +189 +00:10:06,673 --> 00:10:09,710 +all you need to do +is specify the waiting policy, + +190 +00:10:09,743 --> 00:10:12,913 +and if you have stitched-in ads, +you can set the their time ranges + +191 +00:10:12,946 --> 00:10:16,617 +on the playback coordinator +using the AVFoundation API, + +192 +00:10:16,650 --> 00:10:22,589 +or you could use HLS interstitials to let +AVFoundation manage the playback of ads. + +193 +00:10:22,623 --> 00:10:27,160 +The ideal scenario would involve all +participants watching the same content. + +194 +00:10:27,194 --> 00:10:31,164 +As much we'd like for this to be possible, +this may not always be the case. + +195 +00:10:31,198 --> 00:10:34,535 +You could have a premium subscriber tier +that receive no ads + +196 +00:10:34,568 --> 00:10:38,639 +and you might also have participants +receiving ads of different durations. + +197 +00:10:38,672 --> 00:10:41,175 +In scenarios such as these, +your premium subscribers + +198 +00:10:41,208 --> 00:10:44,111 +don't have to be +staring at a waiting screen. + +199 +00:10:44,144 --> 00:10:47,347 +You could show them some +interesting content as we saw earlier + +200 +00:10:47,381 --> 00:10:50,984 +or you could even build other experiences +to keep them entertained. + +201 +00:10:51,018 --> 00:10:54,721 +For more ideas on how to build +custom experiences in SharePlay, + +202 +00:10:54,755 --> 00:10:57,758 +check out the +Make a great SharePlay experience talk. + +203 +00:10:57,791 --> 00:10:59,726 +Hope you found this session informative. + +204 +00:10:59,760 --> 00:11:01,595 +Enjoy the rest of WWDC. + +205 +00:11:01,628 --> 00:11:03,263 +Thank you. + diff --git a/eng/2022 Session 110381 Meet the expanded San Francisco font family en.srt b/eng/2022 Session 110381 Meet the expanded San Francisco font family en.srt new file mode 100644 index 0000000..3114cbd --- /dev/null +++ b/eng/2022 Session 110381 Meet the expanded San Francisco font family en.srt @@ -0,0 +1,1543 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,309 --> 00:00:11,912 +Hi, my name is Vincenzo, + +3 +00:00:11,945 --> 00:00:15,048 +and I'm a type designer +on the Apple Design Team. + +4 +00:00:15,082 --> 00:00:18,185 +I'm part of the group responsible +for the design and expansion + +5 +00:00:18,218 --> 00:00:21,255 +of many of the font families +used in our platforms. + +6 +00:00:21,288 --> 00:00:24,558 +We focus on making functional +and expressive fonts + +7 +00:00:24,591 --> 00:00:27,594 +that are essential +in enabling great typography. + +8 +00:00:27,628 --> 00:00:30,531 +I'm here to share some awesome +updates on San Francisco + +9 +00:00:30,564 --> 00:00:33,934 +and tell you about all the latest +additions to the system fonts. + +10 +00:00:33,967 --> 00:00:37,604 +We'll start with an overview +of the San Francisco family of fonts. + +11 +00:00:37,638 --> 00:00:40,040 +I'll take you through +the various available families, + +12 +00:00:40,073 --> 00:00:42,776 +styles, and Optical Sizes. + +13 +00:00:42,809 --> 00:00:46,079 +Then I'll introduce +the new width styles in SF Pro: + +14 +00:00:46,113 --> 00:00:48,515 +Condensed, Compressed, and Expanded. + +15 +00:00:48,549 --> 00:00:52,219 +I'll also share a few examples +of how and why to choose these styles + +16 +00:00:52,252 --> 00:00:54,855 +while designing User Interfaces. + +17 +00:00:54,888 --> 00:00:57,224 +And to wrap up, +I'll talk about how we're bringing + +18 +00:00:57,257 --> 00:01:00,928 +many of the features of San Francisco +to other writing systems, + +19 +00:01:00,961 --> 00:01:04,531 +introducing SF Arabic +and SF Arabic Rounded. + +20 +00:01:04,565 --> 00:01:06,200 +Let's jump right in, + +21 +00:01:06,233 --> 00:01:09,303 +starting with a quick overview +of San Francisco. + +22 +00:01:09,336 --> 00:01:12,706 +San Francisco is a functional +and versatile family of fonts + +23 +00:01:12,739 --> 00:01:16,009 +designed by Apple +and used throughout our platforms. + +24 +00:01:16,043 --> 00:01:18,612 +Over the years San Francisco +has grown in parallel + +25 +00:01:18,645 --> 00:01:21,248 +with an eco-system of products +and experiences, + +26 +00:01:21,281 --> 00:01:23,417 +providing new styles and families + +27 +00:01:23,450 --> 00:01:26,820 +capable of adapting +to the various needs of UI design. + +28 +00:01:26,854 --> 00:01:30,057 +Today San Francisco includes +different font families + +29 +00:01:30,090 --> 00:01:32,492 +which share a common visual language + +30 +00:01:32,526 --> 00:01:36,029 +but with unique features designed +specifically for different environments + +31 +00:01:36,063 --> 00:01:39,533 +and applications, +such as SF Pro and SF Pro Rounded, + +32 +00:01:39,566 --> 00:01:42,669 +the main system fonts +used in many of our platforms, + +33 +00:01:42,703 --> 00:01:47,107 +like iOS, iPadOS, macOS, and tvOS. + +34 +00:01:47,140 --> 00:01:49,643 +SF Compact and SF Compact Rounded, + +35 +00:01:49,676 --> 00:01:55,148 +a variation of San Francisco optimized +for narrow columns and small point sizes, + +36 +00:01:55,182 --> 00:01:58,252 +also the default system fonts in watchOS. + +37 +00:01:58,285 --> 00:02:01,221 +And SF Mono, +the monospaced SF: + +38 +00:02:01,255 --> 00:02:05,626 +used in coding environments +like Xcode and Swift Playgrounds. + +39 +00:02:05,659 --> 00:02:09,363 +All of these font families, together, +provide great functionality + +40 +00:02:09,396 --> 00:02:11,031 +and range of expression + +41 +00:02:11,064 --> 00:02:14,968 +that allows text to be rendered +in a variety of contexts. + +42 +00:02:15,002 --> 00:02:18,872 +Most SF families come with +a comprehensive palette of weights, + +43 +00:02:18,906 --> 00:02:21,542 +spanning from Ultralight to Black, + +44 +00:02:21,575 --> 00:02:23,610 +providing enough stylistic variation + +45 +00:02:23,644 --> 00:02:27,147 +to create emphasis +and typographic hierarchies. + +46 +00:02:27,181 --> 00:02:30,851 +Also, most of the font families come +with Optical Sizes, + +47 +00:02:30,884 --> 00:02:34,221 +allowing the fonts to automatically adjust +certain design features + +48 +00:02:34,254 --> 00:02:37,024 +based on the point size of text. + +49 +00:02:37,057 --> 00:02:41,895 +For example, moving the dot on top of +the "i" further away from the base shape, + +50 +00:02:41,929 --> 00:02:45,899 +or opening up +the apertures of certain glyphs, + +51 +00:02:45,933 --> 00:02:50,671 +as well as progressively tracking out text +at smaller point sizes. + +52 +00:02:50,704 --> 00:02:55,342 +All of these adjustments optimize +the fonts generating unique designs + +53 +00:02:55,375 --> 00:02:57,744 +to fit different point sizes, + +54 +00:02:57,778 --> 00:03:00,681 +for both Display and Text typesetting, + +55 +00:03:00,714 --> 00:03:05,485 +typographic terms used in reference +to Large and Small text. + +56 +00:03:05,519 --> 00:03:09,223 +The San Francisco fonts offer control +and flexibility + +57 +00:03:09,256 --> 00:03:14,928 +to render clear and legible interfaces +consistently across all Apple platforms. + +58 +00:03:14,962 --> 00:03:17,197 +These are some of the highlights of SF, + +59 +00:03:17,231 --> 00:03:18,732 +but there's so much more. + +60 +00:03:18,765 --> 00:03:22,135 +If you want to dive deeper +into Optical Sizes, Variable fonts, + +61 +00:03:22,169 --> 00:03:24,671 +and Dynamic Type, +I encourage you to check out + +62 +00:03:24,705 --> 00:03:29,877 +Loïc and Jiang's session "The details +of UI typography" from WWDC 2020. + +63 +00:03:29,910 --> 00:03:33,380 +Now, today we're going to be focusing +on SF Pro, + +64 +00:03:33,413 --> 00:03:37,084 +which is already an incredible tool +to design User Interfaces. + +65 +00:03:37,117 --> 00:03:41,788 +However, while weights and optical sizes +already cover many designers' needs, + +66 +00:03:41,822 --> 00:03:44,491 +we identified a set of specific tasks + +67 +00:03:44,525 --> 00:03:47,928 +that couldn't be fulfilled by +just Weight and Optical axes. + +68 +00:03:47,961 --> 00:03:50,297 +For instance, in the Photos app, + +69 +00:03:50,330 --> 00:03:54,368 +where we expanded the stylistic variations +of the Titles in Memories. + +70 +00:03:54,401 --> 00:03:59,106 +Or in News, where SF serves +as the editorial voice in headlines, + +71 +00:03:59,139 --> 00:04:02,376 +with expressive +and space-efficient styles. + +72 +00:04:02,409 --> 00:04:06,246 +And lastly, in Maps, +where providing more stylistic options + +73 +00:04:06,280 --> 00:04:09,383 +increased typographic contrast +and legibility. + +74 +00:04:09,416 --> 00:04:12,753 +So we further expanded +the capabilities of SF + +75 +00:04:12,786 --> 00:04:15,255 +by designing a new axis. + +76 +00:04:15,289 --> 00:04:18,325 +With Weights and Optical Sizes +already available, + +77 +00:04:18,358 --> 00:04:22,896 +adding a Width axis was the natural +evolution of SF: + +78 +00:04:22,930 --> 00:04:27,034 +Joining the Regular Width +we have three more styles: + +79 +00:04:27,067 --> 00:04:30,237 +Condensed, +narrow and space-efficient; + +80 +00:04:30,270 --> 00:04:34,474 +Compressed, the most compact +style currently available in SF; + +81 +00:04:34,508 --> 00:04:39,213 +and last but not least, on +the other end of the spectrum, Expanded. + +82 +00:04:39,246 --> 00:04:41,982 +These are all the new available Widths +in SF, + +83 +00:04:42,015 --> 00:04:44,117 +which in combination with all the Weights, + +84 +00:04:44,151 --> 00:04:46,687 +add a tremendous amount of options, + +85 +00:04:46,720 --> 00:04:49,556 +bringing a whole set of new voices +and functionalities + +86 +00:04:49,590 --> 00:04:52,626 +to accommodate a broad range +of applications. + +87 +00:04:52,659 --> 00:04:56,864 +Some of these styles are more neutral; +others are more expressive. + +88 +00:04:56,897 --> 00:04:58,832 +Every style is functional +in its own right, + +89 +00:04:58,866 --> 00:05:03,270 +but depending on the design intent, +some will perform better than others. + +90 +00:05:03,303 --> 00:05:05,839 +There are no absolute rules in typography, + +91 +00:05:05,873 --> 00:05:09,076 +but you can expect the styles that +gravitate towards the center + +92 +00:05:09,109 --> 00:05:12,713 +of the stylistic map of SF +to have a more neutral voice, + +93 +00:05:12,746 --> 00:05:15,449 +while the styles +around the perimeter of the map, + +94 +00:05:15,482 --> 00:05:18,552 +which feature more extreme Weight +and Width proportions, + +95 +00:05:18,585 --> 00:05:21,722 +to be more expressive, +have a stronger voice, + +96 +00:05:21,755 --> 00:05:25,592 +attributes that can be useful +if you want to draw people's attention + +97 +00:05:25,626 --> 00:05:27,761 +towards a certain area of the UI + +98 +00:05:27,794 --> 00:05:30,497 +or strengthen a typographic hierarchy. + +99 +00:05:30,531 --> 00:05:34,101 +Let's explore the shared and unique +features of these styles + +100 +00:05:34,134 --> 00:05:36,537 +and learn how to make +more informed decisions + +101 +00:05:36,570 --> 00:05:40,107 +during the process of choosing +and pairing fonts. + +102 +00:05:40,140 --> 00:05:44,711 +First things first, all the styles provide +the same language support + +103 +00:05:44,745 --> 00:05:47,181 +and can be used to typeset +most of the languages + +104 +00:05:47,214 --> 00:05:51,285 +which use the Latin script, +from English to Vietnamese. + +105 +00:05:51,318 --> 00:05:56,290 +But also Greek and languages +based on the Cyrillic writing system. + +106 +00:05:56,323 --> 00:06:01,261 +Something else that all the styles +have in common is the vertical proportions + +107 +00:06:01,295 --> 00:06:06,466 +so that they can be used without any +visual scale discrepancies across Weight. + +108 +00:06:06,500 --> 00:06:11,538 +A feature that makes swapping +or mixing fonts predictable and easy, + +109 +00:06:11,572 --> 00:06:16,677 +so when using different Width styles, +the focus is solely on the Width itself. + +110 +00:06:16,710 --> 00:06:20,214 +In fact, only the horizontal proportions +of the glyphs change + +111 +00:06:20,247 --> 00:06:23,083 +and of course, as a byproduct, +the line length. + +112 +00:06:23,116 --> 00:06:27,120 +The progressively narrower proportions +highlight a defining aspect + +113 +00:06:27,154 --> 00:06:29,156 +that differs between the styles, + +114 +00:06:29,189 --> 00:06:32,826 +which is the relationship between +the stem thickness of each style, which– + +115 +00:06:32,860 --> 00:06:36,597 +albeit some optical adjustments– +remains fairly consistent. + +116 +00:06:36,630 --> 00:06:39,199 +Instead, the negative area +inside the glyphs, + +117 +00:06:39,233 --> 00:06:41,235 +which has a much more dramatic shift, + +118 +00:06:41,268 --> 00:06:45,305 +becomes much tighter or wider +depending on the Width style. + +119 +00:06:45,339 --> 00:06:49,042 +This balance has stylistic +and functional implications + +120 +00:06:49,076 --> 00:06:53,347 +in regards to the legibility of a font, +but also the personality. + +121 +00:06:53,380 --> 00:06:57,017 +So here are some considerations +regarding each style. + +122 +00:06:57,050 --> 00:07:00,554 +I'm sure it doesn't come as a surprise +that the Regular Width + +123 +00:07:00,587 --> 00:07:03,757 +is the most ubiquitous +and versatile style. + +124 +00:07:03,790 --> 00:07:07,794 +It features standard proportions, +making text easy to read. + +125 +00:07:07,828 --> 00:07:11,598 +You can stick to the Regular Width +when the functionalities offered + +126 +00:07:11,632 --> 00:07:14,034 +by the other Widths are not necessary. + +127 +00:07:14,067 --> 00:07:18,038 +Condensed, while looking narrower +compared to the Regular style, + +128 +00:07:18,071 --> 00:07:20,274 +still features a comfortable Width, + +129 +00:07:20,307 --> 00:07:25,045 +allowing itself to be used to fit +more text without being overly present. + +130 +00:07:25,078 --> 00:07:28,615 +Compressed features +very dense proportions, + +131 +00:07:28,649 --> 00:07:33,320 +with flat-sided shapes, which allow +the design to become much tighter, + +132 +00:07:33,353 --> 00:07:35,589 +making it very space-efficient + +133 +00:07:35,622 --> 00:07:39,426 +but also graphical and +ideal as a Display style. + +134 +00:07:39,459 --> 00:07:41,528 +And lastly, Expanded, + +135 +00:07:41,562 --> 00:07:43,931 +with a striking loose and open appearance + +136 +00:07:43,964 --> 00:07:46,133 +that can find use in Display typesetting + +137 +00:07:46,166 --> 00:07:49,269 +but also smaller labels, +and secondary content. + +138 +00:07:49,303 --> 00:07:52,539 +We can see them in action, +typesetting the same text + +139 +00:07:52,573 --> 00:07:54,842 +using the same Weight +but different Widths, + +140 +00:07:54,875 --> 00:07:59,246 +calls attention to one of the most obvious +use cases for these new styles, + +141 +00:07:59,279 --> 00:08:01,748 +which is space-efficiency. + +142 +00:08:01,782 --> 00:08:06,954 +In fact, we can notice how tighter +or wider SF looks at the same point size. + +143 +00:08:06,987 --> 00:08:10,657 +But also how we can typeset +Condensed or Compressed much larger + +144 +00:08:10,691 --> 00:08:12,860 +while still fitting in +the same line length, + +145 +00:08:12,893 --> 00:08:16,897 +which is very useful when +dealing with titles and headlines. + +146 +00:08:16,930 --> 00:08:19,700 +But it's not only about Display sizes. + +147 +00:08:19,733 --> 00:08:24,338 +In fact, the Width styles can +help control longer passages of text, + +148 +00:08:24,371 --> 00:08:27,708 +providing the ability +to fit more characters per line, + +149 +00:08:27,741 --> 00:08:31,745 +achieving a more ideal and comfortable +reading experience for narrow columns. + +150 +00:08:31,778 --> 00:08:34,348 +or just shortening +or lengthening a paragraph, + +151 +00:08:34,381 --> 00:08:37,518 +for filling or freeing space in a layout. + +152 +00:08:37,551 --> 00:08:41,622 +However, this example highlights +also how the Compressed style, + +153 +00:08:41,655 --> 00:08:44,458 +while still functional +for small single lines, + +154 +00:08:44,491 --> 00:08:48,896 +might be too tight for longer +passages of text, like you see here. + +155 +00:08:48,929 --> 00:08:54,201 +So always consider legibility +when opting for a non-Regular Width. + +156 +00:08:54,234 --> 00:08:57,104 +Now that we've seen the main features +of these new styles, + +157 +00:08:57,137 --> 00:08:59,907 +let me walk you through a few examples +of how they've helped us + +158 +00:08:59,940 --> 00:09:02,676 +improve and expand +the typography in our apps. + +159 +00:09:02,709 --> 00:09:06,613 +Let's start with Photos, +where the new range of Widths, + +160 +00:09:06,647 --> 00:09:09,650 +joined other styles +bringing even more visual interest + +161 +00:09:09,683 --> 00:09:11,852 +in the Titles of Memories + +162 +00:09:11,885 --> 00:09:15,155 +with text treatments that celebrate +and elevate the content + +163 +00:09:15,189 --> 00:09:17,224 +from the rest of the UI. + +164 +00:09:17,257 --> 00:09:20,994 +Taking a closer look, +we can see that the difference in scale + +165 +00:09:21,028 --> 00:09:24,464 +takes care of establishing +the typographic hierarchy, + +166 +00:09:24,498 --> 00:09:28,101 +telling us clearly +which is the primary piece of information + +167 +00:09:28,135 --> 00:09:31,238 +and which is the secondary. + +168 +00:09:31,271 --> 00:09:35,542 +Two contrasting Width styles, +like Compressed and Expanded, + +169 +00:09:35,576 --> 00:09:40,080 +take on the job of making the text +more unique and visually impactful. + +170 +00:09:40,113 --> 00:09:44,418 +Pairing the wide and condensed styles +of SF with different weights + +171 +00:09:44,451 --> 00:09:48,322 +can allow for a near-endless variety +of font pairings. + +172 +00:09:48,355 --> 00:09:51,458 +For instance, you can decide +to stick to just one Width style– + +173 +00:09:51,491 --> 00:09:53,193 +Expanded in this case– + +174 +00:09:53,227 --> 00:09:57,097 +and use contrasting Weights, +like Black and Light. + +175 +00:09:57,130 --> 00:10:01,235 +Or you can pick a single Weight +and use different contrasting Widths, + +176 +00:10:01,268 --> 00:10:03,837 +like Expanded and Compressed. + +177 +00:10:03,871 --> 00:10:08,642 +You can also combine both approaches +by mixing opposite Width and Weight, + +178 +00:10:08,675 --> 00:10:11,712 +for an even more decided contrast. + +179 +00:10:11,745 --> 00:10:14,481 +Together with the other +San Francisco families, + +180 +00:10:14,515 --> 00:10:17,751 +like SF Pro Rounded, SF Mono, + +181 +00:10:17,784 --> 00:10:20,888 +and also New York, +the serif system font, + +182 +00:10:20,921 --> 00:10:25,692 +the new Width styles open +a world of possible combinations. + +183 +00:10:25,726 --> 00:10:30,831 +In News, in addition to complementing +the editorial context of the app, + +184 +00:10:30,864 --> 00:10:35,502 +the Width styles provided +extra flexibility and practicality, + +185 +00:10:35,536 --> 00:10:36,803 +like in this example, + +186 +00:10:36,837 --> 00:10:41,742 +where using Condensed allows a long +Headline to be more space-efficient, + +187 +00:10:41,775 --> 00:10:44,611 +wrapping into four lines instead of five, + +188 +00:10:44,645 --> 00:10:46,680 +or even tighter, with Compressed, + +189 +00:10:46,713 --> 00:10:49,316 +which brings the title down +to three lines. + +190 +00:10:49,349 --> 00:10:51,718 +Opening up quite a bit of real estate +in the layout, + +191 +00:10:51,752 --> 00:10:55,956 +as well as making the Title stand up, +giving it more personality. + +192 +00:10:55,989 --> 00:11:00,127 +Notice also how the Expanded Widths, +used to contrast the Headline, + +193 +00:11:00,160 --> 00:11:03,130 +feel at home in captions and bylines. + +194 +00:11:03,163 --> 00:11:08,635 +We can see another specific but very +interesting use case for Expanded in Maps, + +195 +00:11:08,669 --> 00:11:11,505 +where the wide horizontal proportions +of this style + +196 +00:11:11,538 --> 00:11:13,507 +paired with loose tracking + +197 +00:11:13,540 --> 00:11:17,377 +work perfectly when used to represent +large geographical areas, + +198 +00:11:17,411 --> 00:11:19,446 +like mountain ranges. + +199 +00:11:19,479 --> 00:11:22,683 +But of course, +cartography's not just about mountains. + +200 +00:11:22,716 --> 00:11:28,355 +In fact, Maps adopted other Widths of SF +to create a rich typographic system, + +201 +00:11:28,388 --> 00:11:30,791 +with styles coherent between each other + +202 +00:11:30,824 --> 00:11:33,493 +but also diverse and contrasting, + +203 +00:11:33,527 --> 00:11:37,297 +making it easier to read +and tell apart all kinds of labels: + +204 +00:11:37,331 --> 00:11:40,801 +such as roads, +countries, continents, et cetera. + +205 +00:11:40,834 --> 00:11:44,137 +While, in most cases, +two or three styles are more than enough + +206 +00:11:44,171 --> 00:11:46,440 +to design an effective hierarchy, + +207 +00:11:46,473 --> 00:11:49,476 +this is a great demonstration +of how SF Pro can support + +208 +00:11:49,510 --> 00:11:52,412 +even the most elaborate graphic systems. + +209 +00:11:52,446 --> 00:11:57,284 +And those are just a few examples of how +to take advantage of the new Width styles. + +210 +00:11:57,317 --> 00:12:00,387 +The expanded functionality +and range of expression + +211 +00:12:00,420 --> 00:12:03,891 +make SF Pro an even more powerful +communication tool. + +212 +00:12:03,924 --> 00:12:08,262 +However, with great typographic power, +comes great responsibility. + +213 +00:12:08,295 --> 00:12:12,766 +Remember that fonts are design tools that +are meant to make interfaces beautiful, + +214 +00:12:12,799 --> 00:12:16,103 +but most importantly +legible and accessible. + +215 +00:12:16,136 --> 00:12:18,438 +Let's do a quick recap +with some of the considerations made + +216 +00:12:18,472 --> 00:12:22,476 +on the new Compressed, +Condensed, and Expanded styles. + +217 +00:12:22,509 --> 00:12:26,580 +First and foremost, the new Width styles +unlock an unprecedented level of control + +218 +00:12:26,613 --> 00:12:31,118 +in your layouts, giving you the ability +to make text wider or tighter + +219 +00:12:31,151 --> 00:12:36,056 +to efficiently design and arrange +typographic elements in your apps. + +220 +00:12:36,089 --> 00:12:39,026 +Also, +in addition to Weight, Size, and Color, + +221 +00:12:39,059 --> 00:12:42,029 +the new Width styles provide +more stylistic options + +222 +00:12:42,062 --> 00:12:46,700 +to create even more clear +and precise typographic hierarchies. + +223 +00:12:46,733 --> 00:12:49,903 +The fonts with more extreme +Weight and Width proportions + +224 +00:12:49,937 --> 00:12:54,208 +provide expressive styles that work great +at Display point sizes, + +225 +00:12:54,241 --> 00:12:58,345 +to elevate titles and headlines +from the rest of the UI. + +226 +00:12:58,378 --> 00:13:02,015 +All the SF Pro fonts cover most languages +using the Latin, + +227 +00:13:02,049 --> 00:13:04,017 +Greek, and Cyrillic alphabets. + +228 +00:13:04,051 --> 00:13:07,287 +However, if localization is +an essential aspect of your app + +229 +00:13:07,321 --> 00:13:09,923 +and you're looking to provide +a consistent experience + +230 +00:13:09,957 --> 00:13:11,859 +across multiple writing systems, + +231 +00:13:11,892 --> 00:13:13,994 +other than the aforementioned ones, + +232 +00:13:14,027 --> 00:13:15,963 +make sure you have a fallback plan, + +233 +00:13:15,996 --> 00:13:20,634 +by identifying alternative solutions +that can help you localize your app. + +234 +00:13:20,667 --> 00:13:24,805 +Okay, so we did go through many +of the new features of the system font, + +235 +00:13:24,838 --> 00:13:26,440 +but we're not done yet. + +236 +00:13:26,473 --> 00:13:29,910 +There is another way +the family is expanding. + +237 +00:13:29,943 --> 00:13:33,113 +At Apple, we design for a global audience. + +238 +00:13:33,146 --> 00:13:36,416 +Our devices are used by people +from all around the world + +239 +00:13:36,450 --> 00:13:39,553 +who read and write different scripts +and languages. + +240 +00:13:39,586 --> 00:13:42,356 +Fonts play a huge role +in designing interfaces + +241 +00:13:42,389 --> 00:13:45,125 +that are both stylistically +and functionally consistent + +242 +00:13:45,158 --> 00:13:47,528 +across multiple languages, + +243 +00:13:47,561 --> 00:13:51,899 +providing integrity to the many +graphical elements featuring text. + +244 +00:13:51,932 --> 00:13:53,834 +And text is everywhere, + +245 +00:13:53,867 --> 00:13:58,172 +like in the Large Titles and Table Rows +in these screens, + +246 +00:13:58,205 --> 00:14:00,507 +but also in Buttons, + +247 +00:14:00,541 --> 00:14:03,544 +Platters, Icon labels. + +248 +00:14:03,577 --> 00:14:06,213 +Text is in Widgets, Keyboards, + +249 +00:14:06,246 --> 00:14:08,248 +Tab bars and many more, + +250 +00:14:08,282 --> 00:14:11,618 +in many more languages! + +251 +00:14:11,652 --> 00:14:14,221 +One area where +we incorporate these principles + +252 +00:14:14,254 --> 00:14:17,691 +and we can achieve +the highest level of consistency + +253 +00:14:17,724 --> 00:14:20,761 +is with experiences +that are self-contained + +254 +00:14:20,794 --> 00:14:24,765 +and can use fonts we design +for the ultimate localized experience, + +255 +00:14:24,798 --> 00:14:28,635 +allowing your screensaver on the Mac +to greet you in Japanese + +256 +00:14:28,669 --> 00:14:32,139 +and many other languages with +the iconic "hello" cursive style. + +257 +00:14:32,172 --> 00:14:35,876 +or the iPhone to display the time +using Arabic, + +258 +00:14:35,909 --> 00:14:38,679 +Arabic-Indic, or Devanagari numerals + +259 +00:14:38,712 --> 00:14:41,682 +with the same consistent typeface. + +260 +00:14:41,715 --> 00:14:43,650 +Or on the Typograph watch-face, + +261 +00:14:43,684 --> 00:14:46,353 +which features +five different numeral sets, + +262 +00:14:46,386 --> 00:14:49,122 +including the new Chinese numerals. + +263 +00:14:49,156 --> 00:14:53,427 +We want to bring the same level +of consistency to the system fonts, + +264 +00:14:53,460 --> 00:14:56,830 +which have to work +for a much broader range of applications. + +265 +00:14:56,864 --> 00:15:00,868 +This requires extending +the linguistic coverage of San Francisco + +266 +00:15:00,901 --> 00:15:03,971 +and allowing other writing systems +to benefit from features + +267 +00:15:04,004 --> 00:15:06,340 +like Weights and Optical Sizes, + +268 +00:15:06,373 --> 00:15:08,809 +providing a reading experience that is, +at the same time, + +269 +00:15:08,842 --> 00:15:10,844 +visually engaging and comfortable, + +270 +00:15:10,878 --> 00:15:13,981 +for every script and language, +in any of our platforms. + +271 +00:15:14,014 --> 00:15:16,683 +This expansion started last year + +272 +00:15:16,717 --> 00:15:19,553 +when we introduced +the new Arabic system font, + +273 +00:15:19,586 --> 00:15:21,321 +SF Arabic. + +274 +00:15:21,355 --> 00:15:24,224 +We designed SF Arabic +to seamlessly integrate + +275 +00:15:24,258 --> 00:15:27,094 +with the rest +of the San Francisco families. + +276 +00:15:27,127 --> 00:15:30,964 +SF Arabic is a contemporary interpretation +of the Naskh style + +277 +00:15:30,998 --> 00:15:34,001 +with a rational and flexible design, + +278 +00:15:34,034 --> 00:15:38,172 +and it's the latest writing system, +after Latin, Greek, and Cyrillic, + +279 +00:15:38,205 --> 00:15:41,074 +to benefit from the most advanced +font technologies, + +280 +00:15:41,108 --> 00:15:43,944 +available on the Apple platforms. + +281 +00:15:43,977 --> 00:15:46,313 +Like the other San Francisco families, + +282 +00:15:46,346 --> 00:15:49,049 +it features the full gamut of weights, + +283 +00:15:49,082 --> 00:15:51,351 +Ultralight through Black, + +284 +00:15:51,385 --> 00:15:55,355 +and each of the nine weights has been +carefully designed to fit and combine + +285 +00:15:55,389 --> 00:15:59,226 +with the other scripts +and languages supported in SF. + +286 +00:15:59,259 --> 00:16:03,163 +Also, SF Arabic comes with Optical Sizes + +287 +00:16:03,197 --> 00:16:06,233 +tailored specifically +for the Arabic script. + +288 +00:16:06,266 --> 00:16:09,603 +Allowing the fonts to automatically adjust +the stroke contrast + +289 +00:16:09,636 --> 00:16:11,972 +and terminals and spacing, + +290 +00:16:12,005 --> 00:16:16,376 +adapting and scaling the design +between Display and Text sizes. + +291 +00:16:16,410 --> 00:16:20,380 +The Text styles are fine-tuned +for high Legibility and familiarity, + +292 +00:16:20,414 --> 00:16:23,984 +featuring a higher stroke contrast +and wider spacing. + +293 +00:16:24,017 --> 00:16:28,555 +While the Display styles morph into +a design with a simpler geometry, + +294 +00:16:28,589 --> 00:16:32,359 +blending with the clean +and contemporary look of San Francisco. + +295 +00:16:32,392 --> 00:16:35,596 +And all of this is automatically +and dynamically taken care of + +296 +00:16:35,629 --> 00:16:37,064 +by the system, + +297 +00:16:37,097 --> 00:16:41,301 +enabling a comfortable +reading experience at all sizes. + +298 +00:16:41,335 --> 00:16:44,505 +Like the Latin, +and Cyrillic writing systems, + +299 +00:16:44,538 --> 00:16:47,274 +the Arabic script is highly multilingual + +300 +00:16:47,307 --> 00:16:50,544 +and is used to write +not only the Arabic language, + +301 +00:16:50,577 --> 00:16:52,446 +but also a variety of other languages, + +302 +00:16:52,479 --> 00:16:54,548 +such as Persian, Pashto, + +303 +00:16:54,581 --> 00:16:59,353 +Sindhi, and many more, +all supported by SF Arabic. + +304 +00:16:59,386 --> 00:17:02,856 +Numerous vocalization, +tone, and poetic marks, + +305 +00:17:02,890 --> 00:17:07,327 +extended vowel signs, honorifics, +and Quranic annotations + +306 +00:17:07,361 --> 00:17:12,132 +complete the extensive character set +supported in SF Arabic. + +307 +00:17:12,165 --> 00:17:15,235 +And I'm excited to announce +that this year we're introducing + +308 +00:17:15,269 --> 00:17:18,172 +a new member to the San Francisco family, + +309 +00:17:18,205 --> 00:17:20,307 +SF Arabic Rounded. + +310 +00:17:20,340 --> 00:17:23,744 +Which packs the same great features +of SF Arabic, + +311 +00:17:23,777 --> 00:17:29,116 +coming with a full range of Weights, +and Optical Sizes. + +312 +00:17:29,149 --> 00:17:32,920 +The new Rounded design +enriches many apps and UIs. + +313 +00:17:32,953 --> 00:17:36,990 +Such as in Reminders and the Fitness app. + +314 +00:17:37,024 --> 00:17:40,227 +It expands the stylistic range +of SF Arabic, + +315 +00:17:40,260 --> 00:17:42,729 +and joins the rest of the system fonts, + +316 +00:17:42,763 --> 00:17:49,303 +making Arabic typography on Apple +platforms modern, clear, and refined. + +317 +00:17:49,336 --> 00:17:52,840 +If you want to see all the new +SF Arabic fonts in action + +318 +00:17:52,873 --> 00:17:56,109 +and want to learn more about the design +and optimization of apps + +319 +00:17:56,143 --> 00:17:57,711 +for the Arabic audience, + +320 +00:17:57,744 --> 00:18:01,615 +be sure to check out Mohamed's session, +"Design for Arabic". + +321 +00:18:01,648 --> 00:18:05,886 +The latest SF Pro, +SF Arabic, SF Arabic Rounded, + +322 +00:18:05,919 --> 00:18:07,421 +and all the other font families, + +323 +00:18:07,454 --> 00:18:12,392 +are available for download +on the developer.apple.com website. + +324 +00:18:12,426 --> 00:18:14,595 +And that's a wrap for this session. + +325 +00:18:14,628 --> 00:18:16,964 +I hope you enjoyed learning +about all the latest additions + +326 +00:18:16,997 --> 00:18:18,632 +to the system fonts. + +327 +00:18:18,665 --> 00:18:20,033 +Thank you for watching. + +328 +00:18:20,067 --> 00:18:21,635 +Ciao! + diff --git a/eng/2022 Session 110384 Support multiple users in tvOS apps en.srt b/eng/2022 Session 110384 Support multiple users in tvOS apps en.srt new file mode 100644 index 0000000..173556a --- /dev/null +++ b/eng/2022 Session 110384 Support multiple users in tvOS apps en.srt @@ -0,0 +1,1061 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:11,345 +Hi, my name is Felipe. + +3 +00:00:11,378 --> 00:00:14,081 +I'm a software engineer on the tvOS team. + +4 +00:00:14,114 --> 00:00:18,652 +Today I'm excited to share with you +the improvements we've made in tvOS 16 + +5 +00:00:18,685 --> 00:00:22,623 +to make it easier to support +multiple users in your apps. + +6 +00:00:22,656 --> 00:00:26,460 +Apple TV is designed to be used +by everyone. + +7 +00:00:26,493 --> 00:00:31,899 +We've been improving multiple users +on Apple TV since tvOS 13. + +8 +00:00:31,932 --> 00:00:35,802 +In tvOS 16 we have new features +that will make it easier + +9 +00:00:35,836 --> 00:00:38,872 +to support multiple users in all apps. + +10 +00:00:38,906 --> 00:00:43,243 +I'll start with an overview of +the multiple users features on Apple TV. + +11 +00:00:43,277 --> 00:00:48,582 +Showing how easy it is personalize apps +on a device used by everyone. + +12 +00:00:48,615 --> 00:00:53,187 +Then I'll demonstrate how simple it is +to store the profile selection + +13 +00:00:53,220 --> 00:00:56,490 +for each user of a streaming app. + +14 +00:00:56,523 --> 00:01:00,827 +Finishing with recommendations +for the best approach for your own app. + +15 +00:01:00,861 --> 00:01:05,799 +In tvOS 16 we are making it easier +to have the entire family on Apple TV. + +16 +00:01:06,600 --> 00:01:09,903 +When you press and hold the TV button +on your Siri Remote, + +17 +00:01:09,937 --> 00:01:12,339 +Control Center shows up. + +18 +00:01:12,372 --> 00:01:16,844 +At the top is the list of users +that are on this Apple TV. + +19 +00:01:16,877 --> 00:01:18,812 +New in tvOS 16, + +20 +00:01:18,846 --> 00:01:21,648 +the list of users includes +iCloud Family members + +21 +00:01:21,682 --> 00:01:23,450 +that haven't been added yet, + +22 +00:01:23,483 --> 00:01:27,654 +making it easier than ever +to have the entire family on Apple TV. + +23 +00:01:29,122 --> 00:01:32,526 +Here, Anne isn't on the Apple TV yet. + +24 +00:01:32,559 --> 00:01:35,863 +The plus icon indicates a suggested user. + +25 +00:01:35,896 --> 00:01:40,033 +To have Anne join, +we can simply select the suggested user. + +26 +00:01:41,835 --> 00:01:48,542 +All Anne needs to do is bring their iPhone +to the same room as the Apple TV, + +27 +00:01:48,575 --> 00:01:52,646 +and on the iPhone confirm +the connection. + +28 +00:01:52,679 --> 00:01:54,348 +And that is all. + +29 +00:01:54,381 --> 00:01:58,485 +Anne has now access +to their data on Apple TV. + +30 +00:01:58,519 --> 00:02:02,756 +Now let's go back to Anne's iPhone +for a moment. + +31 +00:02:02,789 --> 00:02:06,059 +Let's take a look at Anne's +favorite video streaming app. + +32 +00:02:06,093 --> 00:02:08,695 +The app is used by the entire family + +33 +00:02:08,729 --> 00:02:12,165 +and each person has their own profile. + +34 +00:02:12,199 --> 00:02:15,802 +When the app first launches, +it presents a profile picker, + +35 +00:02:15,836 --> 00:02:18,539 +Anne picks her profile. + +36 +00:02:18,572 --> 00:02:21,475 +The app, running on a personal device, + +37 +00:02:21,508 --> 00:02:25,979 +can simply store the preferred profile +in NSUserDefaults, + +38 +00:02:26,013 --> 00:02:30,350 +or even put in CloudKit +to have it synced to all devices. + +39 +00:02:31,919 --> 00:02:34,888 +The iPhone of each member of the family + +40 +00:02:34,922 --> 00:02:38,058 +remembers +that person's preferred profile. + +41 +00:02:38,091 --> 00:02:42,896 +There is no need to keep asking +which profile to use when the app opens. + +42 +00:02:42,930 --> 00:02:45,999 +Apple TV is a device shared +by the entire family, + +43 +00:02:46,033 --> 00:02:50,404 +and we think it is really important +that everyone who uses Apple TV + +44 +00:02:50,437 --> 00:02:54,007 +is able to access +their own content and data. + +45 +00:02:54,575 --> 00:03:01,315 +In tvOS 14 we introduced the ability +for apps to run as the current user. + +46 +00:03:01,348 --> 00:03:05,219 +With a single checkbox to add +the "Runs as Current User" entitlement, + +47 +00:03:05,252 --> 00:03:07,454 +and no code changes at all, + +48 +00:03:07,487 --> 00:03:12,292 +apps can access each user's own data, +just like on iPhone. + +49 +00:03:16,964 --> 00:03:22,102 +Your iOS code, calling those APIs, +can run as is on Apple TV, + +50 +00:03:22,135 --> 00:03:27,174 +behaving like each person +is using their own personal Apple TV. + +51 +00:03:27,207 --> 00:03:30,844 +When the app launches, +tvOS takes care of everything. + +52 +00:03:30,878 --> 00:03:34,982 +Privacy and security, +all handled by the system. + +53 +00:03:35,015 --> 00:03:38,752 +The "runs as current user" entitlement +is perfect for games + +54 +00:03:38,785 --> 00:03:43,557 +and any apps that are all +about individual progress. + +55 +00:03:43,590 --> 00:03:47,794 +Media apps with profiles, though, +need a little bit more. + +56 +00:03:47,828 --> 00:03:52,232 +In my example so far, +I skipped an important part: the sign-in. + +57 +00:03:52,266 --> 00:03:58,872 +We believe signing in needs to be +as easy and infrequent as possible. + +58 +00:03:58,906 --> 00:04:03,944 +In tvOS 15 we introduced a feature +that allows people to use their iPhone + +59 +00:04:03,977 --> 00:04:09,550 +or iPad to seamlessly sign in +on your tvOS apps. + +60 +00:04:09,583 --> 00:04:12,019 +It is a first-class sign-in experience, + +61 +00:04:12,052 --> 00:04:14,154 +where the devices work together + +62 +00:04:14,188 --> 00:04:18,492 +to offer the most convenient way +to sign in on Apple TV. + +63 +00:04:18,525 --> 00:04:21,495 +Features not directly available +on Apple TV, + +64 +00:04:21,528 --> 00:04:25,933 +like your passwords in iCloud Keychain, +are just a tap away. + +65 +00:04:25,966 --> 00:04:29,069 +To learn how to build +great sign-in experiences, + +66 +00:04:29,102 --> 00:04:34,741 +watch "Simplify sign-in for +your tvOS apps" from WWDC 2021. + +67 +00:04:36,109 --> 00:04:39,546 +And this year +we're introducing support for OAuth + +68 +00:04:39,580 --> 00:04:42,049 +and passkeys on tvOS. + +69 +00:04:42,082 --> 00:04:45,152 +Passkeys are an awesome +new authentication technology + +70 +00:04:45,185 --> 00:04:48,722 +for replacing passwords +in your apps and websites. + +71 +00:04:48,755 --> 00:04:53,894 +To learn more about passkeys, +watch the "Meet Passkeys" video. + +72 +00:04:53,927 --> 00:04:57,431 +But ease and convenience isn't all. + +73 +00:04:57,464 --> 00:05:01,568 +It is still important +to have sign-in happen only once. + +74 +00:05:01,602 --> 00:05:05,506 +To achieve this optimal user experience, +with "Runs as Current User," + +75 +00:05:05,539 --> 00:05:09,910 +we're introducing +a simple new API in tvOS 16. + +76 +00:05:09,943 --> 00:05:12,913 +It is a new constant in Keychain Services + +77 +00:05:12,946 --> 00:05:17,484 +that allows apps to access +a user-independent Keychain. + +78 +00:05:17,518 --> 00:05:23,423 +Items stored using this new key are always +accessible by all users on Apple TV. + +79 +00:05:23,457 --> 00:05:26,293 +Let's look at an example. + +80 +00:05:26,326 --> 00:05:30,163 +Here is a method that saves +an item in the Keychain, + +81 +00:05:30,197 --> 00:05:33,367 +with username and password. + +82 +00:05:33,400 --> 00:05:36,403 +To save the item +to the user-independent Keychain, + +83 +00:05:36,436 --> 00:05:40,440 +all you have to do is add a new key to the +attributes dictionary + +84 +00:05:40,474 --> 00:05:43,810 +that is passed to the function SecItemAdd. + +85 +00:05:43,844 --> 00:05:49,850 +We simply pass true as the value for +the key kSecUseUserIndependentKeychain. + +86 +00:05:51,084 --> 00:05:53,353 +With the user-independent Keychain set, + +87 +00:05:53,387 --> 00:05:58,458 +the items you read and write +will be visible to all users. + +88 +00:05:58,492 --> 00:06:01,528 +The combination of both +the new Keychain API + +89 +00:06:01,562 --> 00:06:03,664 +and "Runs as Current User" entitlement + +90 +00:06:03,697 --> 00:06:08,335 +allows your apps to have the best +of iPhone and the best of Apple TV: + +91 +00:06:08,368 --> 00:06:12,272 +simple per-user data separation +on a shared device + +92 +00:06:12,306 --> 00:06:16,677 +while maintaining +the single sign-in experience on Apple TV. + +93 +00:06:16,710 --> 00:06:21,615 +In tvOS 16 we've deprecated +the methods to manually map profiles + +94 +00:06:21,648 --> 00:06:24,618 +to system users in TVUserManager. + +95 +00:06:24,651 --> 00:06:29,189 +There is no need for you to maintain +a map of users to profiles anymore. + +96 +00:06:29,223 --> 00:06:31,325 +The system will handle it for you. + +97 +00:06:31,358 --> 00:06:36,763 +The same APIs you use on iOS can be used, +as is, on Apple TV. + +98 +00:06:36,797 --> 00:06:39,800 +It is so much easier. + +99 +00:06:39,833 --> 00:06:42,436 +Let me show you it all +in action with a demo. + +100 +00:06:42,469 --> 00:06:47,274 +I'll walk you through a sample app +that represents media apps with profiles. + +101 +00:06:47,307 --> 00:06:50,978 +This is my streaming video app. + +102 +00:06:51,011 --> 00:06:55,315 +Currently it doesn't use any +of the features I talked about. + +103 +00:06:55,349 --> 00:07:00,821 +It doesn't run as the current user, +and it doesn't use the new Keychain API. + +104 +00:07:00,854 --> 00:07:04,124 +I'll go ahead and sign in to show you. + +105 +00:07:06,860 --> 00:07:10,531 +After sign-in +I'm presented with a list of profiles. + +106 +00:07:10,564 --> 00:07:14,001 +The current user is Mei, +so I'll pick her profile. + +107 +00:07:16,270 --> 00:07:20,807 +Picking a profile shows the personalized +contents Mei wants to see. + +108 +00:07:22,176 --> 00:07:26,680 +Later, Anne sits on the couch to watch TV. + +109 +00:07:26,713 --> 00:07:29,082 +They open Control Center + +110 +00:07:29,116 --> 00:07:32,486 +by long pressing the TV button +on the Siri Remote. + +111 +00:07:32,519 --> 00:07:37,457 +Here at the top in Control Center +is the list of users. + +112 +00:07:37,491 --> 00:07:40,627 +The green checkmark indicates +the current user. + +113 +00:07:40,661 --> 00:07:43,897 +I'm going to switch to Anne now +and open the app. + +114 +00:07:45,566 --> 00:07:49,269 +As expected, +the app presents the profile picker again. + +115 +00:07:49,303 --> 00:07:51,905 +So Anne can select their profile. + +116 +00:07:54,174 --> 00:07:57,945 +But watch what happens +when switching back to Mei. + +117 +00:08:02,349 --> 00:08:04,651 +The app stays in Anne's profile. + +118 +00:08:04,685 --> 00:08:08,856 +It doesn't remember that Mei +had picked her preferred profile before. + +119 +00:08:08,889 --> 00:08:11,525 +With the new features in tvOS 16, + +120 +00:08:11,558 --> 00:08:14,228 +the user experience can be much better. + +121 +00:08:14,261 --> 00:08:19,166 +I want people using the app to not +have to select again who is watching. + +122 +00:08:19,199 --> 00:08:22,769 +Let's go to Xcode, +and I'll show you how easy it is. + +123 +00:08:22,803 --> 00:08:27,074 +Making my app executes +as the current user is simple. + +124 +00:08:27,107 --> 00:08:31,612 +All I have to do is add one capability +to my app's target. + +125 +00:08:31,645 --> 00:08:33,614 +Here in my app's target, + +126 +00:08:33,647 --> 00:08:37,451 +I'll go over +to the Signing & Capabilities tab. + +127 +00:08:37,484 --> 00:08:40,254 +Just under the tab, on the top left here, + +128 +00:08:40,287 --> 00:08:43,757 +I'll click the Add Capability button. + +129 +00:08:43,790 --> 00:08:46,393 +It opens the list of capabilities. + +130 +00:08:46,426 --> 00:08:49,363 +The one I'm interested in +is called User Management. + +131 +00:08:49,396 --> 00:08:52,165 +So I'll type "user" to filter the list. + +132 +00:08:53,700 --> 00:08:57,437 +Now I just double-click the capability +I want to add to my app. + +133 +00:08:58,805 --> 00:09:02,509 +Down here +the "Runs as Current User" checkbox + +134 +00:09:02,543 --> 00:09:05,512 +is selected by default. + +135 +00:09:05,546 --> 00:09:07,981 +That is exactly what I need. + +136 +00:09:08,015 --> 00:09:11,251 +With this entitlement, my app's process +will always be launched + +137 +00:09:11,285 --> 00:09:13,620 +as the currently selected user. + +138 +00:09:13,654 --> 00:09:17,991 +The system will make sure +that the data for each user is distinct. + +139 +00:09:18,025 --> 00:09:20,961 +All of it, including the Keychain. + +140 +00:09:20,994 --> 00:09:24,298 +But for my app, +having a distinct Keychain per user, + +141 +00:09:24,331 --> 00:09:26,900 +would have an unexpected side-effect. + +142 +00:09:26,934 --> 00:09:30,804 +It would require +each user to sign in separately. + +143 +00:09:30,838 --> 00:09:36,610 +In tvOS 16 we can maintain the experience +of having a single account for all users + +144 +00:09:36,643 --> 00:09:39,446 +while running as the current user. + +145 +00:09:39,479 --> 00:09:44,885 +By using the new Keychain API +to access the user-independent Keychain. + +146 +00:09:44,918 --> 00:09:47,855 +I'll go now to +my KeychainController class, + +147 +00:09:47,888 --> 00:09:51,158 +I use it to read +and write to the Keychain. + +148 +00:09:52,993 --> 00:09:57,231 +Here I have the property baseQuery +which has the keys I need + +149 +00:09:57,264 --> 00:10:00,701 +to access the credentials +for my app in the Keychain. + +150 +00:10:00,734 --> 00:10:04,004 +To update it to use the new +user-independent Keychain, + +151 +00:10:04,037 --> 00:10:07,808 +all I have to do +is add a new element to the dictionary. + +152 +00:10:07,841 --> 00:10:11,411 +I'll add the new +kSecUseUserIndependentKeychain, + +153 +00:10:11,445 --> 00:10:13,247 +and set it to true. + +154 +00:10:15,649 --> 00:10:19,553 +And that is all I need to change +to make the app share its credentials + +155 +00:10:19,586 --> 00:10:20,621 +with all users. + +156 +00:10:20,654 --> 00:10:22,256 +Nothing else. + +157 +00:10:22,289 --> 00:10:24,224 +It is important to note, though, + +158 +00:10:24,258 --> 00:10:27,361 +all other data is still distinct per user. + +159 +00:10:27,394 --> 00:10:29,930 +Like UserDefaults, +which I'm going to use + +160 +00:10:29,963 --> 00:10:32,966 +to remember the profile selection +for each user. + +161 +00:10:34,434 --> 00:10:37,237 +Now let's open my ProfileData class. + +162 +00:10:37,271 --> 00:10:41,642 +This file is shared +between my iOS and tvOS projects. + +163 +00:10:41,675 --> 00:10:46,180 +In fact, the iOS version of my app +already stores the profile selection + +164 +00:10:46,213 --> 00:10:48,282 +in UserDefaults, + +165 +00:10:48,315 --> 00:10:50,484 +but only when running on iOS. + +166 +00:10:50,517 --> 00:10:54,555 +And that is because +iPhones are personal devices. + +167 +00:10:54,588 --> 00:10:57,524 +But now that my app runs +as the current user, + +168 +00:10:57,558 --> 00:11:00,260 +Apple TV can be as personal. + +169 +00:11:00,294 --> 00:11:05,799 +I can remove the iOS-check and have +the code work the same way on Apple TV. + +170 +00:11:05,832 --> 00:11:12,005 +The system will make sure to use +the correct UserDefaults for each user. + +171 +00:11:12,039 --> 00:11:15,542 +When I run the new version of the app, +it is launched as the current user, + +172 +00:11:15,576 --> 00:11:16,710 +which is Mei. + +173 +00:11:16,743 --> 00:11:20,113 +Mei signs in and pick her profile. + +174 +00:11:20,147 --> 00:11:24,251 +With the app running as the current user, +it behaves like Apple TV is, + +175 +00:11:24,284 --> 00:11:26,486 +at that moment, a personal device. + +176 +00:11:26,520 --> 00:11:28,121 +Mei's device. + +177 +00:11:28,155 --> 00:11:32,526 +Mei's profile selection +gets stored in her UserDefaults. + +178 +00:11:32,559 --> 00:11:34,561 +When it's time for Anne to watch, + +179 +00:11:34,595 --> 00:11:38,232 +they switch to their user +using Control Center. + +180 +00:11:38,265 --> 00:11:41,568 +The system will put up +a switching user UI, + +181 +00:11:41,602 --> 00:11:43,904 +while it gives time to your process + +182 +00:11:43,937 --> 00:11:48,442 +to finish any last tasks +before it is relaunched. + +183 +00:11:48,475 --> 00:11:51,979 +tvOS now relaunches the app as Anne. + +184 +00:11:52,012 --> 00:11:54,615 +Now Apple TV is Anne's device. + +185 +00:11:54,648 --> 00:11:58,318 +The sign-in prompt is skipped, +because the credentials are stored + +186 +00:11:58,352 --> 00:12:02,523 +in the user-independent Keychain, +accessible by all users. + +187 +00:12:02,556 --> 00:12:06,994 +But Anne doesn't have a profile selection +stored in their UserDefaults yet, + +188 +00:12:07,027 --> 00:12:09,062 +and the profile picker is shown. + +189 +00:12:09,096 --> 00:12:14,968 +Picking Anne's profile, +saves the selection in their UserDefaults. + +190 +00:12:15,002 --> 00:12:19,706 +Okay now, at this point Mei and Anne +both have selected their profile. + +191 +00:12:19,740 --> 00:12:21,141 +When switching users, + +192 +00:12:21,175 --> 00:12:24,511 +the app won't show +any interstitial prompts anymore. + +193 +00:12:24,545 --> 00:12:27,881 +It will always go straight to the content, + +194 +00:12:27,915 --> 00:12:31,451 +automatically selecting +the correct profile for each person. + +195 +00:12:31,485 --> 00:12:32,819 +Every time. + +196 +00:12:32,853 --> 00:12:36,790 +That is how, in tvOS 16, +we've made it much easier + +197 +00:12:36,823 --> 00:12:39,393 +to remember each user's profiles. + +198 +00:12:39,426 --> 00:12:43,664 +A simple new API to access +the user-independent Keychain, + +199 +00:12:43,697 --> 00:12:47,968 +with the system handling +separating each user's data. + +200 +00:12:48,001 --> 00:12:51,605 +Now that you've seen how to remember +each user's profile selection + +201 +00:12:51,638 --> 00:12:55,075 +without requiring everyone +to sign in separately, + +202 +00:12:55,108 --> 00:12:58,345 +let's review how apps work on Apple TV. + +203 +00:12:58,378 --> 00:13:01,782 +Apps without the runs +as current user entitlement + +204 +00:13:01,815 --> 00:13:04,952 +use the resources of the Default User. + +205 +00:13:04,985 --> 00:13:09,623 +You can think of it as being +the Apple TV's own resources. + +206 +00:13:09,656 --> 00:13:14,061 +Switching users have no influence +on the app's process. + +207 +00:13:14,094 --> 00:13:18,332 +Here are my recommendations +for when you do want to personalize. + +208 +00:13:18,365 --> 00:13:21,201 +This table shows +the available capabilities + +209 +00:13:21,235 --> 00:13:25,839 +depending on which features +you use on your tvOS apps. + +210 +00:13:25,873 --> 00:13:29,142 +If your app is a media app, +or like one, + +211 +00:13:29,176 --> 00:13:32,212 +shows personalized content +through profiles; + +212 +00:13:32,246 --> 00:13:36,717 +while having a single account shared +between everyone using the Apple TV, + +213 +00:13:36,750 --> 00:13:39,553 +you adopt +the Runs as Current User entitlement + +214 +00:13:39,586 --> 00:13:44,224 +and use the new user-independent +Keychain API. + +215 +00:13:44,258 --> 00:13:48,395 +If, like a game, +your app is all about individual progress, + +216 +00:13:48,428 --> 00:13:52,266 +all you have to do is adopt +the Runs as Current User entitlement, + +217 +00:13:52,299 --> 00:13:55,169 +tvOS will handle the rest. + +218 +00:13:55,202 --> 00:13:56,937 +And for all other apps, + +219 +00:13:56,970 --> 00:13:59,973 +apps that present the same content +to all users– + +220 +00:14:00,007 --> 00:14:02,242 +like for example, a recipe app– + +221 +00:14:02,276 --> 00:14:04,111 +you don't have to do anything. + +222 +00:14:04,144 --> 00:14:08,515 +Just create a new tvOS app project +in Xcode. + +223 +00:14:08,549 --> 00:14:11,218 +To learn more about +running as the current user, + +224 +00:14:11,251 --> 00:14:14,922 +watch our session from WWDC 2020. + +225 +00:14:14,955 --> 00:14:21,161 +We believe a streamlined direct-to-content +experience is the best one for a tvOS app. + +226 +00:14:21,195 --> 00:14:23,830 +I hope all these features help you +implement + +227 +00:14:23,864 --> 00:14:26,600 +great big-screen experiences in your apps. + +228 +00:14:26,633 --> 00:14:29,269 +Thanks for watching, +and enjoy the conference. + diff --git a/eng/2022 Session 110401 Create Swift Package plugins en.srt b/eng/2022 Session 110401 Create Swift Package plugins en.srt new file mode 100644 index 0000000..88b660b --- /dev/null +++ b/eng/2022 Session 110401 Create Swift Package plugins en.srt @@ -0,0 +1,1598 @@ +1 +00:00:00,767 --> 00:00:06,773 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:11,078 +Hi, my name is Boris, + +3 +00:00:11,111 --> 00:00:13,714 +and welcome to the session +"Create Swift package plugins." + +4 +00:00:14,848 --> 00:00:18,252 +We introduced support for Swift packages +in Xcode 11 + +5 +00:00:18,285 --> 00:00:21,588 +to offer a straightforward approach +to distributing libraries as source code. + +6 +00:00:22,523 --> 00:00:26,527 +In Xcode 14, we want to bring +that same great way to structure and share + +7 +00:00:26,560 --> 00:00:28,729 +components to development workflows, + +8 +00:00:28,762 --> 00:00:31,765 +such as generating source code +or automating release tasks, + +9 +00:00:31,798 --> 00:00:33,834 +with Swift package plugins. + +10 +00:00:34,701 --> 00:00:36,870 +First, a quick overview of the talk. + +11 +00:00:36,904 --> 00:00:38,839 +After learning the basics of plugins, + +12 +00:00:38,872 --> 00:00:42,242 +we'll build our first custom +command plugin in a demo. + +13 +00:00:42,276 --> 00:00:45,612 +Next, we'll look at more details +about creating plugins, + +14 +00:00:45,646 --> 00:00:49,416 +followed by building both an in-build +and a pre-build command plugin + +15 +00:00:49,449 --> 00:00:50,751 +in further demos. + +16 +00:00:51,818 --> 00:00:56,356 +A package plugin is Swift code +that uses the PackagePlugin API, + +17 +00:00:56,390 --> 00:00:58,725 +similar to a package manifest. + +18 +00:00:58,759 --> 00:01:01,028 +Plugins can extend +the functionality of Xcode + +19 +00:01:01,061 --> 00:01:04,598 +or the Swift Package Manager +through well-defined extension points. + +20 +00:01:06,567 --> 00:01:09,002 +How do package plugins work? + +21 +00:01:09,036 --> 00:01:12,539 +Xcode will compile and run your plugin, + +22 +00:01:12,573 --> 00:01:15,709 +which can use information +about available executables + +23 +00:01:15,742 --> 00:01:18,178 +and input files to construct commands + +24 +00:01:18,212 --> 00:01:19,913 +which it communicates back to Xcode + +25 +00:01:19,947 --> 00:01:22,182 +in order to execute them as needed. + +26 +00:01:24,718 --> 00:01:27,254 +Package plugins can contribute +custom build tasks + +27 +00:01:27,287 --> 00:01:29,556 +that run before or during the build, + +28 +00:01:29,590 --> 00:01:32,226 +for example, to generate +source code or resource files. + +29 +00:01:33,493 --> 00:01:37,030 +They can also add custom commands +to SwiftPM's command line interface + +30 +00:01:37,064 --> 00:01:39,666 +or menu items to Xcode. + +31 +00:01:39,700 --> 00:01:43,103 +For more information on the basics +of plugins, I would recommend watching + +32 +00:01:43,136 --> 00:01:44,938 +"Meet swift package plugins" first, + +33 +00:01:44,972 --> 00:01:47,841 +and in case you are new +to packages entirely, + +34 +00:01:47,875 --> 00:01:51,645 +you can watch the WWDC19 session +Creating Swift Packages. + +35 +00:01:53,547 --> 00:01:57,084 +Let's look at building +our first custom command plugin. + +36 +00:01:58,886 --> 00:02:01,388 +I'm working +on the tools-support-core package + +37 +00:02:01,421 --> 00:02:02,723 +from Swift open source, + +38 +00:02:02,756 --> 00:02:04,224 +and I'd like to add a text file + +39 +00:02:04,258 --> 00:02:06,793 +that lists +all the contributors to the project. + +40 +00:02:06,827 --> 00:02:10,731 +I also want to regenerate it as needed +from the Git history of the package. + +41 +00:02:12,065 --> 00:02:13,967 +Previously, +I might have written a shell script + +42 +00:02:14,001 --> 00:02:15,536 +or a makefile to do this, + +43 +00:02:15,569 --> 00:02:17,804 +but I'd like to create +a custom command plugin + +44 +00:02:17,838 --> 00:02:20,807 +so that I can re-generate +the file without having to leave Xcode. + +45 +00:02:22,943 --> 00:02:26,813 +First, we have to create +the directory structure for our plugin. + +46 +00:02:26,847 --> 00:02:31,018 +We open the context menu +on the package + +47 +00:02:31,051 --> 00:02:33,887 +and select New Folder + +48 +00:02:33,921 --> 00:02:38,792 +to create a top-level folder +called Plugins + +49 +00:02:38,825 --> 00:02:41,862 +similar to the existing Sources +and Tests. + +50 +00:02:43,697 --> 00:02:48,535 +Next, we will create another nested folder + +51 +00:02:48,569 --> 00:02:51,505 +for the plugin target, +called "GenerateContributors." + +52 +00:02:57,110 --> 00:03:00,747 +And inside there, we create a new file +and call it "plugin.swift." + +53 +00:03:12,426 --> 00:03:15,629 +Next, we need to make some changes +to the package manifest + +54 +00:03:15,662 --> 00:03:18,298 +to declare our new target there. + +55 +00:03:18,332 --> 00:03:22,336 +But first, we need to bump +the tools version for our package to 5.6 + +56 +00:03:22,369 --> 00:03:25,205 +since plugins are only available +since that version. + +57 +00:03:33,046 --> 00:03:35,516 +Next, we can insert our plugin target. + +58 +00:03:44,791 --> 00:03:47,761 +Let's take a look +at the new manifest API here. + +59 +00:03:49,496 --> 00:03:50,931 +We are creating a plugin target + +60 +00:03:50,964 --> 00:03:53,567 +which corresponds to a folder +inside the Plugins folder, + +61 +00:03:53,600 --> 00:03:55,402 +similar to source module targets. + +62 +00:03:56,904 --> 00:03:59,940 +It gets a name that is both relevant +for naming the folder + +63 +00:03:59,973 --> 00:04:02,209 +as well as a menu +item in Xcode. + +64 +00:04:03,477 --> 00:04:05,145 +We specify the capability, + +65 +00:04:05,179 --> 00:04:07,648 +so what type of extension point +we want to use. + +66 +00:04:07,681 --> 00:04:10,417 +In this case, we are making +a custom command. + +67 +00:04:12,119 --> 00:04:15,522 +The intent can define a verb +for the SwiftPM command line + +68 +00:04:15,556 --> 00:04:19,459 +as well as a description +of what the plugin does, + +69 +00:04:19,493 --> 00:04:22,729 +and finally, we can declare permissions +that the plugin requires. + +70 +00:04:24,264 --> 00:04:27,467 +In this case, we want to write a new file +to the root of the package, + +71 +00:04:27,501 --> 00:04:30,304 +so we need permissions +to write to that directory. + +72 +00:04:30,337 --> 00:04:33,106 +The reason string will be shown +to the user of the plugin + +73 +00:04:33,140 --> 00:04:35,909 +so that they know whether +or not to grant the permission, + +74 +00:04:35,943 --> 00:04:39,179 +similar to how permissions +work in the OS itself. + +75 +00:04:39,213 --> 00:04:43,650 +Now that we have declared the plugin, +let's go back to actually implement it. + +76 +00:04:45,686 --> 00:04:49,890 +The plugin will shell out to Git +to get the commit history. + +77 +00:04:49,923 --> 00:04:52,793 +It will read the history from standardout +of the external Git command + +78 +00:04:52,826 --> 00:04:55,229 +and parse the results + +79 +00:04:55,262 --> 00:04:57,998 +and finally write them out to a text +file. + +80 +00:04:59,399 --> 00:05:04,571 +We'll open our plugin source file +we created earlier + +81 +00:05:04,605 --> 00:05:06,173 +and import PackagePlugin. + +82 +00:05:09,109 --> 00:05:10,377 +This is a built-in module, + +83 +00:05:10,410 --> 00:05:13,614 +much like PackageDescription, +that gives us access to the APIs + +84 +00:05:13,647 --> 00:05:15,816 +we can use to implement plugins. + +85 +00:05:17,751 --> 00:05:24,491 +We define a struct GenerateContributors + +86 +00:05:24,525 --> 00:05:26,360 +and conform it to CommandPlugin. + +87 +00:05:32,165 --> 00:05:35,369 +We'll accept the fix-it here, + +88 +00:05:35,402 --> 00:05:39,506 +to get the missing stubs for implementing +the protocol. + +89 +00:05:39,540 --> 00:05:42,042 +We also need to mark our struct +as @main + +90 +00:05:42,075 --> 00:05:44,945 +since it will be the main function +of the plugin executable. + +91 +00:05:46,180 --> 00:05:49,016 +performCommand is the entry point +for our command, + +92 +00:05:49,049 --> 00:05:50,984 +and we receive two arguments: + +93 +00:05:51,018 --> 00:05:54,788 +context, which gives us access +to the resolved package graph + +94 +00:05:54,821 --> 00:05:58,292 +and other information about the context +we are being executed in, + +95 +00:05:58,325 --> 00:06:00,427 +as well as arguments. + +96 +00:06:00,460 --> 00:06:03,597 +Since custom commands +are invoked by the user, + +97 +00:06:03,630 --> 00:06:06,567 +they can provide input +in the form of arguments. + +98 +00:06:06,600 --> 00:06:08,735 +We are creating a simple command, + +99 +00:06:08,769 --> 00:06:10,571 +so we won't actually provide any options + +100 +00:06:10,604 --> 00:06:12,039 +to the user at this time. + +101 +00:06:20,113 --> 00:06:21,582 +Since we want to shell out to Git + +102 +00:06:21,615 --> 00:06:24,251 +in order to get information +about the commit history, + +103 +00:06:24,284 --> 00:06:25,586 +we are importing Foundation + +104 +00:06:25,619 --> 00:06:28,255 +because we want to use the Process API +to do so. + +105 +00:06:35,329 --> 00:06:39,333 +Next, we'll define a process instance +and set it to execute Git log + +106 +00:06:39,366 --> 00:06:41,401 +with some formatting arguments. + +107 +00:06:45,239 --> 00:06:48,509 +We need to create a pipe to capture +the process output. + +108 +00:06:48,542 --> 00:06:51,144 +Then we can run it and wait +until it exits. + +109 +00:06:55,015 --> 00:06:56,617 +After the process has finished, + +110 +00:06:56,650 --> 00:06:58,819 +we read all the data from the pipe + +111 +00:06:58,852 --> 00:07:01,822 +and convert it to a string which will have +all the git log output. + +112 +00:07:05,926 --> 00:07:07,828 +We do some string manipulation to trim + +113 +00:07:07,861 --> 00:07:10,697 +the output down to a list +without duplicates, + +114 +00:07:10,731 --> 00:07:12,232 +and finally, we can write it + +115 +00:07:12,266 --> 00:07:14,168 +to a file called "CONTRIBUTORS.txt," + +116 +00:07:14,201 --> 00:07:16,503 +and since the custom command is executed + +117 +00:07:16,537 --> 00:07:18,105 +in the package's root directory, + +118 +00:07:18,138 --> 00:07:19,506 +we'll store the file there. + +119 +00:07:22,276 --> 00:07:24,778 +Now, if we save and then +right-click on the package + +120 +00:07:24,811 --> 00:07:29,049 +in the project navigator, + +121 +00:07:29,082 --> 00:07:32,085 +there is a new entry for our command +in the context menu. + +122 +00:07:32,119 --> 00:07:33,520 +Let's execute it! + +123 +00:07:37,391 --> 00:07:40,427 +In the following dialog, +we can select the packages or targets + +124 +00:07:40,460 --> 00:07:43,997 +that should be the input +for our plugin as well as any arguments, + +125 +00:07:44,031 --> 00:07:47,234 +but since our plugin doesn't react +to these options, we can click Run. + +126 +00:07:51,538 --> 00:07:55,609 +Next we'll be asked for permissions, +as we defined in the manifest earlier. + +127 +00:07:55,642 --> 00:07:59,379 +Since we just wrote the plugin ourselves, +we can go ahead and run it, + +128 +00:07:59,413 --> 00:08:03,116 +but you should make sure that you only give +extra permissions to plugins you trust. + +129 +00:08:08,288 --> 00:08:11,992 +After running, the CONTRIBUTORS.txt +file shows up in the project navigator. + +130 +00:08:14,828 --> 00:08:18,198 +So now after we extended +Xcode with our first plugin, + +131 +00:08:18,232 --> 00:08:20,334 +let's go a bit deeper +into how plugins work + +132 +00:08:20,367 --> 00:08:22,736 +and what to look out +for when creating one. + +133 +00:08:26,240 --> 00:08:28,342 +Package plugins run in a sandbox, + +134 +00:08:28,375 --> 00:08:31,912 +similar to the evaluation +of the package manifest itself. + +135 +00:08:31,945 --> 00:08:34,915 +Network access and writing +to non-temporary locations + +136 +00:08:34,948 --> 00:08:38,218 +other than the plugin's own work directory +is prohibited. + +137 +00:08:38,252 --> 00:08:41,722 +Custom commands can optionally declare +that they'd like to write + +138 +00:08:41,755 --> 00:08:44,525 +to the package's root directory, +as shown earlier. + +139 +00:08:45,459 --> 00:08:47,895 +If you are wrapping +an existing third-party tool, + +140 +00:08:47,928 --> 00:08:51,098 +you may have to look into how +to confine it to the sandbox model, + +141 +00:08:51,131 --> 00:08:54,234 +for example, by configuring +where generated files get written to. + +142 +00:08:55,669 --> 00:08:58,338 +I talked about the different types +of plugins in the introduction, + +143 +00:08:58,372 --> 00:09:00,607 +and it should be clear whether a problem +is better solved + +144 +00:09:00,641 --> 00:09:02,476 +by a custom command or a build tool, + +145 +00:09:02,509 --> 00:09:05,546 +but let's take a look at the structure +of build tool plugins. + +146 +00:09:07,014 --> 00:09:09,283 +These plugins allow you +to extend the build system + +147 +00:09:09,316 --> 00:09:13,086 +by providing a description +on which executables to run during a build + +148 +00:09:13,120 --> 00:09:15,155 +and specifying their inputs and outputs + +149 +00:09:15,189 --> 00:09:18,725 +which helps with scheduling your work +at the appropriate time during a build. + +150 +00:09:19,726 --> 00:09:21,562 +You might be familiar with the basics here + +151 +00:09:21,595 --> 00:09:23,263 +if you have been creating +run script phases + +152 +00:09:23,297 --> 00:09:24,498 +in Xcode projects. + +153 +00:09:26,400 --> 00:09:29,469 +There are also two different types +of build tool plugins. + +154 +00:09:29,503 --> 00:09:33,273 +The distinguishing factor here is whether +your tool has a defined set of outputs. + +155 +00:09:34,541 --> 00:09:37,411 +If it does, you should create +an in-build command + +156 +00:09:37,444 --> 00:09:39,980 +which will automatically be re-run +by the build system + +157 +00:09:40,013 --> 00:09:43,684 +if your outputs +are out-of-date compared to your inputs. + +158 +00:09:43,717 --> 00:09:47,154 +If you don't have a clear set of outputs, +you can create a pre-build command + +159 +00:09:47,187 --> 00:09:49,556 +which runs at the start of every build. + +160 +00:09:49,590 --> 00:09:51,625 +Because of this, +you should be careful about doing + +161 +00:09:51,658 --> 00:09:53,794 +expensive work in pre-build commands + +162 +00:09:53,827 --> 00:09:56,630 +or come up with a custom strategy +for caching results + +163 +00:09:56,663 --> 00:09:58,532 +that's appropriate to your use case. + +164 +00:10:02,336 --> 00:10:04,972 +For our second demo, +I want to create a new library + +165 +00:10:05,005 --> 00:10:08,942 +that encapsulates icons I'd like to share +between different tools I am working on. + +166 +00:10:11,044 --> 00:10:13,447 +Let's get started and create +a new package from template + +167 +00:10:13,480 --> 00:10:15,115 +and call it "IconLibrary." + +168 +00:10:15,148 --> 00:10:19,987 +And I'm going to drag in some icon assets +I already have into my library's target. + +169 +00:10:20,020 --> 00:10:24,591 +Let's also add a basic SwiftUI view +and a preview to my library. + +170 +00:10:24,625 --> 00:10:28,328 +First, we need to add the required minimum +deployment targets to the manifest. + +171 +00:10:33,800 --> 00:10:37,171 +Next, let's actually add +that basic view and preview. + +172 +00:10:37,204 --> 00:10:40,407 +Here we can use our assets we dragged +in before. + +173 +00:10:44,978 --> 00:10:46,380 +I think it would be nice if, + +174 +00:10:46,413 --> 00:10:48,815 +instead +of having to deal with strings here, + +175 +00:10:48,849 --> 00:10:52,252 +we would have a type-safe way +to reference these images. + +176 +00:10:52,286 --> 00:10:55,289 +This seems like a great use case +for an in-build command plugin + +177 +00:10:55,322 --> 00:10:56,924 +which looks at asset catalogs + +178 +00:10:56,957 --> 00:10:59,560 +and generates some Swift code +based on them. + +179 +00:10:59,593 --> 00:11:01,995 +Let's take a look at an asset catalog +in Finder + +180 +00:11:02,029 --> 00:11:05,432 +to find out how we can extract +the information we need for the plugin. + +181 +00:11:06,466 --> 00:11:11,338 +Each image gets its own imageset directory +with the name of the asset... + +182 +00:11:15,709 --> 00:11:18,946 +And there's a JSON file +which describes the basic contents. + +183 +00:11:21,815 --> 00:11:24,284 +In-build commands work a little different +from custom commands + +184 +00:11:24,318 --> 00:11:27,120 +in that they're providing +a description of executables to run + +185 +00:11:27,154 --> 00:11:28,589 +as well as their inputs and outputs. + +186 +00:11:30,157 --> 00:11:32,559 +The executables +can be provided by the system, + +187 +00:11:32,593 --> 00:11:36,463 +third party packages, or you can create +one tailor-made for your plugin. + +188 +00:11:36,496 --> 00:11:38,298 +We want to take the third approach here. + +189 +00:11:40,300 --> 00:11:42,569 +Plugins get run +at the start of the build process + +190 +00:11:42,603 --> 00:11:45,239 +in order to participate in +computing the build graph. + +191 +00:11:46,807 --> 00:11:50,577 +Based on that, executables get scheduled +as part of build execution. + +192 +00:11:52,279 --> 00:11:54,915 +Now back +to the executable we're building. + +193 +00:11:54,948 --> 00:11:59,453 +We'd like to have a compile-time constant +for each image in an asset catalog + +194 +00:11:59,486 --> 00:12:03,123 +so that, instead of needing to remember +the correct strings for each image, + +195 +00:12:03,156 --> 00:12:05,392 +we'll get them autocompleted +as Swift symbols. + +196 +00:12:07,294 --> 00:12:09,229 +We want to loop +over the directory contents + +197 +00:12:09,263 --> 00:12:12,366 +of the asset catalog +to find all the image sets. + +198 +00:12:12,399 --> 00:12:15,469 +For each image set, we parse its metadata +to determine + +199 +00:12:15,502 --> 00:12:17,137 +if it actually contains any images + +200 +00:12:17,171 --> 00:12:19,740 +and should therefore get code generated +for it. + +201 +00:12:21,408 --> 00:12:24,278 +Then we can generate the code +and write to a file. + +202 +00:12:24,311 --> 00:12:27,381 +Since we declared those files as outputs +of our plugin, + +203 +00:12:27,414 --> 00:12:30,417 +they will automatically be incorporated +into the build of the target + +204 +00:12:30,450 --> 00:12:31,919 +the plugin is being applied to. + +205 +00:12:33,887 --> 00:12:35,789 +We'll need a way to deal with arguments + +206 +00:12:35,822 --> 00:12:39,126 +since that is how we communicate +between the plugin and the executable. + +207 +00:12:40,827 --> 00:12:46,200 +The first argument will be the path +to the asset catalog we are processing, + +208 +00:12:46,233 --> 00:12:49,903 +and the second one will be a path provided +by the plugin for our generated code. + +209 +00:12:51,605 --> 00:12:55,976 +Next we need some model objects +for decoding the contents.json files. + +210 +00:12:57,010 --> 00:13:00,948 +We use Decodable to take advantage +of Swift's built-in JSON decoding. + +211 +00:13:02,416 --> 00:13:06,720 +The only information we +are interested in are the list of images + +212 +00:13:06,753 --> 00:13:09,990 +and their filenames, which are optional +because there might not be + +213 +00:13:10,023 --> 00:13:13,493 +an image for each pixel density. + +214 +00:13:13,527 --> 00:13:15,762 +We'll generate code +in a simplistic manner here + +215 +00:13:15,796 --> 00:13:17,898 +by just building up a string. + +216 +00:13:17,931 --> 00:13:20,234 +We start it with imports +of the frameworks we need, + +217 +00:13:20,267 --> 00:13:21,735 +Foundation and SwiftUI. + +218 +00:13:23,637 --> 00:13:25,506 +We want to loop +over the directory contents + +219 +00:13:25,539 --> 00:13:26,773 +of the asset catalog + +220 +00:13:26,807 --> 00:13:29,543 +to find all the image sets + +221 +00:13:29,576 --> 00:13:32,546 +We need to parse the JSON next. + +222 +00:13:32,579 --> 00:13:35,916 +The filename uses the input parameter. + +223 +00:13:35,949 --> 00:13:39,353 +And we decode using +Foundation's 'JSONDecoder' API. + +224 +00:13:41,121 --> 00:13:43,357 +The main piece of information +we're interested in + +225 +00:13:43,390 --> 00:13:47,728 +is whether there is a defined image +for a given image set, + +226 +00:13:47,761 --> 00:13:50,631 +which we determine by checking +whether there's at least one image + +227 +00:13:50,664 --> 00:13:53,967 +with a non-empty filename. + +228 +00:13:54,001 --> 00:13:55,903 +If the given image set has an image, + +229 +00:13:55,936 --> 00:13:57,838 +we'd like to generate a SwiftUI image + +230 +00:13:57,871 --> 00:14:00,307 +which loads that image +from the package's bundle. + +231 +00:14:02,109 --> 00:14:05,579 +We do that by building a string +with the base name of each image + +232 +00:14:05,612 --> 00:14:07,748 +that loads the given image +from the module bundle, + +233 +00:14:07,781 --> 00:14:10,184 +which is the resource bundle +that the build system creates + +234 +00:14:10,217 --> 00:14:12,186 +for each package with resources. + +235 +00:14:13,620 --> 00:14:17,824 +We can wrap up the work of the executable +by writing the generated code to a file, + +236 +00:14:17,858 --> 00:14:19,426 +as given to us by arguments. + +237 +00:14:21,528 --> 00:14:24,231 +Let's go back to Xcode +and create the executable. + +238 +00:14:30,904 --> 00:14:33,473 +We call it "AssetConstantsExec"... + +239 +00:14:40,447 --> 00:14:41,782 +And add a main file. + +240 +00:14:51,358 --> 00:14:53,961 +Now we have to declare +it in the package manifest. + +241 +00:15:00,067 --> 00:15:03,070 +And we can add the code we just discussed +in its main file. + +242 +00:15:09,243 --> 00:15:11,945 +Now that we have +an executable that can generate code, + +243 +00:15:11,979 --> 00:15:14,681 +we can bring it +into the build system using a plugin. + +244 +00:15:41,408 --> 00:15:43,076 +Let's add the required target + +245 +00:15:43,110 --> 00:15:46,380 +and also add a usage of the plugin +from our library target. + +246 +00:16:09,436 --> 00:16:13,473 +As before, we're importing +the PackagePlugin library + +247 +00:16:13,507 --> 00:16:14,808 +and create a struct, + +248 +00:16:14,842 --> 00:16:17,578 +this time conforming it +to the BuildTool plugin protocol. + +249 +00:16:37,564 --> 00:16:41,068 +The entry point looks similar, +but instead of user arguments, + +250 +00:16:41,101 --> 00:16:42,903 +we are giving a target here. + +251 +00:16:42,936 --> 00:16:45,739 +This is the target that the plugin is +being applied to, + +252 +00:16:45,772 --> 00:16:49,643 +and the entry point will be called once +per target that uses the given plugin. + +253 +00:16:55,649 --> 00:16:59,353 +This plugin will care particularly +about source module targets, + +254 +00:16:59,386 --> 00:17:02,256 +which are any targets +which actually carry source files, + +255 +00:17:02,289 --> 00:17:05,492 +in contrast to, for example, a binary target. + +256 +00:17:05,526 --> 00:17:07,561 +To build up the array of build commands, + +257 +00:17:07,594 --> 00:17:11,164 +we loop over all xcasset bundles +in the target. + +258 +00:17:11,198 --> 00:17:13,534 +We'll extract a string +for the display name + +259 +00:17:13,567 --> 00:17:15,469 +that will show up in the build log, + +260 +00:17:15,502 --> 00:17:18,705 +as well as construct suitable +input and output paths. + +261 +00:17:19,473 --> 00:17:23,544 +We can also look up our executable here +using the plugin API + +262 +00:17:23,577 --> 00:17:25,579 +and then put our build command together. + +263 +00:17:27,047 --> 00:17:29,950 +With this, we're ready +to build the project again. + +264 +00:17:29,983 --> 00:17:32,719 +We can take a look at the build log +for the new build steps + +265 +00:17:32,753 --> 00:17:33,754 +that are happening. + +266 +00:17:40,727 --> 00:17:44,598 +The plugin is being compiled +and run at the start of the build, + +267 +00:17:44,631 --> 00:17:47,601 +from where it adds any generated +commands to the build graph. + +268 +00:17:52,472 --> 00:17:55,342 +Looking at the target, +our new build command ran. + +269 +00:17:59,279 --> 00:18:02,015 +And finally, the generated source file +shows up + +270 +00:18:02,049 --> 00:18:04,451 +as part of compiling Swift files. + +271 +00:18:05,919 --> 00:18:12,359 +Let's go back to our preview, + +272 +00:18:12,392 --> 00:18:16,430 +where we can replace the stringly typed +image construction with our new constants. + +273 +00:18:20,000 --> 00:18:23,203 +We also get autocompletion +for the other image names. + +274 +00:18:28,141 --> 00:18:30,344 +This is nice. +With relatively little code, + +275 +00:18:30,377 --> 00:18:32,479 +we have been able to improve our workflow, + +276 +00:18:32,513 --> 00:18:36,683 +all using familiar Swift APIs +and without having to leave Xcode. + +277 +00:18:39,386 --> 00:18:42,289 +So far, we have looked +into making plugins for our own use, + +278 +00:18:42,322 --> 00:18:45,125 +as part of libraries we were already +working on, + +279 +00:18:45,158 --> 00:18:47,461 +but another powerful attribute of plugins + +280 +00:18:47,494 --> 00:18:49,696 +is that we can share them +in a straightforward way, + +281 +00:18:49,730 --> 00:18:51,198 +similar to libraries. + +282 +00:18:53,033 --> 00:18:54,968 +For the next demo, +I'd like to automate + +283 +00:18:55,002 --> 00:18:56,336 +some pre-build processing + +284 +00:18:56,370 --> 00:18:58,705 +using the genstrings tool +that ships with Xcode. + +285 +00:18:59,473 --> 00:19:02,042 +The tool extracts localized strings +from your code + +286 +00:19:02,075 --> 00:19:05,078 +into a localization directory +for further use. + +287 +00:19:05,112 --> 00:19:07,014 +Since that seems generally useful, + +288 +00:19:07,047 --> 00:19:09,249 +I'd like to make the plugin +a separate package + +289 +00:19:09,283 --> 00:19:11,251 +so that it can be shared independently. + +290 +00:19:12,886 --> 00:19:14,054 +If you'd like to learn more + +291 +00:19:14,087 --> 00:19:16,523 +about resources and localization +in packages, + +292 +00:19:16,557 --> 00:19:20,661 +I would recommend +the WWDC20 session on that topic. + +293 +00:19:20,694 --> 00:19:23,263 +For more information +about localization in general, + +294 +00:19:23,297 --> 00:19:26,633 +check out Localize your SwiftUI app +from WWDC21. + +295 +00:19:28,936 --> 00:19:32,272 +For this plugin, we'll start +by computing the output directory + +296 +00:19:32,306 --> 00:19:34,942 +for localizations. + +297 +00:19:34,975 --> 00:19:36,343 +We'll compute the input files, + +298 +00:19:36,376 --> 00:19:41,215 +which are all the Swift or Objective-C +source files in a given target, + +299 +00:19:41,248 --> 00:19:42,783 +and then construct the pre-build command + +300 +00:19:42,816 --> 00:19:46,220 +for executing the genstrings tool +provided by Xcode. + +301 +00:19:46,253 --> 00:19:49,323 +Note that the biggest difference between +pre- and in-build commands + +302 +00:19:49,356 --> 00:19:52,059 +is that we don't declare +a well-defined set of outputs, + +303 +00:19:52,092 --> 00:19:54,528 +which means these commands run +on every build. + +304 +00:19:56,063 --> 00:19:58,532 +The tool will extract +all the localized strings + +305 +00:19:58,565 --> 00:20:00,834 +from the user's source code + +306 +00:20:00,868 --> 00:20:04,371 +and then write all those strings into +a localization directory, + +307 +00:20:04,404 --> 00:20:07,407 +which can be used as the basis +for the actual localization work + +308 +00:20:07,441 --> 00:20:08,408 +for the user's project. + +309 +00:20:11,078 --> 00:20:13,847 +To start, I have created +the scaffolding here already. + +310 +00:20:13,881 --> 00:20:16,383 +Now in the package manifest, + +311 +00:20:16,416 --> 00:20:21,255 +let’s add a target as before, + +312 +00:20:21,288 --> 00:20:23,724 +but we will also add a plugin product. + +313 +00:20:29,296 --> 00:20:32,299 +Similar to library products, +this is the way to make a plugin + +314 +00:20:32,332 --> 00:20:35,536 +available to clients of a package +instead of just privately. + +315 +00:20:38,572 --> 00:20:42,276 +We can write the code +That we discussed earlier... + +316 +00:20:50,384 --> 00:20:51,818 +Now that we have built our plugin, + +317 +00:20:51,852 --> 00:20:55,155 +we'd like to test it out +in a separate example package. + +318 +00:21:00,694 --> 00:21:03,297 +For that, let's create +a new package from template. + +319 +00:21:04,731 --> 00:21:08,268 +We'll add an API that provides +a localized string to the package... + +320 +00:21:14,608 --> 00:21:17,578 +And add a use of that +in the generated test. + +321 +00:21:35,729 --> 00:21:40,701 +As expected, the test works, +as our API returns the string "World." + +322 +00:21:40,734 --> 00:21:44,271 +Let's add a path-based dependency +on the plugin package... + +323 +00:21:51,745 --> 00:21:54,348 +and a use of the plugin +to the library target. + +324 +00:22:03,757 --> 00:22:05,492 +We can now run again... + +325 +00:22:10,864 --> 00:22:17,804 +and if we look at the build log, + +326 +00:22:17,838 --> 00:22:23,410 +our plugin gets executed +at the start of the build + +327 +00:22:23,443 --> 00:22:26,547 +and the generated files +get added to our target, + +328 +00:22:26,580 --> 00:22:30,017 +so we're getting a resource bundle built + +329 +00:22:30,050 --> 00:22:34,288 +and a resource accessor being generated, + +330 +00:22:34,321 --> 00:22:38,859 +just as if the resource was part +of our target from the beginning. + +331 +00:22:38,892 --> 00:22:42,462 +Now let's change our code to actually use +the resource bundle. + +332 +00:22:53,073 --> 00:22:55,075 +Finally, if we change the code... + +333 +00:23:10,757 --> 00:23:12,993 +and take a peek +at the generated bundle... + +334 +00:23:26,974 --> 00:23:29,343 +we can see the changes reflected here. + +335 +00:23:30,811 --> 00:23:32,946 +Now that we have a test bed +for the plugin, + +336 +00:23:32,980 --> 00:23:34,314 +we could flesh out the test suite + +337 +00:23:34,348 --> 00:23:36,583 +and eventually share +the plugin package with others. + +338 +00:23:37,184 --> 00:23:42,456 +To recap, plugins can be used +to automate and share developer tooling, + +339 +00:23:42,489 --> 00:23:46,293 +custom commands provide a way +to automate common tasks, + +340 +00:23:46,326 --> 00:23:47,561 +and build tools can be used + +341 +00:23:47,594 --> 00:23:51,465 +to generate files during +the build process. + +342 +00:23:51,498 --> 00:23:53,400 +Thanks for listening! + +343 +00:23:53,433 --> 00:23:56,236 +♪ instrumental hip hop music ♪ + diff --git a/eng/2022 Session 110403 Meet Background Assets en.srt b/eng/2022 Session 110403 Meet Background Assets en.srt new file mode 100644 index 0000000..820b5d4 --- /dev/null +++ b/eng/2022 Session 110403 Meet Background Assets en.srt @@ -0,0 +1,3116 @@ +1 +00:00:00,033 --> 00:00:03,003 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:03,003 --> 00:00:09,543 +♪ + +3 +00:00:09,543 --> 00:00:11,144 +Hi! My name is Jared, + +4 +00:00:11,144 --> 00:00:13,347 +and I'm a software engineer +here at Apple. + +5 +00:00:13,347 --> 00:00:15,182 +Today I'd like to talk you +about a new framework + +6 +00:00:15,182 --> 00:00:19,119 +we are introducing this year +to iOS, iPadOS, and macOS. + +7 +00:00:19,119 --> 00:00:21,021 +This new framework is called +Background Assets, + +8 +00:00:21,021 --> 00:00:23,223 +and we believe it will greatly +enrich the user experience + +9 +00:00:23,223 --> 00:00:25,325 +of the apps that we all +know and love, + +10 +00:00:25,325 --> 00:00:27,861 +and more importantly, +that you develop. + +11 +00:00:27,861 --> 00:00:29,596 +To get started, +I'll be introducing you + +12 +00:00:29,596 --> 00:00:32,566 +to the new +Background Assets framework. + +13 +00:00:32,566 --> 00:00:34,701 +Afterwards, I'll show you +how to adopt the new framework + +14 +00:00:34,701 --> 00:00:36,670 +into your app. + +15 +00:00:36,670 --> 00:00:38,805 +This will be followed by a quick +overview of the extension + +16 +00:00:38,805 --> 00:00:41,575 +and what new capabilities +it provides. + +17 +00:00:41,575 --> 00:00:43,410 +Then we'll finish +with some best practices + +18 +00:00:43,410 --> 00:00:45,946 +and everything we've learned. + +19 +00:00:45,946 --> 00:00:47,381 +Before we begin, +let's talk about + +20 +00:00:47,381 --> 00:00:48,949 +what we're trying to solve here. + +21 +00:00:48,949 --> 00:00:50,851 +The truth is, +waiting is not fun. + +22 +00:00:50,851 --> 00:00:53,253 +Anytime we find ourselves asking +those that use our software + +23 +00:00:53,253 --> 00:00:55,756 +to wait, we're increasing +frustration and taking away + +24 +00:00:55,756 --> 00:00:58,292 +from the experience +we want our apps to provide. + +25 +00:00:58,292 --> 00:01:00,260 +For instance, how often +have you found yourself + +26 +00:01:00,260 --> 00:01:02,095 +browsing endlessly +through the App Store + +27 +00:01:02,095 --> 00:01:03,497 +looking for that perfect app? + +28 +00:01:03,497 --> 00:01:06,667 +You finally find it, +and oh does it look so perfect! + +29 +00:01:06,667 --> 00:01:08,402 +You then tap that GET button. + +30 +00:01:08,402 --> 00:01:12,005 +With every moment, your level +of excitement increases. + +31 +00:01:12,005 --> 00:01:13,573 +Then you shortly realize +that depending + +32 +00:01:13,573 --> 00:01:15,943 +on your network connection +or the size of the app, + +33 +00:01:15,943 --> 00:01:19,112 +you may find yourself having +to wait as the app downloads. + +34 +00:01:19,112 --> 00:01:20,814 +Then after a few seconds +of waiting, + +35 +00:01:20,814 --> 00:01:22,983 +you find yourself +putting your phone down, + +36 +00:01:22,983 --> 00:01:26,920 +grabbing a cup of coffee, +reading your favorite book + +37 +00:01:26,920 --> 00:01:28,655 +about the benefits +of practicing mindfulness + +38 +00:01:28,655 --> 00:01:31,358 +and mastering patience. + +39 +00:01:31,358 --> 00:01:35,629 +Then a few hours pass, and you +finally pick up your phone. + +40 +00:01:35,629 --> 00:01:37,664 +Your excitement levels rise +as you're ready to dive into + +41 +00:01:37,664 --> 00:01:41,301 +the perfect app that you've been +waiting to use all day. + +42 +00:01:41,301 --> 00:01:44,171 +Just to launch the app and be +immediately greeted with this: + +43 +00:01:44,171 --> 00:01:45,639 +more downloading. + +44 +00:01:45,639 --> 00:01:46,606 +It's confusing. + +45 +00:01:46,606 --> 00:01:48,241 +You've been away +from your phone all day. + +46 +00:01:48,241 --> 00:01:51,178 +Why is this app now +making you wait even longer? + +47 +00:01:51,178 --> 00:01:52,679 +Couldn't this app have +downloaded this content + +48 +00:01:52,679 --> 00:01:55,148 +automatically after the app +was installed? + +49 +00:01:55,148 --> 00:01:57,284 +For anyone on a slower +internet connection, + +50 +00:01:57,284 --> 00:02:00,520 +this might frustrate them +to close and remove the app. + +51 +00:02:00,520 --> 00:02:02,255 +The truth is, +this is not the experience + +52 +00:02:02,255 --> 00:02:03,724 +any of us want to have. + +53 +00:02:03,724 --> 00:02:04,891 +We know this isn't your fault, + +54 +00:02:04,891 --> 00:02:07,728 +and we believe we can make +this experience so much better! + +55 +00:02:07,728 --> 00:02:09,930 +This is why we're proud +to introduce to you this year, + +56 +00:02:09,930 --> 00:02:11,231 +Background Assets. + +57 +00:02:11,231 --> 00:02:12,466 +This framework was developed + +58 +00:02:12,466 --> 00:02:15,635 +to help you enrich the user +experience of your apps. + +59 +00:02:15,635 --> 00:02:17,170 +So that the moment +your app is launched, + +60 +00:02:17,170 --> 00:02:19,740 +it is providing a phenomenal +first impression! + +61 +00:02:19,740 --> 00:02:21,475 +Background Assets +is designed to be flexible + +62 +00:02:21,475 --> 00:02:23,310 +with your existing workflows. + +63 +00:02:23,310 --> 00:02:24,711 +A lot of you +have already developed + +64 +00:02:24,711 --> 00:02:26,747 +complex asset management +systems, + +65 +00:02:26,747 --> 00:02:29,583 +and we wanted this new framework +to easily fit into the solutions + +66 +00:02:29,583 --> 00:02:31,385 +that you have already developed. + +67 +00:02:31,385 --> 00:02:34,154 +We also know that you want to +be able to push updated content + +68 +00:02:34,154 --> 00:02:37,024 +to your apps without having to +require an additional submission + +69 +00:02:37,024 --> 00:02:38,191 +to the App Store. + +70 +00:02:38,191 --> 00:02:40,193 +It's not uncommon +for games or other apps + +71 +00:02:40,193 --> 00:02:43,463 +to need additional content after +the app has already shipped. + +72 +00:02:43,463 --> 00:02:47,067 +Think updated art textures +or a bug fix in game-level data. + +73 +00:02:47,067 --> 00:02:49,536 +Background Assets provides you +with the ability to schedule + +74 +00:02:49,536 --> 00:02:52,606 +and update your assets +outside of your app's lifecycle. + +75 +00:02:52,606 --> 00:02:54,641 +We believe it's important +that assets are present + +76 +00:02:54,641 --> 00:02:56,009 +before first app launch, + +77 +00:02:56,009 --> 00:02:58,311 +or whenever the app +is updated overnight. + +78 +00:02:58,311 --> 00:03:00,714 +So we've worked to create +a mechanism to help ensure + +79 +00:03:00,714 --> 00:03:03,216 +that your content is present by +the time your app is launched. + +80 +00:03:03,216 --> 00:03:06,119 +And finally, the easier +a framework is to adopt, + +81 +00:03:06,119 --> 00:03:08,989 +the more we can encourage you +to use it in your apps. + +82 +00:03:08,989 --> 00:03:11,191 +We want Background Assets +to be used in any place + +83 +00:03:11,191 --> 00:03:13,727 +where large assets +need to be predownloaded. + +84 +00:03:13,727 --> 00:03:16,596 +That way, we can minimize +the time your app is waiting + +85 +00:03:16,596 --> 00:03:20,067 +and showing a progress bar +before its content is available. + +86 +00:03:20,067 --> 00:03:21,501 +So you may be wondering, + +87 +00:03:21,501 --> 00:03:23,970 +how is this new framework +helping you solve this problem? + +88 +00:03:23,970 --> 00:03:26,807 +Well, in order to be +as extensible as possible, + +89 +00:03:26,807 --> 00:03:28,408 +we've created +a new app extension + +90 +00:03:28,408 --> 00:03:30,544 +for downloading content +in the background. + +91 +00:03:30,544 --> 00:03:32,646 +This new extension is built +on top of the powerful + +92 +00:03:32,646 --> 00:03:34,681 +app extension technology +that other extensions + +93 +00:03:34,681 --> 00:03:36,450 +on our platforms use. + +94 +00:03:36,450 --> 00:03:38,452 +This provides an opportunity +to run code + +95 +00:03:38,452 --> 00:03:40,353 +outside of the app's lifecycle. + +96 +00:03:40,353 --> 00:03:42,456 +For instance, the extension +will run whenever the user + +97 +00:03:42,456 --> 00:03:45,225 +first installs your app +but has yet to launch it. + +98 +00:03:45,225 --> 00:03:47,694 +The extension will also run +whenever the app is updated + +99 +00:03:47,694 --> 00:03:49,529 +automatically in the background. + +100 +00:03:49,529 --> 00:03:51,998 +This helps to ensure that +your content can be scheduled + +101 +00:03:51,998 --> 00:03:54,768 +and downloaded before the user +opens the application + +102 +00:03:54,768 --> 00:03:56,570 +after it has been updated. + +103 +00:03:56,570 --> 00:03:59,473 +Finally, the extension will run +periodically in the background, + +104 +00:03:59,473 --> 00:04:01,341 +allowing you to check +for updated assets + +105 +00:04:01,341 --> 00:04:03,577 +and schedule them +periodically over time. + +106 +00:04:03,577 --> 00:04:04,845 +Though it is important to note, + +107 +00:04:04,845 --> 00:04:06,680 +that extension runtime +is short-lived, + +108 +00:04:06,680 --> 00:04:09,149 +so all work will need to be +scheduled by your extension + +109 +00:04:09,149 --> 00:04:10,417 +with haste. + +110 +00:04:10,417 --> 00:04:12,319 +If downloads are not +scheduled quickly, + +111 +00:04:12,319 --> 00:04:14,387 +the system may terminate +the extension. + +112 +00:04:14,387 --> 00:04:16,289 +It's also important to be aware +that the frequency + +113 +00:04:16,289 --> 00:04:18,291 +of the extension's +ability to run periodically + +114 +00:04:18,291 --> 00:04:20,260 +will back off +based on app usage. + +115 +00:04:20,260 --> 00:04:22,295 +If your app isn't being used +very much, + +116 +00:04:22,295 --> 00:04:25,132 +then the extension will receive +less frequent runtime. + +117 +00:04:25,132 --> 00:04:27,634 +So that's an overview of the +new Background Assets framework. + +118 +00:04:27,634 --> 00:04:29,703 +It provides you with the tools +you need to ensure that + +119 +00:04:29,703 --> 00:04:32,672 +your assets are available by +the time your app is launched. + +120 +00:04:32,672 --> 00:04:34,241 +This is done +with an extension that runs + +121 +00:04:34,241 --> 00:04:36,309 +whenever your app +is installed or updated, + +122 +00:04:36,309 --> 00:04:38,411 +but before the user +has launched your app. + +123 +00:04:38,411 --> 00:04:39,946 +Now, let's take a look +at adopting + +124 +00:04:39,946 --> 00:04:41,781 +the Background Assets framework +into your project + +125 +00:04:41,781 --> 00:04:43,049 +and getting started! + +126 +00:04:43,049 --> 00:04:44,618 +The download manager +within the framework + +127 +00:04:44,618 --> 00:04:46,286 +is the primary vehicle +used to communicate + +128 +00:04:46,286 --> 00:04:49,356 +with the Background Assets +system service. + +129 +00:04:49,356 --> 00:04:50,724 +The manager +is a singleton object + +130 +00:04:50,724 --> 00:04:52,492 +that can be used +throughout your app. + +131 +00:04:52,492 --> 00:04:54,327 +Using the manager, you can +schedule the download + +132 +00:04:54,327 --> 00:04:57,197 +of your assets in either +the foreground or background. + +133 +00:04:57,197 --> 00:04:59,733 +You can also retrieve downloads +that are currently in flight, + +134 +00:04:59,733 --> 00:05:01,735 +which might have started +before your application + +135 +00:05:01,735 --> 00:05:03,103 +was ever launched. + +136 +00:05:03,103 --> 00:05:04,905 +Downloads can also be canceled. + +137 +00:05:04,905 --> 00:05:06,506 +This is useful if they were +already scheduled + +138 +00:05:06,506 --> 00:05:08,642 +or are in the middle of being +downloaded and you no longer + +139 +00:05:08,642 --> 00:05:10,977 +need that asset that you +originally requested. + +140 +00:05:10,977 --> 00:05:13,180 +We've also introduced +a synchronization mechanism + +141 +00:05:13,180 --> 00:05:14,781 +for managing exclusive access + +142 +00:05:14,781 --> 00:05:17,484 +between your app +and the extension + +143 +00:05:17,484 --> 00:05:19,986 +so that both the extension +and application do not end up + +144 +00:05:19,986 --> 00:05:24,090 +scheduling or modifying existing +downloads at the same time. + +145 +00:05:24,090 --> 00:05:27,027 +I have an example to show you, +but for now, more on this later! + +146 +00:05:27,027 --> 00:05:29,429 +Let's take a look at just +how easy it is to start using + +147 +00:05:29,429 --> 00:05:31,097 +Background Assets. + +148 +00:05:31,097 --> 00:05:32,732 +I'm going to begin +by walking you through + +149 +00:05:32,732 --> 00:05:34,701 +some of the basics of the API. + +150 +00:05:34,701 --> 00:05:36,937 +I'll then follow that up later +by showing you how to tie + +151 +00:05:36,937 --> 00:05:39,406 +all of this together +into an app extension. + +152 +00:05:39,406 --> 00:05:41,775 +To get started, you'll import +the Background Assets + +153 +00:05:41,775 --> 00:05:43,076 +framework module. + +154 +00:05:43,076 --> 00:05:45,679 +Then, it's as easy +as defining a URL that points + +155 +00:05:45,679 --> 00:05:48,515 +to the location of where +your remote asset is located. + +156 +00:05:48,515 --> 00:05:50,717 +We then follow that up by +defining an app group container + +157 +00:05:50,717 --> 00:05:53,253 +that your extension +and app are both members of. + +158 +00:05:53,253 --> 00:05:55,322 +Having your app and extension +in the same group + +159 +00:05:55,322 --> 00:05:57,824 +allows them to manage +your assets during the download + +160 +00:05:57,824 --> 00:05:59,459 +and after completion. + +161 +00:05:59,459 --> 00:06:01,261 +If you're not already +familiar with app groups, + +162 +00:06:01,261 --> 00:06:03,930 +you can easily add one from +the Signing & Capability section + +163 +00:06:03,930 --> 00:06:05,565 +of Xcode 14. + +164 +00:06:05,565 --> 00:06:08,468 +They're a powerful feature that +allows two or more applications + +165 +00:06:08,468 --> 00:06:10,870 +to access the same resources, +or in this case, + +166 +00:06:10,870 --> 00:06:12,639 +your app and its extension. + +167 +00:06:12,639 --> 00:06:15,342 +The next thing to do is create +your download object. + +168 +00:06:15,342 --> 00:06:17,410 +The Background Assets framework +is designed to support + +169 +00:06:17,410 --> 00:06:19,512 +multiple different types +of download objects. + +170 +00:06:19,512 --> 00:06:21,414 +However, in this example, + +171 +00:06:21,414 --> 00:06:25,652 +we'll be focusing on the most +common one: BAURLDownload. + +172 +00:06:25,652 --> 00:06:28,688 +Immediately, you'll notice that +the initializer takes in the URL + +173 +00:06:28,688 --> 00:06:30,924 +and the app group identifier. + +174 +00:06:30,924 --> 00:06:32,425 +This information +tells the system + +175 +00:06:32,425 --> 00:06:33,827 +both what we're downloading + +176 +00:06:33,827 --> 00:06:35,629 +and where the resulting file +will end up. + +177 +00:06:35,629 --> 00:06:37,564 +It also takes an identifier. + +178 +00:06:37,564 --> 00:06:39,666 +You'll use this identifier +to track your download + +179 +00:06:39,666 --> 00:06:43,870 +across multiple launches of your +app, and within the extension. + +180 +00:06:43,870 --> 00:06:46,106 +The engine will not allow +more than one download + +181 +00:06:46,106 --> 00:06:48,842 +to be scheduled +with the same identifier. + +182 +00:06:48,842 --> 00:06:51,711 +Therefore, you should make +these identifiers unique. + +183 +00:06:51,711 --> 00:06:54,547 +Next, we'll grab a reference +to the BADownloaderManager + +184 +00:06:54,547 --> 00:06:56,049 +shared object. + +185 +00:06:56,049 --> 00:06:57,884 +The download manager +is your single interface + +186 +00:06:57,884 --> 00:06:59,386 +into Background Assets. + +187 +00:06:59,386 --> 00:07:03,189 +It's what allows you to observe, +cancel, and schedule downloads. + +188 +00:07:03,189 --> 00:07:05,125 +We'll then pass it a weak +reference to a delegate + +189 +00:07:05,125 --> 00:07:07,427 +that conforms to the +BADownloadManagerDelegate + +190 +00:07:07,427 --> 00:07:08,628 +protocol. + +191 +00:07:08,628 --> 00:07:10,230 +I'll go more into +this protocol shortly, + +192 +00:07:10,230 --> 00:07:12,699 +but the most important part +to know now is that it receives + +193 +00:07:12,699 --> 00:07:15,101 +messages about downloads +that have been scheduled. + +194 +00:07:15,101 --> 00:07:17,504 +The only thing left to do +is to ask the download manager + +195 +00:07:17,504 --> 00:07:19,039 +to schedule the download. + +196 +00:07:19,039 --> 00:07:21,107 +If for any reason the download +cannot be scheduled, + +197 +00:07:21,107 --> 00:07:22,609 +then an error is thrown. + +198 +00:07:22,609 --> 00:07:24,778 +In addition to scheduling +downloads in the background, + +199 +00:07:24,778 --> 00:07:27,547 +we also provide API for doing +foreground downloads. + +200 +00:07:27,547 --> 00:07:28,882 +Running in the foreground + +201 +00:07:28,882 --> 00:07:30,750 +not only gives you +increased priority, + +202 +00:07:30,750 --> 00:07:34,120 +but it also enables your +download to begin immediately. + +203 +00:07:34,120 --> 00:07:36,656 +This is similar to using the +default session configuration + +204 +00:07:36,656 --> 00:07:38,224 +within URLSession. + +205 +00:07:38,224 --> 00:07:40,260 +We provide this API +so that your app can promote + +206 +00:07:40,260 --> 00:07:42,395 +any downloads scheduled by your +extension in the background + +207 +00:07:42,395 --> 00:07:43,596 +to the foreground. + +208 +00:07:43,596 --> 00:07:45,165 +One thing to keep in mind: + +209 +00:07:45,165 --> 00:07:47,200 +performing a foreground +download is not available + +210 +00:07:47,200 --> 00:07:48,568 +from within the extension; + +211 +00:07:48,568 --> 00:07:51,104 +it can only be initiated +from the app. + +212 +00:07:51,104 --> 00:07:53,206 +Since extensions +never present UI, + +213 +00:07:53,206 --> 00:07:55,275 +and the user doesn't notice +that they are running, + +214 +00:07:55,275 --> 00:07:58,178 +extensions may only schedule +downloads in the background. + +215 +00:07:58,178 --> 00:07:59,612 +If your app +would like to promote + +216 +00:07:59,612 --> 00:08:01,681 +existing background downloads +to the foreground, + +217 +00:08:01,681 --> 00:08:03,717 +this can easily be accomplished +by fetching the list + +218 +00:08:03,717 --> 00:08:06,319 +of currently active downloads +from the manager. + +219 +00:08:06,319 --> 00:08:08,254 +The list that is returned +contains all downloads + +220 +00:08:08,254 --> 00:08:10,657 +the are currently scheduled, +which may include downloads + +221 +00:08:10,657 --> 00:08:12,726 +in flight or queued up +in the scheduler. + +222 +00:08:12,726 --> 00:08:14,994 +Next, your app can begin +the promotion process + +223 +00:08:14,994 --> 00:08:17,564 +by calling +startForegroundDownload. + +224 +00:08:17,564 --> 00:08:19,399 +If a download is already +in the foreground, + +225 +00:08:19,399 --> 00:08:22,035 +calling this method +will effectively do nothing. + +226 +00:08:22,035 --> 00:08:24,270 +However, if the download +was backgrounded, + +227 +00:08:24,270 --> 00:08:27,173 +it will first be paused, +then resumed in the foreground + +228 +00:08:27,173 --> 00:08:29,676 +without requiring any content +that was already downloaded + +229 +00:08:29,676 --> 00:08:31,945 +up until this point +to be redownloaded. + +230 +00:08:31,945 --> 00:08:33,646 +Together, +this provides an effective + +231 +00:08:33,646 --> 00:08:35,648 +and simple illustration +for how easy it is + +232 +00:08:35,648 --> 00:08:37,617 +to use Background Assets to +promote downloads + +233 +00:08:37,617 --> 00:08:39,886 +scheduled in the background +to the foreground. + +234 +00:08:39,886 --> 00:08:41,688 +It really is as simple as that! + +235 +00:08:41,688 --> 00:08:43,857 +The download manager is your +primary interface that is used + +236 +00:08:43,857 --> 00:08:46,626 +to schedule and monitor +background downloads. + +237 +00:08:46,626 --> 00:08:48,795 +As these download objects +are processed by the system, + +238 +00:08:48,795 --> 00:08:51,231 +you'll receive messages +in your delegate object. + +239 +00:08:51,231 --> 00:08:52,632 +Let's walk through +the delegate now. + +240 +00:08:52,632 --> 00:08:54,634 +The delegate receives messages +for all downloads + +241 +00:08:54,634 --> 00:08:56,536 +that have been scheduled +by either the extension + +242 +00:08:56,536 --> 00:08:58,071 +or your app. + +243 +00:08:58,071 --> 00:09:00,140 +If there are numerous downloads +that were scheduled, + +244 +00:09:00,140 --> 00:09:02,709 +callbacks will be received +for all of them. + +245 +00:09:02,709 --> 00:09:04,711 +This is where you use +the download object's + +246 +00:09:04,711 --> 00:09:07,247 +unique identifier +to distinguish between them. + +247 +00:09:07,247 --> 00:09:08,882 +Your app will begin +to receive callbacks + +248 +00:09:08,882 --> 00:09:10,583 +the moment the delegate +is established + +249 +00:09:10,583 --> 00:09:12,152 +on BADownloadManager. + +250 +00:09:12,152 --> 00:09:14,387 +Callbacks are not enqueued +by the system. + +251 +00:09:14,387 --> 00:09:16,623 +If your app does not handle +one of the delegate methods + +252 +00:09:16,623 --> 00:09:18,558 +or your delegate +is not established, + +253 +00:09:18,558 --> 00:09:21,361 +then your extension will wake +to process the message. + +254 +00:09:21,361 --> 00:09:23,363 +This means that you should +fully expect your extension + +255 +00:09:23,363 --> 00:09:26,232 +to be sent messages if you have +not established a delegate + +256 +00:09:26,232 --> 00:09:29,002 +onto BADownloadManager +within your app. + +257 +00:09:29,002 --> 00:09:30,570 +If your app is currently +in the foreground + +258 +00:09:30,570 --> 00:09:32,038 +being presented to the user + +259 +00:09:32,038 --> 00:09:33,807 +and its delegate +has been established, + +260 +00:09:33,807 --> 00:09:35,642 +then callbacks will be sent +to your app + +261 +00:09:35,642 --> 00:09:37,243 +and the extension +will not be woken. + +262 +00:09:37,243 --> 00:09:38,645 +The extension will only wake + +263 +00:09:38,645 --> 00:09:41,414 +if your app does not handle +its delegate callback. + +264 +00:09:41,414 --> 00:09:43,516 +If a download finishes, +or fails, + +265 +00:09:43,516 --> 00:09:45,418 +and the app does not +process this message, + +266 +00:09:45,418 --> 00:09:46,920 +then the extension will wake. + +267 +00:09:46,920 --> 00:09:49,289 +Keep in mind, +the extension is not woken + +268 +00:09:49,289 --> 00:09:50,957 +for all types of callbacks. + +269 +00:09:50,957 --> 00:09:52,892 +Only callbacks that share +common interfaces + +270 +00:09:52,892 --> 00:09:54,894 +between +BADownloadManagerDelegate + +271 +00:09:54,894 --> 00:09:57,363 +and the BADownloaderExtension +protocol. + +272 +00:09:57,363 --> 00:09:59,999 +A download succeeding or failing +is an example + +273 +00:09:59,999 --> 00:10:02,969 +of a common interface between +the delegate and the protocol. + +274 +00:10:02,969 --> 00:10:04,971 +Although your app extension +has its own entry points + +275 +00:10:04,971 --> 00:10:06,473 +that cause it to wake, + +276 +00:10:06,473 --> 00:10:08,107 +if the extension +is currently running, + +277 +00:10:08,107 --> 00:10:11,244 +it can use BADownloadManager +and establish a delegate. + +278 +00:10:11,244 --> 00:10:13,346 +This will allow both +the app and extension + +279 +00:10:13,346 --> 00:10:16,115 +to receive duplicate messages +to their delegates. + +280 +00:10:16,115 --> 00:10:17,884 +Keep in mind +that extensions do not wake + +281 +00:10:17,884 --> 00:10:19,586 +to process delegate messages. + +282 +00:10:19,586 --> 00:10:22,288 +They only wake at extension +entry points defined in the + +283 +00:10:22,288 --> 00:10:24,290 +BADownloaderExtension protocol. + +284 +00:10:24,290 --> 00:10:25,725 +Let's take a look +at the protocol + +285 +00:10:25,725 --> 00:10:27,260 +for the download manager's +delegate. + +286 +00:10:27,260 --> 00:10:29,295 +The first function +is for receiving messages + +287 +00:10:29,295 --> 00:10:31,164 +whenever a download starts. + +288 +00:10:31,164 --> 00:10:33,399 +This is useful for tracking when +the device has finally chosen + +289 +00:10:33,399 --> 00:10:35,201 +to schedule a specific download. + +290 +00:10:35,201 --> 00:10:38,004 +You may also be notified +if a download pauses. + +291 +00:10:38,004 --> 00:10:40,173 +An example of +a pause occurring would be + +292 +00:10:40,173 --> 00:10:42,542 +if the extension starts +a download in the background + +293 +00:10:42,542 --> 00:10:45,378 +and then your app asks us +to promote it to the foreground. + +294 +00:10:45,378 --> 00:10:47,514 +During this promotion +there will be a small window + +295 +00:10:47,514 --> 00:10:50,149 +where the download pauses +before it is resumed. + +296 +00:10:50,149 --> 00:10:52,218 +The download manager +also allows you to monitor + +297 +00:10:52,218 --> 00:10:55,221 +active progress of your download +as it is being downloaded + +298 +00:10:55,221 --> 00:10:56,589 +in the foreground. + +299 +00:10:56,589 --> 00:10:59,225 +We also provide a mechanism +to answer a challenge request, + +300 +00:10:59,225 --> 00:11:02,028 +which is useful for validating +the authenticity of a connection + +301 +00:11:02,028 --> 00:11:04,664 +or for providing credentials +to authorize a connection. + +302 +00:11:04,664 --> 00:11:06,866 +The most important functions +are for dealing with a failed + +303 +00:11:06,866 --> 00:11:08,268 +or finished download. + +304 +00:11:08,268 --> 00:11:10,436 +If a download fails, +you may need to reschedule it + +305 +00:11:10,436 --> 00:11:12,338 +or determine the cause. + +306 +00:11:12,338 --> 00:11:13,673 +For a successful download, + +307 +00:11:13,673 --> 00:11:15,575 +the system has placed +the file in a location + +308 +00:11:15,575 --> 00:11:17,443 +that is managed +by the operating system. + +309 +00:11:17,443 --> 00:11:19,546 +If the device ends up +low on space, + +310 +00:11:19,546 --> 00:11:21,981 +then the system +will delete the file for you. + +311 +00:11:21,981 --> 00:11:24,584 +We strongly recommend that you +leave the file at the location + +312 +00:11:24,584 --> 00:11:26,519 +that the system has provided. + +313 +00:11:26,519 --> 00:11:28,688 +Only move the file +if you absolutely must + +314 +00:11:28,688 --> 00:11:30,256 +and please do not duplicate it + +315 +00:11:30,256 --> 00:11:33,192 +unless you delete +the originating file afterwards. + +316 +00:11:33,192 --> 00:11:36,129 +As a reminder, the protocol for +the download manager's delegate + +317 +00:11:36,129 --> 00:11:38,331 +is for receiving messages +related to downloads + +318 +00:11:38,331 --> 00:11:40,733 +that your app or extension +has scheduled. + +319 +00:11:40,733 --> 00:11:42,969 +It is not the entry point +for your extension, + +320 +00:11:42,969 --> 00:11:44,704 +which brings us +to our next topic. + +321 +00:11:44,704 --> 00:11:47,140 +Now we'll be taking a look +at the most exciting part + +322 +00:11:47,140 --> 00:11:49,208 +of Background Assets, +the extension! + +323 +00:11:49,208 --> 00:11:51,277 +The extension enables you +to schedule the downloads + +324 +00:11:51,277 --> 00:11:54,080 +of your assets before the user +has launched your app. + +325 +00:11:54,080 --> 00:11:56,416 +This enables you to ensure +that your assets are in place + +326 +00:11:56,416 --> 00:11:58,251 +and ready to go +in order to provide + +327 +00:11:58,251 --> 00:12:01,721 +the best possible experience in +your app with minimal wait time. + +328 +00:12:01,721 --> 00:12:05,258 +As discussed earlier, we're +introducing a new app extension. + +329 +00:12:05,258 --> 00:12:07,527 +This extension can be created +from within Xcode + +330 +00:12:07,527 --> 00:12:09,462 +inside of your existing project. + +331 +00:12:09,462 --> 00:12:11,431 +As a quick reminder, +the extension runs + +332 +00:12:11,431 --> 00:12:13,833 +whenever your app +is installed or updated. + +333 +00:12:13,833 --> 00:12:16,536 +Giving you the flexibility +to make sure changes to your app + +334 +00:12:16,536 --> 00:12:18,738 +always has its latest assets. + +335 +00:12:18,738 --> 00:12:20,340 +The extension +also runs periodically + +336 +00:12:20,340 --> 00:12:22,575 +based on how often +a user uses your app. + +337 +00:12:22,575 --> 00:12:24,377 +If someone uses your app +everyday, + +338 +00:12:24,377 --> 00:12:25,912 +then the system +learns this behavior + +339 +00:12:25,912 --> 00:12:28,281 +and your extension +will run more frequently. + +340 +00:12:28,281 --> 00:12:30,617 +However, if the app +is never launched, + +341 +00:12:30,617 --> 00:12:33,653 +then the frequency of this +periodic check will subside. + +342 +00:12:33,653 --> 00:12:35,788 +The new extension +also has a short lifecycle + +343 +00:12:35,788 --> 00:12:38,324 +and a tight sandbox to ensure +that its usage is limited + +344 +00:12:38,324 --> 00:12:40,360 +to just downloading assets. + +345 +00:12:40,360 --> 00:12:42,929 +You are encouraged to make +quick decisions in the extension + +346 +00:12:42,929 --> 00:12:45,598 +and to limit the extension to +the Background Assets framework. + +347 +00:12:45,598 --> 00:12:47,367 +Before we start navigating +through the extension, + +348 +00:12:47,367 --> 00:12:48,935 +there are a couple of +configurations + +349 +00:12:48,935 --> 00:12:51,104 +that you need to make +before the extension can launch. + +350 +00:12:51,104 --> 00:12:53,106 +These changes are +also a requirement + +351 +00:12:53,106 --> 00:12:56,376 +for your app to be approved for +distribution on the App Store. + +352 +00:12:56,376 --> 00:12:58,578 +In your app's +information property list, + +353 +00:12:58,578 --> 00:13:01,047 +you'll need to define +a couple of additional keys. + +354 +00:13:01,047 --> 00:13:03,816 +These keys should not be placed +in the extension's Info.plist, + +355 +00:13:03,816 --> 00:13:05,251 +only the app's. + +356 +00:13:05,251 --> 00:13:08,187 +The first key is +BAInitialDownloadRestrictions. + +357 +00:13:08,187 --> 00:13:10,723 +This is a dictionary where you +will be specifying restrictions + +358 +00:13:10,723 --> 00:13:12,859 +that will be placed +upon your extension. + +359 +00:13:12,859 --> 00:13:14,894 +These restrictions are reviewed +by App Review, + +360 +00:13:14,894 --> 00:13:17,130 +so try to be as accurate +as possible. + +361 +00:13:17,130 --> 00:13:19,499 +Now, let's dig into +each individual key + +362 +00:13:19,499 --> 00:13:21,234 +inside the dictionary. + +363 +00:13:21,234 --> 00:13:23,670 +The first restriction +is the download allowance. + +364 +00:13:23,670 --> 00:13:26,439 +This is represented in bytes +and is the maximum download size + +365 +00:13:26,439 --> 00:13:28,541 +you're requesting to make +within the extension + +366 +00:13:28,541 --> 00:13:30,643 +during an initial app install. + +367 +00:13:30,643 --> 00:13:33,112 +This size pertains to the sum +of all files combined + +368 +00:13:33,112 --> 00:13:34,814 +that you are requesting +to download, + +369 +00:13:34,814 --> 00:13:37,050 +not the size +of each individual file. + +370 +00:13:37,050 --> 00:13:38,818 +The next item is +the domain AllowList, + +371 +00:13:38,818 --> 00:13:41,721 +which takes an array of domains +represented as strings. + +372 +00:13:41,721 --> 00:13:44,724 +The domain AllowList supports +prefix wildcards and takes in + +373 +00:13:44,724 --> 00:13:46,426 +a list of host names +that your extension + +374 +00:13:46,426 --> 00:13:48,027 +is permitted to download from. + +375 +00:13:48,027 --> 00:13:49,162 +It's important to note + +376 +00:13:49,162 --> 00:13:51,931 +that the keys in the +BAInitialDownloadRestrictions, + +377 +00:13:51,931 --> 00:13:55,334 +such as the DownloadAllowance +and AllowList are only enforced + +378 +00:13:55,334 --> 00:13:57,370 +after first app install. + +379 +00:13:57,370 --> 00:13:58,771 +Whenever your app is launched, + +380 +00:13:58,771 --> 00:14:01,307 +these restrictions +are no longer enforced. + +381 +00:14:01,307 --> 00:14:02,575 +The last required key, + +382 +00:14:02,575 --> 00:14:04,343 +which sits at the root +of your Info.plist + +383 +00:14:04,343 --> 00:14:06,479 +is the maximum size +that your app will require + +384 +00:14:06,479 --> 00:14:09,182 +in additional storage +for these assets. + +385 +00:14:09,182 --> 00:14:11,651 +We expect that you might want +to download compressed assets, + +386 +00:14:11,651 --> 00:14:13,686 +so this value should be +the final extracted + +387 +00:14:13,686 --> 00:14:15,588 +uncompressed size. + +388 +00:14:15,588 --> 00:14:17,023 +The number that is placed here + +389 +00:14:17,023 --> 00:14:18,424 +will be presented +on the App Store + +390 +00:14:18,424 --> 00:14:19,992 +before the app is downloaded. + +391 +00:14:19,992 --> 00:14:21,761 +Now that we've gone over +some housekeeping, + +392 +00:14:21,761 --> 00:14:23,930 +let's talk about the entry +points into your extension + +393 +00:14:23,930 --> 00:14:25,998 +in more detail. + +394 +00:14:25,998 --> 00:14:27,734 +The functions that you define +from the protocol + +395 +00:14:27,734 --> 00:14:30,737 +will be called by the system +and not by your app. + +396 +00:14:30,737 --> 00:14:32,371 +Unlike other app extensions, + +397 +00:14:32,371 --> 00:14:33,940 +where the application +is responsible + +398 +00:14:33,940 --> 00:14:35,575 +for talking to the extension, + +399 +00:14:35,575 --> 00:14:36,843 +the background +download extension + +400 +00:14:36,843 --> 00:14:39,011 +is brokered by the system. + +401 +00:14:39,011 --> 00:14:41,414 +Since the system is maintaining +the lifecycle of the extension, + +402 +00:14:41,414 --> 00:14:43,850 +it should be viewed +as an ephemeral service. + +403 +00:14:43,850 --> 00:14:44,951 +Whenever any of the functions + +404 +00:14:44,951 --> 00:14:46,686 +inside of the protocol +are invoked, + +405 +00:14:46,686 --> 00:14:48,988 +it's important to keep +the work that is done there + +406 +00:14:48,988 --> 00:14:50,223 +to a minimum. + +407 +00:14:50,223 --> 00:14:52,058 +The extension will be terminated +rather quickly + +408 +00:14:52,058 --> 00:14:53,326 +after it is launched. + +409 +00:14:53,326 --> 00:14:55,361 +This is not the place +to kick off decompression + +410 +00:14:55,361 --> 00:14:58,898 +or other complex operations +that may take a while. + +411 +00:14:58,898 --> 00:15:01,300 +One of the great parts +of working in the extension + +412 +00:15:01,300 --> 00:15:03,469 +is that all of the +BackgroundAssets APIs + +413 +00:15:03,469 --> 00:15:07,206 +available to your app are also +available within the extension. + +414 +00:15:07,206 --> 00:15:10,376 +With the only exception being +the ForegroundDownload API. + +415 +00:15:10,376 --> 00:15:12,378 +This means that you'll use +BADownloadManager + +416 +00:15:12,378 --> 00:15:14,580 +just like you would in your app. + +417 +00:15:14,580 --> 00:15:16,482 +As a matter of fact, +it's entirely likely + +418 +00:15:16,482 --> 00:15:18,417 +that you'll discover +the ability to create something + +419 +00:15:18,417 --> 00:15:21,487 +that uses the same code to +schedule and manage your assets + +420 +00:15:21,487 --> 00:15:25,658 +in both your app +and its extension. + +421 +00:15:25,658 --> 00:15:27,860 +Also, when creating +your extension, + +422 +00:15:27,860 --> 00:15:30,630 +it's important to ensure that +both are in a common app group. + +423 +00:15:30,630 --> 00:15:32,465 +You'll want to use +the same group identifier + +424 +00:15:32,465 --> 00:15:34,767 +so that content can be read +and written by your app + +425 +00:15:34,767 --> 00:15:36,169 +and its extension. + +426 +00:15:36,169 --> 00:15:38,471 +Now let's take a look at the +downloader extension protocol + +427 +00:15:38,471 --> 00:15:41,174 +that you will be conforming +your extension to. + +428 +00:15:41,174 --> 00:15:43,142 +The first thing you'll notice +is how similar it looks + +429 +00:15:43,142 --> 00:15:45,778 +to the download manager +delegate protocol. + +430 +00:15:45,778 --> 00:15:49,115 +As I stated earlier, +you can use BADownloadManager + +431 +00:15:49,115 --> 00:15:51,951 +and construct a delegate +from within the extension. + +432 +00:15:51,951 --> 00:15:55,421 +However, only these entry points +can actually wake the extension. + +433 +00:15:55,421 --> 00:15:56,756 +The first function is invoked + +434 +00:15:56,756 --> 00:15:59,292 +whenever your application +is first installed. + +435 +00:15:59,292 --> 00:16:02,028 +The app hasn't launched yet, +but your extension has. + +436 +00:16:02,028 --> 00:16:04,897 +This is the perfect opportunity +to start scheduling downloads + +437 +00:16:04,897 --> 00:16:07,333 +that your app needs +to provide the best experience + +438 +00:16:07,333 --> 00:16:09,402 +once your app has been launched. + +439 +00:16:09,402 --> 00:16:12,104 +It's also important to recall +that during initial app install, + +440 +00:16:12,104 --> 00:16:14,207 +download restrictions +are in effect. + +441 +00:16:14,207 --> 00:16:16,676 +You'll want to consult +the BADownloadRestrictions key + +442 +00:16:16,676 --> 00:16:18,945 +that you defined +in your Info.plist + +443 +00:16:18,945 --> 00:16:21,047 +to know what your maximum +permitted download size + +444 +00:16:21,047 --> 00:16:22,381 +and allowed domains are. + +445 +00:16:22,381 --> 00:16:23,783 +This next function is invoked + +446 +00:16:23,783 --> 00:16:25,718 +whenever the App Store +updates your app. + +447 +00:16:25,718 --> 00:16:28,554 +As long as the user hasn't quit +your app in the app switcher, + +448 +00:16:28,554 --> 00:16:30,489 +your newly updated +extension will wake + +449 +00:16:30,489 --> 00:16:31,991 +and you can begin +scheduling work. + +450 +00:16:31,991 --> 00:16:33,926 +The checkForUpdates function +provides support + +451 +00:16:33,926 --> 00:16:35,695 +for your extension being +periodically awoken + +452 +00:16:35,695 --> 00:16:38,698 +by the system, so that you +can check for any updates + +453 +00:16:38,698 --> 00:16:40,800 +that need to be background +downloaded. + +454 +00:16:40,800 --> 00:16:42,869 +This function is invoked +by the system based on + +455 +00:16:42,869 --> 00:16:44,637 +how often a user uses your app. + +456 +00:16:44,637 --> 00:16:46,205 +We also have support +for responding + +457 +00:16:46,205 --> 00:16:48,441 +to an authentication +challenge request. + +458 +00:16:48,441 --> 00:16:49,976 +So that you can better +restrict and ensure + +459 +00:16:49,976 --> 00:16:51,410 +that the files +you are downloading + +460 +00:16:51,410 --> 00:16:53,346 +have come from a trusted source. + +461 +00:16:53,346 --> 00:16:54,881 +Finally, just like the delegate, + +462 +00:16:54,881 --> 00:16:57,950 +you'll be informed if the +download failed or succeeded. + +463 +00:16:57,950 --> 00:16:59,986 +You'll notice that in the +backgroundDownloadDidFail + +464 +00:16:59,986 --> 00:17:02,121 +function, +there is no error returned. + +465 +00:17:02,121 --> 00:17:04,390 +The error can be retrieved +along with its state + +466 +00:17:04,390 --> 00:17:07,426 +in a variable inside of +the returned BADownload object. + +467 +00:17:07,426 --> 00:17:09,662 +It's also important to note +that the last three functions + +468 +00:17:09,662 --> 00:17:11,397 +can be invoked +even if your extension + +469 +00:17:11,397 --> 00:17:13,232 +isn't what scheduled +the download. + +470 +00:17:13,232 --> 00:17:14,867 +If your app scheduled +a download, + +471 +00:17:14,867 --> 00:17:17,236 +but hasn’t become backgrounded, +then the extension + +472 +00:17:17,236 --> 00:17:19,405 +will be expected +to service the download. + +473 +00:17:19,405 --> 00:17:21,607 +Now that we understand +how to use BADownloaderManager + +474 +00:17:21,607 --> 00:17:24,243 +from our app and its extension, +we have to start thinking about + +475 +00:17:24,243 --> 00:17:26,479 +what it means if both the app +and its extension + +476 +00:17:26,479 --> 00:17:28,114 +are running simultaneously. + +477 +00:17:28,114 --> 00:17:30,249 +For instance, let's say +the system has decided + +478 +00:17:30,249 --> 00:17:31,851 +that it's time +to wake the extension + +479 +00:17:31,851 --> 00:17:35,154 +to have it periodically +check for updates. + +480 +00:17:35,154 --> 00:17:37,490 +And of course, since the +extension needs to access + +481 +00:17:37,490 --> 00:17:39,458 +the network to do this check, +it's going to use + +482 +00:17:39,458 --> 00:17:42,795 +BADownloaderManager to schedule +the download of a catalog + +483 +00:17:42,795 --> 00:17:45,398 +or some other type of metadata +that provides a list + +484 +00:17:45,398 --> 00:17:48,100 +of updated assets +that are available. + +485 +00:17:48,100 --> 00:17:51,570 +For instance, let's say the file +is a small 100KB catalog + +486 +00:17:51,570 --> 00:17:54,140 +that contains a list +of large multi-gigabyte assets + +487 +00:17:54,140 --> 00:17:55,508 +that we need to download. + +488 +00:17:55,508 --> 00:17:56,776 +Since the extension +needs to know + +489 +00:17:56,776 --> 00:18:00,012 +when the download it has +scheduled finished, or failed, + +490 +00:18:00,012 --> 00:18:03,149 +it's going to attach a delegate +onto the download manager. + +491 +00:18:03,149 --> 00:18:04,717 +The download manager's +delegate is used + +492 +00:18:04,717 --> 00:18:06,652 +over its extension entry points + +493 +00:18:06,652 --> 00:18:08,487 +since its downloading +a small file to determine + +494 +00:18:08,487 --> 00:18:10,957 +what larger assets it +will be scheduling, + +495 +00:18:10,957 --> 00:18:12,858 +and extension entry points +are not guaranteed + +496 +00:18:12,858 --> 00:18:14,327 +to be invoked immediately. + +497 +00:18:14,327 --> 00:18:15,628 +After the download +has finished, + +498 +00:18:15,628 --> 00:18:18,431 +the extension receives this +message through its delegate. + +499 +00:18:18,431 --> 00:18:20,433 +Your extension now has access +to the catalog file + +500 +00:18:20,433 --> 00:18:21,801 +and has to make a choice + +501 +00:18:21,801 --> 00:18:23,536 +with what it plans to do +with the downloaded file. + +502 +00:18:23,536 --> 00:18:25,271 +You could imagine that the +extension will read the file + +503 +00:18:25,271 --> 00:18:27,106 +to determine which of the assets +in the catalog + +504 +00:18:27,106 --> 00:18:28,841 +need to be downloaded +to the device. + +505 +00:18:28,841 --> 00:18:31,310 +Then the extension could +schedule background downloads + +506 +00:18:31,310 --> 00:18:32,778 +of those larger assets. + +507 +00:18:32,778 --> 00:18:35,081 +Now that the downloaded file +is no longer necessary, + +508 +00:18:35,081 --> 00:18:37,116 +the extension should delete +the file. + +509 +00:18:37,116 --> 00:18:38,417 +While this seems appropriate, + +510 +00:18:38,417 --> 00:18:40,453 +what happens if your app +launches while your extension + +511 +00:18:40,453 --> 00:18:42,688 +is running and creates its own +BADownloadManager? + +512 +00:18:42,688 --> 00:18:44,423 +Well, let's take a look! + +513 +00:18:44,423 --> 00:18:46,359 +The app launches +and immediately wants to know + +514 +00:18:46,359 --> 00:18:48,794 +if it has updated content. + +515 +00:18:48,794 --> 00:18:50,830 +Perhaps a version number +is stored in the app group + +516 +00:18:50,830 --> 00:18:54,200 +that both consult to determine +if their assets are up to date. + +517 +00:18:54,200 --> 00:18:56,235 +Since the app was launched +before the newer catalog + +518 +00:18:56,235 --> 00:18:58,070 +finished downloading, +it's going to fetch + +519 +00:18:58,070 --> 00:18:59,872 +the current downloads +from the manager + +520 +00:18:59,872 --> 00:19:01,640 +and realize that a download +of the catalog + +521 +00:19:01,640 --> 00:19:04,143 +is currently in flight +and wait for it to be finished + +522 +00:19:04,143 --> 00:19:05,311 +in its delegate. + +523 +00:19:05,311 --> 00:19:06,712 +But we have a problem. + +524 +00:19:06,712 --> 00:19:08,781 +Both the extension +and the app will receive + +525 +00:19:08,781 --> 00:19:11,417 +a download finished message +in their respective delegates + +526 +00:19:11,417 --> 00:19:13,619 +that were hooked into +the download manager. + +527 +00:19:13,619 --> 00:19:16,856 +This means we have a data race +on the file being downloaded. + +528 +00:19:16,856 --> 00:19:20,059 +Both the app and extension will +try to read and delete the file + +529 +00:19:20,059 --> 00:19:21,894 +at the same time; +this is not good. + +530 +00:19:21,894 --> 00:19:23,796 +This means that either +your app or extension + +531 +00:19:23,796 --> 00:19:26,766 +could try to read the file +and it might be missing. + +532 +00:19:26,766 --> 00:19:28,768 +This means that you will need +think about your app + +533 +00:19:28,768 --> 00:19:31,170 +and extension in a similar way +that you would think about + +534 +00:19:31,170 --> 00:19:33,539 +two threads within your app. + +535 +00:19:33,539 --> 00:19:36,042 +Luckily, Background Assets +provides a way to synchronize + +536 +00:19:36,042 --> 00:19:38,110 +between your app +and its extension. + +537 +00:19:38,110 --> 00:19:39,345 +Let's talk about that now! + +538 +00:19:39,345 --> 00:19:41,414 +Synchronizing between +your app and its extension + +539 +00:19:41,414 --> 00:19:44,283 +is extremely simple +with Background Assets. + +540 +00:19:44,283 --> 00:19:46,552 +What we're currently looking at +is the download manager's + +541 +00:19:46,552 --> 00:19:49,755 +delegate function for when +a download has completed. + +542 +00:19:49,755 --> 00:19:52,491 +A URL is provided that contains +a local path to the file + +543 +00:19:52,491 --> 00:19:55,828 +that your app or extension +has access to. + +544 +00:19:55,828 --> 00:19:58,197 +In this example, we'll be +ensuring mutual exclusion + +545 +00:19:58,197 --> 00:19:59,432 +of this file. + +546 +00:19:59,432 --> 00:20:01,600 +Next, we grab a reference +to the download manager + +547 +00:20:01,600 --> 00:20:03,736 +and use the +withExclusiveControl function, + +548 +00:20:03,736 --> 00:20:05,805 +which takes +a completion handler. + +549 +00:20:05,805 --> 00:20:08,340 +All code that is executed within +the completion handler scope + +550 +00:20:08,340 --> 00:20:11,010 +is guaranteed to be mutually +exclusive with other calls + +551 +00:20:11,010 --> 00:20:13,179 +that require exclusive control. + +552 +00:20:13,179 --> 00:20:16,348 +Meaning if your extension calls +withExclusiveControl + +553 +00:20:16,348 --> 00:20:19,185 +while you app has not returned +from its completion handler, + +554 +00:20:19,185 --> 00:20:21,220 +then the extension will wait. + +555 +00:20:21,220 --> 00:20:23,155 +This applies in the other +direction as well. + +556 +00:20:23,155 --> 00:20:25,925 +If the extension acquires +exclusive control first, + +557 +00:20:25,925 --> 00:20:28,294 +then the app will wait until +the extension is terminated + +558 +00:20:28,294 --> 00:20:30,863 +or releases control +by exiting scope. + +559 +00:20:30,863 --> 00:20:32,298 +An important thing +to keep in mind + +560 +00:20:32,298 --> 00:20:35,067 +is that acquiring exclusive +control can fail. + +561 +00:20:35,067 --> 00:20:37,336 +It is extremely unlikely +that this will occur, + +562 +00:20:37,336 --> 00:20:40,072 +but in the event it does, +your code should handle it. + +563 +00:20:40,072 --> 00:20:42,475 +You can detect if exclusive +control could not be acquired + +564 +00:20:42,475 --> 00:20:44,376 +by checking if the error +provided by the function + +565 +00:20:44,376 --> 00:20:45,611 +is not nil. + +566 +00:20:45,611 --> 00:20:47,947 +From this point on, +you are guaranteed that your app + +567 +00:20:47,947 --> 00:20:51,383 +or extension has exclusive +access within its context. + +568 +00:20:51,383 --> 00:20:53,052 +So based on our +earlier example, + +569 +00:20:53,052 --> 00:20:55,588 +it is now perfectly valid to +read the contents of the file + +570 +00:20:55,588 --> 00:20:58,524 +and then clean it up, +if you so choose. + +571 +00:20:58,524 --> 00:21:01,861 +Just make sure to be aware that +when your other app or extension + +572 +00:21:01,861 --> 00:21:04,396 +gets its opportunity +to enter exclusive control + +573 +00:21:04,396 --> 00:21:07,166 +that it knows that you have +already processed the file. + +574 +00:21:07,166 --> 00:21:09,835 +One way this can be accomplished +is by first checking + +575 +00:21:09,835 --> 00:21:12,738 +if the file exists or writing +to a database or plist. + +576 +00:21:12,738 --> 00:21:15,374 +As a reminder, the background +downloader extension + +577 +00:21:15,374 --> 00:21:18,177 +is for collecting and scheduling +the downloads of large assets + +578 +00:21:18,177 --> 00:21:19,578 +for your app. + +579 +00:21:19,578 --> 00:21:21,080 +Its runtime is short lived, + +580 +00:21:21,080 --> 00:21:22,715 +so please keep +the work that is done + +581 +00:21:22,715 --> 00:21:24,550 +within the extension +to a minimum. + +582 +00:21:24,550 --> 00:21:26,318 +You should also place +your extension and app + +583 +00:21:26,318 --> 00:21:28,787 +within a shared app group +so that both can access files + +584 +00:21:28,787 --> 00:21:30,923 +that are downloaded +by one another. + +585 +00:21:30,923 --> 00:21:33,592 +And finally, the extension +is brokered by the system + +586 +00:21:33,592 --> 00:21:34,693 +and not your app. + +587 +00:21:34,693 --> 00:21:36,095 +Now that you know +how to develop + +588 +00:21:36,095 --> 00:21:38,130 +a basic background +download extension, + +589 +00:21:38,130 --> 00:21:39,932 +you have everything you need +to start implementing + +590 +00:21:39,932 --> 00:21:41,700 +Background Assets into your app. + +591 +00:21:41,700 --> 00:21:43,469 +Now, let's go over +what we've learned. + +592 +00:21:43,469 --> 00:21:45,604 +The download manager is used +to coordinate and schedule + +593 +00:21:45,604 --> 00:21:48,707 +downloads between your app +and its extension, + +594 +00:21:48,707 --> 00:21:50,809 +therefore you should be using +the download manager + +595 +00:21:50,809 --> 00:21:52,244 +in both places. + +596 +00:21:52,244 --> 00:21:55,347 +Your extension runs even if your +app is not in the foreground. + +597 +00:21:55,347 --> 00:21:58,083 +This can occur during +app installation, update, + +598 +00:21:58,083 --> 00:22:01,053 +or periodically at an interval +determined by the system. + +599 +00:22:01,053 --> 00:22:02,388 +If your app is launched + +600 +00:22:02,388 --> 00:22:04,290 +and content that was being +downloaded in the background + +601 +00:22:04,290 --> 00:22:05,925 +is now be waited on, + +602 +00:22:05,925 --> 00:22:08,961 +please immediately promote those +downloads to the foreground. + +603 +00:22:08,961 --> 00:22:11,897 +The extension can only schedule +downloads in the background. + +604 +00:22:11,897 --> 00:22:14,233 +By having your app promote them +to the foreground, + +605 +00:22:14,233 --> 00:22:17,203 +ensures that your content will +arrive as quickly as possible. + +606 +00:22:17,203 --> 00:22:19,271 +If you ever find yourself +needing exclusive access + +607 +00:22:19,271 --> 00:22:23,075 +to the download manager, please +use the exclusive control APIs. + +608 +00:22:23,075 --> 00:22:25,678 +This will ensure that only +your app or extension + +609 +00:22:25,678 --> 00:22:27,913 +will have runtime +within that window. + +610 +00:22:27,913 --> 00:22:30,683 +This is extremely useful so that +you don't have to think about + +611 +00:22:30,683 --> 00:22:33,485 +your extension racing your app +when accessing its container + +612 +00:22:33,485 --> 00:22:34,820 +or managing downloads. + +613 +00:22:34,820 --> 00:22:37,456 +If there is anything you should +take from this presentation, + +614 +00:22:37,456 --> 00:22:39,925 +it's that waiting results +in a poor app experience. + +615 +00:22:39,925 --> 00:22:42,161 +Minimize waiting +by making your app usable + +616 +00:22:42,161 --> 00:22:45,164 +while the task +you are waiting on is underway. + +617 +00:22:45,164 --> 00:22:47,333 +One of the ways you can +minimize waiting in your app + +618 +00:22:47,333 --> 00:22:49,235 +is to adopt the new +Background Assets framework + +619 +00:22:49,235 --> 00:22:51,804 +along with the underlying +background download extension. + +620 +00:22:51,804 --> 00:22:53,772 +This helps to ensure +that your app will have + +621 +00:22:53,772 --> 00:22:57,176 +all of its content ready +before the app is launched. + +622 +00:22:57,176 --> 00:22:59,044 +Make sure to also check out +the documentation, + +623 +00:22:59,044 --> 00:23:01,547 +which includes extra information +that may not have been + +624 +00:23:01,547 --> 00:23:03,649 +incorporated into +this presentation; + +625 +00:23:03,649 --> 00:23:05,718 +including how to test +your extension + +626 +00:23:05,718 --> 00:23:08,387 +and to simulate +its entry points. + +627 +00:23:08,387 --> 00:23:10,122 +We're really excited +to get to share + +628 +00:23:10,122 --> 00:23:12,691 +Background Assets with you, +and we value your feedback. + +629 +00:23:12,691 --> 00:23:14,827 +Please use Feedback Assistant +to let us know + +630 +00:23:14,827 --> 00:23:17,896 +what is working for you +and what you'd like us improve. + +631 +00:23:17,896 --> 00:23:20,332 +This is a new framework +and we have the opportunity + +632 +00:23:20,332 --> 00:23:23,369 +to make adjustments +during seeding. + +633 +00:23:23,369 --> 00:23:24,837 +We have +some additional sessions + +634 +00:23:24,837 --> 00:23:26,438 +that we think you might +find interesting + +635 +00:23:26,438 --> 00:23:28,340 +and we encourage you +to check them out. + +636 +00:23:28,340 --> 00:23:31,810 +"Accelerating networking with +HTTP3" is a fantastic session + +637 +00:23:31,810 --> 00:23:33,912 +that pairs well with +Background Assets. + +638 +00:23:33,912 --> 00:23:37,082 +Also, I encourage you +to check out another session: + +639 +00:23:37,082 --> 00:23:39,952 +the "Introducing on demand +resources" presentation + +640 +00:23:39,952 --> 00:23:41,920 +covers an alternative +to Background Assets + +641 +00:23:41,920 --> 00:23:44,023 +where your content +is hosted by Apple + +642 +00:23:44,023 --> 00:23:46,659 +and files are downloaded +at your request. + +643 +00:23:46,659 --> 00:23:48,260 +Both of these sessions +are really engaging + +644 +00:23:48,260 --> 00:23:49,895 +and have a lot to offer. + +645 +00:23:49,895 --> 00:23:51,597 +Thank you for spending +your time with me, + +646 +00:23:51,597 --> 00:23:53,032 +and on behalf of everyone +at Apple, + +647 +00:23:53,032 --> 00:23:55,000 +we hope you have +a fantastic WWDC! + +648 +00:23:55,000 --> 00:23:59,004 +♪ + diff --git a/eng/2022 Session 110404 Implement proactive in-app purchase restore en.srt b/eng/2022 Session 110404 Implement proactive in-app purchase restore en.srt new file mode 100644 index 0000000..6e19550 --- /dev/null +++ b/eng/2022 Session 110404 Implement proactive in-app purchase restore en.srt @@ -0,0 +1,1783 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,309 --> 00:00:11,245 +Hi, I’m David Wendland, + +3 +00:00:11,278 --> 00:00:14,414 +a Commerce Technical Advocate +for the App Store. + +4 +00:00:14,448 --> 00:00:18,452 +Today, I’ll show you how your app +can deliver a first class experience + +5 +00:00:18,485 --> 00:00:21,555 +by proactively identifying +a customer's new, current, + +6 +00:00:21,588 --> 00:00:25,859 +and past purchases, +without the customer taking any action. + +7 +00:00:25,893 --> 00:00:30,330 +I’ll cover how to do this with StoreKit 2 +and the original StoreKit, + +8 +00:00:30,364 --> 00:00:33,100 +so you can optimize +your app's onboarding experience + +9 +00:00:33,133 --> 00:00:36,003 +for all your customers. + +10 +00:00:36,036 --> 00:00:39,907 +Let me start by defining +proactive in-app purchase restore. + +11 +00:00:39,940 --> 00:00:42,309 +This means that when +a customer launches your app, + +12 +00:00:42,342 --> 00:00:45,946 +you use the data readily available, +on device, + +13 +00:00:45,979 --> 00:00:48,582 +to proactively check for the transactions + +14 +00:00:48,615 --> 00:00:53,420 +in order to determine +if they are a new or existing customer + +15 +00:00:53,453 --> 00:00:56,557 +and doing so +without requiring any customer action, + +16 +00:00:56,590 --> 00:01:01,328 +not even tapping a "Restore Purchases" +button or entering a password. + +17 +00:01:01,361 --> 00:01:04,164 +This enables you +to tailor your app experience + +18 +00:01:04,198 --> 00:01:07,801 +to your customer's purchase history +and state + +19 +00:01:07,835 --> 00:01:12,039 +so your app unlocks products or services +for your current customers, + +20 +00:01:12,072 --> 00:01:16,743 +or your app merchandises your latest +product offering to new customers, + +21 +00:01:16,777 --> 00:01:18,345 +or for those past subscribers, + +22 +00:01:18,378 --> 00:01:21,815 +you present them subscription offers +to win them back. + +23 +00:01:21,849 --> 00:01:24,418 +This is what proactive restore is about, + +24 +00:01:24,451 --> 00:01:27,221 +using StoreKit to optimize +your app's experience + +25 +00:01:27,254 --> 00:01:33,360 +for new, existing, and past customers, +on all of their devices, automatically. + +26 +00:01:33,393 --> 00:01:35,462 +Let’s look at this example. + +27 +00:01:35,495 --> 00:01:38,232 +Here we have our Ocean Journal app. + +28 +00:01:38,265 --> 00:01:40,667 +This is a common merchandising experience, + +29 +00:01:40,701 --> 00:01:44,304 +where the customer has a few different +calls to action to choose from. + +30 +00:01:44,338 --> 00:01:47,107 +Either I can attempt +to buy the in-app purchase + +31 +00:01:47,140 --> 00:01:50,777 +and authenticate with biometrics, +such as FaceID, + +32 +00:01:50,811 --> 00:01:52,412 +or if I’ve created an app account, + +33 +00:01:52,446 --> 00:01:56,250 +I could sign in and possibly use Keychain +to enter my password, + +34 +00:01:56,283 --> 00:01:58,652 +or if I believe I’m an active subscriber, + +35 +00:01:58,685 --> 00:02:01,388 +I could use the "Restore Purchases" +button. + +36 +00:02:01,421 --> 00:02:03,757 +For your active subscribers +on a new device, + +37 +00:02:03,790 --> 00:02:07,427 +knowing which option to choose +isn’t always clear to them. + +38 +00:02:07,461 --> 00:02:10,297 +And with the data readily available +to your app, + +39 +00:02:10,330 --> 00:02:12,199 +this experience can be streamlined + +40 +00:02:12,232 --> 00:02:15,469 +with our proactive in-app +purchase restore best practice. + +41 +00:02:16,036 --> 00:02:21,475 +So, if I launched this app on a new device +but was already an active subscriber, + +42 +00:02:21,508 --> 00:02:26,013 +upon launch, the app would proactively +restore my service, automatically, + +43 +00:02:26,046 --> 00:02:28,649 +without requiring any action from me. + +44 +00:02:28,682 --> 00:02:31,318 +So here the app recognized +my Pro subscription + +45 +00:02:31,351 --> 00:02:34,488 +and loaded my favorite beach, +complete with surf conditions + +46 +00:02:34,521 --> 00:02:36,990 +and enabled the live cam feature. + +47 +00:02:37,024 --> 00:02:41,061 +This experience differentiates +your app from the others + +48 +00:02:41,094 --> 00:02:45,899 +and I will cover how to do this +with StoreKit 2 on iOS 15 and newer. + +49 +00:02:45,933 --> 00:02:49,636 +Additionally, if your app supports +previous versions of iOS, + +50 +00:02:49,670 --> 00:02:51,905 +I will cover how to create this same +great experience + +51 +00:02:51,939 --> 00:02:55,542 +with original StoreKit +and the verifyReceipt endpoint. + +52 +00:02:55,576 --> 00:02:58,178 +With that background, +here’s what I’ll cover. + +53 +00:02:58,212 --> 00:03:02,149 +First, I’ll describe in detail +the core customer product states + +54 +00:03:02,182 --> 00:03:06,019 +that your app uses +to generate personalized experiences + +55 +00:03:06,053 --> 00:03:10,257 +based on the customer's in-app purchases +with StoreKit. + +56 +00:03:10,290 --> 00:03:14,194 +Then I'll review the steps to implement, +using StoreKit 2, + +57 +00:03:14,228 --> 00:03:19,132 +complete with sample code +using the SK Demo app. + +58 +00:03:19,166 --> 00:03:21,335 +Let’s look at each in-app purchase type, +their core customer product states, + +59 +00:03:23,370 --> 00:03:27,975 +and review a few examples of +a personalized onboarding experience. + +60 +00:03:28,008 --> 00:03:30,711 +To start, +the in-app purchase types that apply + +61 +00:03:30,744 --> 00:03:35,148 +to proactive restore are non-consumables, +non-renewing subscriptions, + +62 +00:03:35,182 --> 00:03:37,217 +and auto-renewable subscriptions, + +63 +00:03:37,251 --> 00:03:40,454 +as they are all persistent +in the customer's transaction history + +64 +00:03:40,487 --> 00:03:43,056 +and will always be available +with StoreKit. + +65 +00:03:43,090 --> 00:03:46,994 +Therefore your app can identify +per customer their purchase state + +66 +00:03:47,027 --> 00:03:51,298 +for each product or subscription group. + +67 +00:03:51,331 --> 00:03:54,001 +For non-renewing and auto-renewable +subscriptions, + +68 +00:03:54,034 --> 00:03:57,104 +I will use the term "subscriptions" +to reference them both + +69 +00:03:57,137 --> 00:04:01,608 +as we review the customer product states. + +70 +00:04:01,642 --> 00:04:05,078 +Here are the three core states your app +can personalize against. + +71 +00:04:05,112 --> 00:04:08,549 +Let’s review in-depth new customers. + +72 +00:04:08,582 --> 00:04:11,785 +This state represents +a signed in App Store Apple ID + +73 +00:04:11,818 --> 00:04:16,456 +that does not have any current +or past in-app purchase transactions. + +74 +00:04:16,490 --> 00:04:21,328 +This state is typically used as the app’s +default merchandising experience. + +75 +00:04:21,361 --> 00:04:25,599 +Our Ocean Journal app is merchandising +its monthly and annual subscription + +76 +00:04:25,632 --> 00:04:28,268 +with a one month free trial. + +77 +00:04:28,302 --> 00:04:33,307 +Looking at our second core state, +we have Purchased and Active Subscriber. + +78 +00:04:33,340 --> 00:04:36,210 +In this state, +a customer has an active transaction + +79 +00:04:36,243 --> 00:04:39,046 +and your app is obligated +to grant the customer access + +80 +00:04:39,079 --> 00:04:41,548 +to the purchased product or service. + +81 +00:04:41,582 --> 00:04:44,852 +Here, our Ocean Journal app +immediately presents the customer + +82 +00:04:44,885 --> 00:04:48,722 +their preferred beach +with the premium live beach cam. + +83 +00:04:48,755 --> 00:04:53,560 +No buy buttons are visible, +as service was proactively restored. + +84 +00:04:53,594 --> 00:04:56,263 +For each purchased product +or active subscription, + +85 +00:04:56,296 --> 00:05:00,501 +the transaction has a static +and unique original transaction ID, + +86 +00:05:00,534 --> 00:05:04,505 +which persists for the customer's +Apple ID and storefront. + +87 +00:05:04,538 --> 00:05:07,274 +To maintain status +of the customer's transactions, + +88 +00:05:07,307 --> 00:05:12,012 +associate the original transaction IDs +with an account on your system. + +89 +00:05:12,045 --> 00:05:14,147 +It can be either an anonymous account, + +90 +00:05:14,181 --> 00:05:17,017 +or an account that the user created +with your system. + +91 +00:05:17,050 --> 00:05:20,053 +Knowing the original transaction ID +is critical + +92 +00:05:20,087 --> 00:05:23,290 +when leveraging the power +of App Store Server Notifications, + +93 +00:05:23,323 --> 00:05:27,194 +allowing your server to remain current +on the transaction status. + +94 +00:05:27,227 --> 00:05:30,397 +One scenario to highlight +is when a customer's subscription + +95 +00:05:30,430 --> 00:05:32,199 +failed to auto-renew, + +96 +00:05:32,232 --> 00:05:35,836 +therefore it falls into +what we call the billing retry state, + +97 +00:05:35,869 --> 00:05:39,740 +where we attempt to recover +the subscription for up to 60 days. + +98 +00:05:39,773 --> 00:05:43,544 +If you have opted in to the Billing +grace period feature in App Store Connect, + +99 +00:05:43,577 --> 00:05:46,046 +then subscribers in billing retry +with grace period + +100 +00:05:46,079 --> 00:05:48,949 +would continue to have access +to their subscription service, + +101 +00:05:48,982 --> 00:05:52,119 +while we attempt to recover +their subscription. + +102 +00:05:52,152 --> 00:05:54,688 +And while they have still access +to your service, + +103 +00:05:54,721 --> 00:05:57,291 +be sure to present them +a simple call to action + +104 +00:05:57,324 --> 00:05:59,459 +to resolve their payment issue. + +105 +00:05:59,493 --> 00:06:02,596 +To learn more about Billing retry and +Billing grace period, + +106 +00:06:02,629 --> 00:06:04,598 +check out our sessions links and resources + +107 +00:06:04,631 --> 00:06:08,569 +about reducing involuntary +subscriber loss. + +108 +00:06:08,602 --> 00:06:13,574 +The final core state is the inactive +purchase or inactive subscriber. + +109 +00:06:13,607 --> 00:06:17,678 +This state represents customers +who previously made in-app purchases, + +110 +00:06:17,711 --> 00:06:20,747 +but are no longer entitled +to that product or service, + +111 +00:06:20,781 --> 00:06:23,984 +due to expiry or if revoked. + +112 +00:06:24,017 --> 00:06:28,188 +These transactions are persistent +and contain an original transaction ID, + +113 +00:06:28,222 --> 00:06:32,893 +which allows you to maintain status +across devices and platforms. + +114 +00:06:32,926 --> 00:06:37,130 +For Subscriptions, inactive is determined +by the expires date. + +115 +00:06:37,164 --> 00:06:38,966 +And for all in-app purchase types, + +116 +00:06:38,999 --> 00:06:42,135 +they can be inactive +if there is a revocation date. + +117 +00:06:42,169 --> 00:06:44,638 +This occurs when a transaction +has been refunded + +118 +00:06:44,671 --> 00:06:48,575 +or if access granted through +Family Sharing has been revoked. + +119 +00:06:48,609 --> 00:06:52,746 +For your inactive subscribers, +due to expiration or revoked, + +120 +00:06:52,779 --> 00:06:56,250 +consider presenting +subscription offers to win them back. + +121 +00:06:56,283 --> 00:06:58,519 +And for those in the billing retry state, + +122 +00:06:58,552 --> 00:07:01,221 +don’t forget to present them +that same call to action + +123 +00:07:01,255 --> 00:07:03,891 +to resolve their payment details. + +124 +00:07:03,924 --> 00:07:08,562 +In review, here are the three core +customer product states your app will use + +125 +00:07:08,595 --> 00:07:11,465 +to proactively restore in-app purchases + +126 +00:07:11,498 --> 00:07:15,068 +and tailor your app's experience +to your customers. + +127 +00:07:15,102 --> 00:07:19,473 +Let's see how these experiences look +side-by-side with our Ocean Journal app. + +128 +00:07:21,475 --> 00:07:26,146 +New customers will see your latest +product offering and introductory offers. + +129 +00:07:26,180 --> 00:07:29,616 +Your current active customers +will have the feeling it just works, + +130 +00:07:29,650 --> 00:07:33,187 +as your app has streamlined access +to your products and services + +131 +00:07:33,220 --> 00:07:35,289 +on all of their devices. + +132 +00:07:35,322 --> 00:07:37,257 +And for your inactive subscribers, + +133 +00:07:37,291 --> 00:07:39,826 +you can present them +your latest win-back offers + +134 +00:07:39,860 --> 00:07:42,763 +using offer codes or promotional offers. + +135 +00:07:43,363 --> 00:07:46,967 +Okay, we’ve covered +the three core customer product states, + +136 +00:07:47,000 --> 00:07:50,971 +and how supporting these states alone +is a huge win for your customers. + +137 +00:07:51,004 --> 00:07:54,775 +But of course, there are opportunities +to take the experience further. + +138 +00:07:54,808 --> 00:07:57,611 +Your app could expand +or refine the customer experience + +139 +00:07:57,644 --> 00:08:00,414 +to fit your product offering, +business model, + +140 +00:08:00,447 --> 00:08:02,883 +policies, and prioritizations. + +141 +00:08:02,916 --> 00:08:04,885 +But here are few things to consider + +142 +00:08:04,918 --> 00:08:08,655 +when preparing to implement +proactive restore into your app. + +143 +00:08:09,756 --> 00:08:12,726 +If you support multiple products +or subscription groups, + +144 +00:08:12,759 --> 00:08:18,098 +the customer's state is determined for +each product and each subscription group. + +145 +00:08:18,131 --> 00:08:20,567 +Therefore, you may need +to account for hybrid states + +146 +00:08:20,601 --> 00:08:23,937 +or any other dependencies. + +147 +00:08:23,971 --> 00:08:26,240 +Consider any off-platform activity + +148 +00:08:26,273 --> 00:08:30,377 +and how that factors into +your customer's product state. + +149 +00:08:30,410 --> 00:08:33,280 +And be sure to check out +App Store Server Notifications, + +150 +00:08:33,313 --> 00:08:36,683 +as these are critical to maintaining +status, server-to-server, + +151 +00:08:36,717 --> 00:08:39,319 +for all in-app purchase types. + +152 +00:08:39,353 --> 00:08:43,090 +And with Version +2, the new notification types and subtypes + +153 +00:08:43,123 --> 00:08:45,292 +support 28 unique events, + +154 +00:08:45,325 --> 00:08:48,929 +sent securely to your server +in near real time. + +155 +00:08:48,962 --> 00:08:52,399 +Learn more about integrating +or migrating to Version 2 + +156 +00:08:52,432 --> 00:08:56,970 +in the session, "Explore in-app purchase +integration and migration." + +157 +00:08:57,004 --> 00:09:00,274 +Alex and Gabriel also cover compatibility +with StoreKit 2 + +158 +00:09:00,307 --> 00:09:04,278 +and the original StoreKit framework, +and best practices. + +159 +00:09:04,311 --> 00:09:07,614 +We’ve talked through +the customer product states to support + +160 +00:09:07,648 --> 00:09:11,151 +and what that experience can be +for your customers. + +161 +00:09:11,185 --> 00:09:14,221 +Now let’s walk through +the implementation details. + +162 +00:09:14,254 --> 00:09:16,323 +I’ll be using our SK Demo App + +163 +00:09:16,356 --> 00:09:20,027 +that we’ve updated with proactive restore +using StoreKit 2. + +164 +00:09:20,060 --> 00:09:24,565 +Note that the SK Demo app will be +available for download with this session. + +165 +00:09:24,598 --> 00:09:28,569 +Let's review the SK Demo’s +default experience for new customers, + +166 +00:09:28,602 --> 00:09:31,405 +those without any active in-app purchases. + +167 +00:09:31,438 --> 00:09:34,942 +To view our products, +tap the “Shop” button, + +168 +00:09:34,975 --> 00:09:38,045 +where up top we have our inventory +of available cars + +169 +00:09:38,078 --> 00:09:41,114 +as non-consumable in-app purchases. + +170 +00:09:41,148 --> 00:09:45,519 +And then we have our navigation service +as a monthly auto-renewable subscription, + +171 +00:09:45,552 --> 00:09:50,190 +which offers three different levels +of service for customers to choose from. + +172 +00:09:50,224 --> 00:09:53,293 +And down below, we have +a non-renewing subscription option, + +173 +00:09:53,327 --> 00:09:55,729 +providing one-time access. + +174 +00:09:55,762 --> 00:09:58,398 +This covers our app's +new customer experience, + +175 +00:09:58,432 --> 00:10:00,901 +when no products have been purchased. + +176 +00:10:00,934 --> 00:10:03,904 +Now let's look at +how our app is able to determine + +177 +00:10:03,937 --> 00:10:07,708 +if the customer has current +or past purchases. + +178 +00:10:07,741 --> 00:10:10,310 +It requires your app +to execute three steps + +179 +00:10:10,344 --> 00:10:12,646 +immediately upon app launch. + +180 +00:10:12,679 --> 00:10:15,649 +What is most important +is that these steps are completed + +181 +00:10:15,682 --> 00:10:19,119 +before a "Buy" button is merchandised +to the customer. + +182 +00:10:19,853 --> 00:10:22,456 +The first step is your app +will need to begin listening + +183 +00:10:22,489 --> 00:10:25,425 +for transactions from the App Store. + +184 +00:10:25,459 --> 00:10:30,430 +This is an App Store best practice, +as transactions can show up at any time + +185 +00:10:30,464 --> 00:10:33,166 +from features such as +Family Sharing Ask to Buy, + +186 +00:10:33,200 --> 00:10:35,936 +code redemptions, +subscription auto-renewals, + +187 +00:10:35,969 --> 00:10:38,539 +or when a purchase gets interrupted. + +188 +00:10:38,572 --> 00:10:41,975 +In addition, your app can receive revoked +transactions, + +189 +00:10:42,009 --> 00:10:44,211 +where access is lost due to a refund + +190 +00:10:44,244 --> 00:10:47,347 +or is no longer shared via Family sharing. + +191 +00:10:47,381 --> 00:10:49,883 +This will apply more +in subsequent app launches, + +192 +00:10:49,917 --> 00:10:52,152 +when access has already been granted + +193 +00:10:52,186 --> 00:10:56,356 +and their state is moving +from active to inactive. + +194 +00:10:56,390 --> 00:11:00,527 +If transactions are found, they are +considered unfinished transactions, + +195 +00:11:00,561 --> 00:11:03,330 +and need to be validated, +delivered to the customer, + +196 +00:11:03,363 --> 00:11:05,599 +and marked as finished. + +197 +00:11:05,632 --> 00:11:08,435 +This ensures your app +won’t miss any transactions + +198 +00:11:08,468 --> 00:11:10,971 +and delivers a great customer experience. + +199 +00:11:11,004 --> 00:11:16,043 +Now let’s look at how our SK Demo app +listens for transactions in StoreKit 2. + +200 +00:11:16,076 --> 00:11:18,946 +Here I’m using the function +listenForTransactions. + +201 +00:11:18,979 --> 00:11:23,684 +It will return any unfinished transactions +or updates to a transaction + +202 +00:11:23,717 --> 00:11:26,787 +for the signed-in App Store customer. + +203 +00:11:26,820 --> 00:11:28,622 +For any transactions found, + +204 +00:11:28,655 --> 00:11:32,626 +here, StoreKit 2 will verify +the authenticity of these transactions. + +205 +00:11:32,659 --> 00:11:35,696 +And then, +after my app delivers the content, + +206 +00:11:35,729 --> 00:11:38,765 +grants access, +or updates the customer product status, + +207 +00:11:38,799 --> 00:11:40,667 +I will then finish the transaction + +208 +00:11:40,701 --> 00:11:44,872 +to indicate to the App Store +that the purchase has been delivered. + +209 +00:11:44,905 --> 00:11:48,642 +Once a transaction is finished, +it will no longer be returned to your app, + +210 +00:11:48,675 --> 00:11:51,812 +on any device, via StoreKit. + +211 +00:11:51,845 --> 00:11:54,448 +That first step is critical for all apps + +212 +00:11:54,481 --> 00:11:58,185 +and will occur on every app launch +going forward. + +213 +00:11:58,218 --> 00:12:01,922 +Step 2 is determining +that customer product state, + +214 +00:12:01,955 --> 00:12:06,326 +and this is done by proactively requesting +the customer's active transactions + +215 +00:12:06,360 --> 00:12:08,529 +using currentEntitlements. + +216 +00:12:08,562 --> 00:12:11,365 +And specifically for auto-renewable +subscriptions, + +217 +00:12:11,398 --> 00:12:13,367 +to account for +the customer's renewal state, + +218 +00:12:13,400 --> 00:12:16,737 +such as cancelled, +billing retry, or pending downgrades, + +219 +00:12:16,770 --> 00:12:22,576 +you will additionally use +Product.SubscriptionInfo.RenewalState. + +220 +00:12:22,609 --> 00:12:27,214 +Let’s look at the SK Demo app +and see how we accomplish this. + +221 +00:12:27,247 --> 00:12:30,651 +This starts with the function, +updateCustomerProductStatus, + +222 +00:12:30,684 --> 00:12:33,687 +which keeps track +of the customer's product states + +223 +00:12:33,720 --> 00:12:37,758 +for each of our persistent +in-app purchase types. + +224 +00:12:37,791 --> 00:12:40,427 +I then loop through +each of the purchase types + +225 +00:12:40,460 --> 00:12:43,997 +using StoreKit 2’s +currentEntitlements method. + +226 +00:12:44,031 --> 00:12:49,603 +This returns transactions for products +that the customer may be entitled to. + +227 +00:12:49,636 --> 00:12:52,940 +And we record these transactions +per product type. + +228 +00:12:52,973 --> 00:12:55,642 +Here, for our non-consumables products, + +229 +00:12:55,676 --> 00:12:58,846 +and here for our non-renewing +subscription product. + +230 +00:12:58,879 --> 00:13:03,350 +In order to determine if they are +an active, or inactive subscriber, + +231 +00:13:03,383 --> 00:13:06,553 +I’ve added additional logic +to calculate an expiration date + +232 +00:13:06,587 --> 00:13:09,423 +for our non-renewing subscription. + +233 +00:13:09,456 --> 00:13:14,294 +And lastly, I will check for +an active auto-renewable subscription, + +234 +00:13:14,328 --> 00:13:17,731 +and apply that state to +the subscription group. + +235 +00:13:17,764 --> 00:13:22,202 +To account for inactive states such as +billing retry, expired, and revoked, + +236 +00:13:22,236 --> 00:13:24,738 +our variable subscription +group status uses + +237 +00:13:24,771 --> 00:13:29,176 +Product.SubscriptionInfo.RenewalState + +238 +00:13:29,209 --> 00:13:31,411 +Now that we've retrieved +the user's transactions + +239 +00:13:31,445 --> 00:13:35,682 +and determined the customer status +for each product or subscription group, + +240 +00:13:35,716 --> 00:13:38,519 +our app has logic +to personalize the app experience + +241 +00:13:38,552 --> 00:13:40,687 +for the various use cases. + +242 +00:13:40,721 --> 00:13:44,791 +Let’s take a look at +the SK Demo app source code. + +243 +00:13:44,825 --> 00:13:46,994 +If no active transactions are determined + +244 +00:13:47,027 --> 00:13:49,796 +for all three in-app purchase +product types, + +245 +00:13:49,830 --> 00:13:52,900 +the customer will then see +the default new customer experience + +246 +00:13:52,933 --> 00:13:54,868 +that we reviewed earlier, + +247 +00:13:54,902 --> 00:13:58,672 +where they will have a simple +call to action to our "Shop" page. + +248 +00:13:58,705 --> 00:14:01,175 +If the customer has any active purchases, + +249 +00:14:01,208 --> 00:14:04,144 +then upon app launch, +they will see their purchases + +250 +00:14:04,178 --> 00:14:08,015 +and update "Buy" buttons +on all products accordingly. + +251 +00:14:08,048 --> 00:14:11,585 +So here for non-consumables, +we present what they’ve purchased + +252 +00:14:11,618 --> 00:14:14,421 +and the app either shows +their purchased non-consumables, + +253 +00:14:14,454 --> 00:14:16,190 +or the app provides a call to action + +254 +00:14:16,223 --> 00:14:19,092 +for the customer to visit +the shop experience. + +255 +00:14:19,126 --> 00:14:21,695 +For active products, +here we handle if the customer is + +256 +00:14:21,728 --> 00:14:25,199 +an active subscriber +of the navigation service + +257 +00:14:25,232 --> 00:14:29,102 +for non-renewable subscriptions +and auto-renewable subscriptions. + +258 +00:14:29,136 --> 00:14:32,439 +And in our last portion, +we account for inactive subscribers. + +259 +00:14:32,472 --> 00:14:35,676 +Those with subscriptions +that have expired, been revoked, + +260 +00:14:35,709 --> 00:14:38,612 +or are in the billing retry state. + +261 +00:14:38,645 --> 00:14:41,915 +Okay, let's now go to the SK Demo app. + +262 +00:14:41,949 --> 00:14:44,184 +We want to simulate an active customer + +263 +00:14:44,218 --> 00:14:48,422 +for both a non-consumable +and auto-renewable subscription. + +264 +00:14:48,455 --> 00:14:52,226 +So if I purchase the race car +and subscribe to the pro navigation, + +265 +00:14:52,259 --> 00:14:54,595 +the demo app will apply green checkmarks + +266 +00:14:54,628 --> 00:14:59,132 +to indicate the app has confirmed +those purchases were successful, verified, + +267 +00:14:59,166 --> 00:15:01,068 +and has enabled them. + +268 +00:15:01,101 --> 00:15:03,570 +With these purchases, +my customer product state + +269 +00:15:03,604 --> 00:15:06,440 +for the non-consumable is purchased. + +270 +00:15:06,473 --> 00:15:10,177 +And for our subscription, +I’m an active subscriber. + +271 +00:15:10,210 --> 00:15:12,546 +Now, if I install the app on a new device, + +272 +00:15:12,579 --> 00:15:15,349 +when I launch the SK Demo app +for the first time, + +273 +00:15:15,382 --> 00:15:19,720 +it will proactively perform +steps one, two, and three. + +274 +00:15:19,753 --> 00:15:24,324 +Here you see our demo app has proactively +restored access to both of my purchases, + +275 +00:15:24,358 --> 00:15:26,360 +without any action from me. + +276 +00:15:26,393 --> 00:15:27,828 +As this is a demo app, + +277 +00:15:27,861 --> 00:15:30,597 +that is the extent of products +being delivered. + +278 +00:15:30,631 --> 00:15:33,800 +But in your app, this process would ensure +these active customers + +279 +00:15:33,834 --> 00:15:37,337 +are not offered products to purchase +that they already own, + +280 +00:15:37,371 --> 00:15:41,308 +and that those products and services +are enabled for them automatically. + +281 +00:15:41,875 --> 00:15:44,111 +For your current customers, this is great. + +282 +00:15:44,144 --> 00:15:48,015 +No need to require customers to sign-in +or tap "Restore Purchases." + +283 +00:15:48,048 --> 00:15:49,316 +It just worked. + +284 +00:15:49,349 --> 00:15:53,921 +Your app can use +the APIs and data readily available. + +285 +00:15:53,954 --> 00:15:57,391 +So we’ve covered the three steps +to do this with StoreKit 2. + +286 +00:15:57,424 --> 00:16:01,328 +Now I want to discuss how to implement +this same experience for your customers + +287 +00:16:01,361 --> 00:16:03,430 +on previous versions of iOS + +288 +00:16:03,463 --> 00:16:05,999 +where you cannot leverage +the power of StoreKit 2. + +289 +00:16:06,800 --> 00:16:10,437 +With original StoreKit, you will perform +the same steps as StoreKit 2 + +290 +00:16:10,470 --> 00:16:12,172 +to determine the customer product state + +291 +00:16:12,206 --> 00:16:17,411 +by proactively restoring in-app purchases +on iOS 7 or later. + +292 +00:16:17,444 --> 00:16:22,015 +To do this, it will require your server +to use the verifyReceipt endpoint + +293 +00:16:22,049 --> 00:16:25,352 +to validate and retrieve +the latest transactions + +294 +00:16:25,385 --> 00:16:29,556 +in order to determine +customer's product state. + +295 +00:16:29,590 --> 00:16:34,294 +The app receipt is present on-device +when an app is installed from App Store. + +296 +00:16:34,328 --> 00:16:37,998 +But keep in mind, +when testing with Sandbox or TestFlight, + +297 +00:16:38,031 --> 00:16:39,867 +the app receipt is only present + +298 +00:16:39,900 --> 00:16:43,704 +after an in-app purchase +has been completed or restored. + +299 +00:16:43,737 --> 00:16:48,509 +If your app finds no app receipt present, +this should only occur in Sandbox + +300 +00:16:48,542 --> 00:16:52,312 +and your app can consider this scenario +the same as a new customer + +301 +00:16:52,346 --> 00:16:55,883 +where no in-app purchases are found. + +302 +00:16:55,916 --> 00:16:58,252 +An app receipt +created in the past is sufficient + +303 +00:16:58,285 --> 00:17:01,555 +to retrieve the latest transactions +from the App Store. + +304 +00:17:01,588 --> 00:17:04,424 +Therefore, no customer actions +like a "Restore Purchase," + +305 +00:17:04,458 --> 00:17:06,693 +or receiptRefresh are necessary. + +306 +00:17:06,727 --> 00:17:10,631 +Just include the shared secret +with your request to verifyReceipt + +307 +00:17:10,664 --> 00:17:13,367 +in order to receive +transactions + +308 +00:17:13,400 --> 00:17:16,136 +for non-consumables, +non-renewing subscriptions, + +309 +00:17:16,170 --> 00:17:19,006 +and auto-renewable subscriptions. + +310 +00:17:19,039 --> 00:17:23,610 +Let’s look back at the three +implementation steps we reviewed earlier. + +311 +00:17:23,644 --> 00:17:25,712 +The difference lies within Step 2, + +312 +00:17:25,746 --> 00:17:29,683 +where you identify +the customer's product state. + +313 +00:17:29,716 --> 00:17:34,121 +How we determine customer product state +starts with the app receipt on device, + +314 +00:17:34,154 --> 00:17:38,992 +that, in turn, your server validates +with the App Store verifyReceipt endpoint. + +315 +00:17:39,026 --> 00:17:41,528 +Let’s look at this process. + +316 +00:17:41,562 --> 00:17:44,031 +First, we need to retrieve +the App Receipt, + +317 +00:17:44,064 --> 00:17:48,335 +and be sure you are using +the appStoreReceiptURL property, + +318 +00:17:48,368 --> 00:17:52,706 +as you can see in this sample +from our developer documentation. + +319 +00:17:52,739 --> 00:17:55,976 +With the app receipt, +let’s see how this is sent from the device + +320 +00:17:56,009 --> 00:18:00,047 +to your server and the App Store. + +321 +00:18:00,080 --> 00:18:02,683 +Your app on a device is here on the left. + +322 +00:18:02,716 --> 00:18:05,686 +it will first, retrieve the app receipt, + +323 +00:18:05,719 --> 00:18:08,088 +and send it to your server, + +324 +00:18:08,121 --> 00:18:12,159 +then validate it with +the App Store verifyReceipt endpoint. + +325 +00:18:12,192 --> 00:18:16,129 +From that response, +you will determine customer product state, + +326 +00:18:16,163 --> 00:18:19,867 +and send those states to your app. + +327 +00:18:19,900 --> 00:18:22,002 +To determine customer product state, + +328 +00:18:22,035 --> 00:18:23,904 +we used the Entitlement Engine + +329 +00:18:23,937 --> 00:18:26,673 +from WWDC2020. + +330 +00:18:26,707 --> 00:18:31,011 +It’s updated to support non-consumables +and non-renewing subscriptions, + +331 +00:18:31,044 --> 00:18:35,182 +and now handles the new customer state +when there are no in-app purchases. + +332 +00:18:36,517 --> 00:18:39,119 +To learn more about +using our Entitlement Engine, + +333 +00:18:39,152 --> 00:18:42,990 +I encourage you to check out the +"Architecting for subscriptions" session + +334 +00:18:43,023 --> 00:18:45,092 +and download the sample project. + +335 +00:18:45,125 --> 00:18:50,364 +You can find links to this session +and more with this video’s resources. + +336 +00:18:50,864 --> 00:18:52,533 +That completes Step 2, + +337 +00:18:52,566 --> 00:18:55,769 +where your app will receive the customer +product state from your server. + +338 +00:18:56,336 --> 00:19:00,507 +Now your app will personalize +the app experience immediately on launch + +339 +00:19:00,541 --> 00:19:05,345 +using the StoreKit 2 +and original StoreKit frameworks. + +340 +00:19:05,379 --> 00:19:08,148 +I want to share some final best practices. + +341 +00:19:08,182 --> 00:19:12,653 +First, continue providing a "Restore +Purchases" button within your app. + +342 +00:19:12,686 --> 00:19:13,854 +While not used often, + +343 +00:19:13,887 --> 00:19:16,723 +it does give customers an opportunity to +force an app + +344 +00:19:16,757 --> 00:19:20,694 +to restore their Apple ID’s transactions +in case of an issue + +345 +00:19:20,727 --> 00:19:24,831 +or if the customer uses +a different Apple ID. + +346 +00:19:24,865 --> 00:19:29,770 +When your app first proactively restores +a customer's in-app purchases on a device, + +347 +00:19:29,803 --> 00:19:33,574 +it’s recommended to optimize your app +and store data securely + +348 +00:19:33,607 --> 00:19:36,643 +to assist in determining +customer product state. + +349 +00:19:36,677 --> 00:19:40,647 +CloudKit is a feature to consider +with its flexibility, security, + +350 +00:19:40,681 --> 00:19:45,052 +and ability to sync +across a customer's devices. + +351 +00:19:45,085 --> 00:19:48,388 +Testing your implementation +is critical when using StoreKit. + +352 +00:19:48,422 --> 00:19:52,259 +And with StoreKit 2, you can test +your proactive restore implementation + +353 +00:19:52,292 --> 00:19:57,364 +with Sandbox, TestFlight +and Xcode StoreKit testing. + +354 +00:19:57,397 --> 00:19:59,766 +And if you are using original StoreKit, + +355 +00:19:59,800 --> 00:20:03,070 +it’s important to remember an app receipt +may not be present + +356 +00:20:03,103 --> 00:20:06,206 +when testing in Sandbox and TestFlight, + +357 +00:20:06,240 --> 00:20:10,077 +while it is always present when the app +is installed from the App Store. + +358 +00:20:10,110 --> 00:20:11,945 +If an app receipt isn’t present, + +359 +00:20:11,979 --> 00:20:16,083 +it is suggested your app uses +its default new customer experience, + +360 +00:20:16,116 --> 00:20:20,220 +and ensure you have a Restore Purchases +button readily available. + +361 +00:20:20,254 --> 00:20:23,857 +In conclusion, update your app +to proactively check for purchases + +362 +00:20:23,891 --> 00:20:28,529 +without any customer action, +no taps or authentication. + +363 +00:20:28,562 --> 00:20:33,300 +Allow your app to tailor the customer's +experience immediately at launch + +364 +00:20:33,333 --> 00:20:38,839 +to fit your new, active, +and inactive customers' product states. + +365 +00:20:38,872 --> 00:20:42,009 +Maintain status on +all your customer's transactions, + +366 +00:20:42,042 --> 00:20:45,145 +server to server, +for all in-app purchase types, + +367 +00:20:45,179 --> 00:20:49,583 +by implementing App Store +Server Notifications Version 2. + +368 +00:20:49,616 --> 00:20:52,886 +This enables your backend +to know in near real-time + +369 +00:20:52,920 --> 00:20:55,556 +any change that has occurred +with a transaction, + +370 +00:20:55,589 --> 00:20:58,625 +such as refunds, or revoked transactions, + +371 +00:20:58,659 --> 00:21:02,963 +or subscription renewals, +billing retry, and expirations. + +372 +00:21:03,597 --> 00:21:05,933 +Thank you for watching, +and be sure to check out + +373 +00:21:05,966 --> 00:21:09,069 +this additional session, +"What's new with in-app purchases”, + +374 +00:21:09,102 --> 00:21:11,772 +where Dani and Ian will tell you +about all the great updates + +375 +00:21:11,805 --> 00:21:15,776 +to StoreKit, the Server API, +and Server Notifications Version 2. + +376 +00:21:16,243 --> 00:21:18,111 +Thank you. Take care. + +377 +00:21:18,145 --> 00:21:21,148 +♪ ♪ + diff --git a/eng/2022 Session 110427 What's new in Xcode en.srt b/eng/2022 Session 110427 What's new in Xcode en.srt new file mode 100644 index 0000000..e929599 --- /dev/null +++ b/eng/2022 Session 110427 What's new in Xcode en.srt @@ -0,0 +1,1729 @@ +1 +00:00:00,000 --> 00:00:02,870 +♪ instrumental hip hop music ♪ + +2 +00:00:02,870 --> 00:00:09,977 +♪ + +3 +00:00:09,977 --> 00:00:12,379 +Hello, everyone. +I'm Jonathon Mah. + +4 +00:00:12,379 --> 00:00:13,714 +And I'm Lisa Xiao. + +5 +00:00:13,714 --> 00:00:14,681 +And we'd love +to introduce you + +6 +00:00:14,681 --> 00:00:17,517 +to what's new in Xcode 14. + +7 +00:00:17,517 --> 00:00:18,852 +Today, we'll look +at new features + +8 +00:00:18,852 --> 00:00:20,787 +and enhancements +throughout Xcode, + +9 +00:00:20,787 --> 00:00:24,091 +including source editing +and SwiftUI previews, + +10 +00:00:24,091 --> 00:00:27,060 +multiplatform applications, +TestFlight feedback, + +11 +00:00:27,060 --> 00:00:28,962 +and performance improvements. + +12 +00:00:28,962 --> 00:00:31,565 +There's a lot to cover, +so let's get to it. + +13 +00:00:31,565 --> 00:00:33,367 +The first thing you'll notice +is how much faster it is + +14 +00:00:33,367 --> 00:00:34,768 +just getting started. + +15 +00:00:34,768 --> 00:00:37,404 +Xcode 14 is 30 percent smaller. + +16 +00:00:37,404 --> 00:00:40,107 +It downloads and installs +significantly faster. + +17 +00:00:40,107 --> 00:00:42,809 +You can download additional +platforms and simulators + +18 +00:00:42,809 --> 00:00:44,211 +on demand. + +19 +00:00:44,211 --> 00:00:46,713 +If you need them immediately, +you can get them here -- + +20 +00:00:46,713 --> 00:00:49,483 +or later, when you first +try to use them. + +21 +00:00:51,752 --> 00:00:55,555 +We're building Food Truck, +a food-delivery application. + +22 +00:00:58,325 --> 00:01:01,261 +SwiftUI with live previews +is a great workflow, + +23 +00:01:01,261 --> 00:01:03,397 +and it's getting even better. + +24 +00:01:03,397 --> 00:01:06,099 +Now the preview canvas +is interactive by default, + +25 +00:01:06,099 --> 00:01:08,035 +so your changes +are immediately live + +26 +00:01:08,035 --> 00:01:10,637 +as you make them. + +27 +00:01:10,637 --> 00:01:12,272 +The canvas has a new control + +28 +00:01:12,272 --> 00:01:15,042 +to create additional variants +of each preview + +29 +00:01:15,042 --> 00:01:16,910 +without writing any code. + +30 +00:01:16,910 --> 00:01:19,012 +You can vary the color scheme, +text size, + +31 +00:01:19,012 --> 00:01:21,048 +or device orientation, +and then immediately + +32 +00:01:21,048 --> 00:01:24,785 +see your interface +rendered in each scenario. + +33 +00:01:24,785 --> 00:01:26,553 +Let's see how our view looks + +34 +00:01:26,553 --> 00:01:29,556 +with different +Dynamic Type sizes. + +35 +00:01:37,798 --> 00:01:39,700 +With these previews +side by side, + +36 +00:01:39,700 --> 00:01:41,368 +it's easy to validate +your interface + +37 +00:01:41,368 --> 00:01:43,704 +and make sure +things look just right. + +38 +00:01:43,704 --> 00:01:46,239 +Let's check the larger sizes. + +39 +00:01:51,244 --> 00:01:52,879 +The first icon is very wide + +40 +00:01:52,879 --> 00:01:56,316 +and causes the text +to wrap awkwardly. + +41 +00:01:56,316 --> 00:01:58,752 +Luckily, my designer has +just asked me to change it, + +42 +00:01:58,752 --> 00:02:00,387 +but only in the header. + +43 +00:02:00,387 --> 00:02:02,289 +Our CardView doesn't support + +44 +00:02:02,289 --> 00:02:04,291 +a different icon +for the header yet, + +45 +00:02:04,291 --> 00:02:07,394 +so let's start by adding +that functionality. + +46 +00:02:07,394 --> 00:02:10,230 +I'll switch over +to the implementation. + +47 +00:02:15,902 --> 00:02:22,242 +The header and content elements +are both using the same image. + +48 +00:02:22,242 --> 00:02:26,580 +Let's add another image property +specifically for the header. + +49 +00:02:36,923 --> 00:02:38,325 +In most of the cards, + +50 +00:02:38,325 --> 00:02:41,194 +using the same image +in both places looks great. + +51 +00:02:41,194 --> 00:02:44,097 +We can save ourselves the time +of updating each existing call + +52 +00:02:44,097 --> 00:02:48,335 +by adding a custom initializer +with a default value. + +53 +00:02:53,006 --> 00:02:55,075 +When I start to type +the initializer, + +54 +00:02:55,075 --> 00:02:59,012 +Xcode 14 now offers to complete +the whole thing! + +55 +00:02:59,012 --> 00:03:01,281 +I can accept the default value +as a starting point + +56 +00:03:01,281 --> 00:03:07,621 +for my customizations, +saving a ton of time. + +57 +00:03:07,621 --> 00:03:10,991 +This also works +for the codable methods. + +58 +00:03:10,991 --> 00:03:14,694 +Let's give the headerIcon +parameter a default value. + +59 +00:03:24,538 --> 00:03:27,107 +Now, we'll return +to the CardStack view + +60 +00:03:27,107 --> 00:03:30,477 +and use this new functionality. + +61 +00:03:36,483 --> 00:03:39,920 +My designer asked me to use +the calendar symbol here. + +62 +00:03:39,920 --> 00:03:42,756 +The library now includes +all of the SF Symbols, + +63 +00:03:42,756 --> 00:03:46,793 +so it's easy to make sure +I'm using the right one. + +64 +00:03:54,134 --> 00:03:56,169 +I can press Return +to insert the right code + +65 +00:03:56,169 --> 00:03:58,171 +to use the symbol. + +66 +00:04:06,079 --> 00:04:08,482 +This symbol looks great. + +67 +00:04:08,482 --> 00:04:11,885 +The new side-by-side comparisons +we get from preview variants + +68 +00:04:11,885 --> 00:04:14,121 +make it easy to ensure +your app looks good + +69 +00:04:14,121 --> 00:04:17,190 +with all the settings +your users have chosen. + +70 +00:04:17,190 --> 00:04:20,227 +In addition to initializer +and codable definitions, + +71 +00:04:20,227 --> 00:04:23,130 +Xcode 14 provides more +intelligent recommendations + +72 +00:04:23,130 --> 00:04:26,533 +so you can write your ideas +more quickly and easily. + +73 +00:04:26,533 --> 00:04:28,535 +Let me show you some more. + +74 +00:04:33,240 --> 00:04:37,477 +When I start to add +another CardView, + +75 +00:04:37,477 --> 00:04:43,316 +the initializers now appear +directly in the completion list. + +76 +00:04:43,316 --> 00:04:45,919 +The headerIcon parameter +is in italic. + +77 +00:04:45,919 --> 00:04:48,421 +That's because +it has a default value. + +78 +00:04:48,421 --> 00:04:50,123 +If I accept the completion now, + +79 +00:04:50,123 --> 00:04:52,359 +it won't include +the headerIcon label, + +80 +00:04:52,359 --> 00:04:55,795 +it will use the default value +we just added. + +81 +00:04:55,795 --> 00:04:57,531 +Here, I want to specialize +the icon again, + +82 +00:04:57,531 --> 00:05:00,467 +so I can type part of the name +to opt in. + +83 +00:05:03,136 --> 00:05:10,277 +I'll give my new card +some values. + +84 +00:05:14,447 --> 00:05:17,184 +I'd like a divider +before my new card. + +85 +00:05:17,184 --> 00:05:19,085 +Adding a Divider to the VStack + +86 +00:05:19,085 --> 00:05:22,055 +gives a line across +the entire width. + +87 +00:05:29,062 --> 00:05:30,397 +To make it more subtle, + +88 +00:05:30,397 --> 00:05:34,668 +I'll use frame +to set a maximum width. + +89 +00:05:36,670 --> 00:05:40,240 +The frame modifier has +a lot of optional arguments. + +90 +00:05:40,240 --> 00:05:42,175 +The new completion features +make it a snap + +91 +00:05:42,175 --> 00:05:46,079 +to get just the arguments +I need. + +92 +00:05:51,084 --> 00:05:54,387 +That's code completion +in Xcode 14. + +93 +00:05:54,387 --> 00:05:57,090 +This first card's title +comes from a method. + +94 +00:05:57,090 --> 00:05:59,292 +I've heard it isn't handling +all numbers correctly, + +95 +00:05:59,292 --> 00:06:01,461 +so let's see +how it's implemented. + +96 +00:06:01,461 --> 00:06:05,899 +I'll Command-click and choose +Jump to Definition. + +97 +00:06:11,104 --> 00:06:13,707 +The redesigned definition list +highlights what's different + +98 +00:06:13,707 --> 00:06:15,208 +about each of the results + +99 +00:06:15,208 --> 00:06:17,777 +so that you can quickly +choose the one you want. + +100 +00:06:17,777 --> 00:06:20,647 +Since our text(for:) method +is from a protocol, + +101 +00:06:20,647 --> 00:06:22,782 +there are multiple options. + +102 +00:06:22,782 --> 00:06:25,518 +The list shows the specific +types that define the method: + +103 +00:06:25,518 --> 00:06:27,187 +we have the declaration +from the protocol + +104 +00:06:27,187 --> 00:06:29,489 +and two implementations. + +105 +00:06:29,489 --> 00:06:33,927 +I'll navigate to the +server-backed implementation. + +106 +00:06:36,429 --> 00:06:37,797 +This class implements parsing + +107 +00:06:37,797 --> 00:06:39,833 +using a regular expression +literal, + +108 +00:06:39,833 --> 00:06:42,168 +which is new in Swift 5.7. + +109 +00:06:44,571 --> 00:06:45,939 +My coworker told me + +110 +00:06:45,939 --> 00:06:48,441 +the problem is being caught +by our unit tests. + +111 +00:06:48,441 --> 00:06:50,443 +Let's run them now. + +112 +00:07:02,389 --> 00:07:03,690 +Yep, we have some failures. + +113 +00:07:03,690 --> 00:07:05,692 +Let's take a look. + +114 +00:07:09,729 --> 00:07:12,932 +Something seems wrong +with extracting the event count. + +115 +00:07:12,932 --> 00:07:15,435 +Let's check how this function +is called. + +116 +00:07:15,435 --> 00:07:16,936 +I can see that directly + +117 +00:07:16,936 --> 00:07:18,805 +by opening the callers +of this method + +118 +00:07:18,805 --> 00:07:20,540 +by Command-clicking. + +119 +00:07:20,540 --> 00:07:23,410 +Just like the definition list, +the callers list + +120 +00:07:23,410 --> 00:07:25,145 +shows the different files +and functions + +121 +00:07:25,145 --> 00:07:27,280 +that contain calls +to this method, + +122 +00:07:27,280 --> 00:07:30,450 +along with a preview +of each call site. + +123 +00:07:38,458 --> 00:07:40,927 +Here's a call +from the failing test method. + +124 +00:07:40,927 --> 00:07:41,928 +The preview shows me + +125 +00:07:41,928 --> 00:07:45,098 +the test is passing +the string "0 records", + +126 +00:07:45,098 --> 00:07:47,167 +which gives me a hint +about the problem. + +127 +00:07:47,167 --> 00:07:49,502 +And now I can spot the bug: + +128 +00:07:49,502 --> 00:07:52,906 +the regular expression is +matching against the digits 1-9, + +129 +00:07:52,906 --> 00:07:55,241 +but I forgot to include zero! + +130 +00:07:55,241 --> 00:07:59,279 +I could fix this by updating +the character range to use 0-9, + +131 +00:07:59,279 --> 00:08:01,247 +or switch +to the more descriptive + +132 +00:08:01,247 --> 00:08:03,917 +digit character class. + +133 +00:08:11,391 --> 00:08:13,059 +Something's still not right, + +134 +00:08:13,059 --> 00:08:16,062 +but now the compiler +is telling me why. + +135 +00:08:16,062 --> 00:08:18,031 +Regular expressions +are a first-class feature + +136 +00:08:18,031 --> 00:08:20,100 +in Swift 5.7. + +137 +00:08:20,100 --> 00:08:22,268 +The compiler checks +my regular expression + +138 +00:08:22,268 --> 00:08:24,003 +like it does with other code, + +139 +00:08:24,003 --> 00:08:26,973 +and Xcode highlights my typo +immediately. + +140 +00:08:26,973 --> 00:08:28,908 +Notice that when I correct +the expression, + +141 +00:08:28,908 --> 00:08:30,543 +two things happen: + +142 +00:08:30,543 --> 00:08:32,445 +syntax highlighting +in the regular expression + +143 +00:08:32,445 --> 00:08:36,349 +confirms my edit, +and the errors dim gray. + +144 +00:08:41,020 --> 00:08:42,622 +This new dimming +shows that Xcode + +145 +00:08:42,622 --> 00:08:45,191 +is reevaluating +the diagnostics. + +146 +00:08:45,191 --> 00:08:49,729 +When I pause for a moment, +the file is reprocessed + +147 +00:08:49,729 --> 00:08:52,899 +and Xcode confirms +the errors are resolved. + +148 +00:08:52,899 --> 00:08:55,402 +This dimming happens +during long builds as well, + +149 +00:08:55,402 --> 00:08:56,469 +so you can easily tell + +150 +00:08:56,469 --> 00:08:58,405 +which problems +are from the latest build + +151 +00:08:58,405 --> 00:09:02,642 +and which are from +a previous build. + +152 +00:09:02,642 --> 00:09:04,978 +Let's go over to the test. + +153 +00:09:15,255 --> 00:09:17,190 +This jumped me +straight to the call, + +154 +00:09:17,190 --> 00:09:19,426 +which is in the middle +of a test method. + +155 +00:09:19,426 --> 00:09:21,060 +Take a look +at the top of the editor. + +156 +00:09:21,060 --> 00:09:24,631 +Xcode 14 shows the definitions +containing the visible code, + +157 +00:09:24,631 --> 00:09:27,767 +even when they're +scrolled out of view. + +158 +00:09:32,772 --> 00:09:37,310 +I can use the test diamonds +at the top to rerun the tests. + +159 +00:09:44,184 --> 00:09:49,222 +Fantastic, +the fix passes all the tests. + +160 +00:09:49,222 --> 00:09:50,423 +With Xcode 14, + +161 +00:09:50,423 --> 00:09:53,126 +you can write code faster +with new code completions, + +162 +00:09:53,126 --> 00:09:56,162 +design fluidly with +SwiftUI preview variants, + +163 +00:09:56,162 --> 00:09:59,165 +be more informed with improved +error presentation, + +164 +00:09:59,165 --> 00:10:02,035 +and navigate confidently +with jump to definition. + +165 +00:10:02,035 --> 00:10:04,237 +And there's even more. +Here's Lisa. + +166 +00:10:04,237 --> 00:10:05,572 +Thank you, Jonathon. + +167 +00:10:05,572 --> 00:10:10,510 +Let's take a look at the build +performance improvements. + +168 +00:10:10,510 --> 00:10:12,912 +When Xcode +builds multiple targets, + +169 +00:10:12,912 --> 00:10:16,449 +like a framework +and application, + +170 +00:10:16,449 --> 00:10:20,153 +first it compiles +the framework sources. + +171 +00:10:20,153 --> 00:10:23,156 +Then it generates a module. + +172 +00:10:23,156 --> 00:10:24,424 +That enables linking + +173 +00:10:24,424 --> 00:10:27,427 +and compiling +the application sources. + +174 +00:10:27,427 --> 00:10:29,395 +And then it links +the application, + +175 +00:10:29,395 --> 00:10:31,631 +completing the build. + +176 +00:10:31,631 --> 00:10:36,402 +Xcode 14 rearranges your build +for improved parallelism. + +177 +00:10:36,402 --> 00:10:38,738 +By eagerly producing +Swift modules, + +178 +00:10:38,738 --> 00:10:44,110 +Xcode unblocks build tasks +and increases parallelism. + +179 +00:10:44,110 --> 00:10:46,946 +It shortens all the critical +paths of your builds, + +180 +00:10:46,946 --> 00:10:49,215 +all while being more respectful + +181 +00:10:49,215 --> 00:10:53,119 +of your simultaneous +use of the machine. + +182 +00:10:53,119 --> 00:10:55,121 +But we didn't stop there. + +183 +00:10:55,121 --> 00:10:57,924 +We also made the linker +up to two times faster + +184 +00:10:57,924 --> 00:11:00,660 +through increased parallelism. + +185 +00:11:00,660 --> 00:11:04,264 +All together, +Xcode 14 builds projects + +186 +00:11:04,264 --> 00:11:07,267 +up to 25 percent faster, + +187 +00:11:07,267 --> 00:11:09,068 +with machines +with the most cores + +188 +00:11:09,068 --> 00:11:12,672 +seeing the biggest +improvements. + +189 +00:11:12,672 --> 00:11:15,742 +Even with the improvements +to build scheduling, + +190 +00:11:15,742 --> 00:11:17,477 +sometimes your project can have + +191 +00:11:17,477 --> 00:11:21,381 +internal dependencies +on long synchronous tasks. + +192 +00:11:21,381 --> 00:11:23,816 +It can be hard to tell +this is happening + +193 +00:11:23,816 --> 00:11:27,086 +without a visualization. + +194 +00:11:27,086 --> 00:11:31,424 +Good news, we've included that +in Xcode 14! + +195 +00:11:31,424 --> 00:11:33,259 +You can open +the new build timeline + +196 +00:11:33,259 --> 00:11:36,429 +on any build log +or result bundle. + +197 +00:11:36,429 --> 00:11:40,033 +It will help you identify +unexpectedly long tasks + +198 +00:11:40,033 --> 00:11:42,101 +and bottlenecks. + +199 +00:11:42,101 --> 00:11:44,971 +Here's the build timeline +for Food Truck. + +200 +00:11:44,971 --> 00:11:46,472 +I can see a script phase + +201 +00:11:46,472 --> 00:11:49,842 +constraining the build +to a single core. + +202 +00:11:49,842 --> 00:11:51,444 +You can get critical insights + +203 +00:11:51,444 --> 00:11:54,380 +and identify performance +opportunities in your build + +204 +00:11:54,380 --> 00:11:57,817 +with the new build timeline. + +205 +00:11:57,817 --> 00:12:01,087 +You can learn all about +parallelizing builds, + +206 +00:12:01,087 --> 00:12:03,156 +the new build timeline. +and linking + +207 +00:12:03,156 --> 00:12:06,593 +in "Demystify parallelization +in Xcode builds" + +208 +00:12:06,593 --> 00:12:10,930 +and "Link Fast: +Improve build and launch." + +209 +00:12:10,930 --> 00:12:12,565 +Parallel testing in Xcode + +210 +00:12:12,565 --> 00:12:16,235 +has been a great way +to run your tests faster. + +211 +00:12:16,235 --> 00:12:19,572 +In Xcode 14, +it's even better. + +212 +00:12:19,572 --> 00:12:22,542 +We used the same techniques +to improve build performance + +213 +00:12:22,542 --> 00:12:25,011 +that I just showed you. + +214 +00:12:25,011 --> 00:12:27,981 +Xcode 14 eliminates +scheduling dependencies + +215 +00:12:27,981 --> 00:12:30,183 +between targets +and test classes + +216 +00:12:30,183 --> 00:12:34,754 +to increase parallelism +during testing even more. + +217 +00:12:34,754 --> 00:12:36,356 +If you have long-running tests + +218 +00:12:36,356 --> 00:12:38,858 +in different test classes +and targets, + +219 +00:12:38,858 --> 00:12:41,728 +this feature could improve +your test execution time + +220 +00:12:41,728 --> 00:12:45,231 +by up to 30 percent. + +221 +00:12:45,231 --> 00:12:48,968 +To learn about how to improve +test speed and reliability, + +222 +00:12:48,968 --> 00:12:50,203 +please check out + +223 +00:12:50,203 --> 00:12:55,675 +"Author fast and reliable tests +for Xcode Cloud." + +224 +00:12:55,675 --> 00:12:58,978 +Building is faster, +testing is faster, + +225 +00:12:58,978 --> 00:13:03,483 +and preparing your macOS app +for distribution is faster too. + +226 +00:13:03,483 --> 00:13:09,555 +We have sped up notarization +by four times in Xcode 14. + +227 +00:13:09,555 --> 00:13:11,724 +For projects using +Interface Builder, + +228 +00:13:11,724 --> 00:13:14,027 +I have more good news! + +229 +00:13:14,027 --> 00:13:17,230 +Document loading +is up to 50 percent faster, + +230 +00:13:17,230 --> 00:13:19,532 +and switching between +iPhones and iPads + +231 +00:13:19,532 --> 00:13:23,302 +in the device bar +is up to 30 percent faster. + +232 +00:13:23,302 --> 00:13:26,105 +Canvas editing operations +are incremental + +233 +00:13:26,105 --> 00:13:28,574 +and prioritize the scenes +you're looking at + +234 +00:13:28,574 --> 00:13:30,710 +so that it gives you +immediate feedback, + +235 +00:13:30,710 --> 00:13:34,180 +even in large storyboards. + +236 +00:13:34,180 --> 00:13:38,851 +Xcode 14 is faster, +and it's easier to use, too. + +237 +00:13:38,851 --> 00:13:41,354 +Let me show you how. + +238 +00:13:41,354 --> 00:13:43,222 +Xcode 14 makes it easy + +239 +00:13:43,222 --> 00:13:45,992 +to bring your app +to different platforms. + +240 +00:13:45,992 --> 00:13:49,562 +You can use a single target +to define your app, + +241 +00:13:49,562 --> 00:13:52,298 +and list what platforms +you support. + +242 +00:13:52,298 --> 00:13:56,269 +This eliminates the need to keep +settings and files in sync, + +243 +00:13:56,269 --> 00:14:01,441 +so you only have to describe +what's unique on each platform. + +244 +00:14:01,441 --> 00:14:05,078 +Check out "Use Xcode +to build a multiplatform app" + +245 +00:14:05,078 --> 00:14:09,816 +to take advantage of this +feature in your project. + +246 +00:14:09,816 --> 00:14:13,920 +Xcode also has great tools +for making your app smaller. + +247 +00:14:13,920 --> 00:14:16,222 +The memory debugger +has always been great + +248 +00:14:16,222 --> 00:14:19,192 +for exploring leaks +in your application. + +249 +00:14:19,192 --> 00:14:21,427 +It zeros in +on the shortest paths + +250 +00:14:21,427 --> 00:14:25,164 +from root objects +to unexpectedly live objects + +251 +00:14:25,164 --> 00:14:29,068 +so that you can investigate +why they've leaked. + +252 +00:14:29,068 --> 00:14:31,738 +Xcode 14 expands +these capabilities + +253 +00:14:31,738 --> 00:14:34,173 +so that you can see +all reference paths + +254 +00:14:34,173 --> 00:14:37,510 +in and out of an object. + +255 +00:14:37,510 --> 00:14:40,613 +In addition to a more +thorough explanation of leaks, + +256 +00:14:40,613 --> 00:14:47,253 +now you can gauge the total +weight of your objects. + +257 +00:14:47,253 --> 00:14:51,224 +In Xcode 14, you can also +extend Xcode itself + +258 +00:14:51,224 --> 00:14:53,793 +with Swift Package plugins. + +259 +00:14:53,793 --> 00:14:56,229 +Now packages +can integrate plugins + +260 +00:14:56,229 --> 00:14:58,664 +that process your code +in place, + +261 +00:14:58,664 --> 00:15:02,034 +like linters and formatters, +and you can invoke them + +262 +00:15:02,034 --> 00:15:05,538 +directly from +the project navigator. + +263 +00:15:05,538 --> 00:15:08,908 +You can also integrate +build tools that generate code + +264 +00:15:08,908 --> 00:15:12,211 +or process resources +while building. + +265 +00:15:12,211 --> 00:15:15,615 +For example, you could translate +a high-level description + +266 +00:15:15,615 --> 00:15:18,017 +of a remote procedure call +interface + +267 +00:15:18,017 --> 00:15:21,487 +into low-level glue code +during the build. + +268 +00:15:21,487 --> 00:15:24,190 +You could also compress +or optimize resources + +269 +00:15:24,190 --> 00:15:26,259 +while building. + +270 +00:15:26,259 --> 00:15:28,961 +For an in-depth look +at package plugins, + +271 +00:15:28,961 --> 00:15:31,831 +check out +"Meet Swift Package plugins" + +272 +00:15:31,831 --> 00:15:36,002 +and +"Create Swift Package plugins." + +273 +00:15:36,002 --> 00:15:38,437 +You can also localize +package resources + +274 +00:15:38,437 --> 00:15:40,973 +just like applications. + +275 +00:15:40,973 --> 00:15:44,177 +Now you can set your package's +default localization, + +276 +00:15:44,177 --> 00:15:50,049 +export the localization catalog, +translate it, and reimport it. + +277 +00:15:50,049 --> 00:15:52,418 +To learn more +about localization, + +278 +00:15:52,418 --> 00:15:58,558 +check out "Building global apps: +Localization by example." + +279 +00:15:58,558 --> 00:16:03,129 +Next, let's move on +to the run destination chooser. + +280 +00:16:03,129 --> 00:16:06,966 +Switching between devices +is something I do all the time, + +281 +00:16:06,966 --> 00:16:10,670 +and now it's even easier +to get the device you need + +282 +00:16:10,670 --> 00:16:13,739 +with the updated +run destination chooser. + +283 +00:16:13,739 --> 00:16:15,741 +Let's take a look. + +284 +00:16:18,744 --> 00:16:20,880 +When you open +the run destination chooser, + +285 +00:16:20,880 --> 00:16:23,549 +it shows all the available +run destinations + +286 +00:16:23,549 --> 00:16:26,819 +for the selected scheme. + +287 +00:16:26,819 --> 00:16:30,590 +I often switch between +an iPhone and an iPad. + +288 +00:16:30,590 --> 00:16:34,627 +The new run destination chooser +prioritizes recent choices, + +289 +00:16:34,627 --> 00:16:39,565 +which makes this +really convenient. + +290 +00:16:39,565 --> 00:16:42,034 +It's also easy +to select other devices + +291 +00:16:42,034 --> 00:16:44,537 +that I haven't used lately. + +292 +00:16:44,537 --> 00:16:46,205 +I can filter the list here, + +293 +00:16:46,205 --> 00:16:51,143 +and type "max" to bring +all the Max devices together + +294 +00:16:51,143 --> 00:16:56,115 +and easily pick the one I want. + +295 +00:16:56,115 --> 00:16:58,351 +These same features +are all available + +296 +00:16:58,351 --> 00:17:00,920 +in the Scheme chooser, too. + +297 +00:17:00,920 --> 00:17:04,957 +Next, let's take a look +at the Organizer window. + +298 +00:17:04,957 --> 00:17:08,060 +We've made some great +improvements to the Organizer + +299 +00:17:08,060 --> 00:17:12,131 +to help you identify new issues +and improve your apps. + +300 +00:17:12,131 --> 00:17:14,767 +There are two new reports +in the Organizer + +301 +00:17:14,767 --> 00:17:17,169 +to help you understand +how your app is doing + +302 +00:17:17,169 --> 00:17:21,407 +on your users' devices: +Feedback and Hangs. + +303 +00:17:21,407 --> 00:17:23,409 +Let's dive in. + +304 +00:17:26,279 --> 00:17:29,382 +The Feedback organizer shows +all of your TestFlight feedback + +305 +00:17:29,382 --> 00:17:31,884 +directly in Xcode. + +306 +00:17:31,884 --> 00:17:35,354 +Our TestFlight users sent +great comments and screenshots + +307 +00:17:35,354 --> 00:17:39,125 +for our recent builds. + +308 +00:17:39,125 --> 00:17:41,794 +The inspector shows +additional details -- + +309 +00:17:41,794 --> 00:17:45,698 +like tester information +and the device configuration -- + +310 +00:17:45,698 --> 00:17:49,702 +that can help to identify +the underlying problems. + +311 +00:17:49,702 --> 00:17:51,604 +If I need a bit more context, + +312 +00:17:51,604 --> 00:17:57,143 +I can email my tester +directly with this button. + +313 +00:17:57,143 --> 00:18:00,179 +TestFlight feedback +is from beta users, + +314 +00:18:00,179 --> 00:18:03,349 +but there are some issues +that can slip past testing + +315 +00:18:03,349 --> 00:18:05,952 +and make it into +the App Store. + +316 +00:18:05,952 --> 00:18:11,257 +One of the most common types +of bugs like this are hangs. + +317 +00:18:11,257 --> 00:18:14,260 +Your app hangs +when it uses the main thread + +318 +00:18:14,260 --> 00:18:17,663 +without taking a break +to process user input. + +319 +00:18:17,663 --> 00:18:20,333 +Your code might be doing +important work, + +320 +00:18:20,333 --> 00:18:26,872 +but the user experiences +an unresponsive app. + +321 +00:18:26,872 --> 00:18:28,874 +This new Hangs report shows + +322 +00:18:28,874 --> 00:18:32,144 +the highest-impact hangs +from App Store users + +323 +00:18:32,144 --> 00:18:34,680 +so that you know +which code to restructure + +324 +00:18:34,680 --> 00:18:38,517 +to have the biggest impact. + +325 +00:18:38,517 --> 00:18:41,487 +On the left, +there's a list of hangs + +326 +00:18:41,487 --> 00:18:43,956 +ordered by severity. + +327 +00:18:43,956 --> 00:18:46,492 +Each one has +a weighted backtrace + +328 +00:18:46,492 --> 00:18:49,328 +showing the problematic code. + +329 +00:18:49,328 --> 00:18:53,299 +Our app supports +many devices and OS versions. + +330 +00:18:53,299 --> 00:18:57,837 +Some hangs impact certain +configurations more than others. + +331 +00:18:57,837 --> 00:18:59,805 +In the inspector, +it is helpful + +332 +00:18:59,805 --> 00:19:05,711 +to find that this hang +happens mainly on iOS 15.3. + +333 +00:19:05,711 --> 00:19:07,313 +When I'm ready to work on this, + +334 +00:19:07,313 --> 00:19:09,382 +I can jump +straight into the code + +335 +00:19:09,382 --> 00:19:13,219 +with this Open in Project +button. + +336 +00:19:13,219 --> 00:19:15,454 +The new Hangs +and Feedback reports + +337 +00:19:15,454 --> 00:19:16,956 +help you triage issues + +338 +00:19:16,956 --> 00:19:20,793 +and get the right fixes +to users quickly. + +339 +00:19:20,793 --> 00:19:22,862 +To learn more about +fixing hangs, + +340 +00:19:22,862 --> 00:19:28,367 +check out "Track down hangs with +Xcode and on-device detection." + +341 +00:19:28,367 --> 00:19:33,039 +Next, let's take a look +at icons. + +342 +00:19:33,039 --> 00:19:36,642 +Our app icon +looks great at every size, + +343 +00:19:36,642 --> 00:19:39,011 +because we've taken the time +to pixel hint + +344 +00:19:39,011 --> 00:19:43,149 +and to choose the right number +of stripes for every resolution + +345 +00:19:43,149 --> 00:19:46,619 +so that it always looks crisp. + +346 +00:19:46,619 --> 00:19:49,455 +All of this pixel hinting +can be totally essential + +347 +00:19:49,455 --> 00:19:54,193 +for some icons +and unnecessary for others. + +348 +00:19:54,193 --> 00:19:56,529 +Now we have a new icon. + +349 +00:19:56,529 --> 00:19:59,932 +Let's take a look at it. + +350 +00:19:59,932 --> 00:20:03,235 +These simpler textures +don't need any hinting, + +351 +00:20:03,235 --> 00:20:07,540 +and will look great +drawn at any size. + +352 +00:20:07,540 --> 00:20:14,280 +With a double click, +I can select the new image. + +353 +00:20:14,280 --> 00:20:16,382 +In Xcode 14, +you can choose + +354 +00:20:16,382 --> 00:20:19,418 +the level of detail +that's right for you. + +355 +00:20:19,418 --> 00:20:22,955 +For this icon, +our single image looks great. + +356 +00:20:22,955 --> 00:20:24,890 +I'll use the new +Single Size feature + +357 +00:20:24,890 --> 00:20:27,693 +to tell Xcode +to automatically create + +358 +00:20:27,693 --> 00:20:32,565 +all of our different sizes +from this one. + +359 +00:20:32,565 --> 00:20:39,572 +To do that, I can select +Single Size in the inspector. + +360 +00:20:39,572 --> 00:20:42,074 +And that's it! + +361 +00:20:42,074 --> 00:20:46,112 +That was a brief overview +of what's new in Xcode 14. + +362 +00:20:46,112 --> 00:20:49,582 +It is faster and easier +to help you develop. + +363 +00:20:49,582 --> 00:20:51,183 +Thank you for watching! + +364 +00:20:51,183 --> 00:20:53,819 +Jonathan: Go download it +and get started today! + +365 +00:20:53,819 --> 00:20:58,257 +♪ + diff --git a/eng/2022 Session 110429 Discover advancements in iOS camera capture - Depth, focus, and multitasking en.srt b/eng/2022 Session 110429 Discover advancements in iOS camera capture - Depth, focus, and multitasking en.srt new file mode 100644 index 0000000..187463a --- /dev/null +++ b/eng/2022 Session 110429 Discover advancements in iOS camera capture - Depth, focus, and multitasking en.srt @@ -0,0 +1,1627 @@ +1 +00:00:00,334 --> 00:00:06,340 +♪ instrumental hip hop music ♪ + +2 +00:00:09,810 --> 00:00:11,478 +Hello, and welcome to + +3 +00:00:11,512 --> 00:00:14,147 +“Discover advancements +in iOS camera capture”. + +4 +00:00:14,181 --> 00:00:16,383 +I'm Nikolas Gelo +from the Camera Software team, + +5 +00:00:16,416 --> 00:00:20,754 +and I'll be presenting some exciting +new camera features in iOS and iPadOS. + +6 +00:00:20,787 --> 00:00:25,025 +I'll begin with how to stream depth +from LiDAR Scanners using AVFoundation. + +7 +00:00:25,058 --> 00:00:28,495 +Next, a look at how your app +will receive improved face rendering + +8 +00:00:28,529 --> 00:00:31,565 +with face-driven auto focus +and auto exposure. + +9 +00:00:31,598 --> 00:00:36,303 +Then, I'll take you through advanced +AVCaptureSession streaming configurations. + +10 +00:00:36,336 --> 00:00:38,772 +And lastly, +I'll show you how your app will + +11 +00:00:38,805 --> 00:00:41,675 +be able to use the camera +while multitasking. + +12 +00:00:41,708 --> 00:00:45,646 +I'll begin with how to stream depth +from LiDAR Scanners using AVFoundation. + +13 +00:00:45,679 --> 00:00:50,150 +The iPhone 12 Pro, +iPhone 13 Pro, and iPad Pro are equipped + +14 +00:00:50,184 --> 00:00:53,887 +with LiDAR Scanners capable +of outputting dense depth maps. + +15 +00:00:53,921 --> 00:00:57,157 +The LiDAR Scanner works +by shooting light onto the surroundings, + +16 +00:00:57,191 --> 00:01:00,594 +and then collecting the light +reflected off the surfaces in the scene. + +17 +00:01:00,627 --> 00:01:03,397 +The depth is estimated by measuring +the time it took for the light to go + +18 +00:01:03,430 --> 00:01:07,401 +from the LiDAR to the environment +and reflect back to the scanner. + +19 +00:01:07,434 --> 00:01:11,138 +This entire process runs millions +of times every second. + +20 +00:01:11,171 --> 00:01:14,308 +I'll show you the LiDAR Scanner +in action using AVFoundation. + +21 +00:01:14,341 --> 00:01:17,945 +Here on an iPhone 13 Pro Max, +I'm running an app that uses + +22 +00:01:17,978 --> 00:01:20,914 +the new LiDAR Depth Camera +AVCaptureDevice. + +23 +00:01:20,948 --> 00:01:24,551 +The app renders streaming depth data +on top of the live camera feed. + +24 +00:01:24,585 --> 00:01:29,189 +Blue is shown for objects that are close +and red for objects that are further away. + +25 +00:01:29,223 --> 00:01:33,360 +And using the slider, +I can adjust the opacity of the depth. + +26 +00:01:33,393 --> 00:01:36,430 +This app also takes photos +with high resolution depth maps. + +27 +00:01:36,463 --> 00:01:39,566 +When I take a photo, +the same depth overlay is applied + +28 +00:01:39,600 --> 00:01:42,903 +but with an even greater resolution +for the still. + +29 +00:01:42,936 --> 00:01:45,305 +This app has one more trick up its sleeve. + +30 +00:01:45,339 --> 00:01:49,076 +When I press the torch button, +the app uses the high resolution depth map + +31 +00:01:49,109 --> 00:01:52,880 +with the color image to render a spotlight +on the scene using RealityKit. + +32 +00:01:52,913 --> 00:01:57,150 +I can tap around and point the spotlight +at different objects in the scene. + +33 +00:01:57,184 --> 00:01:59,720 +Look how the spotlight +highlights the guitar. + +34 +00:01:59,753 --> 00:02:02,222 +Or if I tap on the right spot +in the corner of the wall, + +35 +00:02:02,256 --> 00:02:04,691 +the spotlight forms the shape of a heart. + +36 +00:02:04,725 --> 00:02:08,028 +Let's go back to that guitar. +It looks so cool. + +37 +00:02:09,363 --> 00:02:14,668 +API for the LiDAR Scanner was +first introduced in ARKit in iPadOS 13.4. + +38 +00:02:14,701 --> 00:02:19,039 +If you haven't seen the WWDC +2020 presentation “Explore ARKit 4”, + +39 +00:02:19,072 --> 00:02:21,508 +I encourage you to watch it. + +40 +00:02:21,542 --> 00:02:26,547 +New in iOS 15.4, your app can access +the LiDAR Scanner with AVFoundation. + +41 +00:02:26,580 --> 00:02:28,982 +We have introduced +a new AVCapture Device Type, + +42 +00:02:29,016 --> 00:02:33,153 +the built-in LiDAR Depth Camera, +which delivers video and depth. + +43 +00:02:33,187 --> 00:02:36,924 +It produces high-quality, +high-accuracy depth information. + +44 +00:02:36,957 --> 00:02:40,194 +This new AVCaptureDevice uses +the rear-facing wide-angle camera + +45 +00:02:40,227 --> 00:02:43,764 +to deliver video +with the LiDAR Scanner to capture depth. + +46 +00:02:43,797 --> 00:02:47,935 +Both the video and depth are captured +in the wide-angle camera's field of view. + +47 +00:02:47,968 --> 00:02:50,304 +And like the TrueDepth AVCaptureDevice, + +48 +00:02:50,337 --> 00:02:53,941 +all of its formats +support depth data delivery. + +49 +00:02:53,974 --> 00:02:57,110 +This new AVCaptureDevice produces +high quality depth data + +50 +00:02:57,144 --> 00:02:59,546 +by fusing sparse output +from the LiDAR Scanner + +51 +00:02:59,580 --> 00:03:02,449 +with the color image +from the rear-facing wide-angle camera. + +52 +00:03:02,482 --> 00:03:06,086 +The LiDAR and color inputs are processed +using a machine learning model + +53 +00:03:06,119 --> 00:03:08,455 +that outputs a dense depth map. + +54 +00:03:08,488 --> 00:03:11,692 +Because the LiDAR Depth Camera +uses the rear-facing wide-angle camera, + +55 +00:03:11,725 --> 00:03:14,761 +the Telephoto and Ultra Wide cameras +can be used in addition + +56 +00:03:14,795 --> 00:03:17,030 +with an AVCaptureMultiCamSession. + +57 +00:03:17,064 --> 00:03:20,934 +This is useful for apps that wish +to use multiple cameras at the same time. + +58 +00:03:20,968 --> 00:03:24,304 +The LiDAR Depth Camera +exposes many formats, + +59 +00:03:24,338 --> 00:03:27,107 +from video resolutions of 640 by 480 + +60 +00:03:27,140 --> 00:03:32,179 +to a full 12-megapixel image +at 4032 by 3024. + +61 +00:03:32,212 --> 00:03:36,917 +While streaming, +it can output depth maps up to 320 by 240. + +62 +00:03:36,950 --> 00:03:42,990 +And for photo capture, +you can receive depth maps of 768 by 576. + +63 +00:03:43,023 --> 00:03:47,961 +Note, the depth resolutions are slightly +different for 16 by 9 and 4 by 3 formats. + +64 +00:03:47,995 --> 00:03:50,998 +This is to match the video's aspect ratio. + +65 +00:03:51,031 --> 00:03:53,867 +The LiDAR Depth Camera AVCaptureDevice +is available + +66 +00:03:53,901 --> 00:03:58,672 +on iPhone 12 Pro, iPhone 13 Pro, +and iPad Pro 5th generation. + +67 +00:03:58,705 --> 00:04:03,477 +iPhone 13 Pro can deliver depth data using +a combination of the rear facing cameras. + +68 +00:04:03,510 --> 00:04:07,147 +The AVFoundation Capture API +refers to these as “virtual devices” + +69 +00:04:07,181 --> 00:04:09,449 +that consist of physical devices. + +70 +00:04:09,483 --> 00:04:11,451 +On the back of the iPhone 13 Pro, + +71 +00:04:11,485 --> 00:04:15,255 +there are four virtual AVCaptureDevices +available to use: + +72 +00:04:15,289 --> 00:04:18,192 +The new LiDAR Depth Camera +uses the LiDAR Scanner + +73 +00:04:18,225 --> 00:04:20,427 +with the wide-angle camera. + +74 +00:04:20,460 --> 00:04:24,231 +The Dual Camera uses the Wide +and Telephoto cameras. + +75 +00:04:24,264 --> 00:04:25,766 +The Dual Wide Camera, + +76 +00:04:25,799 --> 00:04:28,669 +which uses the Wide +and Ultra Wide cameras. + +77 +00:04:28,702 --> 00:04:30,003 +And the Triple Camera, + +78 +00:04:30,037 --> 00:04:33,707 +that uses the Wide, +Ultra Wide, and Telephoto cameras. + +79 +00:04:33,740 --> 00:04:37,611 +There are differences in the type +of depth these devices produce. + +80 +00:04:37,644 --> 00:04:41,081 +The LiDAR Depth Camera +produces “absolute depth.” + +81 +00:04:41,114 --> 00:04:45,586 +The time of flight technique used allows +for real-world scale to be calculated. + +82 +00:04:45,619 --> 00:04:49,690 +For example, this is great for computer +vision tasks like measuring. + +83 +00:04:49,723 --> 00:04:52,125 +The TrueDepth, Dual, Dual Wide, + +84 +00:04:52,159 --> 00:04:56,296 +and Triple Cameras produce relative, +disparity-based depth. + +85 +00:04:56,330 --> 00:05:00,734 +This uses less power and is great +for apps that render photo effects. + +86 +00:05:00,767 --> 00:05:04,571 +AVFoundation represents depth +using the AVDepthData class. + +87 +00:05:04,605 --> 00:05:07,174 +This class has a pixel buffer +containing the depth + +88 +00:05:07,207 --> 00:05:08,976 +with other properties describing it, + +89 +00:05:09,009 --> 00:05:13,213 +including the depth data type, +the accuracy, and whether it is filtered. + +90 +00:05:13,247 --> 00:05:16,149 +It is delivered +by a depth-capable AVCaptureDevice, + +91 +00:05:16,183 --> 00:05:18,118 +like the new LiDAR Depth Camera. + +92 +00:05:18,151 --> 00:05:20,888 +You can stream depth +from an AVCaptureDepthDataOutput + +93 +00:05:20,921 --> 00:05:25,425 +or receive depth attached to photos +from an AVCapturePhotoOutput. + +94 +00:05:25,459 --> 00:05:27,728 +Depth data is filtered by default. + +95 +00:05:27,761 --> 00:05:29,329 +Filtering reduces noise + +96 +00:05:29,363 --> 00:05:32,566 +and fills in missing values, +or holes, in the depth map. + +97 +00:05:32,599 --> 00:05:34,768 +This is great for video +and photography apps, + +98 +00:05:34,801 --> 00:05:37,070 +so artifacts don't appear +when using the depth map + +99 +00:05:37,104 --> 00:05:39,506 +to apply effects on a color image. + +100 +00:05:39,540 --> 00:05:43,110 +However, computer vision apps +should prefer non-filtered depth data + +101 +00:05:43,143 --> 00:05:45,979 +to preserve the original values +in the depth map. + +102 +00:05:46,013 --> 00:05:48,482 +When filtering is disabled, +the LiDAR Depth Camera + +103 +00:05:48,515 --> 00:05:51,118 +excludes low confidence points. + +104 +00:05:51,151 --> 00:05:53,187 +To disable depth data filtering, + +105 +00:05:53,220 --> 00:05:57,991 +set the isFilteringEnabled property +on your AVCaptureDepthDataOutput to false, + +106 +00:05:58,025 --> 00:06:01,495 +and when you receive an AVDepthData +object from your delegate callback, + +107 +00:06:01,528 --> 00:06:03,463 +it will not be filtered. + +108 +00:06:03,497 --> 00:06:06,433 +Since ARKit already provided access +to the LiDAR Scanner, + +109 +00:06:06,466 --> 00:06:09,503 +you might ask, +“How does AVFoundation compare?” + +110 +00:06:10,804 --> 00:06:14,141 +AVFoundation is designed +for video and photography apps. + +111 +00:06:14,174 --> 00:06:16,577 +With AVFoundation, +you can embed depth data + +112 +00:06:16,610 --> 00:06:20,047 +captured with the LiDAR Scanner +into high-resolution photos. + +113 +00:06:20,080 --> 00:06:23,183 +ARKit is best suited +for augmented reality apps, + +114 +00:06:23,217 --> 00:06:24,852 +as the name suggests. + +115 +00:06:24,885 --> 00:06:28,322 +With the LiDAR Scanner, +ARKit is capable of delivering features + +116 +00:06:28,355 --> 00:06:31,058 +like scene geometry and object placement. + +117 +00:06:31,091 --> 00:06:33,994 +AVFoundation can +deliver high-resolution video + +118 +00:06:34,027 --> 00:06:36,763 +that is great for recording movies +and taking photos. + +119 +00:06:36,797 --> 00:06:42,236 +AVFoundation's LiDAR Depth Camera +can output depth up to 768 by 576. + +120 +00:06:42,269 --> 00:06:47,774 +This is more than twice as big +as ARKit's depth resolution of 256 by 192. + +121 +00:06:47,808 --> 00:06:50,511 +ARKit uses lower resolution depth maps, + +122 +00:06:50,544 --> 00:06:54,982 +so it can apply augmented +reality algorithms for its features. + +123 +00:06:55,015 --> 00:06:59,686 +For more “in-depth” information on how +to use AVFoundation to capture depth data, + +124 +00:06:59,720 --> 00:07:03,323 +watch our previous session +“Capturing Depth in iPhone Photography” + +125 +00:07:03,357 --> 00:07:05,859 +from WWDC 2017. + +126 +00:07:05,893 --> 00:07:07,661 +We're excited to see the interesting ways + +127 +00:07:07,694 --> 00:07:10,430 +you can use the LiDAR Depth Camera +in your apps. + +128 +00:07:10,464 --> 00:07:15,068 +Next up, I'll discuss how improvements to +the auto focus and auto exposure systems + +129 +00:07:15,102 --> 00:07:18,372 +help to improve the visibility +of faces in the scene for your app. + +130 +00:07:18,405 --> 00:07:21,608 +The auto focus and auto exposure +systems analyze the scene + +131 +00:07:21,642 --> 00:07:23,110 +to capture the best image. + +132 +00:07:23,143 --> 00:07:27,247 +The auto focus system adjusts the lens +to keep the subject in focus, + +133 +00:07:27,281 --> 00:07:29,883 +and the auto exposure system +balances the brightest + +134 +00:07:29,917 --> 00:07:33,654 +and darkest regions of a scene +to keep the subject visible. + +135 +00:07:33,687 --> 00:07:36,156 +However, sometimes +the automatic adjustments made + +136 +00:07:36,190 --> 00:07:38,926 +do not keep your subject's face in focus. + +137 +00:07:38,959 --> 00:07:41,595 +And other times, +the subject's face can be difficult + +138 +00:07:41,628 --> 00:07:44,831 +to see with bright backlit scenes. + +139 +00:07:44,865 --> 00:07:49,036 +A common feature of DSLRs and other +pro cameras is to track faces in the scene + +140 +00:07:49,069 --> 00:07:52,940 +to dynamically adjust the focus +and exposure to keep them visible. + +141 +00:07:52,973 --> 00:07:58,612 +New in iOS 15.4, the focus and exposure +systems will prioritize faces. + +142 +00:07:58,645 --> 00:08:01,849 +We liked the benefits of this so much +that we have enabled it by default + +143 +00:08:01,882 --> 00:08:04,852 +for all apps linked on iOS 15.4 or later. + +144 +00:08:04,885 --> 00:08:07,354 +I'll show you some examples. + +145 +00:08:07,387 --> 00:08:10,557 +Without face-driven auto focus, +the camera stays focused + +146 +00:08:10,591 --> 00:08:13,227 +on the background +without refocusing on the face. + +147 +00:08:13,260 --> 00:08:14,428 +Watch it again. + +148 +00:08:14,461 --> 00:08:16,964 +Look at how his face remains +out of focus as he turns around + +149 +00:08:16,997 --> 00:08:19,499 +and that the trees +in the background stay sharp. + +150 +00:08:19,533 --> 00:08:23,637 +With face-driven auto focus enabled, +you can clearly see his face. + +151 +00:08:23,670 --> 00:08:27,708 +And when he turns away, the camera +changes its focus to the background. + +152 +00:08:28,742 --> 00:08:32,246 +When we compare the videos side by side, +the difference is clear. + +153 +00:08:32,279 --> 00:08:34,648 +On the right +with face-driven auto focus enabled, + +154 +00:08:34,681 --> 00:08:37,618 +you can see +the finer details in his beard. + +155 +00:08:37,651 --> 00:08:42,456 +With bright backlit scenes, it can +be challenging to keep faces well exposed. + +156 +00:08:42,489 --> 00:08:45,359 +But with the auto exposure system +prioritizing faces, + +157 +00:08:45,392 --> 00:08:47,561 +we can easily see him. + +158 +00:08:48,896 --> 00:08:52,299 +Comparing side by side, +we can see the difference here again. + +159 +00:08:52,332 --> 00:08:55,969 +Notice that by keeping his face +well-exposed in the picture on the right, + +160 +00:08:56,003 --> 00:08:57,938 +the trees +in the background appear brighter. + +161 +00:08:57,971 --> 00:08:59,339 +And the sky does too. + +162 +00:08:59,373 --> 00:09:03,310 +The exposure of the whole scene +is adjusted when prioritizing faces. + +163 +00:09:04,545 --> 00:09:08,415 +In iOS 15.4, there are new properties +on AVCaptureDevice to control + +164 +00:09:08,448 --> 00:09:11,718 +when face-driven auto focus +and auto exposure are enabled. + +165 +00:09:11,752 --> 00:09:14,955 +You can control whether the device will +“automatically adjust” these settings + +166 +00:09:14,988 --> 00:09:17,224 +and decide when it should be enabled. + +167 +00:09:17,257 --> 00:09:19,359 +Before toggling +the “isEnabled” properties, + +168 +00:09:19,393 --> 00:09:23,030 +you must first +disable the automatic adjustment. + +169 +00:09:23,063 --> 00:09:26,600 +The automatic enablement of this behavior +is great for photography apps. + +170 +00:09:26,633 --> 00:09:28,302 +It's used by Apple's Camera app. + +171 +00:09:28,335 --> 00:09:30,304 +It's also great +for video conferencing apps + +172 +00:09:30,337 --> 00:09:32,506 +to keep faces visible during calls. + +173 +00:09:32,539 --> 00:09:34,541 +FaceTime takes advantage of this, + +174 +00:09:34,575 --> 00:09:36,944 +but sometimes +it's not best suited for an app + +175 +00:09:36,977 --> 00:09:40,480 +to have the auto focus and auto exposure +systems be driven by faces. + +176 +00:09:40,514 --> 00:09:43,183 +For example, +if you want your app to give the user + +177 +00:09:43,217 --> 00:09:46,887 +manual control over the captured image, +you might consider turning this off. + +178 +00:09:48,355 --> 00:09:50,891 +If you decide face-driven auto focus +or auto exposure is not right + +179 +00:09:50,924 --> 00:09:53,493 +for your app, +you can opt out of this behavior. + +180 +00:09:53,527 --> 00:09:56,964 +First, lock the AVCaptureDevice +for configuration. + +181 +00:09:56,997 --> 00:09:59,199 +Then, turn off the automatic adjustment + +182 +00:09:59,233 --> 00:10:02,002 +of face-driven auto focus +or auto exposure. + +183 +00:10:02,035 --> 00:10:05,772 +Next, disable face-driven +auto focus or auto exposure. + +184 +00:10:05,806 --> 00:10:09,510 +And lastly, +unlock the device for configuration. + +185 +00:10:10,511 --> 00:10:13,547 +I'll talk about how you can +use advanced streaming configurations + +186 +00:10:13,580 --> 00:10:18,085 +to receive audio and video data +that is tailored for your app's needs. + +187 +00:10:18,118 --> 00:10:20,521 +The AVFoundation Capture API allows + +188 +00:10:20,554 --> 00:10:23,023 +developers to build +immersive apps using the camera. + +189 +00:10:23,056 --> 00:10:27,694 +The AVCaptureSession manages data flow +from inputs like cameras and microphones + +190 +00:10:27,728 --> 00:10:31,031 +that are connected to AVCaptureOutputs, +that can deliver video, + +191 +00:10:31,064 --> 00:10:33,800 +audio, photos, and more. + +192 +00:10:33,834 --> 00:10:36,537 +Let's take a common camera app +use case for example: + +193 +00:10:36,570 --> 00:10:40,407 +Applying custom effects like filters +or overlays to recorded video. + +194 +00:10:40,440 --> 00:10:42,409 +An app like this would have: + +195 +00:10:42,442 --> 00:10:46,847 +An AVCaptureSession with two inputs, +a camera and a mic, + +196 +00:10:46,880 --> 00:10:51,752 +that are connected to two outputs, +one for video data and one for audio data. + +197 +00:10:51,785 --> 00:10:54,121 +The video data +then has the effects applied, + +198 +00:10:54,154 --> 00:10:56,423 +and the processed video +is sent two places, + +199 +00:10:56,456 --> 00:10:57,824 +to the video preview + +200 +00:10:57,858 --> 00:11:00,127 +and an AVAssetWriter for recording. + +201 +00:11:00,160 --> 00:11:01,562 +The audio data is also sent + +202 +00:11:01,595 --> 00:11:03,697 +to the AVAssetWriter. + +203 +00:11:03,730 --> 00:11:07,601 +New in iOS 16 and iPadOS 16, apps can use + +204 +00:11:07,634 --> 00:11:10,871 +multiple AVCaptureVideoDataOutputs +at the same time. + +205 +00:11:10,904 --> 00:11:14,775 +For each video data output, +you can customize the resolution, + +206 +00:11:14,808 --> 00:11:19,213 +stabilization, orientation, +and pixel format. + +207 +00:11:19,246 --> 00:11:21,315 +Let's go back +to the example camera app. + +208 +00:11:21,348 --> 00:11:25,385 +There are competing capture +requirements this app is balancing. + +209 +00:11:25,419 --> 00:11:28,956 +The app wants to show a live video preview +of the content being captured + +210 +00:11:28,989 --> 00:11:31,959 +and record high quality video +for later playback. + +211 +00:11:31,992 --> 00:11:36,063 +For preview, the resolution needs to be +just big enough for the device's screen. + +212 +00:11:36,096 --> 00:11:39,466 +And the processing needs to be +fast enough for low-latency preview. + +213 +00:11:39,499 --> 00:11:42,603 +But when recording, +its best to capture in high resolution + +214 +00:11:42,636 --> 00:11:44,872 +with quality effects applied. + +215 +00:11:44,905 --> 00:11:46,473 +With the ability to add a second + +216 +00:11:46,507 --> 00:11:48,075 +AVCaptureVideoDataOutput, + +217 +00:11:48,108 --> 00:11:50,711 +the capture graph can be extended. + +218 +00:11:50,744 --> 00:11:52,679 +Now the video data outputs + +219 +00:11:52,713 --> 00:11:53,981 +can be optimized. + +220 +00:11:54,014 --> 00:11:55,849 +One output can deliver smaller buffers + +221 +00:11:55,883 --> 00:11:57,184 +for preview, + +222 +00:11:57,217 --> 00:11:58,619 +and the other can provide + +223 +00:11:58,652 --> 00:12:01,522 +full-sized 4K buffers for recording. + +224 +00:12:01,555 --> 00:12:04,024 +Also, the app could render a simpler, + +225 +00:12:04,057 --> 00:12:05,325 +more performant version of the effect + +226 +00:12:05,359 --> 00:12:07,160 +on smaller preview buffers + +227 +00:12:07,194 --> 00:12:08,929 +and reserve high quality effects + +228 +00:12:08,962 --> 00:12:11,565 +for full-size buffers when recording. + +229 +00:12:11,598 --> 00:12:13,267 +Now the app no longer has + +230 +00:12:13,300 --> 00:12:14,501 +to compromise its preview + +231 +00:12:14,535 --> 00:12:16,503 +or recorded videos. + +232 +00:12:17,671 --> 00:12:20,674 +Another reason to use separate +video data outputs for preview + +233 +00:12:20,707 --> 00:12:24,311 +and recording is +to apply different stabilization modes. + +234 +00:12:24,344 --> 00:12:26,947 +Video stabilization +introduces additional latency + +235 +00:12:26,980 --> 00:12:28,582 +to the video capture pipeline. + +236 +00:12:28,615 --> 00:12:31,018 +For preview, latency is not desirable, + +237 +00:12:31,051 --> 00:12:34,188 +as the noticeable delay +makes it hard to capture content. + +238 +00:12:34,221 --> 00:12:36,557 +For recording, +stabilization can be applied + +239 +00:12:36,590 --> 00:12:38,959 +for a better experience +when watching the video later. + +240 +00:12:38,992 --> 00:12:42,996 +So you can have no stabilization applied +on one video data output + +241 +00:12:43,030 --> 00:12:45,098 +for low-latency preview + +242 +00:12:45,132 --> 00:12:48,702 +and apply stabilization +to the other for later playback. + +243 +00:12:48,735 --> 00:12:52,739 +There are many ways to configure +the resolution of your video data output. + +244 +00:12:52,773 --> 00:12:56,677 +For full-size output, first, +disable automatic configuration + +245 +00:12:56,710 --> 00:12:58,412 +of output buffer dimensions. + +246 +00:12:58,445 --> 00:13:02,282 +Then disable delivery +of preview-sized output buffers. + +247 +00:13:02,316 --> 00:13:04,918 +In most cases, however, +the video data output + +248 +00:13:04,952 --> 00:13:08,455 +is already configured +for full-size output. + +249 +00:13:08,488 --> 00:13:12,893 +For preview-sized output, again, +disable the automatic configuration, + +250 +00:13:12,926 --> 00:13:16,830 +but instead, enable delivery +of preview-sized output buffers. + +251 +00:13:16,864 --> 00:13:21,535 +This is enabled by default when +using the photo AVCaptureSessionPreset. + +252 +00:13:21,568 --> 00:13:25,272 +To request a custom resolution, +specify the width and height + +253 +00:13:25,305 --> 00:13:27,841 +in the output's video settings dictionary. + +254 +00:13:27,875 --> 00:13:31,245 +The aspect ratio of the width +and height must match the aspect ratio + +255 +00:13:31,278 --> 00:13:32,846 +of the source device's activeFormat. + +256 +00:13:32,880 --> 00:13:35,616 +There are more ways +to configure your video data output. + +257 +00:13:35,649 --> 00:13:39,186 +To apply stabilization, +set the preferred stabilization to a mode + +258 +00:13:39,219 --> 00:13:40,754 +like cinematic extended, + +259 +00:13:40,787 --> 00:13:43,490 +which produces videos +that are great to watch. + +260 +00:13:43,524 --> 00:13:47,561 +You can change the orientation +to receive buffers that are portrait. + +261 +00:13:47,594 --> 00:13:52,399 +And you can specify the pixel format, +to receive 10-bit lossless YUV buffers. + +262 +00:13:53,567 --> 00:13:55,536 +For more information +on selecting pixel formats + +263 +00:13:55,569 --> 00:14:00,274 +for an AVCaptureVideoDataOutput, +see Technote 3121. + +264 +00:14:01,375 --> 00:14:04,077 +In addition +to using multiple video data outputs, + +265 +00:14:04,111 --> 00:14:06,880 +starting in iOS 16 and iPadOS 16, + +266 +00:14:06,914 --> 00:14:09,516 +apps can record +with AVCaptureMovieFileOutput + +267 +00:14:09,550 --> 00:14:12,586 +while receiving data +from AVCaptureVideoDataOutput + +268 +00:14:12,619 --> 00:14:14,922 +and AVCaptureAudioDataOutput. + +269 +00:14:14,955 --> 00:14:17,024 +To determine +what can be added to a session, + +270 +00:14:17,057 --> 00:14:19,159 +you can check +whether an output can be added to it + +271 +00:14:19,193 --> 00:14:21,528 +and query +the session's hardwareCost property + +272 +00:14:21,562 --> 00:14:25,065 +to determine whether +the system can support your configuration. + +273 +00:14:25,098 --> 00:14:28,202 +By receiving video data +with a movie file output, + +274 +00:14:28,235 --> 00:14:33,073 +you can inspect the video while recording +and analyze the scene. + +275 +00:14:33,106 --> 00:14:35,776 +And receiving audio data +with a movie file output, + +276 +00:14:35,809 --> 00:14:37,778 +you can sample audio while recording + +277 +00:14:37,811 --> 00:14:40,547 +and listen to what is being recorded. + +278 +00:14:40,581 --> 00:14:42,316 +With a capture graph like this, + +279 +00:14:42,349 --> 00:14:45,919 +you can offload the mechanics +of recording to AVCaptureMovieFileOutput + +280 +00:14:45,953 --> 00:14:50,224 +while still receiving uncompressed video +and audio samples. + +281 +00:14:50,958 --> 00:14:53,493 +Implementing +these advanced streaming configurations + +282 +00:14:53,527 --> 00:14:55,596 +requires use of no new API. + +283 +00:14:55,629 --> 00:14:59,800 +We've enabled this by allowing you +to do more with existing API. + +284 +00:15:01,068 --> 00:15:03,537 +And lastly, I'll discuss how your app will + +285 +00:15:03,570 --> 00:15:06,340 +be able to use the camera +while the user is multitasking. + +286 +00:15:06,373 --> 00:15:09,409 +On iPad, +users can multitask in many ways. + +287 +00:15:09,443 --> 00:15:14,114 +For example, recording Voice Memos +while reading Notes in Split View + +288 +00:15:14,147 --> 00:15:16,817 +or with Slide Over, +write in Notes in a floating window + +289 +00:15:16,850 --> 00:15:19,520 +above Safari in full screen. + +290 +00:15:19,553 --> 00:15:22,456 +With Picture in Picture, +you can continue video playback + +291 +00:15:22,489 --> 00:15:26,693 +while adding reminders +to watch more WWDC videos. + +292 +00:15:26,727 --> 00:15:29,463 +And with Stage Manager new to iPadOS 16, + +293 +00:15:29,496 --> 00:15:33,767 +users can open multiple apps +in resizable floating windows. + +294 +00:15:33,800 --> 00:15:36,970 +Starting in iOS 16, +AVCaptureSessions will be able + +295 +00:15:37,004 --> 00:15:38,605 +to use the camera while multitasking. + +296 +00:15:38,639 --> 00:15:41,508 +We prevented camera access +while multitasking before + +297 +00:15:41,542 --> 00:15:43,477 +because of concerns +of the quality of service + +298 +00:15:43,510 --> 00:15:46,246 +the camera system can deliver +while multitasking. + +299 +00:15:46,280 --> 00:15:50,284 +Resource-intensive apps like games +running alongside an app using the camera + +300 +00:15:50,317 --> 00:15:54,321 +can induce frame drops and other latency, +resulting in a poor camera feed. + +301 +00:15:54,354 --> 00:15:57,991 +A user watching a video months +or years later that has poor quality + +302 +00:15:58,025 --> 00:16:00,894 +may not remember +that they recorded it while multitasking. + +303 +00:16:00,928 --> 00:16:05,065 +Providing a good camera experience +is our priority. + +304 +00:16:05,098 --> 00:16:07,601 +When the system detects video +from the camera was recorded + +305 +00:16:07,634 --> 00:16:10,204 +while multitasking, +a dialog will be displayed + +306 +00:16:10,237 --> 00:16:13,674 +informing the user about the potential +for lower quality videos. + +307 +00:16:13,707 --> 00:16:16,577 +This dialog will be presented +after recording has finished + +308 +00:16:16,610 --> 00:16:20,314 +with AVCaptureMovieFileOutput +or AVAssetWriter. + +309 +00:16:20,347 --> 00:16:23,050 +It will be shown only once +by the system for all apps + +310 +00:16:23,083 --> 00:16:26,353 +and will have an OK button to dismiss. + +311 +00:16:26,386 --> 00:16:29,723 +There are two new properties added +to AVCaptureSession to indicate + +312 +00:16:29,756 --> 00:16:33,527 +when multitasking camera access +is supported and enabled. + +313 +00:16:33,560 --> 00:16:36,697 +Capture sessions that have this enabled +will no longer be interrupted + +314 +00:16:36,730 --> 00:16:41,468 +with the reason “video device not +available with multiple foreground apps.” + +315 +00:16:41,502 --> 00:16:43,937 +Some apps may wish +to require a full screen experience + +316 +00:16:43,971 --> 00:16:45,305 +to use the camera. + +317 +00:16:45,339 --> 00:16:47,908 +This may be useful if you wish +for your app to not compete + +318 +00:16:47,941 --> 00:16:50,711 +with other foreground apps +for system resources. + +319 +00:16:50,744 --> 00:16:55,048 +For example, ARKit does not support +using the camera while multitasking. + +320 +00:16:56,350 --> 00:16:59,720 +You should ensure your app performs well +when running alongside other apps. + +321 +00:16:59,753 --> 00:17:02,422 +Make your app resilient +to increasing system pressure + +322 +00:17:02,456 --> 00:17:04,658 +by monitoring for its notifications, + +323 +00:17:04,691 --> 00:17:08,295 +and take action to reduce the impact, +like lowering the frame rate. + +324 +00:17:08,328 --> 00:17:10,264 +You can reduce +your app's footprint on the system + +325 +00:17:10,297 --> 00:17:15,002 +by requesting lower-resolution, +binned, or non-HDR formats. + +326 +00:17:15,035 --> 00:17:18,472 +For more information on best practices +of maintaining performance, + +327 +00:17:18,505 --> 00:17:22,042 +read the article “Accessing +the Camera While Multitasking”. + +328 +00:17:23,243 --> 00:17:27,681 +Also, video calling and video conferencing +apps can display remote participants + +329 +00:17:27,714 --> 00:17:30,817 +in a system-provided +Picture in Picture window. + +330 +00:17:30,851 --> 00:17:33,921 +Now your app's users +can seamlessly continue a video call + +331 +00:17:33,954 --> 00:17:36,323 +while multitasking on iPad. + +332 +00:17:36,356 --> 00:17:41,128 +AVKit introduced API in iOS 15 +for apps to designate a view controller + +333 +00:17:41,161 --> 00:17:43,797 +for displaying +remote call participants in. + +334 +00:17:43,830 --> 00:17:46,366 +The video call view +controller allows you to customize + +335 +00:17:46,400 --> 00:17:48,735 +the content of the window. + +336 +00:17:48,769 --> 00:17:50,571 +To learn more about adoption, + +337 +00:17:50,604 --> 00:17:55,008 +please see the “Adopting Picture +in Picture for Video Calls” article. + +338 +00:17:55,042 --> 00:17:58,045 +And this concludes advancements +in iOS camera capture. + +339 +00:17:58,078 --> 00:18:02,182 +I showed how you can stream depth +from LiDAR Scanners using AVFoundation, + +340 +00:18:02,216 --> 00:18:05,185 +how your app will receive +improved face rendering, + +341 +00:18:05,219 --> 00:18:08,956 +Advanced AVCaptureSession streaming +configurations tailored for your app, + +342 +00:18:08,989 --> 00:18:12,526 +and lastly, how your app +can use the camera while multitasking. + +343 +00:18:12,559 --> 00:18:14,695 +I hope your WWDC rocks. + +344 +00:18:14,728 --> 00:18:19,433 +♪ ♪ + diff --git "a/eng/2022 Session 110441 Design for Arabic \302\267 \330\265\331\205\331\221\331\205 \330\250\330\247\331\204\330\271\330\261\330\250\331\212 en.srt" "b/eng/2022 Session 110441 Design for Arabic \302\267 \330\265\331\205\331\221\331\205 \330\250\330\247\331\204\330\271\330\261\330\250\331\212 en.srt" new file mode 100644 index 0000000..b06cd49 --- /dev/null +++ "b/eng/2022 Session 110441 Design for Arabic \302\267 \330\265\331\205\331\221\331\205 \330\250\330\247\331\204\330\271\330\261\330\250\331\212 en.srt" @@ -0,0 +1,1639 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,610 --> 00:00:11,578 +Hi, I am Mohamed Samir, + +3 +00:00:11,612 --> 00:00:14,114 +a designer on the Apple Design team. + +4 +00:00:14,147 --> 00:00:17,117 +Today, I'm going to walk you through +some of the best practices + +5 +00:00:17,150 --> 00:00:19,686 +when it comes to designing an Arabic app. + +6 +00:00:19,720 --> 00:00:23,857 +This session is also available in English, +so feel free to check it out. + +7 +00:00:23,891 --> 00:00:27,027 +And before we get started, +I wanted to touch on why it's important + +8 +00:00:27,060 --> 00:00:30,264 +to consider designing +or optimizing your app or game + +9 +00:00:30,297 --> 00:00:33,333 +to be used by Arab audience. + +10 +00:00:33,367 --> 00:00:37,971 +There are around 660 million people +that use the Arabic script today, + +11 +00:00:38,005 --> 00:00:40,807 +which makes it the third most written +language in the world + +12 +00:00:40,841 --> 00:00:42,976 +after Latin and Chinese. + +13 +00:00:43,010 --> 00:00:45,179 +People from more than 22 countries, + +14 +00:00:45,212 --> 00:00:49,550 +many cities, and regions would potentially +see and use what you've built. + +15 +00:00:49,583 --> 00:00:52,419 +And If you want to reach even a fraction +of that audience, + +16 +00:00:52,452 --> 00:00:55,889 +you would want to consider optimizing +not only for the language, + +17 +00:00:55,923 --> 00:00:59,526 +but also for the directionality of the UI. + +18 +00:00:59,560 --> 00:01:04,364 +And this is because Arabic is a language +that is written from right to left. + +19 +00:01:04,398 --> 00:01:06,967 +To understand this, +let's look at this example, + +20 +00:01:07,768 --> 00:01:11,104 +that says "The Arabian desert" in Arabic, + +21 +00:01:11,138 --> 00:01:15,108 +And as you see, it's written from +the right to the left of the screen. + +22 +00:01:15,142 --> 00:01:17,611 +But this is not only it. + +23 +00:01:17,644 --> 00:01:20,247 +If this phrase exists in a layout, + +24 +00:01:20,280 --> 00:01:24,084 +the entire layout flows +from top to bottom, right to left, + +25 +00:01:24,117 --> 00:01:28,121 +to match the reading behavior +and direction of the language. + +26 +00:01:28,155 --> 00:01:30,924 +This means that things like titles, +paragraphs, + +27 +00:01:30,958 --> 00:01:35,462 +columns, and even imagery +should flow from right to left. + +28 +00:01:35,495 --> 00:01:40,834 +And this directionality behavior +extends beyond the layout to also the UI. + +29 +00:01:41,969 --> 00:01:44,805 +As in this example from the Pages app, + +30 +00:01:44,838 --> 00:01:49,643 +where the Navigation bar order +and direction is from right to left. + +31 +00:01:49,676 --> 00:01:53,213 +And the icons also flow +in the same direction. + +32 +00:01:53,247 --> 00:01:55,482 +And if you start navigating +through the app, + +33 +00:01:55,516 --> 00:01:58,252 +you would find menus, controls, + +34 +00:01:58,285 --> 00:01:59,953 +graphical elements, + +35 +00:01:59,987 --> 00:02:02,990 +and even tables have been designed +to match the natural flow + +36 +00:02:03,023 --> 00:02:05,893 +and behavior of the language. + +37 +00:02:05,926 --> 00:02:09,229 +Now, you might think that this +could be a lot of work to optimize + +38 +00:02:09,263 --> 00:02:11,765 +your app or game to work with Arabic, + +39 +00:02:11,798 --> 00:02:15,302 +but the good news is, a lot of this +has been taken care of by Apple + +40 +00:02:15,335 --> 00:02:18,238 +if you use our native frameworks, +like Swift UI, + +41 +00:02:18,272 --> 00:02:21,742 +so you can focus on the content +and few other UI details + +42 +00:02:21,775 --> 00:02:24,511 +that could be specific +to your app or game. + +43 +00:02:24,545 --> 00:02:27,581 +And in today's session I will share +with you some of these aspects + +44 +00:02:27,614 --> 00:02:31,485 +that you should be looking at +while doing this optimization. + +45 +00:02:31,518 --> 00:02:33,520 +Starting by the UI directionality + +46 +00:02:33,554 --> 00:02:37,024 +and example components +that you would want to consider. + +47 +00:02:37,057 --> 00:02:40,194 +Then we move to the Arabic script +main features + +48 +00:02:40,227 --> 00:02:44,765 +and the typography support +that Apple provides you for Arabic, + +49 +00:02:44,798 --> 00:02:46,466 +and how iconography is a detail + +50 +00:02:46,500 --> 00:02:49,970 +that enhances +the overall Arabic experience. + +51 +00:02:50,003 --> 00:02:55,175 +And at last, the numeral systems +supported by Apple for Arabic usage. + +52 +00:02:55,209 --> 00:02:58,312 +Let's start by the UI directionality. + +53 +00:02:58,345 --> 00:03:00,781 +This is an example from the App Store. + +54 +00:03:00,814 --> 00:03:04,318 +It is a flow that starts +by the story card in the Today tab, + +55 +00:03:04,351 --> 00:03:08,789 +the story page, and it ends +with the product page of an app. + +56 +00:03:08,822 --> 00:03:11,358 +The best way to think about +layout directionality + +57 +00:03:11,391 --> 00:03:14,161 +is to turn it into wireframes. + +58 +00:03:14,194 --> 00:03:16,530 +For Arabic, +you would want to switch the placement + +59 +00:03:16,563 --> 00:03:19,399 +of the UI components of these screens. + +60 +00:03:19,433 --> 00:03:21,969 +Some of the elements would switch +from right to left, + +61 +00:03:22,002 --> 00:03:25,072 +while others from left to right. + +62 +00:03:25,105 --> 00:03:30,711 +Titles, buttons, and the Navigation bar +should change order and position. + +63 +00:03:30,744 --> 00:03:34,081 +Paragraphs should be +always aligned to the right. + +64 +00:03:34,114 --> 00:03:39,119 +Carousals and swipeable elements +should also flow from right to left. + +65 +00:03:39,152 --> 00:03:41,788 +After changing the placement +of the UI components, + +66 +00:03:41,822 --> 00:03:45,759 +localizing the content, +and keeping the images as they are, + +67 +00:03:45,792 --> 00:03:49,329 +now you have a layout +that flows from right to left. + +68 +00:03:49,363 --> 00:03:52,900 +Changing the layout directionality +is only the beginning of your journey + +69 +00:03:52,933 --> 00:03:55,936 +to create an excellent +right-to-left behavior, + +70 +00:03:55,969 --> 00:03:57,604 +You would want to keep in mind + +71 +00:03:57,638 --> 00:04:01,475 +that the entire flow of the app +is now structured differently. + +72 +00:04:01,508 --> 00:04:07,247 +And the user thinks about the navigation +between these pages in a reverse order. + +73 +00:04:07,281 --> 00:04:10,584 +So mentally, they start +by the today tab on the right, + +74 +00:04:10,617 --> 00:04:13,153 +then they navigate through the story card, + +75 +00:04:13,187 --> 00:04:16,089 +and they end with the product page +on the left side– + +76 +00:04:16,123 --> 00:04:20,694 +as if they are navigating +through an Arabic book from right to left. + +77 +00:04:20,727 --> 00:04:23,764 +And as I mentioned earlier, +all of this happens automatically + +78 +00:04:23,797 --> 00:04:26,500 +if you use our native frameworks. + +79 +00:04:26,533 --> 00:04:28,936 +Now I would like to share with you +some other examples + +80 +00:04:28,969 --> 00:04:31,138 +for areas and components +that could be impacted + +81 +00:04:31,171 --> 00:04:34,241 +by changing the directionality of the UI. + +82 +00:04:35,142 --> 00:04:38,245 +Let's look at this example +from the Weather app. + +83 +00:04:38,278 --> 00:04:40,881 +On the left side +you see the English Layout, + +84 +00:04:40,914 --> 00:04:44,117 +while the Arabic layout +in on the right side. + +85 +00:04:45,586 --> 00:04:48,622 +The first thing that you would +notice here is content like images, + +86 +00:04:48,655 --> 00:04:51,725 +videos, and backgrounds remain the same. + +87 +00:04:51,758 --> 00:04:54,995 +In this example, +the sun always rises from the east + +88 +00:04:55,028 --> 00:04:57,631 +regardless the location or the language, + +89 +00:04:57,664 --> 00:04:59,566 +and you would want to avoid +flipping the content + +90 +00:04:59,600 --> 00:05:03,036 +so it doesn't impact +the overall experience. + +91 +00:05:04,872 --> 00:05:09,243 +The second component here is the carousal +of "the weather across the day". + +92 +00:05:09,276 --> 00:05:13,647 +Both, the interaction and the animation +of this component are inverted + +93 +00:05:13,680 --> 00:05:16,583 +to match the UI direction. + +94 +00:05:16,617 --> 00:05:19,720 +The third component that I would like +to share with you in the Weather app + +95 +00:05:19,753 --> 00:05:21,488 +is the temperature scale. + +96 +00:05:21,522 --> 00:05:24,124 +For Arabic, the lowest temperature +is on the right, + +97 +00:05:24,157 --> 00:05:26,126 +while the highest is on the left. + +98 +00:05:26,159 --> 00:05:28,662 +So as the scale gradient and indicator, + +99 +00:05:28,695 --> 00:05:31,899 +which we inverted +to match this behavior. + +100 +00:05:31,932 --> 00:05:34,101 +And as previously said, +the mental model + +101 +00:05:34,134 --> 00:05:37,204 +of switching between pages +is also reversed. + +102 +00:05:37,237 --> 00:05:39,339 +The primary page is on the far right, + +103 +00:05:39,373 --> 00:05:42,576 +and you navigate to secondary pages +on the left. + +104 +00:05:42,609 --> 00:05:47,281 +And therefore the pagination dots +should flow also from right to left. + +105 +00:05:47,314 --> 00:05:50,150 +And our second example here +is from the Calendar app, + +106 +00:05:50,184 --> 00:05:53,654 +in which the progress +and the flow of dates, months, and years + +107 +00:05:53,687 --> 00:05:56,623 +is from right to left +when used in Arabic, + +108 +00:05:56,657 --> 00:06:01,495 +which matches the progress +of physical calendars in the Arab world. + +109 +00:06:01,528 --> 00:06:05,432 +And it is always important to make sure +that your app is culturally relevant. + +110 +00:06:05,465 --> 00:06:07,968 +In this example here +from the Calendar app, + +111 +00:06:08,001 --> 00:06:12,072 +you may have noticed that there are +red lines beneath some dates. + +112 +00:06:12,105 --> 00:06:15,008 +These are actually used to indicate +the beginning of each month + +113 +00:06:15,042 --> 00:06:16,777 +of the Islamic Lunar Calendar, + +114 +00:06:16,810 --> 00:06:22,015 +which we have available for people +in the Arab and Islamic world. + +115 +00:06:22,049 --> 00:06:26,854 +The last example about directionality +is from the battery status in settings + +116 +00:06:26,887 --> 00:06:30,457 +where toggles, segmented controllers, +design, and interaction + +117 +00:06:30,490 --> 00:06:33,994 +are mirrored in the Arabic layout. + +118 +00:06:34,027 --> 00:06:36,396 +And also Charts is another place + +119 +00:06:36,430 --> 00:06:39,533 +where directionality could be impacted +in the UI– + +120 +00:06:39,566 --> 00:06:41,902 +especially charts that include +a time component, + +121 +00:06:41,935 --> 00:06:45,539 +like days, weeks, months, or years. + +122 +00:06:45,572 --> 00:06:47,140 +In the battery usage graph, + +123 +00:06:47,174 --> 00:06:50,010 +days of the week goes from left to right +in the English UI, + +124 +00:06:50,043 --> 00:06:51,745 +while in Arabic it is preferred + +125 +00:06:51,778 --> 00:06:54,715 +to have the days progress +from right to left + +126 +00:06:54,748 --> 00:06:59,086 +to match the calendar behavior that we +referred to in the previous example. + +127 +00:06:59,119 --> 00:07:02,556 +Which means that the early time would be +on the right, + +128 +00:07:02,589 --> 00:07:05,592 +and the late time is on the left. + +129 +00:07:05,626 --> 00:07:09,062 +And other charts and graphs in general +are dependent on the country, + +130 +00:07:09,096 --> 00:07:11,198 +so you would want to double-check +before deciding + +131 +00:07:11,231 --> 00:07:14,535 +on which direction to use for charts. + +132 +00:07:14,568 --> 00:07:16,904 +That was UI directionality. + +133 +00:07:16,937 --> 00:07:19,540 +Now let's talk about typography. + +134 +00:07:19,573 --> 00:07:23,210 +But before we dive into the typefaces +and the typestyle adjustments + +135 +00:07:23,243 --> 00:07:26,813 +that you would want to consider +while designing your app, + +136 +00:07:26,847 --> 00:07:28,782 +let me take you through +a brief introduction + +137 +00:07:28,815 --> 00:07:32,653 +about the Arabic script +and its main features. + +138 +00:07:32,686 --> 00:07:36,623 +These are four letters +that spell the word "Arabic" in Arabic, + +139 +00:07:36,657 --> 00:07:40,727 +but in writing they actually don't +mean anything on their own + +140 +00:07:40,761 --> 00:07:42,896 +until they get connected. + +141 +00:07:44,498 --> 00:07:45,999 +To understand this even more, + +142 +00:07:46,033 --> 00:07:49,970 +let me share with you how I would write +this word in a full Arabic sentence + +143 +00:07:50,003 --> 00:07:52,439 +using the iOS keyboard. + +144 +00:08:02,449 --> 00:08:04,585 +And as you see, +being connected doesn't mean + +145 +00:08:04,618 --> 00:08:08,222 +that all the letters in a given word +would be linked. + +146 +00:08:08,255 --> 00:08:10,791 +So this word is made of two parts. + +147 +00:08:10,824 --> 00:08:13,794 +Each part has two characters. + +148 +00:08:13,827 --> 00:08:16,864 +And this is one of the main features +of the Arabic script– + +149 +00:08:16,897 --> 00:08:19,299 +the connected nature of it. + +150 +00:08:19,333 --> 00:08:22,202 +And this also means +that the possible glyphs for each letter + +151 +00:08:22,236 --> 00:08:25,138 +and each pair can be many. + +152 +00:08:25,172 --> 00:08:29,209 +If we take the first letter here +in this example, the letter "Ain," + +153 +00:08:29,243 --> 00:08:33,080 +its form changes based on its position +in the word, + +154 +00:08:33,113 --> 00:08:35,182 +whether it's isolated, + +155 +00:08:35,215 --> 00:08:37,217 +at the beginning of the word, + +156 +00:08:37,251 --> 00:08:38,719 +in the middle, + +157 +00:08:38,752 --> 00:08:41,154 +or at the end. + +158 +00:08:41,188 --> 00:08:44,925 +And it's worth noting that this +usually makes Arabic fonts libraries + +159 +00:08:44,958 --> 00:08:46,894 +way bigger than Latin. + +160 +00:08:48,195 --> 00:08:51,365 +Another feature of the Arabic script +that in most cases, + +161 +00:08:51,398 --> 00:08:54,201 +words are more concise than Latin + +162 +00:08:54,234 --> 00:08:56,069 +due to the connected nature +that tends to give + +163 +00:08:56,103 --> 00:09:00,140 +a more condensed feel +to words and phrases. + +164 +00:09:00,174 --> 00:09:02,442 +But it's also slightly taller, + +165 +00:09:02,476 --> 00:09:04,378 +especially with the use of dots, + +166 +00:09:04,411 --> 00:09:07,481 +vocalization, and diacritic marks. + +167 +00:09:07,514 --> 00:09:10,350 +Vocalization marks are used +to emphasize certain letters + +168 +00:09:10,384 --> 00:09:13,187 +or distinguish between words +that sound different + +169 +00:09:13,220 --> 00:09:16,123 +but would otherwise be identical +in writing. + +170 +00:09:16,156 --> 00:09:19,893 +And if your app would include +an intense use of vocalization marks, + +171 +00:09:19,927 --> 00:09:24,431 +make sure to have more vertical spacing +in the UI to avoid clipping. + +172 +00:09:25,199 --> 00:09:28,869 +Now we understand the main differences +between Arabic and Latin. + +173 +00:09:28,902 --> 00:09:33,607 +Let's talk about the typefaces that Apple +provides you if you use our system API. + +174 +00:09:34,541 --> 00:09:38,345 +Apple provides an exclusive +Arabic typeface that is designed carefully + +175 +00:09:38,378 --> 00:09:41,748 +with legibility and functionality in mind. + +176 +00:09:41,782 --> 00:09:45,652 +It is also designed to be consistent +with the Latin SF family style + +177 +00:09:45,686 --> 00:09:49,523 +to make if feel natural +in bilingual contexts. + +178 +00:09:49,556 --> 00:09:52,693 +And similar to Latin, +SF Arabic also provides you + +179 +00:09:52,726 --> 00:09:55,696 +with all possible weights +that you may need in your app. + +180 +00:09:55,729 --> 00:09:58,232 +from Ultralight to Black. + +181 +00:09:58,265 --> 00:10:02,536 +You can see the usage of different weights +in many places in our native apps. + +182 +00:10:02,569 --> 00:10:05,606 +The Clock app is a good example for that; + +183 +00:10:05,639 --> 00:10:08,108 +using Bold in the title, + +184 +00:10:08,141 --> 00:10:10,644 +Regular for the different cities, + +185 +00:10:10,677 --> 00:10:13,280 +and Light for the clock numerals. + +186 +00:10:13,313 --> 00:10:15,582 +You can explore more apps +in our ecosystem + +187 +00:10:15,616 --> 00:10:18,051 +to see how different weights can be used. + +188 +00:10:19,219 --> 00:10:22,189 +Like the Health app where bold, +medium, and regular + +189 +00:10:22,222 --> 00:10:25,359 +are used in titles and body copy. + +190 +00:10:25,392 --> 00:10:30,063 +And the Weather app where multiple weights +are used in numerals and body copy. + +191 +00:10:31,031 --> 00:10:34,935 +SF Arabic also has been made +with scalability in mind. + +192 +00:10:34,968 --> 00:10:38,906 +Which means that its form changes slightly +depending on the point size. + +193 +00:10:38,939 --> 00:10:41,842 +And this is what we call optical size. + +194 +00:10:41,875 --> 00:10:43,710 +You can see +the structural differences here + +195 +00:10:43,744 --> 00:10:47,047 +between the largest +and the smallest point sizes. + +196 +00:10:47,080 --> 00:10:50,417 +Large is usually used +for titles and headings. + +197 +00:10:50,450 --> 00:10:55,923 +And is designed to match the contemporary +grotesque feel of the rest of SF Family. + +198 +00:10:55,956 --> 00:10:57,958 +On the other hand, smaller point sizes, + +199 +00:10:57,991 --> 00:11:00,427 +which are used in paragraphs +and body copy, + +200 +00:11:00,460 --> 00:11:05,766 +are designed to prioritize legibility +and functionality over style. + +201 +00:11:05,799 --> 00:11:09,036 +This happens by adding angularity +to the terminals, + +202 +00:11:09,069 --> 00:11:13,540 +width and contrast +to the overall font structure. + +203 +00:11:13,574 --> 00:11:16,543 +It's worth noting that the system +takes care of this. + +204 +00:11:16,577 --> 00:11:21,048 +It is automatically choosing the +right form depending on the point size. + +205 +00:11:21,081 --> 00:11:24,751 +Here is an example from +the App Store Editorial sheet, + +206 +00:11:24,785 --> 00:11:29,923 +where we use display in the title, +and text is used in paragraphs. + +207 +00:11:29,957 --> 00:11:32,793 +And in both you could see how +Arabic and English typefaces + +208 +00:11:32,826 --> 00:11:35,395 +work seamlessly in this bilingual context. + +209 +00:11:36,530 --> 00:11:40,567 +SF Arabic Scalable typeface +is used across our ecosystem, + +210 +00:11:40,601 --> 00:11:44,204 +but it's also available for you +to use in your app or game. + +211 +00:11:44,238 --> 00:11:48,475 +And this year we're introducing +SF Arabic Rounded to the SF family, + +212 +00:11:48,509 --> 00:11:52,379 +including all its various weights, +from Ultralight to Black. + +213 +00:11:53,480 --> 00:11:55,549 +Here is an example from the Reminders app + +214 +00:11:55,582 --> 00:12:00,621 +where we use SF Arabic Rounded +in the titles and the body text. + +215 +00:12:00,654 --> 00:12:03,924 +And as you see, Rounded could +give your app a more practical, + +216 +00:12:03,957 --> 00:12:07,127 +active, or softer look, +depending on the context. + +217 +00:12:08,629 --> 00:12:12,266 +The usage of the Rounded typeface, +the several weights, + +218 +00:12:12,299 --> 00:12:17,538 +and the scalability of SF Arabic +can all be seen in all of our native apps, + +219 +00:12:17,571 --> 00:12:19,339 +and we can't wait for you to use them + +220 +00:12:19,373 --> 00:12:23,043 +to create the best possible experiences +for the Arab users. + +221 +00:12:23,076 --> 00:12:25,379 +If you would like to learn more +about all the new fonts + +222 +00:12:25,412 --> 00:12:28,782 +that have been announced this year, +including SF Arabic Rounded, + +223 +00:12:28,815 --> 00:12:30,684 +make sure to watch this year's session + +224 +00:12:30,717 --> 00:12:34,388 +"Meet the expanded +San Francisco font family". + +225 +00:12:34,421 --> 00:12:36,857 +Now let's talk about +some typestyle considerations + +226 +00:12:36,890 --> 00:12:39,760 +when using Arabic typefaces. + +227 +00:12:39,793 --> 00:12:42,229 +Arabic is a non-case sensitive script. + +228 +00:12:42,262 --> 00:12:47,301 +Digital Arabic fonts are usually designed +to match the lower case Latin. + +229 +00:12:47,334 --> 00:12:51,171 +But when uppercase is used, +it gives Latin more volume + +230 +00:12:51,205 --> 00:12:54,875 +and it makes Arabic feels smaller +in comparison to Latin. + +231 +00:12:54,908 --> 00:12:58,111 +To compensate for this this +optical size difference in the UI, + +232 +00:12:58,145 --> 00:13:02,716 +you may want to increase +the Arabic font size by 10%. + +233 +00:13:02,749 --> 00:13:05,819 +This subtle difference would also help +with legibility + +234 +00:13:05,853 --> 00:13:10,123 +especially when upper case is used +in smaller point sizes. + +235 +00:13:10,157 --> 00:13:13,093 +The other thing to consider +is letter spacing. + +236 +00:13:13,126 --> 00:13:15,529 +Given the fact that Arabic is connected, + +237 +00:13:15,562 --> 00:13:18,565 +some Arabic typefaces +are not fully optimized + +238 +00:13:18,599 --> 00:13:20,901 +to deal properly with spacing. + +239 +00:13:20,934 --> 00:13:24,204 +This can result +in showing misplaced links, + +240 +00:13:24,238 --> 00:13:25,806 +breaking letters apart, + +241 +00:13:25,839 --> 00:13:28,342 +or showing unnecessary spacing. + +242 +00:13:28,375 --> 00:13:32,312 +If the Arabic typeface you're using +isn't fully optimized for letter spacing, + +243 +00:13:32,346 --> 00:13:34,915 +make sure to use 0 tracking. + +244 +00:13:34,948 --> 00:13:39,419 +or simply, use our system font, +which adds the correct letters linkage. + +245 +00:13:39,453 --> 00:13:42,289 +This linkage in Arabic +is called "Kashida" + +246 +00:13:42,322 --> 00:13:44,858 +and the system adds Kashidas +with different lengths + +247 +00:13:44,892 --> 00:13:49,029 +to have more organic +natural spacing in Arabic. + +248 +00:13:49,062 --> 00:13:52,432 +The last thing to look out for +is transparency. + +249 +00:13:52,466 --> 00:13:55,469 +Sometimes you can see visible joints +between letters. + +250 +00:13:55,502 --> 00:13:58,505 +This often happens +if there is transparency in a font + +251 +00:13:58,539 --> 00:14:02,509 +or a system +that is not fully optimized for Arabic. + +252 +00:14:02,543 --> 00:14:04,478 +Thankfully, if you use our system font, + +253 +00:14:04,511 --> 00:14:06,346 +you don't have to worry about this. + +254 +00:14:06,380 --> 00:14:09,283 +Opacity is applied to the entire word +or phrase + +255 +00:14:09,316 --> 00:14:11,985 +to overcome any potential distortion. + +256 +00:14:12,019 --> 00:14:14,788 +In the typography section, +I talked about the main features + +257 +00:14:14,821 --> 00:14:16,356 +of the Arabic script, + +258 +00:14:16,390 --> 00:14:19,059 +the Arabic typefaces +that we use in our ecosystem, + +259 +00:14:19,092 --> 00:14:22,529 +and some considerations +for certain type treatments in Arabic. + +260 +00:14:22,563 --> 00:14:25,599 +Now let's talk about iconography. + +261 +00:14:25,632 --> 00:14:29,937 +Iconography is one of the UI elements +that could be easily missed, + +262 +00:14:29,970 --> 00:14:33,373 +but they are usually the entry point +to a user flow + +263 +00:14:33,407 --> 00:14:35,742 +or a trigger to a certain action, + +264 +00:14:35,776 --> 00:14:38,745 +which makes having the correct iconography +crucial + +265 +00:14:38,779 --> 00:14:40,914 +to having a seamless experience. + +266 +00:14:40,948 --> 00:14:42,883 +For Arabic, +we are committed to choosing + +267 +00:14:42,916 --> 00:14:46,286 +the most relevant symbols +for our customers. + +268 +00:14:46,320 --> 00:14:50,791 +Let's take the App Store tab bar icons +as an example. + +269 +00:14:50,824 --> 00:14:55,929 +Some symbols have been tweaked, while +others remained the same in the Arabic UI. + +270 +00:14:55,963 --> 00:15:00,000 +And to understand why we made this choice, +let's look at some of them. + +271 +00:15:01,802 --> 00:15:06,406 +The Today tab symbol, for example, +symbolizes text direction, + +272 +00:15:06,440 --> 00:15:11,078 +and it is more relevant for Arabic users +to have the lines aligned to the right + +273 +00:15:11,111 --> 00:15:14,715 +to match the natural reading direction +of the language. + +274 +00:15:14,748 --> 00:15:19,553 +While the magnifying glass's direction +implies the angle of the right hand usage, + +275 +00:15:19,586 --> 00:15:22,823 +which is the behavior of +the majority of users in the world, + +276 +00:15:22,856 --> 00:15:24,491 +regardless their location + +277 +00:15:24,525 --> 00:15:27,594 +so we decided to keep it as it is +in the Arabic UI. + +278 +00:15:29,263 --> 00:15:31,798 +And here some other examples +from other apps + +279 +00:15:31,832 --> 00:15:35,402 +that shows how directionality +can impact the way symbols are treated + +280 +00:15:35,435 --> 00:15:37,604 +in an Arabic UI, + +281 +00:15:37,638 --> 00:15:42,609 +like writing from right to left while +maintaining the angularity of the pen + +282 +00:15:42,643 --> 00:15:46,880 +or changing the speaker's direction +to make it feel natural in the UI + +283 +00:15:46,914 --> 00:15:49,016 +while maintaining the slash direction, + +284 +00:15:49,049 --> 00:15:53,120 +which is consistent +across our Apple ecosystem, + +285 +00:15:53,153 --> 00:15:55,556 +or changing the direction +of the calendar dots + +286 +00:15:55,589 --> 00:15:58,025 +that represent the progress of months + +287 +00:15:58,058 --> 00:16:00,260 +while keeping the clock hands as they are + +288 +00:16:00,294 --> 00:16:03,830 +to match the physical clock representation +in the system. + +289 +00:16:03,864 --> 00:16:07,701 +And on top of directionality, +having more locally relevant symbols + +290 +00:16:07,734 --> 00:16:11,371 +is another layer that we're dedicated +to enhancing over time + +291 +00:16:11,405 --> 00:16:14,908 +to ensure excellence +in our international markets. + +292 +00:16:14,942 --> 00:16:17,778 +And here are few examples +for Arabic specific symbols + +293 +00:16:17,811 --> 00:16:19,680 +from the SF symbols library, + +294 +00:16:19,713 --> 00:16:23,050 +including an exclusively drawn +Arabic signature symbol + +295 +00:16:23,083 --> 00:16:25,953 +and other text formatting ones. + +296 +00:16:25,986 --> 00:16:30,657 +All of this and more than 300 Arabic +and right-to-left symbols can be found + +297 +00:16:30,691 --> 00:16:33,060 +in SF Symbols app. + +298 +00:16:33,093 --> 00:16:35,629 +In the app you can easily choose a symbol + +299 +00:16:35,662 --> 00:16:39,199 +and check the localization section +in the info panel + +300 +00:16:39,233 --> 00:16:43,804 +to see the Arabic local variant +and other non-Latin scripts. + +301 +00:16:43,837 --> 00:16:47,274 +All of the right-to-left and the local +symbols should appear automatically + +302 +00:16:47,307 --> 00:16:50,477 +in your app +if you use our system API. + +303 +00:16:50,511 --> 00:16:55,516 +As we saw, using the right symbols +can transform the entire app experience, + +304 +00:16:55,549 --> 00:16:57,718 +as it becomes more relevant to the users. + +305 +00:16:57,751 --> 00:17:01,355 +And especially for countries +and regions that use a non-Latin script, + +306 +00:17:01,388 --> 00:17:03,123 +we need to pay more attention, + +307 +00:17:03,156 --> 00:17:07,594 +as sometimes we forget about the different +linguistic and cultural nuances. + +308 +00:17:07,628 --> 00:17:09,997 +And I really hope to see your contribution + +309 +00:17:10,030 --> 00:17:13,800 +in creating the most relevant icons +for the Arabic customers. + +310 +00:17:13,834 --> 00:17:16,837 +Now let's talk about Arabic numerals. + +311 +00:17:16,870 --> 00:17:19,406 +The numerals that we're all familiar with + +312 +00:17:19,439 --> 00:17:24,811 +and are used in most countries around +the world are called Arabic numerals. + +313 +00:17:24,845 --> 00:17:27,814 +And this is because they were +invented in the Arab world + +314 +00:17:27,848 --> 00:17:30,751 +and they replaced +the Roman numerals back then. + +315 +00:17:31,852 --> 00:17:34,054 +And you can notice that until today + +316 +00:17:34,087 --> 00:17:37,824 +all mathematical calculations happens +from right to left, + +317 +00:17:37,858 --> 00:17:41,728 +matching the reading behavior +of the Arabic language. + +318 +00:17:41,762 --> 00:17:45,866 +Like this example which starts +the summation process by ones, + +319 +00:17:45,899 --> 00:17:49,670 +then tens, then hundreds. + +320 +00:17:49,703 --> 00:17:55,275 +In today's world this form of numerals +is called Western Arabic numerals, + +321 +00:17:55,309 --> 00:17:58,912 +and that is to contrast it +with the other form of Arabic numerals + +322 +00:17:58,946 --> 00:18:01,114 +which is the Eastern one. + +323 +00:18:01,148 --> 00:18:03,851 +Both forms have been invented +in the Arab world + +324 +00:18:03,884 --> 00:18:07,421 +and are currently used +in different Arab countries. + +325 +00:18:07,454 --> 00:18:10,924 +The Western Arabic numerals is used +in West African Arab countries + +326 +00:18:10,958 --> 00:18:14,795 +like Morocco, Algeria, and Tunisia, + +327 +00:18:14,828 --> 00:18:19,566 +while the Eastern is used in +some Levantine and gulf countries. + +328 +00:18:19,600 --> 00:18:23,337 +Countries like Egypt or Saudi Arabia +uses both versions. + +329 +00:18:24,505 --> 00:18:27,407 +Choosing between both systems +happens automatically + +330 +00:18:27,441 --> 00:18:29,543 +according to the user's country, + +331 +00:18:29,576 --> 00:18:32,513 +and also can be triggered +by the user choice. + +332 +00:18:32,546 --> 00:18:34,581 +And you can see a reflection +of this choice + +333 +00:18:34,615 --> 00:18:38,752 +in the all apps that uses numerals +in our ecosystem. + +334 +00:18:38,785 --> 00:18:41,288 +Including Calculator app, + +335 +00:18:41,321 --> 00:18:43,490 +and the Calendar app, + +336 +00:18:43,524 --> 00:18:46,493 +and the Typograph watch face +that is beautifully designed + +337 +00:18:46,527 --> 00:18:48,862 +in both numeral forms. + +338 +00:18:48,896 --> 00:18:53,400 +And many other watch faces that you can +check in our watch face gallery. + +339 +00:18:53,433 --> 00:18:56,370 +If the app that you're developing +includes numerals, + +340 +00:18:56,403 --> 00:18:58,906 +make sure to count for both forms + +341 +00:18:58,939 --> 00:19:01,041 +or to check which country you're targeting + +342 +00:19:01,074 --> 00:19:03,944 +to validate which form +would be more suitable. + +343 +00:19:03,977 --> 00:19:06,713 +And at last, if you would like to have +more design guidance + +344 +00:19:06,747 --> 00:19:08,682 +for right-to-left languages, + +345 +00:19:08,715 --> 00:19:10,918 +please refer to +our Right to Left guidelines + +346 +00:19:10,951 --> 00:19:14,154 +in the Human Interface Guidelines. + +347 +00:19:14,188 --> 00:19:17,291 +Today I talked about Designing in Arabic, + +348 +00:19:17,324 --> 00:19:22,095 +including the impact that the language +can have on the UI directionality. + +349 +00:19:22,129 --> 00:19:23,931 +And in the typography section, + +350 +00:19:23,964 --> 00:19:27,034 +I gave an introduction +about the Arabic script, + +351 +00:19:27,067 --> 00:19:32,172 +the typefaces, +and some Arabic UI type considerations. + diff --git a/eng/2022 Session 110565 Display HDR video in EDR with AVFoundation and Metal en.srt b/eng/2022 Session 110565 Display HDR video in EDR with AVFoundation and Metal en.srt new file mode 100644 index 0000000..af3e740 --- /dev/null +++ b/eng/2022 Session 110565 Display HDR video in EDR with AVFoundation and Metal en.srt @@ -0,0 +1,1786 @@ +1 +00:00:00,501 --> 00:00:08,509 +♪ ♪ + +2 +00:00:09,643 --> 00:00:13,647 +Ken Greenebaum: Hi everyone! +Welcome to WWDC 2022. + +3 +00:00:13,680 --> 00:00:15,082 +My name is Ken Greenebaum, + +4 +00:00:15,115 --> 00:00:18,352 +and I'm with the Color and Display +Technologies team at Apple. + +5 +00:00:18,385 --> 00:00:21,388 +We are thrilled to have three EDR talks +this year. + +6 +00:00:21,421 --> 00:00:25,559 +Hope you've had an opportunity +to watch "Explore EDR on iOS," + +7 +00:00:25,592 --> 00:00:29,263 +where we announced +EDR API support for iOS, + +8 +00:00:29,296 --> 00:00:33,901 +as well as "Display EDR content +with Core Image, Metal, and SwiftUI." + +9 +00:00:33,934 --> 00:00:37,838 +Some of you may have also watched +my EDR talk last year, + +10 +00:00:37,871 --> 00:00:40,240 +where we demonstrated +how to use AVPlayer + +11 +00:00:40,274 --> 00:00:43,177 +to play back HDR video, using EDR. + +12 +00:00:44,178 --> 00:00:46,246 +In this talk we're gonna go deeper, + +13 +00:00:46,280 --> 00:00:49,550 +and explore how to use +Core Media interfaces to provide, + +14 +00:00:49,583 --> 00:00:51,785 +not only EDR playback, + +15 +00:00:51,818 --> 00:00:54,821 +but also how to decode +and playback HDR video, + +16 +00:00:54,855 --> 00:00:57,591 +into your own EDR layers or views. + +17 +00:00:59,326 --> 00:01:02,396 +Then we'll continue beyond +simply playing back content, + +18 +00:01:02,429 --> 00:01:05,732 +to show how to access the decoded +video frames in real time, + +19 +00:01:05,766 --> 00:01:08,101 +via Core Video's display link, + +20 +00:01:08,135 --> 00:01:11,271 +send those frames to CoreImage Filters, +or a Metal Shader, + +21 +00:01:11,305 --> 00:01:13,473 +to add color management, visual effects, + +22 +00:01:13,507 --> 00:01:15,909 +or apply other signal processing, + +23 +00:01:15,943 --> 00:01:20,247 +and finally, plumb +the resulting frames to Metal to render. + +24 +00:01:20,280 --> 00:01:24,751 +We're going to start by reviewing +the EDR compatible video media frameworks, + +25 +00:01:24,785 --> 00:01:28,522 +to help you decide which best matches +your application's requirements. + +26 +00:01:30,057 --> 00:01:32,226 +Next we will briefly discuss +the high level AVKit + +27 +00:01:32,259 --> 00:01:35,028 +and AVFoundation frameworks, + +28 +00:01:35,062 --> 00:01:37,764 +that can do all of the work +of playing HDR video, + +29 +00:01:37,798 --> 00:01:40,701 +if your application +requires straight forward playback. + +30 +00:01:42,302 --> 00:01:44,872 +And finally, +we'll discuss best practices + +31 +00:01:44,905 --> 00:01:48,175 +for using decoded video frames, +with Core Video and Metal, + +32 +00:01:48,208 --> 00:01:51,979 +in your EDR playback, editing, +or image processing engine. + +33 +00:01:54,781 --> 00:01:58,752 +Let's begin by taking a quick survey +of Apple's video frameworks; + +34 +00:01:58,785 --> 00:02:01,355 +Starting with +the highest level interfaces; + +35 +00:02:01,388 --> 00:02:03,323 +which are the easiest to use; + +36 +00:02:03,357 --> 00:02:06,827 +and continuing to lower level frameworks +that offer more opportunities, + +37 +00:02:06,860 --> 00:02:10,364 +at the expense of adding +complexity to your code. + +38 +00:02:10,397 --> 00:02:13,467 +It is best to use +the highest level framework possible + +39 +00:02:13,500 --> 00:02:17,271 +to take advantage of the optimizations +provided automatically for you. + +40 +00:02:17,304 --> 00:02:20,440 +This will get us ready to dive into +the body of the talk, + +41 +00:02:20,474 --> 00:02:23,177 +where we will be exploring +a number of scenarios, + +42 +00:02:23,210 --> 00:02:24,945 +from simple EDR playback + +43 +00:02:24,978 --> 00:02:27,981 +to more sophisticated plumbing +of decoded video frames + +44 +00:02:28,015 --> 00:02:31,652 +to CoreImage or Metal +for real time processing. + +45 +00:02:31,685 --> 00:02:34,154 +At the highest level, there is AVKit. + +46 +00:02:34,188 --> 00:02:38,292 +With AVKit you can create +user interfaces for media playback; + +47 +00:02:38,325 --> 00:02:41,562 +complete with transport controls, +chapter navigation, + +48 +00:02:41,595 --> 00:02:43,030 +Picture in Picture support, + +49 +00:02:43,063 --> 00:02:45,632 +and display of subtitles +and closed captions. + +50 +00:02:45,666 --> 00:02:49,136 +AVKit can playback HDR content as EDR, + +51 +00:02:49,169 --> 00:02:52,840 +as we will demonstrate +using AVPlayerViewController. + +52 +00:02:52,873 --> 00:02:57,377 +However, if your application requires +further processing of video frames, + +53 +00:02:57,411 --> 00:02:59,179 +you will have to use a media framework + +54 +00:02:59,213 --> 00:03:01,815 +that can give you more control +over your pipeline. + +55 +00:03:01,849 --> 00:03:04,618 +Next there is AVFoundation. + +56 +00:03:04,651 --> 00:03:07,688 +AVFoundation is +the full-featured framework + +57 +00:03:07,721 --> 00:03:12,860 +for working with time based audio visual +media on Apple Platforms. + +58 +00:03:12,893 --> 00:03:16,096 +Using AVFoundation, +you can easily play, create, + +59 +00:03:16,129 --> 00:03:19,032 +and edit QuickTime movies +and MPEG 4 files, + +60 +00:03:19,066 --> 00:03:20,634 +play HLS streams, + +61 +00:03:20,667 --> 00:03:24,271 +and build powerful media functionality +into your apps. + +62 +00:03:24,304 --> 00:03:26,306 +We'll be exploring use of AVPlayer + +63 +00:03:26,340 --> 00:03:30,577 +and the related AVPlayerLayer interface +in this talk. + +64 +00:03:30,611 --> 00:03:35,182 +Core Video is a framework that provides +a pipeline model for digital video. + +65 +00:03:35,215 --> 00:03:37,417 +It simplifies the way +you work with videos + +66 +00:03:37,451 --> 00:03:40,220 +by partitioning the process +into discrete steps. + +67 +00:03:40,254 --> 00:03:43,790 +Core Video also makes it easier +for you to access and manipulate + +68 +00:03:43,824 --> 00:03:46,527 +individual frames, +without having to worry about + +69 +00:03:46,560 --> 00:03:48,262 +translating between data types + +70 +00:03:48,295 --> 00:03:51,465 +or worrying about display synchronization. + +71 +00:03:51,498 --> 00:03:53,534 +We'll be demonstrating +use of DisplayLink, + +72 +00:03:53,567 --> 00:03:56,036 +and CVPixelBuffer's with Core Image. + +73 +00:03:56,069 --> 00:03:59,239 +And CVMetalTextureCache, with Metal. + +74 +00:03:59,273 --> 00:04:01,341 +Next there is Video Toolbox. + +75 +00:04:01,375 --> 00:04:04,011 +This is a low-level framework +that provides direct access + +76 +00:04:04,044 --> 00:04:06,446 +to hardware encoders and decoders. + +77 +00:04:06,480 --> 00:04:10,784 +Video Toolbox provides services for +video compression and decompression, + +78 +00:04:10,817 --> 00:04:13,253 +and for conversion between +raster image formats + +79 +00:04:13,287 --> 00:04:15,556 +stored in Core Video pixel buffers. + +80 +00:04:15,589 --> 00:04:19,159 +VTDecompressionSession +is a powerful low-level interface + +81 +00:04:19,193 --> 00:04:21,161 +that is outside of the scope of this talk, + +82 +00:04:21,195 --> 00:04:24,631 +but advanced developers +might want to investigate further. + +83 +00:04:24,665 --> 00:04:26,800 +And finally, there is Core Media. + +84 +00:04:26,834 --> 00:04:30,537 +This framework defines +the media pipeline used by AVFoundation, + +85 +00:04:30,571 --> 00:04:33,373 +and the other high-level media frameworks. + +86 +00:04:33,407 --> 00:04:37,110 +You can always use Core Media's +low-level data types and interfaces + +87 +00:04:37,144 --> 00:04:39,546 +to efficiently process media samples + +88 +00:04:39,580 --> 00:04:41,648 +and manage queues of media data. + +89 +00:04:41,682 --> 00:04:45,319 +In the remainder of this talk +we will demonstrate how and when + +90 +00:04:45,352 --> 00:04:47,688 +to use these frameworks in your app. + +91 +00:04:47,721 --> 00:04:51,225 +First, how to use AVKit +and AVFoundation + +92 +00:04:51,258 --> 00:04:55,262 +to easily playback HDR video +rendered as EDR. + +93 +00:04:55,295 --> 00:04:59,399 +Then a series of more sophisticated +applications of AVPlayer: + +94 +00:04:59,433 --> 00:05:01,235 +to render to your own layer, + +95 +00:05:01,268 --> 00:05:05,105 +to access individually decoded frames +via CADisplayLink + +96 +00:05:05,138 --> 00:05:09,243 +and send the resulting CVPixelBuffers +to Core Image for processing, + +97 +00:05:09,276 --> 00:05:13,247 +and finally, accessing the decoded frames +as Metal textures + +98 +00:05:13,280 --> 00:05:15,182 +via the CVMetalTextureCache + +99 +00:05:15,215 --> 00:05:17,818 +for processing and rendering in Metal. + +100 +00:05:17,851 --> 00:05:22,322 +Now that we have an overview of +the video media layer on Apple platforms, + +101 +00:05:22,356 --> 00:05:26,260 +we'll focus on AVKit +and AVFoundation frameworks. + +102 +00:05:26,293 --> 00:05:28,795 +Let's get things started +by first discussing playback + +103 +00:05:28,829 --> 00:05:30,597 +of your HDR video content + +104 +00:05:30,631 --> 00:05:33,433 +using AVFoundation's AVPlayer interface. + +105 +00:05:33,467 --> 00:05:35,969 +An AVPlayer is a controller object, + +106 +00:05:36,003 --> 00:05:39,773 +used to manage the playback +and timing of a media asset. + +107 +00:05:39,806 --> 00:05:44,244 +The AVPlayer interface can be used for +high-performance playback of HDR video, + +108 +00:05:44,278 --> 00:05:47,314 +automatically rendering +the result as EDR when possible. + +109 +00:05:48,282 --> 00:05:51,785 +With AVPlayer, you can play local, +and remote file based media, + +110 +00:05:51,818 --> 00:05:53,820 +such as QuickTime movies; + +111 +00:05:53,854 --> 00:05:58,025 +as well as streaming media, +served using HLS. + +112 +00:05:58,058 --> 00:06:02,729 +Essentially, AVPlayer is used to play +one media asset at a time. + +113 +00:06:02,763 --> 00:06:07,167 +You can reuse the player instance +to serially play additional media assets, + +114 +00:06:07,201 --> 00:06:12,272 +or even create multiple instances to play +more than one asset simultaneously, + +115 +00:06:12,306 --> 00:06:17,277 +but AVPlayer manages the playback +of only a single media asset at a time. + +116 +00:06:17,311 --> 00:06:21,348 +AVFoundation framework also provides +a subclass of AVPlayer + +117 +00:06:21,381 --> 00:06:24,218 +called AVQueuePlayer +that you can use to create + +118 +00:06:24,251 --> 00:06:29,556 +and manage the queuing and playing +of sequential HDR media assets. + +119 +00:06:29,590 --> 00:06:33,193 +If your application requires +simple playback of HDR video media + +120 +00:06:33,227 --> 00:06:37,164 +rendered to EDR, then AVPlayer +with AVPlayerViewController, + +121 +00:06:37,197 --> 00:06:39,233 +may be the best approach. + +122 +00:06:39,266 --> 00:06:41,668 +Use AVPlayer with AVPlayerLayer + +123 +00:06:41,702 --> 00:06:45,405 +to playback your own views +on iOS or macOS. + +124 +00:06:46,640 --> 00:06:49,810 +These are the most straightforward ways +of using AVPlayer. + +125 +00:06:49,843 --> 00:06:51,845 +Let's look at examples of both. + +126 +00:06:51,879 --> 00:06:55,916 +First we will look how you can +use AVFoundation's AVPlayer interface, + +127 +00:06:55,949 --> 00:06:59,853 +in conjunction with AVKit's +AVPlayer View Controller. + +128 +00:06:59,887 --> 00:07:03,957 +Here, we start by instantiating AVPlayer +from the media's URL. + +129 +00:07:06,493 --> 00:07:09,630 +Next we create +an AVPlayerViewController, + +130 +00:07:09,663 --> 00:07:12,332 +then set the player property +of our viewer controller + +131 +00:07:12,366 --> 00:07:15,235 +to the player we just created +from the media's URL. + +132 +00:07:18,005 --> 00:07:23,076 +And present the view controller modally +to start playback of the video. + +133 +00:07:23,110 --> 00:07:25,579 +AVKit manages all the details for you + +134 +00:07:25,612 --> 00:07:29,116 +and will automatically play back +HDR Video as EDR + +135 +00:07:29,149 --> 00:07:31,852 +on displays supporting EDR. + +136 +00:07:31,885 --> 00:07:35,656 +As I mentioned, some applications will +need to play back HDR video media + +137 +00:07:35,689 --> 00:07:37,491 +into their own view. + +138 +00:07:37,524 --> 00:07:42,563 +Let's look at how to accomplish this +using AVPlayer with AVPlayerLayer. + +139 +00:07:42,596 --> 00:07:46,266 +To play HDR video media as EDR +in your own view, + +140 +00:07:46,300 --> 00:07:51,371 +we again start by creating an AVPlayer +with the media's URL. + +141 +00:07:51,405 --> 00:07:54,474 +However this time +we instantiate an AVPlayerLayer + +142 +00:07:54,508 --> 00:07:57,444 +with the player we just created. + +143 +00:07:57,477 --> 00:08:00,013 +Next we need to set the bounds +on the player layer, + +144 +00:08:00,047 --> 00:08:02,649 +which we get from the view. + +145 +00:08:02,683 --> 00:08:05,385 +Now that the player layer +has the bounds from the view, + +146 +00:08:05,419 --> 00:08:10,023 +we can add the player layer +as a sublayer to the view. + +147 +00:08:10,057 --> 00:08:12,926 +Finally, +to play back the HDR video media, + +148 +00:08:12,960 --> 00:08:15,596 +we call AVPlayer's play method. + +149 +00:08:15,629 --> 00:08:19,333 +That's all that is needed to play back +HDR video media as EDR + +150 +00:08:19,366 --> 00:08:23,971 +in your own layer +using AVPlayer and AVPlayerLayer. + +151 +00:08:24,004 --> 00:08:26,139 +We just explored +the two most straightforward + +152 +00:08:26,173 --> 00:08:29,443 +HDR video playback workflows +using AVPlayer. + +153 +00:08:29,476 --> 00:08:34,014 +However, many applications require more +than simple media playback. + +154 +00:08:35,282 --> 00:08:38,852 +For example, an application +might require image processing, + +155 +00:08:38,886 --> 00:08:43,223 +such as color grading or chroma keying +to be applied to the video. + +156 +00:08:43,257 --> 00:08:48,095 +Let's explore a workflow that gets +decoded video frames from AVPlayer, + +157 +00:08:48,128 --> 00:08:51,865 +applies Core Image filters +or Metal shaders in real time, + +158 +00:08:51,899 --> 00:08:55,369 +and renders the results as EDR. + +159 +00:08:55,402 --> 00:08:59,072 +We will be demonstrating how to use +AVPlayer and the AVPlayerItem + +160 +00:08:59,106 --> 00:09:03,010 +to decode EDR frames +from your HDR video media, + +161 +00:09:03,043 --> 00:09:06,947 +access the decoded frames +from the Core Video display link, + +162 +00:09:06,980 --> 00:09:11,919 +send the resulting pixel buffers +to Core Image or Metal for processing, + +163 +00:09:11,952 --> 00:09:14,354 +then render the results +in a CAMetalLayer + +164 +00:09:14,388 --> 00:09:17,958 +as EDR on displays with EDR support. + +165 +00:09:17,991 --> 00:09:20,360 +With this in mind, +let's first demonstrate + +166 +00:09:20,394 --> 00:09:23,330 +setting a few key properties +on the CAMetalLayer, + +167 +00:09:23,363 --> 00:09:28,202 +which are required to ensure HDR media +will render correctly as EDR. + +168 +00:09:28,235 --> 00:09:30,337 +First we need to get the CAMetalLayer + +169 +00:09:30,370 --> 00:09:34,274 +that we will be rendering +the HDR video content to. + +170 +00:09:34,308 --> 00:09:37,177 +On that layer +we opt into EDR by setting + +171 +00:09:37,211 --> 00:09:40,614 +the wantsExtendedDynamicRangeContent +flag to true. + +172 +00:09:42,883 --> 00:09:48,222 +Please be sure to use a pixel format that +supports Extended Dynamic Range content. + +173 +00:09:48,255 --> 00:09:52,459 +For the AVPlayer example that follows, +we will set the CAMetalLayer to use + +174 +00:09:52,492 --> 00:09:54,528 +a half float pixel format, + +175 +00:09:54,561 --> 00:09:58,265 +however a ten bit format +used in conjunction with a PQ + +176 +00:09:58,298 --> 00:10:01,535 +or HLG transfer function would also work. + +177 +00:10:01,568 --> 00:10:03,904 +To avoid limiting the result to SDR, + +178 +00:10:03,937 --> 00:10:06,707 +we also need to set the layer +to an EDR compatible + +179 +00:10:06,740 --> 00:10:08,909 +extended range color space. + +180 +00:10:10,577 --> 00:10:13,580 +In our examples we will be setting +the half float metal texture + +181 +00:10:13,614 --> 00:10:18,151 +to the extended linear display P3 +color space. + +182 +00:10:18,185 --> 00:10:20,487 +We just scratched the surface +regarding EDR, + +183 +00:10:20,521 --> 00:10:23,090 +color spaces, and pixel buffer formats. + +184 +00:10:23,123 --> 00:10:25,626 +You might want to check out +my session from last year, + +185 +00:10:25,659 --> 00:10:27,528 +"HDR rendering with EDR," + +186 +00:10:27,561 --> 00:10:31,999 +as well as this year's "EDR on iOS," +for more details. + +187 +00:10:33,300 --> 00:10:36,303 +Now that we have set the basic properties +on the CAMetalLayer, + +188 +00:10:36,336 --> 00:10:39,873 +let's continue the demonstration +by adding real time image processing + +189 +00:10:39,907 --> 00:10:42,376 +using a Core Image, or Metal shader. + +190 +00:10:42,409 --> 00:10:45,412 +We'll be using a display link +in conjunction with AVPlayer + +191 +00:10:45,445 --> 00:10:48,415 +to access the decoded video frames +in real time. + +192 +00:10:49,449 --> 00:10:53,820 +For this workflow, you start by creating +an AVPlayer from an AVPlayerItem. + +193 +00:10:53,854 --> 00:10:57,291 +Next, you instantiate +an AVPlayerItemVideoOutput, + +194 +00:10:57,324 --> 00:11:02,496 +configured with appropriate pixel buffer +format and color space for EDR. + +195 +00:11:02,529 --> 00:11:05,399 +Then you create +and configure a Display link. + +196 +00:11:05,432 --> 00:11:08,669 +And lastly, you run the Display link +to get the pixel buffers + +197 +00:11:08,702 --> 00:11:11,805 +to Core Image or Metal for processing. + +198 +00:11:11,839 --> 00:11:16,310 +We will demonstrate a CADisplayLink +as is used on iOS. + +199 +00:11:16,343 --> 00:11:21,381 +Please use the equivalent CVDisplayLink +interface when developing for macOS. + +200 +00:11:21,415 --> 00:11:24,084 +This time we choose +to create an AVPlayerItem + +201 +00:11:24,117 --> 00:11:26,486 +from our media's URL, + +202 +00:11:26,520 --> 00:11:32,326 +and instantiate an AVPlayer with +the AVPlayerItem that we just created. + +203 +00:11:32,359 --> 00:11:35,562 +Now we create a pair of dictionaries +to specify the color space + +204 +00:11:35,596 --> 00:11:38,999 +and pixel buffer format +of the decoded frames. + +205 +00:11:39,032 --> 00:11:41,635 +The first dictionary, +videoColorProperties, + +206 +00:11:41,668 --> 00:11:45,172 +is where the color space +and transfer function are specified. + +207 +00:11:45,205 --> 00:11:48,475 +In this example we request +the Display P3 colorspace, + +208 +00:11:48,509 --> 00:11:51,945 +which corresponds to the color space +of most Apple displays, + +209 +00:11:51,979 --> 00:11:55,115 +and the linear transfer function +which allows AVFoundation + +210 +00:11:55,148 --> 00:11:58,852 +to maintain the extended range values +required for EDR. + +211 +00:12:00,153 --> 00:12:02,756 +The second dictionary, +outputVideoSettings, + +212 +00:12:02,789 --> 00:12:06,026 +specifies the characteristics of +the pixel buffer format + +213 +00:12:06,059 --> 00:12:08,929 +and also provides a reference +to the videoColorProperties dictionary + +214 +00:12:08,962 --> 00:12:11,899 +we just created. + +215 +00:12:11,932 --> 00:12:17,504 +In this example, we request wide color +and the half float pixel buffer format. + +216 +00:12:17,538 --> 00:12:20,908 +It is very helpful +that AVPlayerItemVideoOutput, + +217 +00:12:20,941 --> 00:12:24,378 +not only decodes video +into the pixel buffer format we specify + +218 +00:12:24,411 --> 00:12:26,480 +in the output settings dictionary, + +219 +00:12:26,513 --> 00:12:30,517 +but also automatically performs +any color conversion required + +220 +00:12:30,551 --> 00:12:34,021 +via a pixel transfer session. + +221 +00:12:34,054 --> 00:12:36,890 +Recall, a video might contain +multiple clips, + +222 +00:12:36,924 --> 00:12:39,393 +potentially with different colorspaces. + +223 +00:12:39,426 --> 00:12:42,796 +AVFoundation automatically manages +these for us, + +224 +00:12:42,829 --> 00:12:45,065 +and as we'll soon be demonstrating, + +225 +00:12:45,098 --> 00:12:48,368 +this behavior also allows +the resulting decoded video frames + +226 +00:12:48,402 --> 00:12:51,338 +to be sent to low level frameworks +like Metal + +227 +00:12:51,371 --> 00:12:54,341 +that don't themselves provide +automatic colorspace conversion + +228 +00:12:54,374 --> 00:12:56,643 +to the display's colorspace. + +229 +00:12:57,377 --> 00:13:00,447 +Now we create +the AVPlayerItemVideoOutput + +230 +00:13:00,480 --> 00:13:03,784 +with the outputVideoSettings dictionary. + +231 +00:13:03,817 --> 00:13:06,353 +As the third step, +we setup the Display link, + +232 +00:13:06,386 --> 00:13:10,824 +which will be used to access +the decoded frames in real time. + +233 +00:13:10,858 --> 00:13:15,562 +CADisplayLink takes a call back +that is run on each display update. + +234 +00:13:15,596 --> 00:13:19,366 +In our example we call a local function +that we will explore in a moment + +235 +00:13:19,399 --> 00:13:24,805 +to get the CVPixelBuffers that we will +be sending to Core Image for processing. + +236 +00:13:24,838 --> 00:13:27,508 +Next we create a video player item +observer + +237 +00:13:27,541 --> 00:13:31,512 +to allow us to handle changes +to specified player Item properties. + +238 +00:13:33,113 --> 00:13:35,516 +Our example will execute this code + +239 +00:13:35,549 --> 00:13:38,652 +every time for the player item's +status changes. + +240 +00:13:41,088 --> 00:13:44,191 +When the player item's status +changes to readyToPlay, + +241 +00:13:44,224 --> 00:13:47,060 +we add our AVPlayerItemVideoOutput + +242 +00:13:47,094 --> 00:13:52,165 +to the new AVPlayerItem +that was just returned, + +243 +00:13:52,199 --> 00:13:57,070 +register CADisplayLink +with the main run loop set to common mode, + +244 +00:13:57,104 --> 00:13:59,673 +and start the real time decoding +of the HDR video + +245 +00:13:59,706 --> 00:14:02,976 +by calling play on the video player. + +246 +00:14:04,478 --> 00:14:08,849 +Finally, we will take a look at an example +CADisplayLink callback implementation, + +247 +00:14:08,882 --> 00:14:12,653 +which we referred to earlier +as the `displayLinkCopyPixelBuffers` + +248 +00:14:12,686 --> 00:14:15,355 +local function. + +249 +00:14:15,389 --> 00:14:17,591 +Once the HDR video begins to play, + +250 +00:14:17,624 --> 00:14:22,629 +the CADisplayLink callback function +is called on each display refresh. + +251 +00:14:22,663 --> 00:14:27,401 +For instance it might be called 60 times +a second for a typical display. + +252 +00:14:27,434 --> 00:14:30,671 +This is our code's opportunity +to update the frame displayed + +253 +00:14:30,704 --> 00:14:34,141 +if there is a new CVPixelBuffer. + +254 +00:14:34,174 --> 00:14:37,611 +On each display callback, +we attempt to copy a CVPixelBuffer + +255 +00:14:37,644 --> 00:14:40,447 +containing the decoded video frame +to be displayed + +256 +00:14:40,480 --> 00:14:43,717 +at the current wall clock time. + +257 +00:14:43,750 --> 00:14:47,054 +However, +the `copyPixelBuffer` call might fail, + +258 +00:14:47,087 --> 00:14:50,123 +as there won't always be a new +CVPixelBuffer available + +259 +00:14:50,157 --> 00:14:52,359 +at every display refresh, + +260 +00:14:52,392 --> 00:14:56,797 +especially when the screen refresh rate +exceeds that of the video being played. + +261 +00:14:56,830 --> 00:14:59,032 +If there is not a new CVPixelBuffer, + +262 +00:14:59,066 --> 00:15:01,735 +then the call fails +and we skip the render. + +263 +00:15:01,768 --> 00:15:06,373 +This causes the preceding frame to remain +on-screen for another display refresh. + +264 +00:15:06,406 --> 00:15:10,143 +But if the copy succeeds, +then we have a fresh frame of video + +265 +00:15:10,177 --> 00:15:12,112 +in a CVPixelBuffer. + +266 +00:15:12,145 --> 00:15:16,350 +There are a number of ways that we might +process and render this new frame. + +267 +00:15:16,383 --> 00:15:19,219 +One opportunity is to send +the CVPixelBuffer to Core Image + +268 +00:15:19,253 --> 00:15:21,388 +for processing. + +269 +00:15:21,421 --> 00:15:24,424 +Core Image can string together +one or more CIFilters + +270 +00:15:24,458 --> 00:15:28,195 +to provide GPU accelerated +image processing to the video frame. + +271 +00:15:29,596 --> 00:15:33,066 +Please note that not all CIFilters +are compatible with EDR + +272 +00:15:33,100 --> 00:15:35,369 +and might have trouble with HDR content, + +273 +00:15:35,402 --> 00:15:38,772 +including clamping to SDR or worse. + +274 +00:15:38,805 --> 00:15:42,476 +Core Image provides +many EDR compatible Filters. + +275 +00:15:42,509 --> 00:15:46,180 +Use filter names with +CICategoryHighDynamicRange, + +276 +00:15:46,213 --> 00:15:49,917 +to enumerate EDR compatible +Core Image filters. + +277 +00:15:49,950 --> 00:15:53,787 +In our example, we will be adding +a simple sepia tone effect. + +278 +00:15:53,820 --> 00:15:58,025 +Now let's return to our example +and integrate Core Image. + +279 +00:15:58,058 --> 00:16:01,461 +On each display link callback +that yields a fresh CVPixelBuffer, + +280 +00:16:01,495 --> 00:16:04,398 +create a CIImage from that pixel buffer. + +281 +00:16:06,099 --> 00:16:09,369 +Instance the CIFilter +to implement the desired effect. + +282 +00:16:09,403 --> 00:16:13,574 +I am using the sepia tone filter +because of its parameter-less simplicity, + +283 +00:16:13,607 --> 00:16:16,810 +however there are many CIFilters +built into the system, + +284 +00:16:16,844 --> 00:16:20,314 +and it is straightforward +to write your own, too. + +285 +00:16:20,347 --> 00:16:24,685 +Set the CIFilter's inputImage +to the CIImage we just created. + +286 +00:16:26,153 --> 00:16:29,022 +And the processed video result +will be available + +287 +00:16:29,056 --> 00:16:32,326 +in the filter's outputImage. + +288 +00:16:32,359 --> 00:16:37,097 +Chain as many CIFilters together as are +required to achieve your desired effect. + +289 +00:16:37,130 --> 00:16:39,399 +Then use a CIRenderDestination + +290 +00:16:39,433 --> 00:16:42,970 +to render the resulting image +to your application's view code. + +291 +00:16:44,805 --> 00:16:50,043 +Please refer to the WWDC 2020 talk +"Optimize the Core Image pipeline for your video app" + +292 +00:16:50,077 --> 00:16:51,845 +to learn more about this workflow. + +293 +00:16:51,879 --> 00:16:55,883 +Another opportunity, is to process +and render the fresh CVPixelBuffer + +294 +00:16:55,916 --> 00:16:59,119 +using Metal and custom Metal shaders. + +295 +00:16:59,152 --> 00:17:02,523 +We will briefly describe the process +of converting the CVPixelBuffer + +296 +00:17:02,556 --> 00:17:04,057 +to a Metal texture. + +297 +00:17:04,091 --> 00:17:07,828 +However, implementing this conversion +maintaining best performance + +298 +00:17:07,861 --> 00:17:10,764 +is a deep topic +best left for another talk. + +299 +00:17:10,797 --> 00:17:13,400 +We instead recommend +deriving the Metal texture + +300 +00:17:13,433 --> 00:17:15,569 +from the CoreVideo Metal texture cache, + +301 +00:17:15,602 --> 00:17:19,640 +and will walk through that process +as the final example in this talk. + +302 +00:17:19,673 --> 00:17:24,711 +Generally speaking, the process is to get +the IOSurface from the CVPixelBuffer, + +303 +00:17:24,745 --> 00:17:27,014 +create a MetalTextureDescriptor, + +304 +00:17:27,047 --> 00:17:29,483 +and then create a MetalTexture +from the MetalDevice, + +305 +00:17:29,516 --> 00:17:32,319 +using `newTextureWithDescriptor`. + +306 +00:17:33,887 --> 00:17:37,324 +However, there is a danger +that the textures may be re-used, + +307 +00:17:37,357 --> 00:17:41,428 +and over-drawn, +if careful locking is not applied. + +308 +00:17:41,461 --> 00:17:45,799 +Further, not all PixelBuffer formats +are natively supported by MetalTexture, + +309 +00:17:45,832 --> 00:17:49,169 +which is why we use +half float in this example. + +310 +00:17:49,203 --> 00:17:51,972 +Because of these complications, +we instead recommend + +311 +00:17:52,005 --> 00:17:56,677 +directly accessing Metal textures from +Core Video, as we will now demonstrate. + +312 +00:17:56,710 --> 00:18:00,147 +Let's further explore Core Video +and Metal. + +313 +00:18:00,180 --> 00:18:03,851 +As mentioned, CVMetalTextureCache +is both a straightforward + +314 +00:18:03,884 --> 00:18:07,421 +and efficient way +to use CVPixelBuffers with Metal. + +315 +00:18:07,454 --> 00:18:10,757 +CVMetalTextureCache is handy +because you get a Metal texture + +316 +00:18:10,791 --> 00:18:14,862 +directly from the cache +without need for further conversion. + +317 +00:18:14,895 --> 00:18:18,031 +CVMetalTextureCache +automatically bridges between + +318 +00:18:18,065 --> 00:18:21,101 +CVPixelBuffer's, and MetalTexture's, + +319 +00:18:21,134 --> 00:18:26,073 +thereby both simplifying your code +and keeping you on the fast path. + +320 +00:18:26,106 --> 00:18:28,876 +In conjunction with CVPixelBufferPools, + +321 +00:18:28,909 --> 00:18:32,646 +CVMetalTextureCache +also provides performance benefits, + +322 +00:18:32,679 --> 00:18:36,116 +by keeping MTLTexture +to IOSurface mapping alive. + +323 +00:18:37,784 --> 00:18:41,054 +Finally, using CVMetalTextureCache +removes the need + +324 +00:18:41,088 --> 00:18:43,557 +to manually track IOSurfaces. + +325 +00:18:43,590 --> 00:18:46,293 +Now the final example in our talk: + +326 +00:18:46,326 --> 00:18:49,196 +how to extract Metal textures +directly from Core Video + +327 +00:18:49,229 --> 00:18:51,698 +using CVMetalTextureCache. + +328 +00:18:52,499 --> 00:18:55,469 +Here, we start by getting +the system default Metal device. + +329 +00:18:55,502 --> 00:18:58,071 +We use that to create +a Metal Texture Cache, + +330 +00:18:58,105 --> 00:19:01,341 +and then instantiate +a Core Video Metal Texture Cache + +331 +00:19:01,375 --> 00:19:04,111 +associated with the Metal Texture Cache. + +332 +00:19:04,144 --> 00:19:08,549 +That can then be used to access +decoded video frames as Metal Textures, + +333 +00:19:08,582 --> 00:19:13,353 +which conveniently, +can be directly used in our Metal engine. + +334 +00:19:13,387 --> 00:19:18,292 +In this example, we create and use +the Metal system default device. + +335 +00:19:18,325 --> 00:19:21,261 +Next we create the CVMetalTextureCache + +336 +00:19:21,295 --> 00:19:23,530 +with CVMetalTextureCacheCreate, + +337 +00:19:23,564 --> 00:19:26,967 +specifying the Metal device +we just created. + +338 +00:19:27,000 --> 00:19:29,970 +We get the height and width +of the CVPixelBuffer + +339 +00:19:30,003 --> 00:19:33,574 +needed to create the Core Video +Metal texture. + +340 +00:19:33,607 --> 00:19:37,511 +Then we call +`CVMetalTextureCacheCreateTextureFromImage`, + +341 +00:19:37,544 --> 00:19:40,247 +to instantiate a CVMetalTexture object + +342 +00:19:40,280 --> 00:19:43,750 +and associate that +with the CVPixelBuffer. + +343 +00:19:43,784 --> 00:19:46,653 +Finally we call +`CVMetalTextureGetTexture`, + +344 +00:19:46,687 --> 00:19:50,057 +to get the desired Metal texture. + +345 +00:19:50,090 --> 00:19:54,394 +Swift applications should use +a strong reference for CVMetalTexture, + +346 +00:19:54,428 --> 00:19:57,731 +however, when using Objective-C, +you must ensure that Metal is done + +347 +00:19:57,764 --> 00:20:01,835 +with your texture before you release +the CVMetalTextureRef. + +348 +00:20:01,869 --> 00:20:05,806 +This may be accomplished using +metal command buffer completion handlers. + +349 +00:20:07,674 --> 00:20:09,042 +And that's all, folks! + +350 +00:20:09,076 --> 00:20:11,678 +To review, +we explored a number of workflows + +351 +00:20:11,712 --> 00:20:14,715 +that will render your HDR video media +to EDR, + +352 +00:20:14,748 --> 00:20:17,684 +for playback, editing, +or image processing. + +353 +00:20:18,685 --> 00:20:23,190 +You learned how to go from AVPlayer to +AVKit's AVPlayerViewController, + +354 +00:20:23,223 --> 00:20:26,360 +for playback of HDR media. + +355 +00:20:26,393 --> 00:20:30,264 +You also learned how use AVPlayer, +along with AVPlayerLayer, + +356 +00:20:30,297 --> 00:20:34,234 +to display HDR media on your own view. + +357 +00:20:34,268 --> 00:20:38,505 +And finally, we explored how to add +real time effects during playback. + +358 +00:20:38,539 --> 00:20:42,009 +Connecting AVFoundation's AVPlayer +to CoreVideo + +359 +00:20:42,042 --> 00:20:44,178 +and then to Metal for rendering. + +360 +00:20:44,211 --> 00:20:46,947 +And applying real time effects +using CoreImage filters, + +361 +00:20:46,980 --> 00:20:49,082 +as well as Metal shaders. + +362 +00:20:51,852 --> 00:20:55,789 +If you want to dig deeper, +I recommend a few WWDC sessions + +363 +00:20:55,822 --> 00:20:58,258 +related to creating video workflows, + +364 +00:20:58,292 --> 00:21:02,429 +as well as integrating HDR media with EDR. + +365 +00:21:02,462 --> 00:21:04,565 +I especially want to call out the session + +366 +00:21:04,598 --> 00:21:08,368 +"Edit and play back HDR video +with AVFoundation". + +367 +00:21:08,402 --> 00:21:11,505 +This session explores use +of AVVideoComposition + +368 +00:21:11,538 --> 00:21:17,211 +with `applyingCIFiltersWithHandler` +for applying effects to your HDR media. + +369 +00:21:17,244 --> 00:21:20,414 +In this session you'll also learn +how to use custom compositor, + +370 +00:21:20,447 --> 00:21:23,183 +which can then be used +with a CVPixelBuffer, + +371 +00:21:23,217 --> 00:21:26,820 +when each video frame becomes +available for processing. + +372 +00:21:26,854 --> 00:21:29,857 +As I mentioned at the beginning, +this year we're also presenting + +373 +00:21:29,890 --> 00:21:33,827 +two other sessions on EDR: +"EDR on iOS," + +374 +00:21:33,861 --> 00:21:38,665 +where we announced EDR API support +has expanded to include iOS, + +375 +00:21:38,699 --> 00:21:44,204 +and "HDR content display with +EDR using CoreImage, Metal and SwiftUI," + +376 +00:21:44,238 --> 00:21:48,976 +where we further explore integrating +EDR with other media frameworks. + +377 +00:21:49,009 --> 00:21:52,946 +Hope you incorporate HDR video +into your EDR enabled applications + +378 +00:21:52,980 --> 00:21:56,116 +on both macOS and now iOS. + +379 +00:21:56,149 --> 00:21:57,751 +Thanks for watching. + diff --git a/eng/2022 Session 110929 Monday@WWDC22 en.srt b/eng/2022 Session 110929 Monday@WWDC22 en.srt new file mode 100644 index 0000000..153ca88 --- /dev/null +++ b/eng/2022 Session 110929 Monday@WWDC22 en.srt @@ -0,0 +1,318 @@ +1 +00:00:00,000 --> 00:00:05,672 +♪ Bass-heavy electronic music ♪ + +2 +00:00:05,672 --> 00:00:07,474 +Serenity Caldwell: +Welcome to HQ. + +3 +00:00:07,474 --> 00:00:08,642 +I'm Serenity Caldwell; + +4 +00:00:08,642 --> 00:00:12,346 +here's your day one +debrief of WWDC. + +5 +00:00:12,346 --> 00:00:14,781 +iOS 16 is now mission-ready, + +6 +00:00:14,781 --> 00:00:17,050 +starting with +the reimagined Lock Screen. + +7 +00:00:17,050 --> 00:00:19,720 +Choose from countless fonts, +colors, and widgets + +8 +00:00:19,720 --> 00:00:22,089 +to personalize your iPhone. + +9 +00:00:22,089 --> 00:00:24,558 +Want to set boundaries between +work and home life? + +10 +00:00:24,558 --> 00:00:26,393 +Activate Personal Focus. + +11 +00:00:26,393 --> 00:00:28,362 +And if you've ever sent +an embarrassing message, + +12 +00:00:28,362 --> 00:00:29,963 +now you can fix that. + +13 +00:00:29,963 --> 00:00:33,967 +Or better still, erase it +before anyone spots it. [POP] + +14 +00:00:33,967 --> 00:00:36,470 +With SharePlay you can actually +watch movies and shows + +15 +00:00:36,470 --> 00:00:38,739 +with friends while messaging. +Ted Lasso: Oh, I like this. + +16 +00:00:38,739 --> 00:00:40,107 +Serenity: Want to send a picture +of your best friend + +17 +00:00:40,107 --> 00:00:41,575 +without the background? + +18 +00:00:41,575 --> 00:00:44,278 +Just touch, lift, and send. +[BARK] + +19 +00:00:44,278 --> 00:00:46,580 +Sharing photos with family +just got a lot easier with... + +20 +00:00:46,580 --> 00:00:48,916 +Craig Federighi: ...iCloud +Shared Photo Library. + +21 +00:00:48,916 --> 00:00:52,152 +Serenity: We also got a sneak +peek at the future of CarPlay. + +22 +00:00:52,152 --> 00:00:54,321 +♪ + +23 +00:00:54,321 --> 00:00:56,623 +And arriving in Maps, at last, + +24 +00:00:56,623 --> 00:00:59,159 +the ability to plan +up to 15 stops. + +25 +00:00:59,159 --> 00:01:02,162 +Say hello to some new +Apple Watch features. + +26 +00:01:02,162 --> 00:01:04,798 +There are new Workout views +like heart rate zones. + +27 +00:01:04,798 --> 00:01:06,967 +We made it easier +to customize your workout + +28 +00:01:06,967 --> 00:01:09,102 +and added reminders +to take your meds. + +29 +00:01:09,102 --> 00:01:11,338 +And there's a range +of new watch faces: + +30 +00:01:11,338 --> 00:01:15,642 +Astronomy, Lunar, +Playtime, Metropolitan, + +31 +00:01:15,642 --> 00:01:19,746 +and of course, +we can't forget [BARK] Buddy! + +32 +00:01:19,746 --> 00:01:22,082 +The next generation +of Apple silicon is here. + +33 +00:01:22,082 --> 00:01:23,817 +Meet the mighty M2. + +34 +00:01:23,817 --> 00:01:26,820 +We took all this power +and used it to supercharge + +35 +00:01:26,820 --> 00:01:27,921 +the new MacBook Air. + +36 +00:01:27,921 --> 00:01:30,791 +It's thinner, lighter, +and faster than ever. + +37 +00:01:30,791 --> 00:01:35,028 +We're even bringing M2 +to 13-inch MacBook Pro. + +38 +00:01:35,028 --> 00:01:37,531 +We launched +the newest macOS, Ventura, + +39 +00:01:37,531 --> 00:01:40,801 +with big upgrades to Spotlight +like Enhanced Image Search, + +40 +00:01:40,801 --> 00:01:42,836 +and we introduced... +Craig: ...Stage Manager. + +41 +00:01:42,836 --> 00:01:44,938 +Serenity: Now working between +different windows and apps + +42 +00:01:44,938 --> 00:01:46,940 +is faster and easier. + +43 +00:01:46,940 --> 00:01:50,510 +And you can Schedule Send +and Undo Send emails. + +44 +00:01:50,510 --> 00:01:53,113 +You asked, we delivered. + +45 +00:01:53,113 --> 00:01:55,248 +Introducing Continuity Camera. + +46 +00:01:55,248 --> 00:01:57,217 +Now you can use +your iPhone as a webcam + +47 +00:01:57,217 --> 00:01:59,486 +in ways that were +never possible before. + +48 +00:01:59,486 --> 00:02:01,922 +Don't look now, it's... +Craig: ...iPadOS 16. + +49 +00:02:01,922 --> 00:02:03,790 +Serenity: For the first time, +you can work on multiple + +50 +00:02:03,790 --> 00:02:06,059 +overlapping windows +with Stage Manager. + +51 +00:02:06,059 --> 00:02:07,561 +Craig: And it gets even better. + +52 +00:02:07,561 --> 00:02:09,930 +Serenity: We made collaborating +easier across different apps + +53 +00:02:09,930 --> 00:02:12,366 +and devices, +and coming later this year, + +54 +00:02:12,366 --> 00:02:15,168 +a new app +for live collaboration. + +55 +00:02:15,168 --> 00:02:17,971 +We added new APIs +to all Apple platforms, + +56 +00:02:17,971 --> 00:02:20,340 +brought in top-notch weather +data with WeatherKit, + +57 +00:02:20,340 --> 00:02:23,877 +gave developers more power +to build games with Metal 3, + +58 +00:02:23,877 --> 00:02:26,546 +Made SwiftUI +more powerful and flexible, + +59 +00:02:26,546 --> 00:02:28,882 +raised the bar +with Swift Charts, + +60 +00:02:28,882 --> 00:02:30,450 +Introduced Swift Regex, + +61 +00:02:30,450 --> 00:02:33,653 +and opened Xcode Cloud +to all developers. + +62 +00:02:33,653 --> 00:02:36,890 +And congrats to the 12 winners +of the Apple Design Awards + +63 +00:02:36,890 --> 00:02:39,526 +and all our finalists. + +64 +00:02:39,526 --> 00:02:41,595 +Here's a look at day two. + +65 +00:02:41,595 --> 00:02:47,734 +♪ + +66 +00:02:47,734 --> 00:02:49,603 +That's all for now. + +67 +00:02:49,603 --> 00:02:52,172 +Be sure to report back tomorrow. + +68 +00:02:52,172 --> 00:02:59,012 +♪ + diff --git a/eng/2022 Session 110930 Tuesday@WWDC22 en.srt b/eng/2022 Session 110930 Tuesday@WWDC22 en.srt new file mode 100644 index 0000000..7cd3193 --- /dev/null +++ b/eng/2022 Session 110930 Tuesday@WWDC22 en.srt @@ -0,0 +1,134 @@ +1 +00:00:00,000 --> 00:00:03,337 +♪ Bass-heavy electronic music ♪ + +2 +00:00:03,337 --> 00:00:05,806 +Ready for your day two briefing? + +3 +00:00:05,806 --> 00:00:09,042 +Let's jump right in, shall we? + +4 +00:00:09,042 --> 00:00:10,577 +First, we cooked up new ways + +5 +00:00:10,577 --> 00:00:13,247 +to navigate within +your SwiftUI apps. + +6 +00:00:13,247 --> 00:00:15,716 +- This recipe is our +pièce de résistance. + +7 +00:00:15,716 --> 00:00:19,519 +Then we learned more about +the new features in Xcode 14. + +8 +00:00:19,519 --> 00:00:21,188 +- You can choose +the level of detail + +9 +00:00:21,188 --> 00:00:22,789 +that's right for you. + +10 +00:00:22,789 --> 00:00:24,625 +And Jared showed us +new plug-ins + +11 +00:00:24,625 --> 00:00:27,995 +for adding Apple frameworks +to your Unity game projects. + +12 +00:00:27,995 --> 00:00:30,998 +- The Core Haptics Unity plug-in +gives you the tools you need + +13 +00:00:30,998 --> 00:00:34,501 +to add an entirely new level +of immersion into your games. + +14 +00:00:34,501 --> 00:00:37,237 +- And there's a brand-new +way to do 3D scans + +15 +00:00:37,237 --> 00:00:38,572 +with RoomPlan. + +16 +00:00:38,572 --> 00:00:40,474 +- Apple has enabled +powerful new ways + +17 +00:00:40,474 --> 00:00:43,043 +for people to bring the world +into their apps. + +18 +00:00:43,043 --> 00:00:44,945 +And then Paul showed +developers how they can + +19 +00:00:44,945 --> 00:00:49,049 +use color to make SF Symbols +even more expressive. + +20 +00:00:49,049 --> 00:00:51,885 +And we introduced the new +Push to Talk framework for iOS. + +21 +00:00:51,885 --> 00:00:52,886 +[CHIME] +- Hey Trevor. + +22 +00:00:52,886 --> 00:00:55,355 +Are you ready to cover +your WWDC slides? [CHIME] + +23 +00:00:55,355 --> 00:00:56,089 +- Hey Kevin. + +24 +00:00:56,089 --> 00:00:57,891 +I'll be ready in a few minutes. +[CHIME] + +25 +00:00:57,891 --> 00:01:00,227 +Can you hear me? +Over. + +26 +00:01:00,227 --> 00:01:02,462 +Here's some intel for tomorrow. + +27 +00:01:02,462 --> 00:01:13,540 +♪ + +28 +00:01:13,540 --> 00:01:16,009 +Developers, we'll be in contact. + +29 +00:01:16,009 --> 00:01:21,682 +♪ + diff --git a/eng/2022 Session 110931 Wednesday@WWDC22 en.srt b/eng/2022 Session 110931 Wednesday@WWDC22 en.srt new file mode 100644 index 0000000..0ddb3f6 --- /dev/null +++ b/eng/2022 Session 110931 Wednesday@WWDC22 en.srt @@ -0,0 +1,135 @@ +1 +00:00:00,000 --> 00:00:03,570 +♪ Bass-heavy electronic music ♪ + +2 +00:00:03,570 --> 00:00:05,205 +Now that I have +your attention, + +3 +00:00:05,205 --> 00:00:07,341 +here's your day three +status report. + +4 +00:00:07,341 --> 00:00:09,643 +♪ + +5 +00:00:09,643 --> 00:00:11,311 +We started our training +with Lynn + +6 +00:00:11,311 --> 00:00:14,314 +exploring new ways +to design shortcuts for apps. + +7 +00:00:14,314 --> 00:00:15,816 +- You're well on your way +to an amazing + +8 +00:00:15,816 --> 00:00:17,050 +App Shortcuts experience. + +9 +00:00:17,050 --> 00:00:19,887 +- Then we discovered how +to make desktop-class iPad apps + +10 +00:00:19,887 --> 00:00:20,888 +with Mohammed. + +11 +00:00:20,888 --> 00:00:22,923 +- This allows apps +to express the layout + +12 +00:00:22,923 --> 00:00:24,925 +that's most appropriate +to their content, + +13 +00:00:24,925 --> 00:00:26,727 +while bringing +more functionality + +14 +00:00:26,727 --> 00:00:27,995 +to the forefront of the UI. + +15 +00:00:27,995 --> 00:00:29,596 +- Then found out +how to take advantage + +16 +00:00:29,596 --> 00:00:32,099 +of the latest iOS +camera capture features. + +17 +00:00:32,099 --> 00:00:33,867 +- This app has one more trick +up its sleeve. + +18 +00:00:33,867 --> 00:00:35,669 +I can tap around +and point a spotlight + +19 +00:00:35,669 --> 00:00:36,904 +at different objects +in the scene. + +20 +00:00:36,904 --> 00:00:38,772 +- We also showed off +powerful tools + +21 +00:00:38,772 --> 00:00:42,442 +to create cool new layouts +and views for apps with SwiftUI. + +22 +00:00:42,442 --> 00:00:44,845 +- SwiftUI provides +a rich set of building blocks + +23 +00:00:44,845 --> 00:00:47,414 +that you use to compose +your app's interface. + +24 +00:00:47,414 --> 00:00:50,350 +- Tomorrow's reports +look something like this. + +25 +00:00:50,350 --> 00:01:10,938 +♪ + +26 +00:01:10,938 --> 00:01:12,072 +- Go get 'em, tiger. + +27 +00:01:12,072 --> 00:01:13,674 +- That's all for now. + +28 +00:01:13,674 --> 00:01:15,976 +Await further instructions. + +29 +00:01:15,976 --> 00:01:20,981 +♪ + diff --git a/eng/2022 Session 110932 WWDC22 Day 4 recap en.srt b/eng/2022 Session 110932 WWDC22 Day 4 recap en.srt new file mode 100644 index 0000000..657a065 --- /dev/null +++ b/eng/2022 Session 110932 WWDC22 Day 4 recap en.srt @@ -0,0 +1,135 @@ +1 +00:00:00,000 --> 00:00:05,005 +♪ Bass-heavy electronic music ♪ + +2 +00:00:05,005 --> 00:00:08,208 +It's time to review day four. + +3 +00:00:08,208 --> 00:00:10,777 +Roll the tape! + +4 +00:00:10,777 --> 00:00:12,446 +We checked in with Raj +on how you can + +5 +00:00:12,446 --> 00:00:15,182 +organize your iPad interface +with SwiftUI. + +6 +00:00:15,182 --> 00:00:18,252 +- Better support for +state restoration, deep linking, + +7 +00:00:18,252 --> 00:00:20,487 +and even richer +programmatic control. + +8 +00:00:20,487 --> 00:00:21,822 +- Cool. + +9 +00:00:21,822 --> 00:00:24,625 +- Then Sara showed us how +to bring the power of SwiftUI + +10 +00:00:24,625 --> 00:00:26,493 +to your UIKit apps. + +11 +00:00:26,493 --> 00:00:28,061 +- You can use +the hosting controller + +12 +00:00:28,061 --> 00:00:30,430 +anywhere you can use +a view controller in UIKit. + +13 +00:00:30,430 --> 00:00:31,798 +- Later on, +Brett took us through + +14 +00:00:31,798 --> 00:00:33,166 +the latest updates for Vision. + +15 +00:00:33,166 --> 00:00:35,669 +- Here we compute optical flow +for a short clip + +16 +00:00:35,669 --> 00:00:38,538 +of this dog fetching +a water bottle on a beach. + +17 +00:00:38,538 --> 00:00:39,873 +- And we discovered +how to create + +18 +00:00:39,873 --> 00:00:42,576 +compelling +SharePlay experiences. + +19 +00:00:42,576 --> 00:00:44,578 +- Think of SharePlay +like a portal, + +20 +00:00:44,578 --> 00:00:46,580 +transporting people +through their phones + +21 +00:00:46,580 --> 00:00:48,181 +and into the same space. + +22 +00:00:48,181 --> 00:00:49,983 +- We also got a lesson +in language + +23 +00:00:49,983 --> 00:00:51,785 +from Apple's interface writers. + +24 +00:00:51,785 --> 00:00:54,354 +- Writing for interfaces +begins with curiosity + +25 +00:00:54,354 --> 00:00:56,790 +for who's on the other side +of the screen. + +26 +00:00:56,790 --> 00:00:59,760 +- And now for the intel +on day five. + +27 +00:00:59,760 --> 00:01:10,971 +♪ + +28 +00:01:10,971 --> 00:01:13,440 +Check back in tomorrow +for our final report. + +29 +00:01:13,440 --> 00:01:19,613 +♪ + diff --git a/eng/2022 Session 110933 WWDC22 Day 5 recap en.srt b/eng/2022 Session 110933 WWDC22 Day 5 recap en.srt new file mode 100644 index 0000000..e1c008d --- /dev/null +++ b/eng/2022 Session 110933 WWDC22 Day 5 recap en.srt @@ -0,0 +1,128 @@ +1 +00:00:00,000 --> 00:00:03,437 +♪ Bass-heavy electronic music ♪ + +2 +00:00:03,437 --> 00:00:05,472 +It's time for your final report. + +3 +00:00:05,472 --> 00:00:07,774 +♪ + +4 +00:00:07,774 --> 00:00:10,277 +First, we covered how to improve +battery consumption + +5 +00:00:10,277 --> 00:00:11,879 +for your apps. + +6 +00:00:11,879 --> 00:00:13,947 +- Display is one +of the major sources + +7 +00:00:13,947 --> 00:00:15,048 +of power consumption. + +8 +00:00:15,048 --> 00:00:16,850 +- We learned how to make +a Swift Server app + +9 +00:00:16,850 --> 00:00:18,952 +alongside an iOS app +in Xcode. + +10 +00:00:18,952 --> 00:00:21,455 +- Using Swift for building +server components + +11 +00:00:21,455 --> 00:00:23,090 +helps bridge +this technology gap. + +12 +00:00:23,090 --> 00:00:25,325 +- Then Neil showed us +how to match audio catalogs + +13 +00:00:25,325 --> 00:00:27,160 +with video +using ShazamKit. + +14 +00:00:27,160 --> 00:00:28,829 +- Now we have some +important updates + +15 +00:00:28,829 --> 00:00:32,199 +that streamline working +with custom catalogs at scale. + +16 +00:00:32,199 --> 00:00:34,434 +- We discovered how +to create multiple windows + +17 +00:00:34,434 --> 00:00:35,602 +for our iPad apps. + +18 +00:00:35,602 --> 00:00:36,770 +- Here's an app I've built + +19 +00:00:36,770 --> 00:00:38,572 +to keep track of the books +I'm reading. + +20 +00:00:38,572 --> 00:00:39,873 +- And we closed out the week + +21 +00:00:39,873 --> 00:00:43,844 +with a friendly device-to-device +game of tic-tac-toe. + +22 +00:00:43,844 --> 00:00:47,014 +- And that's how easy it is +to connect devices to Apple TV. + +23 +00:00:47,014 --> 00:00:49,483 +- Our training is complete, +developers. + +24 +00:00:49,483 --> 00:00:51,585 +You're ready for your mission. + +25 +00:00:51,585 --> 00:00:54,988 +This message will self-destruct +in 3... 2... + +26 +00:00:54,988 --> 00:00:56,490 +Ah, just kidding! + +27 +00:00:56,490 --> 00:00:57,991 +See you next year! + +28 +00:00:57,991 --> 00:01:03,964 +♪ + diff --git a/eng/2022 Session 112 Platforms State of the Union (ASL) en.srt b/eng/2022 Session 112 Platforms State of the Union (ASL) en.srt new file mode 100644 index 0000000..8c3dbe4 --- /dev/null +++ b/eng/2022 Session 112 Platforms State of the Union (ASL) en.srt @@ -0,0 +1,6383 @@ +1 +00:00:00,334 --> 00:00:07,641 +♪ ♪ + +2 +00:00:09,243 --> 00:00:17,251 +♪ ♪ + +3 +00:00:22,356 --> 00:00:24,858 +Susan Prescott: Welcome +to the Platform State of the Union + +4 +00:00:24,892 --> 00:00:27,895 +for WWDC 2022. + +5 +00:00:27,928 --> 00:00:31,532 +We're always excited for WWDC +because it's an opportunity + +6 +00:00:31,565 --> 00:00:33,634 +for us to connect with all of you, + +7 +00:00:33,667 --> 00:00:35,636 +share some news +about what we've been working on, + +8 +00:00:35,669 --> 00:00:39,973 +and better understand what you need +from our developer platforms. + +9 +00:00:40,007 --> 00:00:43,510 +What you do as developers is amazing. + +10 +00:00:43,544 --> 00:00:48,048 +You transform your ideas, +the stuff of imagination, + +11 +00:00:48,081 --> 00:00:51,618 +to take users' experiences +to another level. + +12 +00:00:51,652 --> 00:00:55,489 +And we want to help you +take your ideas even further. + +13 +00:00:55,522 --> 00:00:58,625 +In the Keynote, we already talked +about some new capabilities + +14 +00:00:58,659 --> 00:01:00,360 +across iPhone, iPad, Mac, + +15 +00:01:00,394 --> 00:01:02,329 +Apple Watch, and Apple TV, + +16 +00:01:02,362 --> 00:01:05,299 +and the incredible power +of Apple silicon + +17 +00:01:05,332 --> 00:01:09,102 +to help bring even the most ambitious +ideas to life. + +18 +00:01:09,136 --> 00:01:13,273 +We have a lot to cover today. +Let's start with some updates. + +19 +00:01:13,307 --> 00:01:17,711 +Earlier this year, we opened +the brand-new Apple Developer Center, + +20 +00:01:17,744 --> 00:01:20,614 +a place designed for you to connect +and collaborate + +21 +00:01:20,647 --> 00:01:24,518 +with Apple engineers and designers +right here at Apple Park. + +22 +00:01:24,551 --> 00:01:28,488 +And last fall, thousands of you +from all over the world + +23 +00:01:28,522 --> 00:01:30,691 +attended our new online Tech Talks, + +24 +00:01:30,724 --> 00:01:34,361 +with hundreds of live sessions +originating in 11 countries + +25 +00:01:34,394 --> 00:01:36,496 +and in five languages. + +26 +00:01:36,530 --> 00:01:38,932 +For us, one of the best parts +of the Tech Talk series + +27 +00:01:38,966 --> 00:01:40,334 +was one to one meetings, + +28 +00:01:40,367 --> 00:01:43,904 +which were a great opportunity +to hear about what you're up to + +29 +00:01:43,937 --> 00:01:46,807 +and share some advice and guidance. + +30 +00:01:46,840 --> 00:01:49,376 +Last fall, Swift Playgrounds 4 shipped + +31 +00:01:49,409 --> 00:01:53,280 +with the power to build apps and submit +them directly to the App Store + +32 +00:01:53,313 --> 00:01:55,182 +and support for SwiftUI, + +33 +00:01:55,215 --> 00:01:59,686 +making it an incredible tool for +experimentation and UI prototyping. + +34 +00:01:59,720 --> 00:02:02,523 +And of course, there's Xcode Cloud. + +35 +00:02:02,556 --> 00:02:04,157 +We built Xcode Cloud to help you + +36 +00:02:04,191 --> 00:02:07,327 +build better apps +faster and more easily. + +37 +00:02:07,361 --> 00:02:09,663 +It's a continuous integration +and delivery service + +38 +00:02:09,696 --> 00:02:14,268 +built right into Xcode +and hosted in the Cloud. + +39 +00:02:14,301 --> 00:02:18,238 +Xcode Cloud supports development +for all Apple platforms. + +40 +00:02:18,272 --> 00:02:21,175 +It integrates with TestFlight +and App Store Connect + +41 +00:02:21,208 --> 00:02:24,578 +as well as every major git based +source control provider. + +42 +00:02:24,611 --> 00:02:28,015 +It even has REST APIs +to help connect to other aspects + +43 +00:02:28,048 --> 00:02:29,483 +of your development workflow. + +44 +00:02:30,217 --> 00:02:34,154 +It's built with advanced security +to protect you and your projects. + +45 +00:02:34,188 --> 00:02:36,557 +And I'm delighted to say + +46 +00:02:36,590 --> 00:02:38,892 +that Xcode Cloud is available + +47 +00:02:38,926 --> 00:02:40,527 +starting today. + +48 +00:02:41,662 --> 00:02:44,031 +We think nearly every development team + +49 +00:02:44,064 --> 00:02:45,732 +can benefit from Xcode Cloud, + +50 +00:02:45,766 --> 00:02:49,937 +and we've priced it to be accessible +to developers of all sizes. + +51 +00:02:49,970 --> 00:02:53,273 +We're offering +the 25 hour per month subscription + +52 +00:02:53,307 --> 00:02:59,179 +free to all Apple Developer Program +members through the end of 2023! + +53 +00:02:59,213 --> 00:03:03,217 +And you'll be able to subscribe to any +of the Xcode Cloud subscription levels + +54 +00:03:03,250 --> 00:03:06,353 +in the Developer app later this Summer. + +55 +00:03:06,386 --> 00:03:10,824 +Today we're gonna talk +about three big topics. + +56 +00:03:10,858 --> 00:03:15,796 +First, we want to share more about our +vision for developing for Apple platforms, + +57 +00:03:15,829 --> 00:03:19,600 +where we are with our platforms, +and where we're headed. + +58 +00:03:19,633 --> 00:03:23,237 +Then, we'll share some compelling +new ways your apps can integrate + +59 +00:03:23,270 --> 00:03:26,340 +with the system experience +on Apple platforms. + +60 +00:03:26,373 --> 00:03:30,244 +And finally, +we'll discuss some powerful new APIs + +61 +00:03:30,277 --> 00:03:33,847 +and show you how they can enable you +to do even more ambitious things + +62 +00:03:33,881 --> 00:03:34,882 +with your apps. + +63 +00:03:34,915 --> 00:03:37,784 +Let's start with the vision +for our developer platform + +64 +00:03:37,818 --> 00:03:39,286 +and how it's been evolving. + +65 +00:03:39,319 --> 00:03:41,822 +Josh is here to tell you all about it., + +66 +00:03:43,524 --> 00:03:46,760 +Josh Shaffer: A great developer platform +provides tight integration + +67 +00:03:46,793 --> 00:03:49,897 +between programming language, +frameworks and tools. + +68 +00:03:49,930 --> 00:03:52,432 +When all three +fully complement one another, + +69 +00:03:52,466 --> 00:03:55,068 +we can ensure that common things are easy, + +70 +00:03:55,102 --> 00:03:57,337 +and even uncommon things are possible. + +71 +00:03:57,938 --> 00:04:01,375 +Now, getting that right shortens the path +to building a great app, + +72 +00:04:01,408 --> 00:04:03,410 +and it benefits everyone. + +73 +00:04:03,443 --> 00:04:06,146 +Customers get a consistent experience, + +74 +00:04:06,180 --> 00:04:08,916 +like scrolling that always feels perfect. + +75 +00:04:08,949 --> 00:04:13,620 +And you're able to focus your time +and effort on what makes your app unique. + +76 +00:04:13,654 --> 00:04:16,657 +But designs evolve, hardware advances, + +77 +00:04:16,690 --> 00:04:20,561 +and what was once cutting edge +becomes the expected baseline. + +78 +00:04:20,594 --> 00:04:24,064 +The Objective-C language, +AppKit & UIKit frameworks, + +79 +00:04:24,097 --> 00:04:28,001 +and Interface Builder +have empowered generations of developers. + +80 +00:04:28,035 --> 00:04:30,571 +These technologies +were built for each other, + +81 +00:04:30,604 --> 00:04:33,907 +and will continue to serve us well +for a long time to come, + +82 +00:04:33,941 --> 00:04:37,611 +but over time +new abstractions become necessary. + +83 +00:04:37,644 --> 00:04:41,648 +For a while now, you've seen us +hard at work defining the next generation + +84 +00:04:41,682 --> 00:04:45,185 +of integrated language, +frameworks, and tools: + +85 +00:04:45,219 --> 00:04:49,089 +Swift, SwiftUI, and Xcode Previews. + +86 +00:04:50,057 --> 00:04:52,659 +Tight integration +in a development platform like this + +87 +00:04:52,693 --> 00:04:56,563 +requires that all three pieces +be designed and evolved together, + +88 +00:04:56,597 --> 00:04:59,666 +both driving and driven by one another. + +89 +00:04:59,700 --> 00:05:04,972 +Swift result builders were inspired +by SwiftUI's compositional structure. + +90 +00:05:05,005 --> 00:05:10,110 +SwiftUI's declarative views +were enabled by Swift value types. + +91 +00:05:10,143 --> 00:05:15,916 +And Xcode Previews was specifically +designed for, and enabled by, both. + +92 +00:05:15,949 --> 00:05:20,120 +Now, the result is the best development +platform that we have ever built. + +93 +00:05:20,153 --> 00:05:25,626 +And this year, Swift, SwiftUI, and Xcode +all have fantastic updates + +94 +00:05:25,659 --> 00:05:27,661 +that take this vision further, + +95 +00:05:27,694 --> 00:05:31,965 +and make it even easier for you to build +great apps for all of our platforms. + +96 +00:05:31,999 --> 00:05:34,134 +And it all starts with Swift. + +97 +00:05:34,168 --> 00:05:37,938 +Now Ben from the Swift team +is gonna tell you all about what's next. + +98 +00:05:40,107 --> 00:05:48,115 +♪ ♪ + +99 +00:05:49,850 --> 00:05:53,120 +Ben Cohen: +Swift is fast, modern, and safe. + +100 +00:05:53,153 --> 00:05:56,156 +It combines the speed +of a strongly-typed language, + +101 +00:05:56,190 --> 00:05:59,193 +with an expressive syntax +that's easy to read and write. + +102 +00:05:59,226 --> 00:06:03,163 +And its design eliminates +entire categories of programming errors. + +103 +00:06:03,197 --> 00:06:07,267 +Swift is the absolute best language +for building apps across our devices. + +104 +00:06:08,101 --> 00:06:09,736 +Swift is also open source, + +105 +00:06:09,770 --> 00:06:11,638 +with an amazing community of contributors + +106 +00:06:11,672 --> 00:06:13,607 +organized at swift.org, + +107 +00:06:13,640 --> 00:06:16,743 +supporting one another through +initiatives like Diversity in Swift + +108 +00:06:16,777 --> 00:06:18,412 +and the Swift Mentorship Program, + +109 +00:06:18,445 --> 00:06:20,147 +and advancing the language + +110 +00:06:20,180 --> 00:06:22,716 +with working groups on topics +like Swift on server, + +111 +00:06:22,749 --> 00:06:24,585 +and C++ interoperability. + +112 +00:06:25,552 --> 00:06:28,121 +Over the last year, +Swift has gotten even better, + +113 +00:06:28,155 --> 00:06:30,424 +with enhancements in concurrency, + +114 +00:06:30,457 --> 00:06:33,460 +upgrades to make Swift code +easier to read and write, + +115 +00:06:33,493 --> 00:06:35,529 +tooling to help you +customize your workflow, + +116 +00:06:35,562 --> 00:06:38,131 +and amazing under-the-hood improvements. + +117 +00:06:38,165 --> 00:06:39,666 +It started last year, + +118 +00:06:39,700 --> 00:06:41,735 +with the introduction +of Swift Concurrency. + +119 +00:06:42,369 --> 00:06:46,073 +Swift Concurrency dramatically simplified +reading and writing code + +120 +00:06:46,106 --> 00:06:49,009 +that runs in parallel, +and has been a huge hit, + +121 +00:06:49,042 --> 00:06:50,978 +with more than 40,000 apps +in the App Store + +122 +00:06:51,011 --> 00:06:52,980 +adopting it in just the first year. + +123 +00:06:53,447 --> 00:06:56,383 +Because it's such a fundamental +and important improvement + +124 +00:06:56,416 --> 00:06:58,118 +to your app's code base, + +125 +00:06:58,151 --> 00:07:00,621 +it's now possible to deploy code +with Swift Concurrency + +126 +00:07:00,654 --> 00:07:04,625 +to all operating systems +released in the last three years. + +127 +00:07:04,658 --> 00:07:08,729 +Swift Concurrency also introduced +async sequences. + +128 +00:07:08,762 --> 00:07:11,431 +This year, there's a new, +open-source package + +129 +00:07:11,465 --> 00:07:15,769 +that brings concurrency to Swift's +rich set of existing sequence algorithms. + +130 +00:07:15,802 --> 00:07:18,138 +It's called async algorithms. + +131 +00:07:18,172 --> 00:07:20,307 +For example, +where Swift's sequence protocol + +132 +00:07:20,340 --> 00:07:23,610 +supports a zip algorithm +to combine two sequences, + +133 +00:07:23,644 --> 00:07:26,446 +async algorithms brings a version +for zipping together + +134 +00:07:26,480 --> 00:07:28,282 +two asynchronous sequences. + +135 +00:07:28,315 --> 00:07:32,085 +Because async sequences are integrated +directly into the Swift language, + +136 +00:07:32,119 --> 00:07:34,955 +they use familiar constructs +like 'for' loops + +137 +00:07:34,988 --> 00:07:37,024 +that, thanks to the async/await syntax, + +138 +00:07:37,057 --> 00:07:39,026 +looks like regular straight-line code. + +139 +00:07:39,059 --> 00:07:42,062 +You're also able to use +the familiar try/catch pattern + +140 +00:07:42,095 --> 00:07:44,031 +to handle things like network failures + +141 +00:07:44,064 --> 00:07:46,834 +from asynchronous data streaming +over the network. + +142 +00:07:46,867 --> 00:07:49,570 +A key thing about async sequences + +143 +00:07:49,603 --> 00:07:52,172 +is how they deliver data values over time. + +144 +00:07:52,940 --> 00:07:57,511 +Swift now includes a new set of +clock types for representing time units, + +145 +00:07:57,544 --> 00:08:01,882 +and async algorithms builds on them +to provide many time-based algorithms, + +146 +00:08:01,915 --> 00:08:03,383 +like throttle here, + +147 +00:08:03,417 --> 00:08:06,987 +which can help slow down updates +from a sequence. + +148 +00:08:07,020 --> 00:08:11,391 +Swift's concurrency model is designed +to make asynchronous code as easy + +149 +00:08:11,425 --> 00:08:14,161 +and safe to write +as your synchronous code. + +150 +00:08:14,194 --> 00:08:17,064 +A big part of that is Swift's actor model. + +151 +00:08:17,097 --> 00:08:20,000 +Actors allow you to isolate your data + +152 +00:08:20,033 --> 00:08:23,537 +using thread-safe, +concurrently-executing code. + +153 +00:08:23,570 --> 00:08:26,440 +Swift prevents you from +accidentally sharing that state + +154 +00:08:26,473 --> 00:08:27,941 +between parallel threads, + +155 +00:08:27,975 --> 00:08:30,511 +defining away a major source of bugs. + +156 +00:08:31,411 --> 00:08:35,849 +Communication between actors is +easy and efficient through async/await. + +157 +00:08:35,883 --> 00:08:39,653 +Now, Swift takes the idea +of actor isolation further + +158 +00:08:39,686 --> 00:08:41,421 +with distributed actors. + +159 +00:08:41,455 --> 00:08:46,059 +Distributed actors can communicate +across multiple processes or devices. + +160 +00:08:46,093 --> 00:08:49,129 +The "distributed" keyword +marks these actors + +161 +00:08:49,162 --> 00:08:51,632 +and methods that can be accessed remotely, + +162 +00:08:51,665 --> 00:08:54,735 +whether that's between +separate processes on your Mac, + +163 +00:08:54,768 --> 00:08:57,104 +peer to peer between different devices, + +164 +00:08:57,137 --> 00:09:00,741 +or from a device talking to your backend +written with Swift on Server. + +165 +00:09:02,109 --> 00:09:05,012 +Just as actors help Swift +protect your state data + +166 +00:09:05,045 --> 00:09:06,280 +from race conditions, + +167 +00:09:06,313 --> 00:09:10,551 +distributed actors help Swift make them +available outside your process, + +168 +00:09:10,584 --> 00:09:12,986 +using a pluggable transport mechanism. + +169 +00:09:13,020 --> 00:09:16,657 +The Swift compiler can then +perform checks that help guarantee + +170 +00:09:16,690 --> 00:09:19,359 +correct behavior +in a distributed environment, + +171 +00:09:19,393 --> 00:09:23,063 +allowing you to get back to working +on the features that you care about. + +172 +00:09:23,497 --> 00:09:26,600 +Distributed actors +and other concurrency features + +173 +00:09:26,633 --> 00:09:30,070 +show just how easy Swift code can be +to read and write + +174 +00:09:30,103 --> 00:09:33,307 +when enhancements +are crafted deep within the syntax. + +175 +00:09:33,340 --> 00:09:36,543 +To tell you more +about usability enhancements in Swift, + +176 +00:09:36,577 --> 00:09:37,878 +here's Ken. + +177 +00:09:38,879 --> 00:09:40,781 +Ken Orr: Strings are one +of the most important features + +178 +00:09:40,814 --> 00:09:42,516 +of any programming language. + +179 +00:09:42,549 --> 00:09:45,853 +But dealing with strings can +be a common source of frustration. + +180 +00:09:46,653 --> 00:09:48,522 +At some point in a developer's journey, + +181 +00:09:48,555 --> 00:09:50,824 +they may find themselves +needing to extract information + +182 +00:09:50,858 --> 00:09:52,359 +from a string like this. + +183 +00:09:53,493 --> 00:09:56,997 +Writing code to parse the string +is easy to get wrong, + +184 +00:09:57,030 --> 00:09:58,999 +with many details to track. + +185 +00:09:59,032 --> 00:10:02,135 +And the resulting code– +it's hard to read and modify. + +186 +00:10:03,003 --> 00:10:06,673 +Regular expressions are a powerful +solution to this challenge. + +187 +00:10:06,707 --> 00:10:10,010 +They allow you to describe the pattern +you expect to see in your string + +188 +00:10:10,043 --> 00:10:14,114 +and specify which pieces of information +you're interested in capturing. + +189 +00:10:14,147 --> 00:10:18,685 +This year, Swift is delivering a huge +improvement to the developer experience + +190 +00:10:18,719 --> 00:10:20,587 +around regular expressions, + +191 +00:10:20,621 --> 00:10:23,590 +starting with +a new regular expression literal. + +192 +00:10:24,758 --> 00:10:26,593 +They're built directly into the language, + +193 +00:10:26,627 --> 00:10:29,062 +allowing the Swift compiler +to check for correctness. + +194 +00:10:29,096 --> 00:10:31,865 +And they unlock the power +of Swift's type system + +195 +00:10:31,899 --> 00:10:34,334 +when you're extracting information +with a regular expression. + +196 +00:10:34,368 --> 00:10:39,406 +And they take full advantage +of Swift's best-in-class Unicode support. + +197 +00:10:39,439 --> 00:10:41,108 +Let's take a look. + +198 +00:10:41,141 --> 00:10:42,809 +I'm working on an app +called Food Truck + +199 +00:10:42,843 --> 00:10:46,613 +that organizes everything +from taking orders to tracking sales. + +200 +00:10:46,647 --> 00:10:49,583 +And some orders come in as strings, +packed with data. + +201 +00:10:49,616 --> 00:10:53,320 +Now, regular expressions are perfect +for extracting the details I want, + +202 +00:10:53,353 --> 00:10:57,057 +and there's no better place to experiment +with them than here in a Playground. + +203 +00:10:57,090 --> 00:10:59,126 +I'll start by creating a regex literal. + +204 +00:11:00,427 --> 00:11:02,429 +Now I'll type the expression, + +205 +00:11:02,462 --> 00:11:04,498 +and pull out who made the order, + +206 +00:11:04,531 --> 00:11:06,066 +the donut type, + +207 +00:11:06,099 --> 00:11:08,168 +and the number of donuts. + +208 +00:11:08,202 --> 00:11:10,971 +Now, as I type, +the regex is syntax highlighted, + +209 +00:11:11,004 --> 00:11:14,241 +which helps me confirm +my expression is correct. + +210 +00:11:14,274 --> 00:11:15,142 +Now I'll try it out. + +211 +00:11:16,310 --> 00:11:18,178 +Use the order string from above + +212 +00:11:18,212 --> 00:11:20,547 +and look for the first match of the regex. + +213 +00:11:21,682 --> 00:11:24,518 +Now, when I run the Playground, +I can see with the inline result + +214 +00:11:24,551 --> 00:11:28,488 +exactly what parts of the order string +the regex matches. + +215 +00:11:28,522 --> 00:11:31,458 +And here, it's finding +just what I was looking for. + +216 +00:11:31,491 --> 00:11:34,862 +Swift's new regex support +doesn't stop here. + +217 +00:11:34,895 --> 00:11:36,597 +As literals become more complex, + +218 +00:11:36,630 --> 00:11:39,833 +like this one +that matches parts of a log file, + +219 +00:11:39,867 --> 00:11:43,871 +Swift offers an even better way +to craft these patterns– + +220 +00:11:43,904 --> 00:11:45,506 +regex builders. + +221 +00:11:46,974 --> 00:11:49,776 +And it's easy +to convert a literal to a builder. + +222 +00:11:50,577 --> 00:11:52,379 +Now I have code, + +223 +00:11:52,412 --> 00:11:55,816 +and that makes it easier +to read and change. + +224 +00:11:55,849 --> 00:11:58,619 +I can simplify this one even more. + +225 +00:11:58,652 --> 00:12:01,054 +Here, where I'm looking for a hex digit, + +226 +00:12:01,088 --> 00:12:03,590 +I'll use the new .hexDigit CharacterClass, + +227 +00:12:03,624 --> 00:12:06,693 +helping making my intent even clearer. + +228 +00:12:06,727 --> 00:12:09,730 +Now, the builder syntax makes it +so much easier for me to create + +229 +00:12:09,763 --> 00:12:12,065 +and extend my expressions, + +230 +00:12:12,099 --> 00:12:14,768 +and get the results I'm looking for. + +231 +00:12:14,801 --> 00:12:17,304 +And that's the powerful, +new developer experience + +232 +00:12:17,337 --> 00:12:19,907 +around regular expressions in Swift. + +233 +00:12:19,940 --> 00:12:23,277 +Ben: Beyond string syntax, +Swift is also getting easier + +234 +00:12:23,310 --> 00:12:28,315 +to read and write through improvements +to a language feature known as generics. + +235 +00:12:28,348 --> 00:12:30,951 +Generics power features of Swift +you use every day, + +236 +00:12:30,984 --> 00:12:32,319 +like the Array type, + +237 +00:12:32,352 --> 00:12:36,023 +which holds any kind of element, +from strings to your own custom types. + +238 +00:12:36,056 --> 00:12:39,293 +Generic code uses the concept +of a placeholder type + +239 +00:12:39,326 --> 00:12:42,796 +to stand in for another type +to be determined later. + +240 +00:12:42,829 --> 00:12:45,666 +By removing assumptions +about specific types, + +241 +00:12:45,699 --> 00:12:48,302 +you can be more clear +about the intent of your code, + +242 +00:12:48,335 --> 00:12:50,838 +and make it easier to re-use. + +243 +00:12:50,871 --> 00:12:53,540 +But this can also make your code +harder to read. + +244 +00:12:54,174 --> 00:12:57,711 +For example, if you wanted to handle +a generic collection of songs + +245 +00:12:57,744 --> 00:12:59,112 +as a function parameter, + +246 +00:12:59,146 --> 00:13:02,516 +you'd have to write quite a bit +of code to express your intent. + +247 +00:13:03,383 --> 00:13:07,287 +Now in Swift, writing a function +that accepts some collection of songs + +248 +00:13:07,321 --> 00:13:12,259 +is as easy as using the 'some' keyword +to tell Swift about the parameter. + +249 +00:13:12,292 --> 00:13:15,796 +You get the same meaning, +but with less code. + +250 +00:13:15,829 --> 00:13:19,399 +In other cases, +you might need more dynamic behavior, + +251 +00:13:19,433 --> 00:13:22,436 +like with this music library's +array of playlists, + +252 +00:13:22,469 --> 00:13:25,973 +which might need to contain different +types of collections of songs– + +253 +00:13:26,006 --> 00:13:28,742 +sets of songs, or arrays of songs. + +254 +00:13:28,775 --> 00:13:31,512 +That's where the new 'any' keyword +can help. + +255 +00:13:31,545 --> 00:13:33,780 +The 'any' keyword +is built right into Swift, + +256 +00:13:33,814 --> 00:13:38,252 +and allows you to express a type +that can hold any collection of songs. + +257 +00:13:38,285 --> 00:13:41,588 +And it works seamlessly +with generic functions too. + +258 +00:13:41,622 --> 00:13:45,559 +By adopting familiar syntax +and using more natural keywords, + +259 +00:13:45,592 --> 00:13:48,929 +writing generic code in Swift +has never been easier. + +260 +00:13:48,962 --> 00:13:52,733 +Just as important as the features +built into the Swift language + +261 +00:13:52,766 --> 00:13:55,135 +are the tools built around it. + +262 +00:13:55,169 --> 00:13:59,039 +The Swift Package Manager makes it easy +to manage your app's dependencies + +263 +00:13:59,072 --> 00:14:01,341 +and take advantage +of the fantastic packages + +264 +00:14:01,375 --> 00:14:03,977 +published by developers around the world. + +265 +00:14:04,011 --> 00:14:08,215 +To date, those developers have +published thousands of Swift packages, + +266 +00:14:08,248 --> 00:14:11,919 +providing code to help with everything +from authentication and web services + +267 +00:14:11,952 --> 00:14:15,222 +to data management +and reusable UI components. + +268 +00:14:15,255 --> 00:14:18,725 +And this year, the Swift Package Manager +is amplifying the ways + +269 +00:14:18,759 --> 00:14:23,163 +you can create and build code +with all-new Package Plugins. + +270 +00:14:23,197 --> 00:14:26,667 +Plugins are Swift packages +you can add to your project + +271 +00:14:26,700 --> 00:14:29,169 +as easily as any other dependency. + +272 +00:14:29,203 --> 00:14:32,606 +They download and build +automatically on a fresh checkout, + +273 +00:14:32,639 --> 00:14:35,275 +except instead of being code in your app, + +274 +00:14:35,309 --> 00:14:37,010 +they're code that helps build your app. + +275 +00:14:37,945 --> 00:14:41,548 +Package Plugins can be invoked +from the command line or within Xcode, + +276 +00:14:41,582 --> 00:14:44,852 +either as part of your build phase +or on-demand. + +277 +00:14:44,885 --> 00:14:46,920 +They run in a sandbox environment + +278 +00:14:46,954 --> 00:14:50,424 +which prompts you for permission +before reading or modifying your code. + +279 +00:14:51,124 --> 00:14:53,660 +There are endless possibilities +for extending your workflow + +280 +00:14:53,694 --> 00:14:54,695 +with Package Plugins. + +281 +00:14:54,728 --> 00:14:57,331 +You could use them to lint +and format your code + +282 +00:14:57,364 --> 00:14:58,765 +to match the team style guide + +283 +00:14:58,799 --> 00:15:01,168 +with packages +like SwiftLint or SwiftFormat + +284 +00:15:01,201 --> 00:15:03,704 +or automatically generate source code +at build-time + +285 +00:15:03,737 --> 00:15:05,205 +with tools like Sourcery. + +286 +00:15:05,239 --> 00:15:07,441 +Anything that helps you get the job done. + +287 +00:15:08,075 --> 00:15:10,944 +Ken: Package Plugins are a great way +to extend Xcode, + +288 +00:15:10,978 --> 00:15:13,180 +just by writing a little Swift. + +289 +00:15:13,213 --> 00:15:14,815 +And you can do that in two ways. + +290 +00:15:14,848 --> 00:15:17,751 +With command plugins +that you use on demand, + +291 +00:15:17,784 --> 00:15:20,921 +and with build plugins +for whenever your project builds. + +292 +00:15:20,954 --> 00:15:22,956 +Now, back here in our Food Truck app, + +293 +00:15:22,990 --> 00:15:25,259 +here's the code for a command plugin +I created. + +294 +00:15:26,894 --> 00:15:30,163 +My team has a unique code aesthetic. + +295 +00:15:30,197 --> 00:15:33,000 +We like our imports +sorted in string length order. + +296 +00:15:33,033 --> 00:15:35,035 +Shortest to longest. + +297 +00:15:35,068 --> 00:15:38,906 +And since Package Plugins are +all about customization and control, + +298 +00:15:38,939 --> 00:15:42,342 +we created a command plugin that uses +SwiftFormat to take care of that. + +299 +00:15:43,377 --> 00:15:46,079 +It finds all the locally modified files, + +300 +00:15:46,113 --> 00:15:48,415 +and then it sorts their imports. + +301 +00:15:48,448 --> 00:15:52,519 +Now, here's a file I've been editing +with some unsorted imports at the top. + +302 +00:15:52,553 --> 00:15:55,255 +I'll use the command +on the entire project. + +303 +00:15:55,289 --> 00:15:58,792 +I can select any number of targets. +I'll run it on everything. + +304 +00:15:59,326 --> 00:16:01,562 +And I can review +the plugin's code if I want. + +305 +00:16:01,595 --> 00:16:03,063 +I'm all set. +I'll run the command. + +306 +00:16:03,830 --> 00:16:06,733 +And then, just like that, +the plugin goes to work on my files. + +307 +00:16:07,367 --> 00:16:10,337 +It finds all of the locally modified +source files, + +308 +00:16:10,370 --> 00:16:12,773 +and then it sorts them in length order. + +309 +00:16:12,806 --> 00:16:15,809 +With plugins, +you can go beyond just formatting. + +310 +00:16:15,843 --> 00:16:18,679 +You can generate source code, +work with git, + +311 +00:16:18,712 --> 00:16:21,715 +even surface your own custom errors +and warnings. + +312 +00:16:21,748 --> 00:16:25,319 +I have another plugin to make sure +my code is well documented. + +313 +00:16:25,352 --> 00:16:29,389 +It's a build plugin, and it's based on +the open source SwiftLint package. + +314 +00:16:30,157 --> 00:16:33,994 +So now when I build, I can easily see +all the places in my code + +315 +00:16:34,027 --> 00:16:36,463 +where I need to add documentation. + +316 +00:16:36,496 --> 00:16:40,267 +And build plugins extend +all the way to Xcode Cloud, + +317 +00:16:40,300 --> 00:16:42,870 +where they run as part of every build. + +318 +00:16:42,903 --> 00:16:44,638 +With Swift package plugins, + +319 +00:16:44,671 --> 00:16:46,974 +my team and I can create our own commands, + +320 +00:16:47,007 --> 00:16:49,943 +customize builds locally +and in Xcode Cloud, + +321 +00:16:49,977 --> 00:16:52,212 +and then share those plugins with others. + +322 +00:16:52,246 --> 00:16:55,449 +All using the power +of a few lines of Swift. + +323 +00:16:55,482 --> 00:16:57,885 +And that's a quick look +at the ways Package Plugins + +324 +00:16:57,918 --> 00:17:00,487 +can level up your development workflow. + +325 +00:17:00,521 --> 00:17:04,591 +Ben: Finally, Swift has some impressive +changes under the hood. + +326 +00:17:04,625 --> 00:17:06,560 +Building Swift projects +is quicker than ever. + +327 +00:17:06,593 --> 00:17:08,795 +Thanks to new parallelization efforts, + +328 +00:17:08,829 --> 00:17:11,131 +link time is up to twice as fast. + +329 +00:17:11,164 --> 00:17:13,000 +And the Swift concurrency runtime + +330 +00:17:13,033 --> 00:17:15,769 +is now more tightly integrated with the OS + +331 +00:17:15,802 --> 00:17:18,939 +to better ensure the priority +of your asynchronous tasks, + +332 +00:17:18,972 --> 00:17:21,441 +helping your apps +stay efficient and responsive. + +333 +00:17:21,475 --> 00:17:26,680 +Lastly, launch time for apps written +in Swift is dramatically faster on iOS 16, + +334 +00:17:26,713 --> 00:17:30,851 +with apps like Lyft or Airbnb +launching almost twice as fast + +335 +00:17:30,884 --> 00:17:33,086 +thanks to improvement +in the dynamic linker. + +336 +00:17:33,120 --> 00:17:35,322 +With these under-the-hood improvements, + +337 +00:17:35,355 --> 00:17:36,990 +new abilities in tooling, + +338 +00:17:37,024 --> 00:17:40,027 +an evolved syntax +that's easier to read and write, + +339 +00:17:40,060 --> 00:17:41,895 +and improvements in concurrency, + +340 +00:17:41,929 --> 00:17:44,831 +there has never been a better time +to develop in Swift. + +341 +00:17:44,865 --> 00:17:49,803 +Swift is the absolute best language +for building apps across our devices. + +342 +00:17:49,837 --> 00:17:53,640 +But a language is just part of what +you need to build your best apps. + +343 +00:17:53,674 --> 00:17:57,644 +You have to pair a language with +a powerful user interface framework. + +344 +00:17:57,678 --> 00:17:59,813 +And Eliza is gonna tell you more. + +345 +00:17:59,847 --> 00:18:02,816 +Eliza Block: A powerful UI framework +provides abstractions + +346 +00:18:02,850 --> 00:18:05,319 +that make it easy +to describe your interface, + +347 +00:18:05,352 --> 00:18:08,355 +to populate it with data, +and to keep it up to date. + +348 +00:18:08,388 --> 00:18:10,657 +It should scale well with complexity. + +349 +00:18:10,691 --> 00:18:13,227 +And it should be designed +for the platform you're targeting, + +350 +00:18:13,260 --> 00:18:15,996 +giving you full access +to the power of the device. + +351 +00:18:16,029 --> 00:18:21,034 +Your UI framework should help you make +your app feel familiar and intuitive. + +352 +00:18:21,068 --> 00:18:23,871 +It should make it easy to create +standard controls + +353 +00:18:23,904 --> 00:18:25,639 +and native interaction patterns, + +354 +00:18:25,672 --> 00:18:28,242 +with options for advanced customization. + +355 +00:18:28,275 --> 00:18:30,644 +And it needs to have an expressive API + +356 +00:18:30,677 --> 00:18:33,580 +that allows you to quickly prototype +your ideas + +357 +00:18:33,614 --> 00:18:36,016 +and see the results +across a range of devices. + +358 +00:18:36,583 --> 00:18:39,152 +SwiftUI offers all this and more. + +359 +00:18:39,186 --> 00:18:42,623 +Like Swift, +Swift UI is designed with strong opinions + +360 +00:18:42,656 --> 00:18:44,658 +about the best way to build apps. + +361 +00:18:44,691 --> 00:18:48,462 +It has a declarative syntax +that's easy to read and write. + +362 +00:18:48,495 --> 00:18:52,933 +You describe what your interface +looks like, instead of how to build it. + +363 +00:18:53,934 --> 00:18:57,504 +And this leaves room for SwiftUI +to provide intelligent defaults + +364 +00:18:57,538 --> 00:18:59,606 +for each platform. + +365 +00:18:59,640 --> 00:19:03,944 +SwiftUI automatically keeps your interface +up-to-date with changes + +366 +00:19:03,977 --> 00:19:05,579 +to the underlying data model, + +367 +00:19:05,612 --> 00:19:08,916 +so your app's UI never ends up +in an inconsistent state. + +368 +00:19:09,750 --> 00:19:13,320 +SwiftUI handles all these details for you, + +369 +00:19:13,353 --> 00:19:17,391 +so you can focus your time and energy +on what makes your app unique. + +370 +00:19:17,424 --> 00:19:21,028 +Writing a new UI framework +is a huge undertaking. + +371 +00:19:21,061 --> 00:19:23,931 +Since its introduction +we've been continually expanding + +372 +00:19:23,964 --> 00:19:27,167 +SwiftUI's API coverage, +guided by your feedback. + +373 +00:19:27,835 --> 00:19:31,772 +This year we've made it even easier +to adopt SwiftUI incrementally + +374 +00:19:31,805 --> 00:19:33,173 +in your existing apps, + +375 +00:19:33,207 --> 00:19:37,377 +and we've made some exciting enhancements +to its power and flexibility, + +376 +00:19:37,411 --> 00:19:39,780 +starting with app navigation. + +377 +00:19:39,813 --> 00:19:42,482 +With SwiftUI, +it has always been easy to create + +378 +00:19:42,516 --> 00:19:46,453 +the common kinds of navigation +hierarchies found in many apps. + +379 +00:19:46,486 --> 00:19:49,690 +And this year, +SwiftUI is expanding that support + +380 +00:19:49,723 --> 00:19:52,292 +with an all-new navigation API. + +381 +00:19:52,860 --> 00:19:56,897 +The new navigation API makes it easy +to express the style of navigation + +382 +00:19:56,930 --> 00:19:59,633 +that best fits the needs of your app. + +383 +00:19:59,666 --> 00:20:03,770 +With robust programmatic control +over the presentation of your app's views, + +384 +00:20:03,804 --> 00:20:06,540 +you can easily save and restore selection, + +385 +00:20:06,573 --> 00:20:10,344 +and even replace the full contents +of a navigation stack. + +386 +00:20:10,377 --> 00:20:13,480 +This is really useful +when handling important behaviors + +387 +00:20:13,514 --> 00:20:15,716 +like setting the launch state of your app, + +388 +00:20:15,749 --> 00:20:18,552 +managing transitions between size classes, + +389 +00:20:18,585 --> 00:20:20,220 +and responding to deep links. + +390 +00:20:21,154 --> 00:20:24,892 +SwiftUI also has huge improvements +when it comes to controlling + +391 +00:20:24,925 --> 00:20:27,094 +the layout of your app's interface. + +392 +00:20:27,828 --> 00:20:32,065 +The layout of many app interfaces +can be described using SwiftUI's model + +393 +00:20:32,099 --> 00:20:34,868 +of horizontal +or vertical stacks of elements. + +394 +00:20:34,902 --> 00:20:37,571 +And while this model works +for many common layouts, + +395 +00:20:37,604 --> 00:20:40,340 +sometimes +you need something more flexible. + +396 +00:20:40,374 --> 00:20:43,243 +This year, we're adding a new Grid API, + +397 +00:20:43,277 --> 00:20:45,779 +which makes it easier +to lay out a set of views + +398 +00:20:45,812 --> 00:20:48,582 +aligned across multiple rows and columns. + +399 +00:20:48,615 --> 00:20:51,118 +And you can take your layouts even further + +400 +00:20:51,151 --> 00:20:54,154 +with the all-new custom layout API. + +401 +00:20:54,188 --> 00:20:57,157 +The custom layout API +gives you the flexibility + +402 +00:20:57,191 --> 00:20:59,760 +to build any type of layout you want. + +403 +00:20:59,793 --> 00:21:02,029 +For example, +you could create a flow layout + +404 +00:21:02,062 --> 00:21:05,465 +where your views are arranged +like the content of a newspaper, + +405 +00:21:05,499 --> 00:21:08,569 +wrapping to the next column +when more space is needed. + +406 +00:21:08,602 --> 00:21:12,573 +Or you could create a radial layout +that draws your views in a circle, + +407 +00:21:12,606 --> 00:21:14,107 +like the numbers on a watch face. + +408 +00:21:14,842 --> 00:21:19,546 +The custom layout API makes it easy +to re-use your layout logic, + +409 +00:21:19,580 --> 00:21:22,349 +making your view code simpler +and easier to read. + +410 +00:21:22,382 --> 00:21:25,352 +SwiftUI continues to grow +to offer + +411 +00:21:25,385 --> 00:21:27,688 +many more types of interface elements. + +412 +00:21:27,721 --> 00:21:30,858 +Like half sheets, +which define secondary views + +413 +00:21:30,891 --> 00:21:32,759 +that slide above a main view. + +414 +00:21:32,793 --> 00:21:37,564 +These are great to provide quick access +to information on smaller screens. + +415 +00:21:37,598 --> 00:21:40,100 +And SwiftUI now supports Share Sheets, + +416 +00:21:40,133 --> 00:21:41,835 +so your app can easily leverage + +417 +00:21:41,869 --> 00:21:45,005 +all of the Share extensions available +on a user's device. + +418 +00:21:45,038 --> 00:21:49,176 +Share Sheet support is powered +by the new Transferable protocol, + +419 +00:21:49,209 --> 00:21:52,913 +which introduces a type-safe API +for transferring app data. + +420 +00:21:53,914 --> 00:21:58,652 +We've also made it easier to adopt SwiftUI +incrementally in your existing apps + +421 +00:21:58,685 --> 00:22:03,557 +with a special collection view cell +that can host SwiftUI views. + +422 +00:22:03,590 --> 00:22:06,660 +If you already have a collection view +in your UIKit app, + +423 +00:22:06,693 --> 00:22:10,764 +you can now write custom cells +using SwiftUI's declarative syntax. + +424 +00:22:10,797 --> 00:22:15,269 +These cells are tightly integrated +with UIKit, supporting swipe actions, + +425 +00:22:15,302 --> 00:22:19,239 +cell backgrounds, and all the other +features of UICollectionView. + +426 +00:22:19,273 --> 00:22:22,109 +Today we're also introducing +a brand-new framework + +427 +00:22:22,142 --> 00:22:23,677 +that complements SwiftUI + +428 +00:22:23,710 --> 00:22:27,047 +and will allow you to express +even more of your interface. + +429 +00:22:27,080 --> 00:22:29,049 +Here's Jo to tell you more. + +430 +00:22:29,082 --> 00:22:31,218 +Jo Arreaza-Taylor: +Today's world is filled with data. + +431 +00:22:31,251 --> 00:22:33,453 +Data to help understand, make decisions, + +432 +00:22:33,487 --> 00:22:35,055 +and see new perspectives. + +433 +00:22:35,088 --> 00:22:37,724 +A well-designed and accessible +data visualization + +434 +00:22:37,758 --> 00:22:40,060 +can communicate complexities to your users + +435 +00:22:40,093 --> 00:22:42,629 +in a way that feels clear and natural, + +436 +00:22:42,663 --> 00:22:44,798 +empowering them +as they move through the day. + +437 +00:22:45,632 --> 00:22:48,702 +Like helping to show +changing trends in their health, + +438 +00:22:48,735 --> 00:22:51,839 +highlighting their progress +towards personal goals, + +439 +00:22:51,872 --> 00:22:53,807 +and preparing them for what's to come. + +440 +00:22:54,474 --> 00:22:58,312 +Today, we're introducing a new framework +to help empower your users + +441 +00:22:58,345 --> 00:23:00,581 +to unlock the data within your apps. + +442 +00:23:00,614 --> 00:23:03,884 +Say hello to Swift Charts. + +443 +00:23:03,917 --> 00:23:07,354 +Swift Charts is a highly customizable +charting framework + +444 +00:23:07,387 --> 00:23:09,056 +built on top of SwiftUI + +445 +00:23:09,089 --> 00:23:12,793 +that makes it easy +to create gorgeous visualizations. + +446 +00:23:12,826 --> 00:23:16,830 +It uses the same declarative syntax +as SwiftUI to make it easy + +447 +00:23:16,864 --> 00:23:19,600 +to read and write code +which conveys visual information. + +448 +00:23:20,567 --> 00:23:23,337 +Swift Chart lets you customize +the presentation of information + +449 +00:23:23,370 --> 00:23:25,606 +to best fit the needs of your app + +450 +00:23:25,639 --> 00:23:28,175 +to create everything +from line and bar charts + +451 +00:23:28,208 --> 00:23:32,446 +to more sophisticated examples +like heat maps and stream graphs, + +452 +00:23:32,479 --> 00:23:34,314 +and many, many more types. + +453 +00:23:35,349 --> 00:23:38,285 +And because Swift Charts +is built on top of SwiftUI, + +454 +00:23:38,318 --> 00:23:40,821 +charts have great support +for accessibility features, + +455 +00:23:40,854 --> 00:23:44,091 +like a terrific, out-of-the-box +VoiceOver experience + +456 +00:23:44,124 --> 00:23:46,226 +that's easy to customize. + +457 +00:23:46,260 --> 00:23:50,163 +Being built on SwiftUI also means +you can animate your charts, + +458 +00:23:50,197 --> 00:23:53,534 +to help you give your app +just the right look and feel. + +459 +00:23:53,567 --> 00:23:57,871 +And of course, Swift Charts works great +across all our devices. + +460 +00:23:57,905 --> 00:23:59,973 +Eliza: Back in our Food Truck app, + +461 +00:24:00,007 --> 00:24:02,042 +here are the beautiful new Swift Charts + +462 +00:24:02,075 --> 00:24:05,479 +in Xcode's fully redesigned preview area. + +463 +00:24:05,512 --> 00:24:09,516 +I'm also using +the new multicolumn SwiftUI table view. + +464 +00:24:09,550 --> 00:24:12,886 +Let me show you how easy it is +to build this chart. + +465 +00:24:12,920 --> 00:24:16,256 +And as I scroll, check out +the awesome new structured headers + +466 +00:24:16,290 --> 00:24:17,524 +in the source editor. + +467 +00:24:17,558 --> 00:24:20,394 +They make it really easy +to see where you are in the file. + +468 +00:24:21,428 --> 00:24:22,863 +Here's the code for the chart. + +469 +00:24:22,896 --> 00:24:26,767 +Now, this is actually a stacked bar chart, +but you can't really tell. + +470 +00:24:26,800 --> 00:24:29,670 +Let's have each donut +use its own color. + +471 +00:24:31,038 --> 00:24:34,508 +Maybe it would be easier to see +how the donuts compare + +472 +00:24:34,541 --> 00:24:36,844 +if we position the bars side by side. + +473 +00:24:38,111 --> 00:24:40,848 +I love how I can make +all these big changes + +474 +00:24:40,881 --> 00:24:43,217 +with just a couple simple modifiers. + +475 +00:24:43,250 --> 00:24:45,719 +We can customize the styling, too. + +476 +00:24:45,752 --> 00:24:48,689 +Let's make the bars +reflect the donut colors. + +477 +00:24:48,722 --> 00:24:52,292 +And we can even add annotations +to the bars with another modifier. + +478 +00:24:53,493 --> 00:24:54,962 +Looking great. + +479 +00:24:54,995 --> 00:24:56,897 +Previews are now live by default, + +480 +00:24:56,930 --> 00:24:59,700 +so I can immediately interact +with my view. + +481 +00:24:59,733 --> 00:25:01,435 +I'm gonna change the sort order. + +482 +00:25:01,468 --> 00:25:04,371 +Watch how the bars animate beautifully, + +483 +00:25:04,404 --> 00:25:07,241 +with Swift Charts +doing all the heavy lifting. + +484 +00:25:07,274 --> 00:25:09,142 +Let's fetch more data. + +485 +00:25:09,176 --> 00:25:11,612 +The chart and table +both automatically update + +486 +00:25:11,645 --> 00:25:13,146 +as my model changes. + +487 +00:25:13,180 --> 00:25:17,351 +The chart even recalculates its Y axis +to reflect the new totals. + +488 +00:25:17,384 --> 00:25:19,186 +Let me show you one more chart. + +489 +00:25:19,219 --> 00:25:22,723 +I've stubbed out a line chart +which we can add to the view. + +490 +00:25:22,756 --> 00:25:25,192 +I'll jump to the implementation. + +491 +00:25:25,225 --> 00:25:29,263 +Line charts with Swift Charts +have some really cool options. + +492 +00:25:29,296 --> 00:25:32,032 +We can add symbols for each donut. + +493 +00:25:32,065 --> 00:25:34,301 +We can annotate the lines. + +494 +00:25:34,334 --> 00:25:37,004 +We can even smooth the curves +with a variety + +495 +00:25:37,037 --> 00:25:38,839 +of interpolation strategies. + +496 +00:25:38,872 --> 00:25:41,108 +Let's use catmullRom. + +497 +00:25:41,141 --> 00:25:43,610 +Finally, I'll override +the chart scale style + +498 +00:25:43,644 --> 00:25:45,746 +by providing my own mapping. + +499 +00:25:45,779 --> 00:25:49,249 +That will make my chart fit in better +with the app's color scheme. + +500 +00:25:49,283 --> 00:25:50,751 +Really nice. + +501 +00:25:50,784 --> 00:25:53,487 +The redesigned preview area +makes it easier than ever + +502 +00:25:53,520 --> 00:25:56,223 +to see how my view looks +in different environments. + +503 +00:25:56,256 --> 00:25:58,458 +By pressing this button +in the canvas, + +504 +00:25:58,492 --> 00:26:01,261 +I can see my view in Dark and Light mode. + +505 +00:26:01,295 --> 00:26:04,798 +I can even look at my layout +in every interface orientation, + +506 +00:26:04,831 --> 00:26:07,534 +all without adding +a single additional preview. + +507 +00:26:07,568 --> 00:26:10,037 +Let's zoom in on landscape. + +508 +00:26:10,070 --> 00:26:13,207 +It looks like my UI +isn't quite fitting here. + +509 +00:26:13,240 --> 00:26:14,608 +A few controls are offscreen, + +510 +00:26:14,641 --> 00:26:17,744 +and the charts have +an awkward aspect ratio. + +511 +00:26:17,778 --> 00:26:21,048 +Let's see where we're describing +this layout. + +512 +00:26:21,081 --> 00:26:24,384 +These views here are +in an implicit Vstack. + +513 +00:26:25,519 --> 00:26:29,089 +This year there are some powerful +new APIs in SwiftUI + +514 +00:26:29,122 --> 00:26:31,558 +that can create more flexible layouts. + +515 +00:26:31,592 --> 00:26:34,194 +Here, I'm going to use a ViewThatFits + +516 +00:26:34,228 --> 00:26:37,431 +to switch between a vertical +and a horizontal stack, + +517 +00:26:37,464 --> 00:26:39,433 +depending on the available space. + +518 +00:26:40,834 --> 00:26:42,469 +That looks much better. + +519 +00:26:42,503 --> 00:26:43,837 +Let's wire this up + +520 +00:26:43,871 --> 00:26:45,806 +so we can navigate to it +from the main screen. + +521 +00:26:46,840 --> 00:26:49,977 +I'm using SwiftUI's +new navigation split view, + +522 +00:26:50,010 --> 00:26:51,778 +which makes this really easy. + +523 +00:26:51,812 --> 00:26:55,249 +The split view has a sidebar +to track the selection + +524 +00:26:55,282 --> 00:26:57,951 +and a NavigationStack +that changes its content + +525 +00:26:57,985 --> 00:27:00,687 +as the sidebar selection changes. + +526 +00:27:00,721 --> 00:27:02,089 +I'll jump into the sidebar + +527 +00:27:02,122 --> 00:27:05,158 +and add a navigation link +for our Donut Champion view. + +528 +00:27:05,192 --> 00:27:07,661 +And then, we can try it out +in the interactive preview. + +529 +00:27:08,428 --> 00:27:10,831 +I'd like to see my split view +in landscape, + +530 +00:27:10,864 --> 00:27:13,100 +so I'll use the new canvas settings + +531 +00:27:13,133 --> 00:27:15,369 +to rotate the live preview. + +532 +00:27:15,402 --> 00:27:17,004 +Works great. + +533 +00:27:17,037 --> 00:27:19,573 +I'm happy with how this is looking +on iPad, + +534 +00:27:19,606 --> 00:27:21,842 +but now I'd love to bring it to Mac, + +535 +00:27:21,875 --> 00:27:25,145 +and it's only a few clicks +to do that. + +536 +00:27:25,179 --> 00:27:27,648 +I want to take full advantage +of the Mac SDK, + +537 +00:27:27,681 --> 00:27:29,116 +so I'm going to use native. + +538 +00:27:31,385 --> 00:27:33,720 +With just a single target backing my app, + +539 +00:27:33,754 --> 00:27:35,756 +I can share almost all my code, + +540 +00:27:35,789 --> 00:27:39,193 +and SwiftUI makes my app look great +on each platform. + +541 +00:27:39,226 --> 00:27:42,596 +I can also easily add +device-specific features. + +542 +00:27:42,629 --> 00:27:45,465 +For my Mac app, +let's add a menu bar extra. + +543 +00:27:45,499 --> 00:27:47,434 +Those are the little useful icons + +544 +00:27:47,467 --> 00:27:49,169 +on the upper right corner of your screen, + +545 +00:27:49,203 --> 00:27:52,239 +like Wi-Fi and Spotlight. + +546 +00:27:52,272 --> 00:27:55,309 +SwiftUI has a new API for this. + +547 +00:27:55,342 --> 00:27:57,411 +I just add it to the body of my app. + +548 +00:27:57,444 --> 00:27:59,313 +Now let's run this for Mac. + +549 +00:28:02,850 --> 00:28:06,720 +Our Donut Champion view looks great +on Mac right out of the box. + +550 +00:28:06,753 --> 00:28:08,755 +And here's that menu bar extra. + +551 +00:28:08,789 --> 00:28:10,157 +That'll be handy. + +552 +00:28:10,190 --> 00:28:12,459 +And that's a quick look at Swift Charts + +553 +00:28:12,492 --> 00:28:15,929 +and just a few of the enhancements +coming to SwiftUI and Xcode. + +554 +00:28:15,963 --> 00:28:17,798 +And now back to Josh. + +555 +00:28:17,831 --> 00:28:21,001 +Josh: We're continuing to expand +our adoption of SwiftUI + +556 +00:28:21,034 --> 00:28:23,570 +across our apps and system interfaces. + +557 +00:28:23,604 --> 00:28:27,808 +For example, iOS's new Lock Screen widgets +were designed from the ground up + +558 +00:28:27,841 --> 00:28:29,409 +using SwiftUI. + +559 +00:28:29,443 --> 00:28:32,346 +The new Font Book app was +completely rewritten with it. + +560 +00:28:32,379 --> 00:28:36,450 +And the modern, forward-looking design +of the new macOS System Settings app + +561 +00:28:36,483 --> 00:28:37,985 +was built using it. + +562 +00:28:38,018 --> 00:28:41,154 +Swift and SwiftUI were designed +from the start + +563 +00:28:41,188 --> 00:28:45,926 +to provide a single, native language +and API for all Apple platforms. + +564 +00:28:45,959 --> 00:28:48,795 +You can learn them once +and apply them everywhere. + +565 +00:28:48,829 --> 00:28:51,965 +Whether your vision is to provide +quick access to information + +566 +00:28:51,999 --> 00:28:54,001 +at a glance on Apple Watch, + +567 +00:28:54,034 --> 00:28:57,271 +productivity tools +on MacBook Pro and iPad, + +568 +00:28:57,304 --> 00:28:59,173 +new experiences on iPhone, + +569 +00:28:59,206 --> 00:29:01,575 +or a new way to relax with Apple TV, + +570 +00:29:01,608 --> 00:29:04,077 +Swift, SwiftUI, and Xcode + +571 +00:29:04,111 --> 00:29:07,781 +provide a next-generation integrated +development platform + +572 +00:29:07,814 --> 00:29:11,051 +to help you build apps +for all of our products. + +573 +00:29:11,084 --> 00:29:12,686 +Now, if you have an existing app, + +574 +00:29:12,719 --> 00:29:16,156 +it's easy to adopt +these new technologies incrementally. + +575 +00:29:16,190 --> 00:29:19,693 +And if you're new to our platforms +or if you're starting a brand-new app, + +576 +00:29:19,726 --> 00:29:23,830 +the best way to build an app +is with Swift and SwiftUI. + +577 +00:29:23,864 --> 00:29:26,066 +Now, of course +that's just the beginning. + +578 +00:29:26,099 --> 00:29:30,037 +We're also continuing to evolve +the user experience of our platforms + +579 +00:29:30,070 --> 00:29:32,873 +to give you more ways +to engage your users. + +580 +00:29:32,906 --> 00:29:35,242 +And to tell you more, here's Sebastien. + +581 +00:29:36,543 --> 00:29:38,579 +Sebastien Marineau-Mes: +Now, apps are about turning ideas, + +582 +00:29:38,612 --> 00:29:41,315 +code, and APIs into user experiences. + +583 +00:29:41,348 --> 00:29:43,650 +And the best apps +are the ones that can meet users + +584 +00:29:43,684 --> 00:29:45,919 +where they are in the moment. + +585 +00:29:45,953 --> 00:29:49,623 +We've created ways to help you take +user experience beyond your apps, + +586 +00:29:49,656 --> 00:29:53,293 +and build it into the system experience +on Apple devices. + +587 +00:29:54,094 --> 00:29:57,831 +This journey started with extensions, +integration with the Share Sheet, + +588 +00:29:57,865 --> 00:29:59,566 +and custom keyboards. + +589 +00:29:59,600 --> 00:30:01,902 +And more recently, +it's included the ability + +590 +00:30:01,935 --> 00:30:06,206 +to have your app display key information +on the Home Screen using widgets. + +591 +00:30:06,240 --> 00:30:08,041 +Now, this year, +there are a number of new ways + +592 +00:30:08,075 --> 00:30:11,144 +for your app to integrate with the system +experience across our platforms, + +593 +00:30:11,178 --> 00:30:15,649 +and it really starts with the Lock Screen, +which gets its biggest update ever. + +594 +00:30:15,682 --> 00:30:19,186 +It re-imagines how +the Lock Screen looks and how it works, + +595 +00:30:19,219 --> 00:30:23,557 +and it gives your ideas and your apps +another place to engage users. + +596 +00:30:23,590 --> 00:30:25,959 +And to tell you more, here's Robert. + +597 +00:30:34,434 --> 00:30:35,969 +Robert Dhaene: +In reimagining the Lock Screen, + +598 +00:30:36,003 --> 00:30:38,972 +we set out to make it +even more personal and beautiful, + +599 +00:30:39,006 --> 00:30:40,841 +while improving everyday utility. + +600 +00:30:40,874 --> 00:30:43,610 +As part of this, +we knew we needed to bring + +601 +00:30:43,644 --> 00:30:46,580 +the power of widgets +to the all-new Lock Screen. + +602 +00:30:46,613 --> 00:30:49,650 +Widgets have been an incredible way +to elevate key information + +603 +00:30:49,683 --> 00:30:53,654 +from your app and display it +where people can view it at a glance. + +604 +00:30:53,687 --> 00:30:57,324 +They make it easy to access +rich, timely information + +605 +00:30:57,357 --> 00:30:58,725 +right from the Home Screen. + +606 +00:30:59,393 --> 00:31:02,963 +The Lock Screen is the first thing you see +every time you pick up your iPhone, + +607 +00:31:02,996 --> 00:31:05,599 +and it's always been a place +to check the date and time + +608 +00:31:05,632 --> 00:31:07,501 +and look out for key messages. + +609 +00:31:07,534 --> 00:31:11,205 +When thinking about the best format +for displaying even more information here, + +610 +00:31:11,238 --> 00:31:13,740 +we didn't have to look far +for design inspiration. + +611 +00:31:14,374 --> 00:31:18,245 +Complications on Apple Watch +already provide glanceable, relevant, + +612 +00:31:18,278 --> 00:31:19,713 +and up-to-date information, + +613 +00:31:19,746 --> 00:31:22,516 +presented beautifully +right when users need it. + +614 +00:31:23,183 --> 00:31:26,887 +The design language naturally extends +to iOS and feels right at home + +615 +00:31:26,920 --> 00:31:28,789 +on the new Lock Screen. + +616 +00:31:28,822 --> 00:31:31,992 +So using WidgetKit, +we brought some of those same designs + +617 +00:31:32,025 --> 00:31:35,128 +to widgets on the Lock Screen, +including Circular, + +618 +00:31:35,162 --> 00:31:39,833 +which displays a small image, +gauge, or a few characters of text. + +619 +00:31:39,867 --> 00:31:43,537 +Circular widgets are great for displaying +whether you've been active enough today + +620 +00:31:43,570 --> 00:31:45,672 +or if you need to go out for a run. + +621 +00:31:45,706 --> 00:31:48,342 +Rectangular provides a large canvas + +622 +00:31:48,375 --> 00:31:51,745 +for displaying things +like the upcoming weather forecast. + +623 +00:31:51,778 --> 00:31:54,681 +Inline provides a powerful way +to convey information + +624 +00:31:54,715 --> 00:31:59,086 +with a tiny amount of text and SF Symbols +above the clock on iPhone, + +625 +00:31:59,119 --> 00:32:03,156 +next to a system-supplied date string, +such as Monday the 6th. + +626 +00:32:03,190 --> 00:32:08,495 +And by the way, all of these widgets +work on both iOS and watchOS + +627 +00:32:08,529 --> 00:32:10,497 +because starting in watchOS 9, + +628 +00:32:10,531 --> 00:32:13,734 +complications +are also powered by WidgetKit. + +629 +00:32:13,767 --> 00:32:16,336 +For the first time, +you can use the same code + +630 +00:32:16,370 --> 00:32:20,274 +to generate glanceable data +on both platforms. + +631 +00:32:20,307 --> 00:32:23,343 +WidgetKit manages platform differences +for you automatically, + +632 +00:32:23,377 --> 00:32:25,646 +using the appropriate system fonts +by default, + +633 +00:32:25,679 --> 00:32:29,449 +and tinting the widgets on +the Lock Screen for maximum legibility. + +634 +00:32:29,483 --> 00:32:32,953 +To show you how to use WidgetKit to create +widgets for the Lock Screen on iPhone + +635 +00:32:32,986 --> 00:32:35,756 +and complications on Apple Watch +using the same code, + +636 +00:32:35,789 --> 00:32:37,691 +I'll hand it over to Michael. + +637 +00:32:39,960 --> 00:32:41,662 +Michael Kent: Building widgets +on the iPhone Lock Screen + +638 +00:32:41,695 --> 00:32:44,965 +and complications on Apple Watch +is really easy with WidgetKit. + +639 +00:32:44,998 --> 00:32:47,568 +If you've made Home Screen widgets, +you're already most of the way there, + +640 +00:32:47,601 --> 00:32:50,037 +including how your data +and timeline are updated. + +641 +00:32:50,070 --> 00:32:52,573 +In our Food Truck app, +we already have a systemSmall widget + +642 +00:32:52,606 --> 00:32:54,274 +that users can add to their Home Screen + +643 +00:32:54,308 --> 00:32:57,144 +to see how many orders they've filled +out of their quota today. + +644 +00:32:57,177 --> 00:32:59,680 +This kind of information would be great +to show on the Lock Screen + +645 +00:32:59,713 --> 00:33:01,915 +or in a complication on the watch face. + +646 +00:33:01,949 --> 00:33:04,151 +Let's start by building out +the Circular family. + +647 +00:33:05,652 --> 00:33:08,422 +We'll first declare support for it +in our Supported Families array. + +648 +00:33:09,556 --> 00:33:12,192 +You'll notice that we're using +some platform conditionals here. + +649 +00:33:12,226 --> 00:33:15,929 +This is because we want this widget +to continue supporting macOS and iOS + +650 +00:33:15,963 --> 00:33:19,533 +with systemSmall, but that family +isn't available on watchOS. + +651 +00:33:20,367 --> 00:33:22,503 +Then, we'll add a case to define its view. + +652 +00:33:24,171 --> 00:33:26,540 +Let's use a gauge +that shows the current number of orders + +653 +00:33:26,573 --> 00:33:28,208 +from 0 to the daily quota + +654 +00:33:28,242 --> 00:33:31,011 +so the users can quickly see +their progress at a glance. + +655 +00:33:31,845 --> 00:33:34,815 +We'll display the current order count +as text in the center, + +656 +00:33:34,848 --> 00:33:37,384 +along with a donut symbol. +There. + +657 +00:33:37,417 --> 00:33:39,319 +Let's take a look at this +in Xcode Previews now. + +658 +00:33:40,888 --> 00:33:42,256 +Awesome! +That feels right at home + +659 +00:33:42,289 --> 00:33:43,123 +on the Lock Screen. + +660 +00:33:44,091 --> 00:33:45,993 +In order to show a bit more detail +at a glance, + +661 +00:33:46,026 --> 00:33:48,495 +we can also add support +for the rectangular family. + +662 +00:33:52,099 --> 00:33:54,902 +For this view, +we'll make a VStack, + +663 +00:33:54,935 --> 00:33:57,271 +starting with a title for the data +that's shown, + +664 +00:33:57,304 --> 00:33:58,972 +and that same donut symbol. + +665 +00:33:59,673 --> 00:34:01,942 +By using the Headline style for the font, + +666 +00:34:01,975 --> 00:34:04,444 +we'll get a treatment +that looks great on both platforms. + +667 +00:34:04,478 --> 00:34:07,581 +And we'll make sure it pops +with the widgetAccentable modifier. + +668 +00:34:07,614 --> 00:34:10,951 +Since the rectangular family gives us +a bit more space, + +669 +00:34:10,984 --> 00:34:13,720 +we'll show a cool custom segmented gauge + +670 +00:34:13,754 --> 00:34:16,557 +and display the current number of orders +out of the daily quota + +671 +00:34:16,590 --> 00:34:18,258 +for the gauge's label. + +672 +00:34:18,792 --> 00:34:20,327 +Looking back at the canvas, + +673 +00:34:20,360 --> 00:34:22,963 +we can see the rectangular widget +in previews as well. + +674 +00:34:22,996 --> 00:34:25,299 +I really love that gauge. + +675 +00:34:25,332 --> 00:34:29,102 +Now let's take a look at how this widget +would appear as a circular complication + +676 +00:34:29,136 --> 00:34:30,604 +on a watch face. + +677 +00:34:32,739 --> 00:34:34,508 +Well, all right, everything is there, + +678 +00:34:34,541 --> 00:34:37,010 +but for complications, +we also need to consider + +679 +00:34:37,044 --> 00:34:38,312 +the full color rendering mode, + +680 +00:34:38,345 --> 00:34:41,415 +which is the default in Xcode Previews. + +681 +00:34:41,448 --> 00:34:45,586 +Let's do that by adding a tint +to each of our gauges. + +682 +00:34:46,854 --> 00:34:49,923 +And a foreground color +to the rectangular headline. + +683 +00:34:51,758 --> 00:34:53,260 +For a bit of a pop in full color, + +684 +00:34:53,293 --> 00:34:56,864 +we can check the rendering mode +with an environment property + +685 +00:34:56,897 --> 00:35:00,300 +to replace the donut symbol +with a donut emoji + +686 +00:35:00,334 --> 00:35:01,969 +on both the circular + +687 +00:35:02,002 --> 00:35:03,770 +and rectangular views. + +688 +00:35:05,072 --> 00:35:06,306 +That looks really great! + +689 +00:35:06,340 --> 00:35:08,208 +With the new variants UI in previews, + +690 +00:35:08,242 --> 00:35:11,245 +we can change the color +we're previewing with no code at all. + +691 +00:35:12,346 --> 00:35:14,348 +Or even look at several at once. + +692 +00:35:15,115 --> 00:35:18,185 +And since we used default spacing, +system font styles, + +693 +00:35:18,218 --> 00:35:19,786 +and adapted to the rendering mode, + +694 +00:35:19,820 --> 00:35:23,290 +the same views look right at home +on both the Lock Screen and watch face. + +695 +00:35:23,323 --> 00:35:26,960 +It's that easy to make a widget +on the brand-new Lock Screen in iOS 16 + +696 +00:35:26,994 --> 00:35:30,731 +and a great complication in watchOS 9, +all with the same code. + +697 +00:35:30,764 --> 00:35:33,967 +But this isn't the only way we've brought +the power of WidgetKit to the Lock Screen. + +698 +00:35:34,001 --> 00:35:35,602 +Here's Matt to tell you more. + +699 +00:35:36,170 --> 00:35:37,237 +Matt Shepherd: With WidgetKit, + +700 +00:35:37,271 --> 00:35:39,973 +you can give people access +to glanceable information. + +701 +00:35:40,007 --> 00:35:42,476 +But what about those moments +when they need live updates, + +702 +00:35:42,509 --> 00:35:44,178 +information tied to an activity, + +703 +00:35:44,211 --> 00:35:46,613 +or an event +that they care about right now? + +704 +00:35:46,647 --> 00:35:51,285 +For that, we are working on something new +we call Live Activities. + +705 +00:35:51,318 --> 00:35:53,487 +Live Activities makes it easier +to stay on top of things + +706 +00:35:53,520 --> 00:35:55,289 +that are happening in real time, + +707 +00:35:55,322 --> 00:35:56,890 +right from the Lock Screen. + +708 +00:35:56,924 --> 00:36:00,194 +Things like the latest score from a game, + +709 +00:36:00,227 --> 00:36:03,664 +the progress of a ride share, +or a workout, + +710 +00:36:03,697 --> 00:36:06,233 +right on the Lock Screen, +and always up-to-date. + +711 +00:36:06,266 --> 00:36:09,870 +Just like with widgets, +you create Live Activities with WidgetKit. + +712 +00:36:09,903 --> 00:36:12,573 +The difference is, you update +your Live Activity's presentation + +713 +00:36:12,606 --> 00:36:14,374 +and state in real time. + +714 +00:36:14,408 --> 00:36:17,611 +Since they're built with SwiftUI, +you can even animate your updates + +715 +00:36:17,644 --> 00:36:19,746 +from one state to the next. + +716 +00:36:19,780 --> 00:36:21,782 +These updates make sure +your Live Activities + +717 +00:36:21,815 --> 00:36:25,619 +has the most current information +when the user chooses to glance at it. + +718 +00:36:25,652 --> 00:36:30,657 +Live Activities will be available starting +in an update to iOS 16 later this year. + +719 +00:36:30,691 --> 00:36:33,160 +So those are the updates +to the all-new Lock Screen. + +720 +00:36:33,193 --> 00:36:35,262 +We think they're gonna be a great way +to help you give people + +721 +00:36:35,295 --> 00:36:38,565 +more information at a glance +in the moments they need it most. + +722 +00:36:39,299 --> 00:36:42,769 +Next, let's talk about a brand-new way +to enhance collaborative experiences. + +723 +00:36:42,803 --> 00:36:45,105 +To tell you more, here's Pierre. + +724 +00:36:45,138 --> 00:36:47,140 +Pierre de Fillipis: +Collaboration is an important part + +725 +00:36:47,174 --> 00:36:50,677 +of what people do +on iOS, iPadOS, and macOS. + +726 +00:36:50,711 --> 00:36:53,547 +And that's due in large part +to the incredible wealth of apps + +727 +00:36:53,580 --> 00:36:55,182 +that many of you have built, + +728 +00:36:55,215 --> 00:36:58,051 +to support teams who are collaborating +across any distance. + +729 +00:36:58,085 --> 00:37:01,755 +There's collaboration for work, +like a product road map in Airtable, + +730 +00:37:01,788 --> 00:37:06,393 +and there's also collaboration for play, +like finding your dream home in Redfin. + +731 +00:37:06,426 --> 00:37:08,262 +Whether it's for work or play, + +732 +00:37:08,295 --> 00:37:11,431 +collaboration often starts +with a conversation. + +733 +00:37:11,465 --> 00:37:13,967 +And with the new +Messages Collaboration API, + +734 +00:37:14,001 --> 00:37:17,137 +you can bring your app's +existing collaboration experiences + +735 +00:37:17,171 --> 00:37:18,939 +into Messages and FaceTime. + +736 +00:37:18,972 --> 00:37:21,875 +When users share a link +to content in your app, + +737 +00:37:21,909 --> 00:37:25,579 +the API makes it easy for you +to mark that link as collaborative, + +738 +00:37:25,612 --> 00:37:27,681 +enabling a seamless experience. + +739 +00:37:27,714 --> 00:37:29,816 +We provide the identifiers you need + +740 +00:37:29,850 --> 00:37:32,319 +so you can give access +to the recipients immediately + +741 +00:37:32,352 --> 00:37:34,154 +when they tap the link to join. + +742 +00:37:34,188 --> 00:37:37,090 +And of course, +this works without compromising privacy. + +743 +00:37:37,124 --> 00:37:41,161 +Messages identities and app identities +remain private and are not shared. + +744 +00:37:41,195 --> 00:37:44,998 +And the best part is, you can do this +with existing technologies + +745 +00:37:45,032 --> 00:37:47,467 +your app is most likely already using. + +746 +00:37:47,501 --> 00:37:51,638 +With one object, +your users can initiate collaboration + +747 +00:37:51,672 --> 00:37:54,741 +in two convenient ways +that they're already familiar with. + +748 +00:37:54,775 --> 00:37:57,444 +One, the Share Sheet, +which has been updated + +749 +00:37:57,477 --> 00:37:59,613 +to put collaboration front and center, + +750 +00:37:59,646 --> 00:38:01,181 +and two, drag & drop, + +751 +00:38:01,215 --> 00:38:03,917 +where you can share content +you want to collaborate on + +752 +00:38:03,951 --> 00:38:07,120 +by dragging it directly +into the Messages conversation. + +753 +00:38:07,154 --> 00:38:10,724 +And once the conversation is started, +you can even post notices + +754 +00:38:10,757 --> 00:38:14,228 +about content updates +right to the Messages conversation. + +755 +00:38:14,261 --> 00:38:17,965 +With a couple lines of code, +your users can get back to collaborating + +756 +00:38:17,998 --> 00:38:21,201 +in your app with a single tap in Messages. + +757 +00:38:21,235 --> 00:38:22,870 +And with the collaboration popover, + +758 +00:38:22,903 --> 00:38:26,740 +your users can get back to the +conversation in Messages or FaceTime + +759 +00:38:26,773 --> 00:38:28,075 +right from your app. + +760 +00:38:28,108 --> 00:38:30,110 +So with the Messages Collaboration API, + +761 +00:38:30,143 --> 00:38:33,947 +your app is truly woven into the fabric +of Messages and FaceTime. + +762 +00:38:33,981 --> 00:38:37,851 +We take care of giving your users +powerful communication tools + +763 +00:38:37,885 --> 00:38:40,921 +so you can focus +on the powerful collaboration tools + +764 +00:38:40,954 --> 00:38:42,589 +you deliver in your app. + +765 +00:38:42,623 --> 00:38:47,027 +So this is going to level up collaboration +on iOS, iPadOS, and macOS, + +766 +00:38:47,060 --> 00:38:50,197 +creating a consistent experience +that's deeply rooted + +767 +00:38:50,230 --> 00:38:52,566 +in the connection +between the people collaborating, + +768 +00:38:52,599 --> 00:38:54,101 +whether for work or play. + +769 +00:38:54,134 --> 00:38:56,637 +Next is Ari, who's going to tell you + +770 +00:38:56,670 --> 00:38:59,239 +about a new framework called App Intents. + +771 +00:38:59,273 --> 00:39:02,776 +Ari Weinstein: I'm excited to tell you +about the App Intents framework, + +772 +00:39:02,809 --> 00:39:05,412 +which makes your app's features +available to the system, + +773 +00:39:05,445 --> 00:39:09,349 +so people can use them automatically +through Siri and Shortcuts. + +774 +00:39:09,383 --> 00:39:11,718 +People love using Shortcuts +with their apps. + +775 +00:39:11,752 --> 00:39:15,189 +They let them get things done so fast, +just by asking Siri, + +776 +00:39:15,222 --> 00:39:18,492 +or by quickly tapping a shortcut +on the Home Screen. + +777 +00:39:18,525 --> 00:39:21,595 +And it's amazing +to see how people remix app capabilities + +778 +00:39:21,628 --> 00:39:23,697 +into totally new pieces of functionality + +779 +00:39:23,730 --> 00:39:25,465 +with custom shortcuts. + +780 +00:39:25,499 --> 00:39:29,870 +Today, people have to add shortcuts +manually before they can use them at all. + +781 +00:39:29,903 --> 00:39:34,208 +We're making this automatic in iOS 16 +with the new App Intents framework. + +782 +00:39:35,242 --> 00:39:39,279 +App Intents works together with Shortcuts +to form App Shortcuts, + +783 +00:39:39,313 --> 00:39:41,448 +which people can use with Siri +right away, + +784 +00:39:41,481 --> 00:39:43,283 +without having to set anything up first, + +785 +00:39:43,317 --> 00:39:46,553 +like, "Hey, Siri, clean the kitchen +with Roomba." + +786 +00:39:46,587 --> 00:39:48,155 +But it's not just Siri. + +787 +00:39:48,188 --> 00:39:50,724 +App shortcuts give your users +a front-row seat + +788 +00:39:50,757 --> 00:39:52,860 +to your app's features +throughout the system, + +789 +00:39:52,893 --> 00:39:56,496 +like in Spotlight, where anytime +people search for your app, + +790 +00:39:56,530 --> 00:39:58,699 +your shortcuts show up, too, + +791 +00:39:58,732 --> 00:40:02,469 +and your shortcuts will be suggested +right below app suggestions + +792 +00:40:02,503 --> 00:40:06,473 +without needing to adopt +any additional APIs, like donations. + +793 +00:40:06,507 --> 00:40:09,576 +Your shortcuts also appear immediately +in the Shortcuts app, + +794 +00:40:09,610 --> 00:40:11,678 +where people can run them with a tap. + +795 +00:40:12,346 --> 00:40:15,516 +App Intents is the next step +for the SiriKit Intents framework + +796 +00:40:15,549 --> 00:40:17,751 +that we introduced in iOS10. + +797 +00:40:17,784 --> 00:40:21,188 +If you adopt Intents to integrate +with Widgets or domains + +798 +00:40:21,221 --> 00:40:22,556 +like media or messaging, + +799 +00:40:22,589 --> 00:40:25,792 +you should keep using +the SiriKit Intents framework, + +800 +00:40:25,826 --> 00:40:29,229 +but for developers who build +custom intents for Siri and Shortcuts, + +801 +00:40:29,263 --> 00:40:31,465 +you should go ahead and upgrade +to App Intents. + +802 +00:40:31,498 --> 00:40:34,668 +You can easily upgrade to App Intents +in Xcode + +803 +00:40:34,701 --> 00:40:37,971 +by pushing the Convert button +in your intent definition file. + +804 +00:40:38,005 --> 00:40:41,008 +Xcode will generate the equivalent +App Intents source code, + +805 +00:40:41,041 --> 00:40:43,477 +and then you fill in the blanks +with your intent handling code. + +806 +00:40:43,510 --> 00:40:46,747 +The App Intents framework is really easy +to develop for + +807 +00:40:46,780 --> 00:40:49,883 +because it's designed +from the ground up for Swift, + +808 +00:40:49,917 --> 00:40:52,252 +and it requires much less code. + +809 +00:40:52,286 --> 00:40:55,622 +The Swift code that you write +is the only source of truth, + +810 +00:40:55,656 --> 00:40:57,925 +There's no separate +intent definition files + +811 +00:40:57,958 --> 00:41:00,227 +or code generation to keep in sync. + +812 +00:41:00,260 --> 00:41:02,863 +And the code is easy to add +to your project. + +813 +00:41:02,896 --> 00:41:05,399 +You don't need to rearchitect +your codebase. + +814 +00:41:05,432 --> 00:41:08,502 +Even if you have Objective-C code, +you can use it with App Intents + +815 +00:41:08,535 --> 00:41:10,137 +by wrapping it with Swift. + +816 +00:41:10,170 --> 00:41:13,540 +An app intent represents something +people can do inside of your app, + +817 +00:41:13,574 --> 00:41:16,777 +and it makes it possible to do it +from outside of your app. + +818 +00:41:16,810 --> 00:41:19,513 +You can define an intent +and add an app shortcut + +819 +00:41:19,546 --> 00:41:21,215 +in just a few lines of code. + +820 +00:41:21,248 --> 00:41:23,417 +Let's give it a try together. + +821 +00:41:23,450 --> 00:41:26,119 +Back in the Food Truck app, +I have this great chart view + +822 +00:41:26,153 --> 00:41:28,722 +that lets me see the top five +best-selling donuts + +823 +00:41:28,755 --> 00:41:32,626 +over a given period of time, +like today or this week. + +824 +00:41:32,659 --> 00:41:34,795 +I want to expose this +to Siri and Shortcuts + +825 +00:41:34,828 --> 00:41:37,564 +so people pull it up super quickly, + +826 +00:41:37,598 --> 00:41:41,001 +so first, in Xcode, +I'll go to a new Swift file. + +827 +00:41:41,034 --> 00:41:44,338 +I'll import the App Intents framework +and SwiftUI. + +828 +00:41:45,839 --> 00:41:48,375 +Then I define the intent +by defining a struct + +829 +00:41:48,408 --> 00:41:51,044 +that conforms to the AppIntent protocol. + +830 +00:41:51,078 --> 00:41:53,413 +I'll give it a title. + +831 +00:41:53,447 --> 00:41:57,417 +And I'll add a parameter +for which time frame of trends to look at. + +832 +00:41:57,451 --> 00:42:01,388 +This uses the time frame enum +that's already defined in my codebase. + +833 +00:42:01,421 --> 00:42:05,092 +I need to extend it to conform +to the AppEnum protocol + +834 +00:42:05,125 --> 00:42:07,394 +so that we can extract +human-readable names + +835 +00:42:07,427 --> 00:42:08,829 +for each enum case, + +836 +00:42:08,862 --> 00:42:11,231 +like "today" and "this week." + +837 +00:42:11,265 --> 00:42:15,736 +Next, on the intent, +I'll implement the perform method. + +838 +00:42:15,769 --> 00:42:19,806 +Here, I return a result that includes +the SwiftUI chart view. + +839 +00:42:19,840 --> 00:42:22,809 +I could also include a dialogue +or output value. + +840 +00:42:22,843 --> 00:42:26,547 +I want people to be able to use +this intent automatically, without setup, + +841 +00:42:26,580 --> 00:42:28,282 +so I'll define an app shortcut. + +842 +00:42:30,184 --> 00:42:32,886 +This includes the phrase +that people can say to Siri + +843 +00:42:32,920 --> 00:42:34,655 +to use this intent. + +844 +00:42:34,688 --> 00:42:38,425 +The phrase has to include the app name +as a variable, + +845 +00:42:38,458 --> 00:42:41,061 +and I've included the time frame parameter + +846 +00:42:41,094 --> 00:42:44,131 +so people can say +"Food truck trends for today," + +847 +00:42:44,164 --> 00:42:47,134 +or "Food truck trends for this week." + +848 +00:42:47,167 --> 00:42:50,637 +The last thing I need to do is make this +discoverable for my users. + +849 +00:42:50,671 --> 00:42:52,840 +People need to see the phrase +at some point, + +850 +00:42:52,873 --> 00:42:54,641 +so they know what to say to Siri, + +851 +00:42:54,675 --> 00:42:57,945 +so I'm going to switch to the file +for the Top 5 Donuts view + +852 +00:42:57,978 --> 00:42:59,713 +that we were looking at a second ago, + +853 +00:42:59,746 --> 00:43:01,815 +and I'll add a Siri tip. + +854 +00:43:03,383 --> 00:43:07,554 +Now I can build and run the app +and hop over to my phone. + +855 +00:43:07,588 --> 00:43:08,622 +Let's give it a try. + +856 +00:43:11,124 --> 00:43:13,760 +I can see the shortcut now appears +in the Shortcuts app, + +857 +00:43:13,794 --> 00:43:16,029 +with variants for each parameter value, + +858 +00:43:16,063 --> 00:43:19,600 +and I can run one +just by tapping on it. + +859 +00:43:19,633 --> 00:43:22,936 +And I can run them from Siri +just by saying the phrase, + +860 +00:43:22,970 --> 00:43:25,005 +"Food truck trends for today." + +861 +00:43:26,807 --> 00:43:30,177 +Or I could say, +"food truck trends for this week." + +862 +00:43:32,446 --> 00:43:34,848 +When people are in +the Top Five view of my app, + +863 +00:43:34,882 --> 00:43:36,950 +they'll see this tip we added +at the bottom, + +864 +00:43:36,984 --> 00:43:40,487 +so they know what to say to Siri +to ask for this feature. + +865 +00:43:40,521 --> 00:43:43,824 +Lastly, people can quickly access these +in Spotlight + +866 +00:43:43,857 --> 00:43:45,926 +when they search for the app, like this. + +867 +00:43:48,161 --> 00:43:50,297 +It's super useful. + +868 +00:43:50,330 --> 00:43:54,268 +App Intents will make it easier than ever +before to make your app's functionality + +869 +00:43:54,301 --> 00:43:58,939 +available throughout the system experience +on all of these platforms. + +870 +00:43:58,972 --> 00:44:01,441 +Next, Ricky will tell us +about some big updates + +871 +00:44:01,475 --> 00:44:03,377 +to authentication technologies. + +872 +00:44:04,044 --> 00:44:05,612 +Ricky Mondello: +For as long as we can remember, + +873 +00:44:05,646 --> 00:44:08,215 +we've been creating and using passwords. + +874 +00:44:08,248 --> 00:44:10,417 +But passwords have serious issues, + +875 +00:44:10,450 --> 00:44:14,588 +like phishing, reuse across accounts, +and website leaks. + +876 +00:44:14,621 --> 00:44:18,258 +The good news is that together +we can solve these issues. + +877 +00:44:18,292 --> 00:44:21,328 +And we can do this today with Passkeys. + +878 +00:44:21,361 --> 00:44:24,298 +Passkeys will streamline +your authentication flows + +879 +00:44:24,331 --> 00:44:27,601 +and address the top security issues +with passwords. + +880 +00:44:27,634 --> 00:44:30,904 +Passkeys were designed +to be incredibly easy to use. + +881 +00:44:30,938 --> 00:44:34,441 +The interface uses familiar +Autofill-style UI + +882 +00:44:34,474 --> 00:44:37,945 +and FaceID and TouchID +for biometric verification. + +883 +00:44:37,978 --> 00:44:41,615 +These elements create a seamless +transition away from passwords, + +884 +00:44:41,648 --> 00:44:45,152 +while delivering +a profound increase in security. + +885 +00:44:45,185 --> 00:44:47,354 +Let's check out Passkeys in action. + +886 +00:44:48,155 --> 00:44:50,257 +When setting up an account with a passkey, + +887 +00:44:50,290 --> 00:44:52,526 +I don't need to create a password. + +888 +00:44:52,559 --> 00:44:57,664 +I'll type a user name and save +the passkey to my iCloud Keychain. + +889 +00:44:57,698 --> 00:45:02,135 +This will securely sync this passkey +to all of my other Apple devices. + +890 +00:45:02,169 --> 00:45:06,273 +And if I sign out, +signing back in is a breeze. + +891 +00:45:06,306 --> 00:45:09,476 +Just Face ID, and I'm in. + +892 +00:45:09,510 --> 00:45:12,713 +Because passkeys are built +on open industry standards + +893 +00:45:12,746 --> 00:45:14,448 +that platforms are adopting, + +894 +00:45:14,481 --> 00:45:17,751 +I can use the passkey +I just created on my iPhone + +895 +00:45:17,784 --> 00:45:21,255 +to sign into the Food Truck website +on my friend's PC. + +896 +00:45:21,288 --> 00:45:25,359 +On the website, +I'll type my username, submit, + +897 +00:45:25,392 --> 00:45:29,062 +and choose the option to sign in +using a phone, + +898 +00:45:29,096 --> 00:45:31,498 +scan the QR code, + +899 +00:45:31,532 --> 00:45:36,737 +let the iPhone and PC securely connect, + +900 +00:45:36,770 --> 00:45:39,306 +and I'm signed in. + +901 +00:45:39,339 --> 00:45:42,709 +In Safari on my Mac, +it's even easier to sign in. + +902 +00:45:42,743 --> 00:45:45,779 +My passkey is already here, +thanks to iCloud Keychain, + +903 +00:45:45,812 --> 00:45:49,449 +and I can sign in directly +from the website's username field. + +904 +00:45:49,483 --> 00:45:53,287 +It's easy to integrate passkeys +into existing sign-in flows. + +905 +00:45:53,320 --> 00:45:57,591 +For example, this website's username field +lets me sign in with a passkey + +906 +00:45:57,624 --> 00:45:59,259 +or a password. + +907 +00:45:59,293 --> 00:46:04,031 +If I type a username +for a password based account, + +908 +00:46:04,064 --> 00:46:05,599 +I can quickly sign in. + +909 +00:46:05,632 --> 00:46:08,402 +With passkeys, +the device does the hard work, + +910 +00:46:08,435 --> 00:46:10,771 +and it's secure every time. + +911 +00:46:10,804 --> 00:46:12,306 +When creating a passkey, + +912 +00:46:12,339 --> 00:46:16,143 +the device generates a unique key +that is specific to the website or app + +913 +00:46:16,176 --> 00:46:19,880 +it was created for +and protects it behind biometrics. + +914 +00:46:19,913 --> 00:46:22,449 +It's impossible to have a weak passkey. + +915 +00:46:22,482 --> 00:46:25,385 +It can't be forgotten, reused, or guessed. + +916 +00:46:25,419 --> 00:46:28,555 +Passkeys are based +on public key cryptography, + +917 +00:46:28,589 --> 00:46:32,259 +which makes credential leaks +from servers a thing of the past. + +918 +00:46:32,292 --> 00:46:35,596 +Instead of storing salted, +hashed passwords, + +919 +00:46:35,629 --> 00:46:40,734 +that can leak and be cracked, +your server keeps only a public key. + +920 +00:46:40,767 --> 00:46:43,270 +Public keys are designed +to be truly public, + +921 +00:46:43,303 --> 00:46:45,572 +and not at all valuable to hackers. + +922 +00:46:45,606 --> 00:46:49,376 +This significantly reduces your risk +as a website owner. + +923 +00:46:49,409 --> 00:46:52,346 +With passkeys– +and this point is huge– + +924 +00:46:52,379 --> 00:46:55,382 +credential phishing as we know it today +is gone, + +925 +00:46:55,415 --> 00:46:59,620 +eliminating the number one +security vulnerability that users face. + +926 +00:46:59,653 --> 00:47:03,690 +Passkeys are intrinsically linked to the +website or app they were set up for, + +927 +00:47:03,724 --> 00:47:07,995 +so users can never be tricked into +using their passkey on the wrong website. + +928 +00:47:08,028 --> 00:47:09,696 +And unlike passwords, + +929 +00:47:09,730 --> 00:47:12,533 +it's not possible to type +or copy a passkey + +930 +00:47:12,566 --> 00:47:14,668 +into a convincing fake website, + +931 +00:47:14,701 --> 00:47:18,405 +or even give anything away to +someone looking over your shoulder. + +932 +00:47:18,438 --> 00:47:21,308 +When you put it all together, +what we're talking about here + +933 +00:47:21,341 --> 00:47:23,810 +is a new era of account security. + +934 +00:47:23,844 --> 00:47:27,915 +Bringing passkeys to your app +and website takes only a few steps. + +935 +00:47:27,948 --> 00:47:31,251 +First, you'll teach your account backend +to store public keys + +936 +00:47:31,285 --> 00:47:33,854 +and issue authentication challenges. + +937 +00:47:33,887 --> 00:47:36,356 +Then, on your website and in your app, + +938 +00:47:36,390 --> 00:47:38,225 +you'll offer passkeys to users + +939 +00:47:38,258 --> 00:47:42,496 +and adopt API to create +a new passkey and sign in with it. + +940 +00:47:42,529 --> 00:47:47,100 +Passkeys are based on the Web +Authentication, or WebAuthn, standard, + +941 +00:47:47,134 --> 00:47:50,037 +which has been a collaborative effort +across the industry + +942 +00:47:50,070 --> 00:47:53,574 +from both platform vendors +and service owners. + +943 +00:47:53,607 --> 00:47:56,777 +The standard itself is mature +and well documented, + +944 +00:47:56,810 --> 00:47:59,279 +and passkeys fit it like a glove. + +945 +00:47:59,313 --> 00:48:02,850 +All of this is ready +for you to build on right now. + +946 +00:48:02,883 --> 00:48:06,620 +Next generation security, +a seamless user experience, + +947 +00:48:06,653 --> 00:48:10,991 +and a design that works beautifully +alongside passwords during the transition. + +948 +00:48:11,024 --> 00:48:12,626 +Back to you, Sebastien. + +949 +00:48:12,659 --> 00:48:15,362 +Sebastien: You've just seen +a few of the newest ways + +950 +00:48:15,395 --> 00:48:19,900 +that you can integrate your apps with the +system experience on all of our platforms. + +951 +00:48:19,933 --> 00:48:23,637 +And beyond those integration points, +there are a ton of new APIs + +952 +00:48:23,670 --> 00:48:25,839 +and frameworks +across all of our platforms + +953 +00:48:25,873 --> 00:48:29,643 +that open up even more possibilities +for you and your apps this year. + +954 +00:48:30,477 --> 00:48:32,479 +And I'd like to walk you through a few + +955 +00:48:32,513 --> 00:48:35,949 +before diving into some others +in more detail. + +956 +00:48:35,983 --> 00:48:38,585 +Let's start with iPadOS. + +957 +00:48:38,619 --> 00:48:43,090 +With iPadOS 16, you'll be able to make +the most powerful iPad apps yet, + +958 +00:48:43,123 --> 00:48:46,493 +with a consistent, +desktop-like experience. + +959 +00:48:46,527 --> 00:48:50,097 +There's a seamless find-and-replace +experience for UI text views + +960 +00:48:50,130 --> 00:48:52,299 +that your apps get automatically, + +961 +00:48:52,332 --> 00:48:54,601 +as well as updates to the navigation bar, + +962 +00:48:54,635 --> 00:48:56,570 +toolbars, the document menu, + +963 +00:48:56,603 --> 00:48:59,506 +that make it easy for your users +to manage documents + +964 +00:48:59,540 --> 00:49:01,742 +and customize their experience. + +965 +00:49:01,775 --> 00:49:05,913 +To enable even more powerful applications +of iPad with connected hardware, + +966 +00:49:05,946 --> 00:49:07,915 +DriverKit comes to iPad, + +967 +00:49:07,948 --> 00:49:11,451 +helping to unlock +the incredible power of the M1 chip. + +968 +00:49:11,485 --> 00:49:14,154 +It's the same API +that's available on Mac today, + +969 +00:49:14,188 --> 00:49:18,525 +enabling you to easily deliver support +for your USB, audio, + +970 +00:49:18,559 --> 00:49:21,428 +and PCI devices +to an even larger audience. + +971 +00:49:22,763 --> 00:49:27,000 +Now, watchOS is creating new opportunities +for apps through deeper integration + +972 +00:49:27,034 --> 00:49:28,869 +with system services. + +973 +00:49:28,902 --> 00:49:31,405 +The CallKit framework in watchOS 9 + +974 +00:49:31,438 --> 00:49:33,907 +includes a new +Voiceover IP background mode + +975 +00:49:33,941 --> 00:49:37,377 +that lets apps make voice calls +directly from Apple Watch, + +976 +00:49:37,411 --> 00:49:42,049 +with the same familiar user experience +as FaceTime audio and phone calls. + +977 +00:49:42,082 --> 00:49:45,452 +And Bluetooth-connected medical devices +get more robust connectivity + +978 +00:49:45,485 --> 00:49:46,854 +and data delivery, + +979 +00:49:46,887 --> 00:49:50,457 +allowing for timely alerts +when a critical condition is detected. + +980 +00:49:51,859 --> 00:49:55,796 +Now, tvOS 16 gives you new ways +to create connected experiences + +981 +00:49:55,829 --> 00:49:57,564 +between your apps on Apple TV + +982 +00:49:57,598 --> 00:50:02,069 +and iPhone, iPad, or Apple Watch apps +on nearby devices. + +983 +00:50:02,102 --> 00:50:05,239 +So a workout could use motion data +from Apple Watch, + +984 +00:50:05,272 --> 00:50:06,907 +or you could use iPhone or iPad + +985 +00:50:06,940 --> 00:50:10,344 +as a custom controller +for your turn-based games, + +986 +00:50:10,377 --> 00:50:14,314 +And tvOS manages device discovery +and connection for you, + +987 +00:50:14,348 --> 00:50:16,617 +so your app doesn't even need +to be running on the other device. + +988 +00:50:16,650 --> 00:50:18,886 +In fact, if your app isn't installed, + +989 +00:50:18,919 --> 00:50:21,421 +the user is automatically prompted +to download it + +990 +00:50:21,455 --> 00:50:23,090 +right from the App Store. + +991 +00:50:23,957 --> 00:50:27,861 +Now, for Phone and iPad, +there are new tools for advertisers. + +992 +00:50:27,895 --> 00:50:31,632 +We know that effective advertising +is important to a lot of your businesses, + +993 +00:50:31,665 --> 00:50:34,568 +which is why we created SKAdNetwork. + +994 +00:50:34,601 --> 00:50:37,371 +It's an API that helps ad networks +and advertisers + +995 +00:50:37,404 --> 00:50:40,741 +measure the performance of campaigns +without tracking users, + +996 +00:50:40,774 --> 00:50:45,112 +and we've been pleased to see +many third-party ad networks adopt it. + +997 +00:50:45,145 --> 00:50:47,848 +Now, we've heard feedback from +ad networks and developers, + +998 +00:50:47,881 --> 00:50:51,318 +and this year, we made a number +of improvements to SKAdNetwork + +999 +00:50:51,351 --> 00:50:53,187 +that reflect some of the biggest requests + +1000 +00:50:53,220 --> 00:50:55,656 +and give you +dramatically more flexibility, + +1001 +00:50:55,689 --> 00:50:58,825 +all without compromising user privacy. + +1002 +00:50:59,960 --> 00:51:02,529 +Now, on iPhone and iPad, +there are new cool features + +1003 +00:51:02,563 --> 00:51:04,798 +that use AR and LiDAR scanning + +1004 +00:51:04,831 --> 00:51:07,201 +with ScanKit and RoomPlan. + +1005 +00:51:07,234 --> 00:51:11,038 +These APIs let your apps create +rich 3D parametric room models + +1006 +00:51:11,071 --> 00:51:13,774 +in USD and USDZ formats. + +1007 +00:51:13,807 --> 00:51:16,910 +So you can create +a variety of workflows and experiences, + +1008 +00:51:16,944 --> 00:51:20,514 +from architecture and design, +to retail and hospitality, + +1009 +00:51:20,547 --> 00:51:24,117 +and the models include furniture +classification for categories + +1010 +00:51:24,151 --> 00:51:29,590 +such as sofas, cabinets, TVs, +and yes, even kitchen sinks. + +1011 +00:51:29,623 --> 00:51:34,094 +Now, last year, we introduced Focus +for iPhone, iPad, Mac, and Apple Watch, + +1012 +00:51:34,127 --> 00:51:39,800 +and with it, ways for your app to manage +notifications based on a user's Focus. + +1013 +00:51:39,833 --> 00:51:43,337 +And this year, +Focus goes further with Focus filters. + +1014 +00:51:43,370 --> 00:51:46,940 +They're built on top of App Intents, +and Focus filters let you adjust + +1015 +00:51:46,974 --> 00:51:50,544 +the content of your app +based on the user's current focus. + +1016 +00:51:50,577 --> 00:51:53,380 +So, for example, +an app could create a Focus filter + +1017 +00:51:53,413 --> 00:51:57,985 +to only show work accounts +when the user is in their Work Focus. + +1018 +00:51:58,018 --> 00:52:01,555 +And those examples are really +just scratching the surface. + +1019 +00:52:01,588 --> 00:52:04,625 +Across the board at every level, +there are new tools and APIs + +1020 +00:52:04,658 --> 00:52:08,529 +with the power that you need +to take your apps further than ever + +1021 +00:52:08,562 --> 00:52:11,265 +and to create entirely new apps +and experiences. + +1022 +00:52:11,298 --> 00:52:13,700 +So next, let's go a little deeper + +1023 +00:52:13,734 --> 00:52:18,205 +starting with Metal, a technology that's +really taking things to the next level. + +1024 +00:52:18,238 --> 00:52:21,108 +And to tell you more, let's go to Sarah. + +1025 +00:52:21,141 --> 00:52:28,148 +♪ ♪ + +1026 +00:52:29,416 --> 00:52:32,452 +Sarah Clawson: Metal is the powerful +graphics and compute API + +1027 +00:52:32,486 --> 00:52:36,590 +that helps you create amazing games +and pro apps for Apple platforms. + +1028 +00:52:36,623 --> 00:52:38,492 +Metal makes it easy to take advantage + +1029 +00:52:38,525 --> 00:52:42,062 +of the groundbreaking Apple GPUs +and unified memory system + +1030 +00:52:42,095 --> 00:52:45,165 +now spanning the latest iPhone, +iPad, and Mac lineups + +1031 +00:52:45,199 --> 00:52:46,867 +shipping with Apple silicon. + +1032 +00:52:46,900 --> 00:52:50,938 +And this year, we're introducing Metal 3, + +1033 +00:52:50,971 --> 00:52:52,673 +with powerful new features that help you + +1034 +00:52:52,706 --> 00:52:53,841 +render immersive graphics + +1035 +00:52:53,874 --> 00:52:55,442 +with even higher frame rates + +1036 +00:52:55,475 --> 00:52:56,643 +and enable new levels + +1037 +00:52:56,677 --> 00:52:58,478 +of computational performance. + +1038 +00:52:58,512 --> 00:53:01,181 +For instance, +you'll get huge performance gains + +1039 +00:53:01,215 --> 00:53:03,350 +for the machine learning framework, +PyTorch, + +1040 +00:53:03,383 --> 00:53:05,252 +which now uses the new Metal backend + +1041 +00:53:05,285 --> 00:53:07,721 +to enable ML training with the GPU. + +1042 +00:53:07,754 --> 00:53:10,424 +And the biggest focus area is on gaming, + +1043 +00:53:10,457 --> 00:53:12,359 +starting with game loading, + +1044 +00:53:12,392 --> 00:53:14,661 +a key element to the gaming experience + +1045 +00:53:14,695 --> 00:53:17,364 +that can affect launch time +and loading new levels. + +1046 +00:53:17,397 --> 00:53:20,067 +Modern games deliver +a rich gaming experience + +1047 +00:53:20,100 --> 00:53:22,436 +by providing high-quality assets, + +1048 +00:53:22,469 --> 00:53:26,440 +and loading these assets quickly from +storage to the GPU can be challenging. + +1049 +00:53:26,473 --> 00:53:30,344 +Often, games will hide asset loading +behind a loading screen, + +1050 +00:53:30,377 --> 00:53:32,846 +and one technique +to launch gameplay faster + +1051 +00:53:32,880 --> 00:53:34,982 +is to load and draw +a lower quality version + +1052 +00:53:35,015 --> 00:53:37,651 +until the high-quality visuals +are available. + +1053 +00:53:37,684 --> 00:53:39,786 +This is not an ideal gaming experience + +1054 +00:53:39,820 --> 00:53:43,323 +since the user sees +lower quality graphics for longer. + +1055 +00:53:43,357 --> 00:53:46,093 +Metal 3 introduces fast resource loading + +1056 +00:53:46,126 --> 00:53:47,794 +with the Metal IO API + +1057 +00:53:47,828 --> 00:53:51,598 +that takes advantage of the Apple GPU's +unified memory architecture + +1058 +00:53:51,632 --> 00:53:53,267 +to minimize loading overhead + +1059 +00:53:53,300 --> 00:53:55,936 +and ensures +that the high-speed SSD storage + +1060 +00:53:55,969 --> 00:53:58,038 +that ships with every Apple silicon Mac + +1061 +00:53:58,071 --> 00:54:01,742 +has enough requests in its queues +to maximize throughput. + +1062 +00:54:01,775 --> 00:54:05,045 +This new API provides faster +and more consistent performance + +1063 +00:54:05,078 --> 00:54:07,981 +so that more time is spent +drawing at the ideal quality. + +1064 +00:54:09,316 --> 00:54:12,319 +In addition to moving resources +from storage to memory, + +1065 +00:54:12,352 --> 00:54:15,122 +game loading +is also about shader compilation. + +1066 +00:54:15,155 --> 00:54:16,924 +Shaders always need to be compiled + +1067 +00:54:16,957 --> 00:54:19,359 +for the user's unique +hardware configuration, + +1068 +00:54:19,393 --> 00:54:22,296 +and with the wide variety +of PC hardware permutations, + +1069 +00:54:22,329 --> 00:54:24,631 +this usually has to be done at runtime. + +1070 +00:54:24,665 --> 00:54:27,901 +This in-game compilation can affect +the gamer's experience + +1071 +00:54:27,935 --> 00:54:31,505 +causing dropped frames, +slower frame rates, and longer loading. + +1072 +00:54:31,538 --> 00:54:35,542 +In contrast, Apple silicon and Metal 3 +are designed together + +1073 +00:54:35,576 --> 00:54:37,311 +to support all Apple devices. + +1074 +00:54:37,344 --> 00:54:39,713 +And now, with offline shader compilation, + +1075 +00:54:39,746 --> 00:54:43,350 +you can generate GPU shader binaries +at project build time, + +1076 +00:54:43,383 --> 00:54:46,320 +enabling you to eliminate +in-game shader compilation + +1077 +00:54:46,353 --> 00:54:49,556 +to reduce load times +and improve rendering performance. + +1078 +00:54:49,590 --> 00:54:54,294 +Another important aspect to gaming +is providing rich, detailed assets, + +1079 +00:54:54,328 --> 00:54:57,497 +and one way to increase the visual +fidelity of your game's graphics + +1080 +00:54:57,531 --> 00:55:01,502 +is by generating much more sophisticated +geometric meshes. + +1081 +00:55:01,535 --> 00:55:04,037 +Traditionally this is done +with a compute pass + +1082 +00:55:04,071 --> 00:55:06,507 +that'll evaluate the surface +and generate geometry + +1083 +00:55:06,540 --> 00:55:08,609 +to be used in a later render pass. + +1084 +00:55:08,642 --> 00:55:11,245 +The challenge is that +this can introduce latency + +1085 +00:55:11,278 --> 00:55:13,614 +and take an unpredictable +amount of memory. + +1086 +00:55:13,647 --> 00:55:16,650 +Metal 3 introduces a new Mesh Shading API, + +1087 +00:55:16,683 --> 00:55:20,821 +which gives you precise control over +an optimized geometry processing pipeline + +1088 +00:55:20,854 --> 00:55:22,456 +from a single render pass. + +1089 +00:55:22,489 --> 00:55:25,359 +The Object shader decides +how many meshes to generate, + +1090 +00:55:25,392 --> 00:55:27,628 +and the Mesh shader +generates the actual geometry + +1091 +00:55:27,661 --> 00:55:30,063 +to be sent directly to the rasterizer, + +1092 +00:55:30,097 --> 00:55:33,433 +avoiding a trip to device memory +and increasing performance. + +1093 +00:55:33,467 --> 00:55:36,236 +Gamers also want to see +these stunning visuals + +1094 +00:55:36,270 --> 00:55:38,005 +at the highest possible frame rate, + +1095 +00:55:38,038 --> 00:55:41,241 +but rendering advanced graphics +at ultra-high resolutions + +1096 +00:55:41,275 --> 00:55:42,976 +can cost precious milliseconds. + +1097 +00:55:43,010 --> 00:55:46,246 +MetalFX upscaling helps +you render immersive graphics + +1098 +00:55:46,280 --> 00:55:47,948 +in less time per frame. + +1099 +00:55:47,981 --> 00:55:49,516 +Here's how it works. + +1100 +00:55:49,550 --> 00:55:52,986 +Previously, you would render +your full frame at native resolution, + +1101 +00:55:53,020 --> 00:55:56,423 +but the GPU render time +might not hit the target frame time. + +1102 +00:55:56,456 --> 00:56:00,260 +Now, you can render the same +complex scene at a lower resolution + +1103 +00:56:00,294 --> 00:56:03,363 +to meet the target frame times, +and use MetalFX framework + +1104 +00:56:03,397 --> 00:56:07,835 +to perform temporal antialiasing +and upscaling to the target resolution. + +1105 +00:56:07,868 --> 00:56:10,804 +With Apple silicon and Metal 3's +optimized features, + +1106 +00:56:10,838 --> 00:56:13,273 +gaming has never looked so good +on the Mac. + +1107 +00:56:13,307 --> 00:56:14,975 +And developers agree. + +1108 +00:56:15,008 --> 00:56:18,412 +Leading game studios have plans to bring +their titles to the Mac, + +1109 +00:56:18,445 --> 00:56:21,448 +like Grid Legends, +taking advantage of Apple silicon + +1110 +00:56:21,481 --> 00:56:23,784 +to help you reach maximum speeds. + +1111 +00:56:23,817 --> 00:56:27,554 +Or Resident Evil Village, +using features like MetalFX upscaling + +1112 +00:56:27,588 --> 00:56:31,525 +to deliver hauntingly beautiful scenes +at the highest resolution. + +1113 +00:56:31,558 --> 00:56:34,127 +And No Man's Sky, +taking advantage of Metal 3 + +1114 +00:56:34,161 --> 00:56:35,963 +to explore rich, expansive worlds + +1115 +00:56:35,996 --> 00:56:37,731 +on both Mac and iPad. + +1116 +00:56:37,764 --> 00:56:41,502 +Metal 3 is incredible, +with features to boost the performance + +1117 +00:56:41,535 --> 00:56:44,771 +of your apps +and provide an amazing gaming experience. + +1118 +00:56:44,805 --> 00:56:49,276 +Now to tell us more about the direction +MapKit is headed, here's Kathy. + +1119 +00:56:49,309 --> 00:56:51,445 +Kathy Lin: Whether you're navigating +to a favorite restaurant, + +1120 +00:56:51,478 --> 00:56:53,146 +planning that next vacation, + +1121 +00:56:53,180 --> 00:56:56,016 +or just checking where your favorite +food truck is parked on a map, + +1122 +00:56:56,049 --> 00:57:00,654 +we rely on our devices more than ever +to help us explore the world around us. + +1123 +00:57:00,687 --> 00:57:04,558 +MapKit is the best way to help users +discover and navigate the world + +1124 +00:57:04,591 --> 00:57:07,528 +with rich and flexible mapping +and location services, + +1125 +00:57:07,561 --> 00:57:11,632 +powered by Apple Maps +and available to developers for free. + +1126 +00:57:11,665 --> 00:57:15,836 +With MapKit, you can display map +or satellite imagery in your app, + +1127 +00:57:15,869 --> 00:57:17,771 +find and call out points of interest, + +1128 +00:57:17,804 --> 00:57:19,406 +add annotations and overlays, + +1129 +00:57:19,439 --> 00:57:21,575 +get directions, and more. + +1130 +00:57:21,608 --> 00:57:24,011 +MapKit is powered by our all-new map, + +1131 +00:57:24,044 --> 00:57:26,413 +built from the ground up by Apple. + +1132 +00:57:26,446 --> 00:57:28,415 +It offers improved detail and accuracy, + +1133 +00:57:28,448 --> 00:57:32,119 +and can bring useful mapping +and location services to your app. + +1134 +00:57:32,152 --> 00:57:34,588 +With iOS 16, +we're building on this map + +1135 +00:57:34,621 --> 00:57:37,357 +to introduce our biggest update ever +for MapKit, + +1136 +00:57:37,391 --> 00:57:42,196 +starting with making the 3D City +Experience available to all developers. + +1137 +00:57:42,229 --> 00:57:45,199 +Users of your apps will be able +to see incredible details, + +1138 +00:57:45,232 --> 00:57:47,968 +like 3D elevation, turn lanes, + +1139 +00:57:48,001 --> 00:57:49,436 +crosswalks and bike lanes, + +1140 +00:57:49,469 --> 00:57:52,206 +and amazing handcrafted 3D landmarks + +1141 +00:57:52,239 --> 00:57:54,675 +like the Golden Gate Bridge, +or the Ferry Building. + +1142 +00:57:54,708 --> 00:57:58,579 +The additional detail of the map allows +you to provide context and precision + +1143 +00:57:58,612 --> 00:58:01,348 +that was never before possible. + +1144 +00:58:01,381 --> 00:58:03,550 +You can, for example, +show that a point of interest + +1145 +00:58:03,584 --> 00:58:07,054 +is between the crosswalk +and where the bike lane starts. + +1146 +00:58:07,087 --> 00:58:09,323 +No other digital map lets you do that, + +1147 +00:58:09,356 --> 00:58:12,526 +and we've made it incredibly easy +to implement. + +1148 +00:58:12,559 --> 00:58:14,428 +To show you more, +let's create an experience + +1149 +00:58:14,461 --> 00:58:17,898 +that makes it easy for a user to find +where their favorite food truck is parked + +1150 +00:58:17,931 --> 00:58:20,567 +using the details of the new map. + +1151 +00:58:20,601 --> 00:58:23,971 +Map views like this one will automatically +get the 3D City Experience + +1152 +00:58:24,004 --> 00:58:25,205 +where it's available. + +1153 +00:58:25,239 --> 00:58:28,408 +Just select iOS 16 +as the deployment target. + +1154 +00:58:28,442 --> 00:58:31,745 +Next, I can utilize +the extraordinary detail of the map + +1155 +00:58:31,778 --> 00:58:35,482 +to illustrate the exact location +of the food truck. + +1156 +00:58:35,516 --> 00:58:39,520 +MapKit has powerful controls that allow us +to position the camera in 3D space + +1157 +00:58:39,553 --> 00:58:42,289 +to create a precise view of the map. + +1158 +00:58:42,322 --> 00:58:44,825 +Here, I can choose how far +we want to be zoomed in + +1159 +00:58:44,858 --> 00:58:48,929 +by setting the distance of the center +coordinate of the camera to 600 meters. + +1160 +00:58:48,962 --> 00:58:52,165 +By adjusting the pitch and heading +and tilting the camera into 3D, + +1161 +00:58:52,199 --> 00:58:55,169 +you can see amazing +and useful details like turn lanes, + +1162 +00:58:55,202 --> 00:58:57,938 +crosswalks, and even trees. + +1163 +00:58:57,971 --> 00:59:00,641 +By default, elevation will be flattened. + +1164 +00:59:00,674 --> 00:59:03,577 +In order to help users understand +the terrain they'll encounter, + +1165 +00:59:03,610 --> 00:59:07,981 +I can specify a preferredConfiguration +with elevationStyle 'realistic' + +1166 +00:59:08,015 --> 00:59:09,950 +to include 3D elevation. + +1167 +00:59:11,018 --> 00:59:15,422 +When adding an annotation or a route line +sourced from MapKit's Directions API, + +1168 +00:59:15,455 --> 00:59:17,624 +MapKit automatically handles elevation + +1169 +00:59:17,658 --> 00:59:20,093 +and will adjust the annotation +or route line + +1170 +00:59:20,127 --> 00:59:23,997 +by placing it on top of the 3D terrain. + +1171 +00:59:24,031 --> 00:59:26,633 +Animating the camera heading +by adding a slow pan + +1172 +00:59:26,667 --> 00:59:29,903 +really brings the map view to life. + +1173 +00:59:29,937 --> 00:59:31,605 +When a user switches into Dark Mode, + +1174 +00:59:31,638 --> 00:59:34,408 +the map will adjust together +with the rest of the UI. + +1175 +00:59:34,441 --> 00:59:37,344 +We're very excited to make +this immersive experience available + +1176 +00:59:37,377 --> 00:59:39,813 +to developers with iOS 16. + +1177 +00:59:39,847 --> 00:59:43,984 +In addition, we're also bringing another +popular Apple Maps feature to MapKit, + +1178 +00:59:44,017 --> 00:59:45,619 +Look Around, + +1179 +00:59:45,652 --> 00:59:49,122 +which is a great way +to explore the world at ground level, + +1180 +00:59:49,156 --> 00:59:52,559 +with high resolution 3D photography +and smooth animations. + +1181 +00:59:52,593 --> 00:59:55,462 +Users can simply tap +to move down the street. + +1182 +00:59:57,164 --> 01:00:00,067 +I can add a static Look Around preview +just below the map + +1183 +01:00:00,100 --> 01:00:03,770 +by dropping in a View Controller +and specifying a MapItem. + +1184 +01:00:03,804 --> 01:00:07,407 +The Look Around view automatically frames +the location correctly. + +1185 +01:00:07,441 --> 01:00:09,776 +When a user taps on the preview, +I can choose to provide + +1186 +01:00:09,810 --> 01:00:11,678 +a full screen Look Around view, + +1187 +01:00:11,712 --> 01:00:13,647 +where users can see the address, + +1188 +01:00:13,680 --> 01:00:15,516 +the date the imagery was collected, + +1189 +01:00:15,549 --> 01:00:17,751 +and they can tap to move around freely + +1190 +01:00:17,784 --> 01:00:20,587 +to get a better understanding +of their surroundings. + +1191 +01:00:20,621 --> 01:00:23,390 +There's one more new, +highly-requested capability + +1192 +01:00:23,423 --> 01:00:26,193 +we're introducing to MapKit in iOS 16– + +1193 +01:00:26,226 --> 01:00:28,462 +Apple Maps Server APIs. + +1194 +01:00:28,495 --> 01:00:30,330 +Maps Server APIs are RESTful + +1195 +01:00:30,364 --> 01:00:33,534 +and support four of the most used +functions of MapKit: + +1196 +01:00:33,567 --> 01:00:36,703 +Geocode, +which turns a lat/long into an address; + +1197 +01:00:36,737 --> 01:00:39,039 +Reverse Geocode, +which does the opposite– + +1198 +01:00:39,072 --> 01:00:41,775 +it turns an address into GPS coordinates; + +1199 +01:00:41,808 --> 01:00:44,278 +Search; +and Estimated Times of Arrival. + +1200 +01:00:44,311 --> 01:00:47,080 +Our new Maps Server APIs +are a great way + +1201 +01:00:47,114 --> 01:00:51,552 +to make your own backend services richer +and more performant. + +1202 +01:00:51,585 --> 01:00:53,887 +Of course, +MapKit is built from the ground up + +1203 +01:00:53,921 --> 01:00:56,156 +on the same foundation of privacy +as Apple Maps, + +1204 +01:00:56,190 --> 01:00:59,393 +and does not associate users' data +with their identity + +1205 +01:00:59,426 --> 01:01:02,062 +or keep a history of where they've been. + +1206 +01:01:02,095 --> 01:01:05,666 +And that's a quick look at +what's new with MapKit in iOS 16. + +1207 +01:01:05,699 --> 01:01:09,102 +Now for the weather, or at least +how you can build it into your app, + +1208 +01:01:09,136 --> 01:01:10,804 +here's Novall. + +1209 +01:01:10,838 --> 01:01:15,142 +Novall Khan: We announced today that we're +bringing the Weather app to iPad and Mac, + +1210 +01:01:15,175 --> 01:01:17,544 +and introducing powerful new features, + +1211 +01:01:17,578 --> 01:01:21,248 +including severe weather notifications, +rich detail views, + +1212 +01:01:21,281 --> 01:01:25,185 +and ten days of hourly temperature +and precipitation forecasts. + +1213 +01:01:25,219 --> 01:01:29,423 +And there are all kinds of +other experiences across Apple devices + +1214 +01:01:29,456 --> 01:01:32,826 +and platforms that get better because +of the weather data we provide– + +1215 +01:01:32,860 --> 01:01:35,462 +from asking Siri for today's forecast, + +1216 +01:01:35,495 --> 01:01:38,398 +to rerouting navigation around flooding. + +1217 +01:01:38,432 --> 01:01:42,202 +All of that is built on +our Apple Weather service. + +1218 +01:01:42,236 --> 01:01:45,539 +Apple Weather delivers +a world-class global weather forecast + +1219 +01:01:45,572 --> 01:01:48,208 +using high-resolution +meteorological models + +1220 +01:01:48,242 --> 01:01:51,078 +combined with machine learning +and prediction algorithms. + +1221 +01:01:51,111 --> 01:01:53,614 +Apple Weather provides current weather, + +1222 +01:01:53,647 --> 01:01:56,683 +10-day hourly forecasts, daily forecasts, + +1223 +01:01:56,717 --> 01:02:00,521 +and historical weather +so you can evaluate trends in data. + +1224 +01:02:00,554 --> 01:02:03,257 +Severe weather alerts +and minute-by-minute precipitation + +1225 +01:02:03,290 --> 01:02:07,027 +are also available +for select countries around the world. + +1226 +01:02:07,060 --> 01:02:10,497 +Forecasts feature 10 days +of hour-by-hour temperature, + +1227 +01:02:10,531 --> 01:02:13,166 +precipitation, UV index forecasts, + +1228 +01:02:13,200 --> 01:02:14,535 +and much more. + +1229 +01:02:15,269 --> 01:02:19,339 +And all of this data is available to you +through WeatherKit. + +1230 +01:02:19,373 --> 01:02:23,377 +WeatherKit is a native Swift API +for all Apple platforms, + +1231 +01:02:23,410 --> 01:02:25,779 +and a REST API you can use from anywhere. + +1232 +01:02:25,812 --> 01:02:29,616 +These APIs deliver accurate, +hyperlocal weather forecasts, + +1233 +01:02:29,650 --> 01:02:33,954 +to help your users stay safe, +informed, and prepared. + +1234 +01:02:33,987 --> 01:02:36,356 +Let me show you how easy it is +to get weather information + +1235 +01:02:36,390 --> 01:02:39,760 +through WeatherKit's great Swift API +in a quick demo. + +1236 +01:02:39,793 --> 01:02:42,029 +Let's revisit our Food Truck app. + +1237 +01:02:42,062 --> 01:02:44,398 +To make sure my customers +don't get caught in the rain, + +1238 +01:02:44,431 --> 01:02:48,135 +my app is set up to recommend +a parking spot with clear skies. + +1239 +01:02:48,168 --> 01:02:50,637 +Let me show you how I can get the weather. + +1240 +01:02:50,671 --> 01:02:53,407 +Here I have a list of safe parking spots. + +1241 +01:02:53,440 --> 01:02:56,276 +I've already added the WeatherKit +capability in Xcode, + +1242 +01:02:56,310 --> 01:02:58,712 +and all it takes is a few lines of code. + +1243 +01:02:58,745 --> 01:03:01,815 +With Swift Concurrency, +requesting weather is simple. + +1244 +01:03:01,849 --> 01:03:06,186 +We call weather(for:) on WeatherService, +and pass in a location. + +1245 +01:03:06,220 --> 01:03:11,558 +Then I can get the relevant data +I need for my app, like condition, + +1246 +01:03:11,592 --> 01:03:14,494 +precipitation, + +1247 +01:03:14,528 --> 01:03:16,363 +and cloud cover. + +1248 +01:03:18,098 --> 01:03:21,235 +Now that I have the data I need +for each of my parking spots, + +1249 +01:03:21,268 --> 01:03:22,536 +when I run my app, + +1250 +01:03:22,569 --> 01:03:26,907 +my custom view has updated to +recommend a location with clear skies. + +1251 +01:03:28,675 --> 01:03:30,944 +There are all kinds of ways +you can use weather data + +1252 +01:03:30,978 --> 01:03:33,881 +to make the experiences +in your apps better. + +1253 +01:03:33,914 --> 01:03:36,783 +You might use weather forecasts +to help you with inventory, + +1254 +01:03:36,817 --> 01:03:38,485 +predicting that +your ice-cream filled donuts + +1255 +01:03:38,519 --> 01:03:40,754 +are going to be a popular order +on a hot day, + +1256 +01:03:40,787 --> 01:03:43,290 +so you should stock up on ice cream. + +1257 +01:03:43,323 --> 01:03:45,459 +That's all it takes +to get the weather for our food truck, + +1258 +01:03:45,492 --> 01:03:48,762 +and there's so much more +to uncover with WeatherKit. + +1259 +01:03:48,795 --> 01:03:51,131 +In keeping with Apple's commitment +to privacy, + +1260 +01:03:51,164 --> 01:03:54,368 +location is used +only to provide weather forecasts, + +1261 +01:03:54,401 --> 01:03:57,671 +is not associated with +any personally identifying information, + +1262 +01:03:57,704 --> 01:04:00,541 +and is never shared or sold. + +1263 +01:04:00,574 --> 01:04:03,677 +Privacy is a shared responsibility, +and through WeatherKit, + +1264 +01:04:03,710 --> 01:04:08,248 +you can get accurate weather data +while protecting user privacy. + +1265 +01:04:08,282 --> 01:04:11,418 +Because we want to make it easy for you +to get started with WeatherKit, + +1266 +01:04:11,451 --> 01:04:15,889 +we're including 500,000 +weather(for:location) API calls per month + +1267 +01:04:15,923 --> 01:04:18,525 +in your Apple Developer Program +membership. + +1268 +01:04:18,559 --> 01:04:22,262 +Those of you who need more will be able +to purchase additional tiers of service + +1269 +01:04:22,296 --> 01:04:24,831 +right in the developer app, +starting this fall. + +1270 +01:04:24,865 --> 01:04:28,969 +So that's WeatherKit, +accurate, hyperlocal weather forecasts + +1271 +01:04:29,002 --> 01:04:31,071 +powered by the Apple Weather service. + +1272 +01:04:31,104 --> 01:04:35,809 +We're starting with a beta, +and it's available now on all platforms. + +1273 +01:04:35,843 --> 01:04:40,080 +There are so many creative ways +that you can use WeatherKit in your apps. + +1274 +01:04:40,113 --> 01:04:42,216 +And now here's Ryan +to give us some perspective + +1275 +01:04:42,249 --> 01:04:44,518 +on what your apps can see with Live Text. + +1276 +01:04:44,551 --> 01:04:47,221 +Ryan Dixon: +Our users are loving Live Text, + +1277 +01:04:47,254 --> 01:04:48,689 +and we have heard from many of you + +1278 +01:04:48,722 --> 01:04:50,924 +that you want to bring it +to your apps too. + +1279 +01:04:50,958 --> 01:04:53,293 +So this year, +we are expanding VisionKit + +1280 +01:04:53,327 --> 01:04:56,496 +with two new APIs +that will allow you to do just that. + +1281 +01:04:57,798 --> 01:05:01,869 +The Live Text API unlocks the ability +to analyze image content, + +1282 +01:05:01,902 --> 01:05:04,538 +allowing users to interact with text +and QR codes + +1283 +01:05:04,571 --> 01:05:07,541 +found in photos and paused video frames, + +1284 +01:05:07,574 --> 01:05:08,942 +and it provides quick actions + +1285 +01:05:08,976 --> 01:05:11,011 +so your users are just a tap away + +1286 +01:05:11,044 --> 01:05:13,547 +from taking action on relevant data. + +1287 +01:05:13,580 --> 01:05:15,916 +It's great for any app +that displays visual media, + +1288 +01:05:15,949 --> 01:05:19,553 +like Apollo for Reddit or Vimeo. + +1289 +01:05:19,586 --> 01:05:23,924 +And the Data Scanner API unlocks +the ability to analyze a live camera feed. + +1290 +01:05:23,957 --> 01:05:27,861 +It dramatically simplifies text +and barcode ingestion for users. + +1291 +01:05:27,895 --> 01:05:31,498 +All you need to do +is add any overlays or custom controls + +1292 +01:05:31,532 --> 01:05:35,335 +that tailor the live camera experience +to the needs of your app. + +1293 +01:05:35,369 --> 01:05:39,306 +This is especially useful for +consumer apps that rely on QR codes + +1294 +01:05:39,339 --> 01:05:42,709 +or enterprise apps built for +back-of-warehouse inventory management, + +1295 +01:05:42,743 --> 01:05:44,378 +pick-and pack delivery services, + +1296 +01:05:44,411 --> 01:05:45,846 +and point of sale kiosks. + +1297 +01:05:46,713 --> 01:05:49,149 +Both the Live Text and Data Scanner APIs + +1298 +01:05:49,183 --> 01:05:51,818 +support automatic detection +of nine languages, + +1299 +01:05:51,852 --> 01:05:54,821 +including this year's additions +of Japanese and Korean. + +1300 +01:05:55,822 --> 01:05:59,459 +These VisionKit APIs will bring years +of computer vision innovation + +1301 +01:05:59,493 --> 01:06:02,563 +to your app with just a few lines of code. + +1302 +01:06:02,596 --> 01:06:04,598 +Here's Jenny to show you how. + +1303 +01:06:04,631 --> 01:06:08,101 +Jenny Chen: To show you a demo, we're back +to our trusty pop-up Food Truck app. + +1304 +01:06:08,135 --> 01:06:11,638 +We're doing a promotion where if +users post a picture to a social channel + +1305 +01:06:11,672 --> 01:06:15,375 +holding a sign up with the hashtag +#freedonut and their address on the app, + +1306 +01:06:15,409 --> 01:06:18,378 +we'll drive to their address +and deliver a free donut to them. + +1307 +01:06:18,412 --> 01:06:20,314 +We'll head to our social donut feed. + +1308 +01:06:20,347 --> 01:06:23,584 +We want to add Live Text to the images +so that drivers can extract the text + +1309 +01:06:23,617 --> 01:06:25,819 +to get the addresses for delivery. + +1310 +01:06:26,620 --> 01:06:29,389 +Here's where the new Live Text APIs +play in. + +1311 +01:06:29,423 --> 01:06:32,492 +I can easily add an ImageInteraction +on top of my view, + +1312 +01:06:32,526 --> 01:06:36,063 +and that will add the Live Text button +with quick action support to it. + +1313 +01:06:36,096 --> 01:06:39,299 +While the Live Text button +normally sits on the bottom right, + +1314 +01:06:39,333 --> 01:06:41,568 +I already have a heart button in my app, + +1315 +01:06:41,602 --> 01:06:44,404 +so I can adjust the placement +using custom insets. + +1316 +01:06:44,438 --> 01:06:48,442 +I can also set the button configuration +to customize the style of the button + +1317 +01:06:48,475 --> 01:06:50,744 +so that it matches my app better. + +1318 +01:06:50,777 --> 01:06:55,015 +Now that I've added it in, +I can tap the Live Text button, + +1319 +01:06:55,048 --> 01:06:56,483 +select the text, + +1320 +01:06:56,517 --> 01:06:59,086 +or use quick actions +to easily grab the address. + +1321 +01:07:00,654 --> 01:07:03,824 +I love that users don't have to learn +a new interaction model, + +1322 +01:07:03,857 --> 01:07:08,462 +as it provides the same ease of use +with the Live Text experience. + +1323 +01:07:08,495 --> 01:07:10,597 +The UI is consistent and familiar. + +1324 +01:07:10,631 --> 01:07:12,666 +It feels integrated with the OS, + +1325 +01:07:12,699 --> 01:07:16,670 +but I can still adjust the placement +even when I have my own custom UI. + +1326 +01:07:17,404 --> 01:07:19,439 +Of course, like any good delivery app, + +1327 +01:07:19,473 --> 01:07:22,643 +we also want to provide +the best service to our customers + +1328 +01:07:22,676 --> 01:07:25,646 +and make sure +we're giving people the right donuts. + +1329 +01:07:25,679 --> 01:07:29,149 +So we track our donut orders via QR code. + +1330 +01:07:29,183 --> 01:07:33,587 +Using the new Data Scanner APIs, +I can easily add that as the first step + +1331 +01:07:33,620 --> 01:07:36,089 +of any customer interaction. + +1332 +01:07:36,123 --> 01:07:38,192 +Right now, +that button doesn't do anything, + +1333 +01:07:38,225 --> 01:07:41,261 +but I can easily instantiate +a new DataScanner object + +1334 +01:07:41,295 --> 01:07:44,398 +that looks for text, QR codes, or barcodes + +1335 +01:07:44,431 --> 01:07:46,633 +that I can then import into my app. + +1336 +01:07:46,667 --> 01:07:49,336 +With just a few lines of code, +I can bring up the camera, + +1337 +01:07:49,369 --> 01:07:53,540 +specify that I want QR codes, +and startScanning! + +1338 +01:07:53,574 --> 01:07:55,709 +When the driver taps on the QR code, + +1339 +01:07:55,742 --> 01:07:58,178 +I want to show that the scan is a success. + +1340 +01:07:58,212 --> 01:08:00,314 +I'll add in the delegate handler +in Xcode... + +1341 +01:08:03,684 --> 01:08:06,019 +...and on tap, show an alert to the user + +1342 +01:08:06,053 --> 01:08:07,521 +that the scan was a success + +1343 +01:08:07,554 --> 01:08:10,424 +so that I can go ahead +and get the donut order started. + +1344 +01:08:10,457 --> 01:08:14,394 +Now when I run the app, +this brings up a view controller + +1345 +01:08:14,428 --> 01:08:17,097 +with the camera view, +and I can see the guidance + +1346 +01:08:17,130 --> 01:08:20,367 +and the reticle view +highlighting the QR code. + +1347 +01:08:20,400 --> 01:08:23,737 +When I tap on the QR code, +I can see my scan was a success + +1348 +01:08:23,770 --> 01:08:26,139 +and the donut order is confirmed. + +1349 +01:08:26,173 --> 01:08:30,611 +And with that, +my #freedonut delivery is on its way. + +1350 +01:08:30,644 --> 01:08:34,615 +With VisionKit, +the new Live Text and Data Scanner APIs + +1351 +01:08:34,648 --> 01:08:39,086 +easily allow you to bring these +powerful vision capabilities to your app. + +1352 +01:08:39,119 --> 01:08:41,455 +And now, back to Susan. + +1353 +01:08:41,488 --> 01:08:44,892 +Susan: This is an exciting time +to be building apps. + +1354 +01:08:44,925 --> 01:08:49,396 +Xcode Cloud is now ready +to help you build better apps faster. + +1355 +01:08:49,429 --> 01:08:52,432 +With Swift and SwiftUI, +it's easier than ever + +1356 +01:08:52,466 --> 01:08:57,738 +to transform your ideas into apps +that work across Apple platforms. + +1357 +01:08:57,771 --> 01:09:00,474 +There are cool new ways for your apps +to bring your ideas + +1358 +01:09:00,507 --> 01:09:02,943 +deeper into the system experience. + +1359 +01:09:02,976 --> 01:09:05,045 +Lock Screen widgets and Live Activities + +1360 +01:09:05,078 --> 01:09:07,648 +bring your app to the Lock Screen. + +1361 +01:09:07,681 --> 01:09:10,417 +Messages Collaboration +makes it incredibly easy + +1362 +01:09:10,450 --> 01:09:13,520 +for your users to connect and collaborate. + +1363 +01:09:13,554 --> 01:09:17,958 +And App Intents +help integrate your app with Siri. + +1364 +01:09:17,991 --> 01:09:19,993 +There are entirely new APIs + +1365 +01:09:20,027 --> 01:09:22,529 +and major updates to existing APIs, + +1366 +01:09:22,563 --> 01:09:27,167 +like WeatherKit, MapKit, +Live Text, and Metal. + +1367 +01:09:27,201 --> 01:09:29,036 +And that's not the end of the story. + +1368 +01:09:29,069 --> 01:09:32,239 +It's another big WWDC this year, + +1369 +01:09:32,272 --> 01:09:34,408 +with 175 sessions, + +1370 +01:09:34,441 --> 01:09:39,179 +hundreds of labs, and Digital Lounge +activities running all week. + +1371 +01:09:39,213 --> 01:09:41,481 +We can't wait to connect with you +this week, + +1372 +01:09:41,515 --> 01:09:44,685 +and more importantly, +this week is for you. + +1373 +01:09:44,718 --> 01:09:47,588 +We're eager to see what you create next. + +1374 +01:09:47,621 --> 01:09:49,256 +Thank you! + +1375 +01:09:49,289 --> 01:09:57,297 +♪ ♪ + +1376 +01:10:06,340 --> 01:10:14,348 +. + diff --git a/eng/2022 Session 113 Apple Design Awards (ASL) en.srt b/eng/2022 Session 113 Apple Design Awards (ASL) en.srt new file mode 100644 index 0000000..50c3061 --- /dev/null +++ b/eng/2022 Session 113 Apple Design Awards (ASL) en.srt @@ -0,0 +1,1840 @@ +1 +00:00:00,000 --> 00:00:04,238 +♪ Mellow instrumental +hip-hop music ♪ + +2 +00:00:04,238 --> 00:00:09,510 +♪ + +3 +00:00:09,510 --> 00:00:13,947 +♪ Sparse upbeat +electronic music ♪ + +4 +00:00:13,947 --> 00:00:23,624 +♪ + +5 +00:00:23,624 --> 00:00:28,629 +- Design, for me, is like a way +to express myself, + +6 +00:00:28,629 --> 00:00:31,465 +just like writing a song +or write a book, + +7 +00:00:31,465 --> 00:00:33,734 +but I don't know music, + +8 +00:00:33,734 --> 00:00:36,670 +and I can't write +good languages. + +9 +00:00:36,670 --> 00:00:39,640 +But I can code. + +10 +00:00:39,640 --> 00:00:42,109 +- Design is like an opportunity + +11 +00:00:42,109 --> 00:00:46,880 +where you can create things +that are not only beautiful, + +12 +00:00:46,880 --> 00:00:49,850 +but that also serve a purpose. + +13 +00:00:49,850 --> 00:00:52,152 +- Design is in +every aspect of life. + +14 +00:00:52,152 --> 00:00:55,689 +- Design influences +people's lives on all levels. + +15 +00:00:55,689 --> 00:00:58,492 +- Design can be +the difference between + +16 +00:00:58,492 --> 00:01:01,461 +having a great experience +or having a bad one. + +17 +00:01:01,461 --> 00:01:05,065 +- If no design, +there will be no surprise. + +18 +00:01:05,065 --> 00:01:08,235 +- It's not just how things look, +it's how things work. + +19 +00:01:08,235 --> 00:01:13,240 +- It's one of the most effective +forms of storytelling. + +20 +00:01:13,240 --> 00:01:15,542 +- To design something, +I think we often have to kind of + +21 +00:01:15,542 --> 00:01:17,811 +reduce it down +to a simplified version of it. + +22 +00:01:17,811 --> 00:01:20,047 +- It's like, yeah, +climbing a mountain, + +23 +00:01:20,047 --> 00:01:21,048 +step by step. + +24 +00:01:21,048 --> 00:01:23,984 +- You get to start with, like, +a blank piece of paper + +25 +00:01:23,984 --> 00:01:26,486 +and then you get to turn it +into something + +26 +00:01:26,486 --> 00:01:27,588 +hopefully really cool. + +27 +00:01:27,588 --> 00:01:29,856 +- When you actually make it work +and then you show it to someone, + +28 +00:01:29,856 --> 00:01:32,259 +and they actually can use it +in a simple way, + +29 +00:01:32,259 --> 00:01:34,695 +when it used to be +this really complex thing, + +30 +00:01:34,695 --> 00:01:36,463 +I think that's just +really cool to do. + +31 +00:01:36,463 --> 00:01:39,733 +- When we first met +on our first date, + +32 +00:01:39,733 --> 00:01:43,236 +he said that he's a UX designer. + +33 +00:01:43,236 --> 00:01:46,673 +And I thought, +will I be stupid if I say, + +34 +00:01:46,673 --> 00:01:48,375 +"What does it mean?" +[LAUGHS] + +35 +00:01:48,375 --> 00:01:50,177 +♪ + +36 +00:01:50,177 --> 00:01:52,512 +- Real design +is far more functional. + +37 +00:01:52,512 --> 00:01:53,513 +- Delightful. + +38 +00:01:53,513 --> 00:01:54,548 +- Cute. + +39 +00:01:54,548 --> 00:01:55,882 +- Soothing. +- It's cozy. + +40 +00:01:55,882 --> 00:01:57,551 +- Powerful. +- Intriguing. + +41 +00:01:57,551 --> 00:01:59,186 +- Flashy. +- Tactile. + +42 +00:01:59,186 --> 00:02:00,287 +- Simple and joyful. + +43 +00:02:00,287 --> 00:02:01,088 +- Playful. + +44 +00:02:01,088 --> 00:02:02,155 +- And pretty! + +45 +00:02:02,155 --> 00:02:05,592 +♪ + +46 +00:02:05,592 --> 00:02:07,828 +- Let me think, +do I love design? + +47 +00:02:07,828 --> 00:02:09,796 +[LAUGHS] + +48 +00:02:09,796 --> 00:02:11,631 +- Sometimes I hate it. + +49 +00:02:11,631 --> 00:02:15,469 +But yeah, it will not last +for a long time. + +50 +00:02:15,469 --> 00:02:18,138 +So basically, +I think I love the design. + +51 +00:02:18,138 --> 00:02:20,807 +- In some ways, +I can't escape designing. + +52 +00:02:20,807 --> 00:02:23,977 +- I can't imagine a life +without designing stuff. + +53 +00:02:23,977 --> 00:02:25,879 +- How could anyone hate design? + +54 +00:02:25,879 --> 00:02:27,714 +- It's kind of like magic. + +55 +00:02:27,714 --> 00:02:29,082 +- I couldn't do anything else. + +56 +00:02:29,082 --> 00:02:33,720 +- I know my user can feel +how I'm thinking in my app + +57 +00:02:33,720 --> 00:02:34,955 +through my design. + +58 +00:02:34,955 --> 00:02:38,925 +- I think design +really does change + +59 +00:02:38,925 --> 00:02:41,695 +the way we approach life itself. + +60 +00:02:41,695 --> 00:02:45,332 +♪ Whimsical music ♪ + +61 +00:02:45,332 --> 00:02:50,904 +- I mainly get inspiration +from walking in nature. + +62 +00:02:50,904 --> 00:02:54,074 +Every time I do that, + +63 +00:02:54,074 --> 00:02:56,043 +I have ideas +that come to my mind. + +64 +00:02:56,043 --> 00:02:57,711 +- The inspiration could be +from anywhere. + +65 +00:02:57,711 --> 00:03:01,248 +It doesn't need to be +in the museum or gallery. + +66 +00:03:01,248 --> 00:03:03,884 +It's in the nature. +It's in the city. + +67 +00:03:03,884 --> 00:03:04,851 +- When it's nighttime, + +68 +00:03:04,851 --> 00:03:06,620 +when I know +everybody's sleeping, + +69 +00:03:06,620 --> 00:03:09,523 +when I know the world +is at rest, + +70 +00:03:09,523 --> 00:03:14,194 +it kind of makes it easier +for me to get into the zone. + +71 +00:03:14,194 --> 00:03:17,164 +- If I just spend time away +from technology, + +72 +00:03:17,164 --> 00:03:20,167 +away from my computer, +away from my phone, + +73 +00:03:20,167 --> 00:03:23,070 +that's the time when I can +really do deep thought. + +74 +00:03:23,070 --> 00:03:27,474 +- I just want to make everything +we cannot see in the real life. + +75 +00:03:27,474 --> 00:03:29,810 +- I was living in Bangkok. +There's a lot of cars. + +76 +00:03:29,810 --> 00:03:31,078 +There's a lot of traffic. + +77 +00:03:31,078 --> 00:03:34,147 +So also creating the game +pretty much designed + +78 +00:03:34,147 --> 00:03:37,017 +a way for you +to escape a little bit. + +79 +00:03:37,017 --> 00:03:39,619 +- Biggest challenges +for designing things + +80 +00:03:39,619 --> 00:03:43,623 +is that your assumptions +are usually wrong. [LAUGHS] + +81 +00:03:43,623 --> 00:03:46,226 +- I kind of had the idea +to focus on witchcraft + +82 +00:03:46,226 --> 00:03:49,162 +when the election was happening +in the United Kingdom + +83 +00:03:49,162 --> 00:03:52,132 +and there was a group +of individuals + +84 +00:03:52,132 --> 00:03:54,901 +who had decided they were +going to hex the government. + +85 +00:03:54,901 --> 00:03:56,503 +I think they were pagans. + +86 +00:03:56,503 --> 00:04:00,874 +♪ Piano lounge music ♪ + +87 +00:04:00,874 --> 00:04:03,410 +- What's my biggest failure? +Just in general? + +88 +00:04:03,410 --> 00:04:04,978 +- Well, that's actually +a great question. + +89 +00:04:04,978 --> 00:04:06,713 +- Oh, greatest failure? +There's a lot of them. + +90 +00:04:06,713 --> 00:04:07,414 +OK, hold on. + +91 +00:04:07,414 --> 00:04:10,016 +- As a person +or in my career life? + +92 +00:04:10,016 --> 00:04:11,384 +- Uh... + +93 +00:04:11,384 --> 00:04:14,054 +- Let me -- +I want to think and really... + +94 +00:04:14,054 --> 00:04:17,524 +- It's kind of hard to answer +without making somebody mad. + +95 +00:04:17,524 --> 00:04:19,059 +- Everything. +[LAUGHS] + +96 +00:04:19,059 --> 00:04:22,662 +- Failing is part of +any creation process. + +97 +00:04:22,662 --> 00:04:26,533 +- We spent, like, months +and months trying to iterate + +98 +00:04:26,533 --> 00:04:28,568 +and to do something great. + +99 +00:04:28,568 --> 00:04:31,171 +- Our biggest fail in the game +was actually at launch + +100 +00:04:31,171 --> 00:04:34,841 +that we didn't have +enough levels in the game. + +101 +00:04:34,841 --> 00:04:37,878 +- We had gotten rid +of the main feature of the app. + +102 +00:04:37,878 --> 00:04:40,180 +You know, overnight we lost, + +103 +00:04:40,180 --> 00:04:42,182 +I think, 20 percent +of our users. + +104 +00:04:42,182 --> 00:04:44,818 +Interesting, you know, +experience. + +105 +00:04:44,818 --> 00:04:47,220 +- We got some good feedback +from some customers, + +106 +00:04:47,220 --> 00:04:50,557 +and none of us really wanted +to admit that it kind of sucked. + +107 +00:04:50,557 --> 00:04:53,059 +- We're always happy +that we did change it. + +108 +00:04:53,059 --> 00:04:55,595 +- My adage was always, +if I came up with 50 ideas + +109 +00:04:55,595 --> 00:04:58,298 +in a day and 49 +were absolutely useless + +110 +00:04:58,298 --> 00:05:01,234 +and one was really good, +that's a very good day. + +111 +00:05:01,234 --> 00:05:04,104 +- I think it was mostly +just staying too long + +112 +00:05:04,104 --> 00:05:07,674 +in situations that were +not where I wanted to be. + +113 +00:05:07,674 --> 00:05:10,010 +Management, economics, +and law seemed like + +114 +00:05:10,010 --> 00:05:12,045 +this super logical way to go. + +115 +00:05:12,045 --> 00:05:16,149 +But as soon as basically the +iPhone was announced in 2007, + +116 +00:05:16,149 --> 00:05:17,984 +that was all I was +just thinking about. + +117 +00:05:17,984 --> 00:05:19,819 +I think in hindsight, +my biggest failure + +118 +00:05:19,819 --> 00:05:21,288 +was actually a really good thing + +119 +00:05:21,288 --> 00:05:24,090 +because it led me +to where I am right now. + +120 +00:05:24,090 --> 00:05:26,893 +♪ + +121 +00:05:26,893 --> 00:05:29,062 +Earlier last year, +I was working on an app + +122 +00:05:29,062 --> 00:05:32,799 +that helped people who are mute +to speak on the phone. + +123 +00:05:32,799 --> 00:05:34,568 +And then when I had made that, + +124 +00:05:34,568 --> 00:05:36,269 +I actually wanted +to make something similar + +125 +00:05:36,269 --> 00:05:38,905 +but the other way around, +so that if you're deaf, + +126 +00:05:38,905 --> 00:05:40,173 +and you're listening +to a phone call + +127 +00:05:40,173 --> 00:05:42,776 +that you can actually see +what's being said. + +128 +00:05:42,776 --> 00:05:46,213 +- It started for me just simply +trying to solve my own problem, + +129 +00:05:46,213 --> 00:05:47,948 +which is not drinking +enough water. + +130 +00:05:47,948 --> 00:05:49,916 +I could sit for hours working + +131 +00:05:49,916 --> 00:05:52,919 +until I feel like +I'm in the middle of the desert. + +132 +00:05:52,919 --> 00:05:57,357 +- Drinking water, it becomes +an exciting moment in your day. + +133 +00:05:57,357 --> 00:05:59,826 +- My kids will just -- +they'll do something + +134 +00:05:59,826 --> 00:06:02,662 +as long as it's on a list and +they can strike it off later. + +135 +00:06:02,662 --> 00:06:06,533 +And so we wanted that moment +when you strike off that habit + +136 +00:06:06,533 --> 00:06:10,237 +for the day to really +just feel gratifying. + +137 +00:06:10,237 --> 00:06:13,873 +- I was walking around and saw +a broken neon sign + +138 +00:06:13,873 --> 00:06:16,209 +and that sparked the idea. + +139 +00:06:16,209 --> 00:06:18,812 +- We want to make +the best clock app in the world. + +140 +00:06:18,812 --> 00:06:22,949 +So to do that, +we visit many furniture shops + +141 +00:06:22,949 --> 00:06:28,054 +to see how the clock feels like +when you hold it in your hand. + +142 +00:06:28,054 --> 00:06:31,057 +- In the very first beginning, +we wanted to help ourselves. + +143 +00:06:31,057 --> 00:06:33,226 +Because we were in a meeting, + +144 +00:06:33,226 --> 00:06:35,895 +and we can't even +take proper notes + +145 +00:06:35,895 --> 00:06:37,330 +and at the same time, + +146 +00:06:37,330 --> 00:06:40,734 +be really engaged in +the meeting; talking to people. + +147 +00:06:40,734 --> 00:06:43,036 +So we want to do this app +to help ourselves. + +148 +00:06:43,036 --> 00:06:47,007 +- After really diving deep into +what inclusivity means, + +149 +00:06:47,007 --> 00:06:49,843 +it really changes +the way we build. + +150 +00:06:49,843 --> 00:06:52,979 +- I was riding up a gondola +and this family -- + +151 +00:06:52,979 --> 00:06:56,049 +they were pointing out that +they really wanted to be able + +152 +00:06:56,049 --> 00:06:58,885 +to compare how they were doing +versus last year. + +153 +00:06:58,885 --> 00:07:00,453 +So I went home and wrote it, + +154 +00:07:00,453 --> 00:07:02,922 +and a week later +it was in the App Store. + +155 +00:07:02,922 --> 00:07:05,525 +- Don't pick up the phone +when you're focused + +156 +00:07:05,525 --> 00:07:10,030 +and don't open the lid off +the pot when cooking noodles. + +157 +00:07:10,030 --> 00:07:13,266 +These concepts run +through the Focus Noodles app. + +158 +00:07:13,266 --> 00:07:16,069 +A user can get +into the headspace + +159 +00:07:16,069 --> 00:07:20,006 +of covering their phones +and start to focus. + +160 +00:07:20,006 --> 00:07:21,574 +- If you love something, + +161 +00:07:21,574 --> 00:07:23,944 +you always find +a way to learn better. + +162 +00:07:23,944 --> 00:07:26,913 +And I want people +to love learning Chinese, + +163 +00:07:26,913 --> 00:07:29,549 +and I want to make it +more playful, more fun, + +164 +00:07:29,549 --> 00:07:31,484 +and people will enjoy learning. + +165 +00:07:31,484 --> 00:07:32,819 +- People see this as a game. + +166 +00:07:32,819 --> 00:07:34,187 +We can be +a lot more intentional + +167 +00:07:34,187 --> 00:07:38,258 +to create a game using our +technology to track your body. + +168 +00:07:38,258 --> 00:07:40,760 +Get you to move +your arms, move your body, + +169 +00:07:40,760 --> 00:07:42,462 +and basically get you +into the rhythm + +170 +00:07:42,462 --> 00:07:44,497 +and usually the game +is always like this. + +171 +00:07:44,497 --> 00:07:46,032 +That's where we took +the inspiration + +172 +00:07:46,032 --> 00:07:48,034 +to create Active Arcade. + +173 +00:07:48,034 --> 00:07:49,235 +Just like choreography; + +174 +00:07:49,235 --> 00:07:51,037 +just like teaching you +how to dance. + +175 +00:07:51,037 --> 00:07:53,707 +- We were in the middle +of quite a long project. + +176 +00:07:53,707 --> 00:07:55,542 +We realized it would be +really good fun + +177 +00:07:55,542 --> 00:07:57,978 +to just do a really fast project + +178 +00:07:57,978 --> 00:08:00,647 +and we could never +lose motivation for it. + +179 +00:08:00,647 --> 00:08:04,117 +- So the concept of the game +is you've murdered your husband, + +180 +00:08:04,117 --> 00:08:08,421 +and your goal is to make sure +other people don't find out. + +181 +00:08:08,421 --> 00:08:09,222 +[LAUGHS] + +182 +00:08:09,222 --> 00:08:11,725 +- The character +of Rebel Girls app + +183 +00:08:11,725 --> 00:08:17,163 +is like your feminist aunt +who is very kind + +184 +00:08:17,163 --> 00:08:19,065 +and who's full of stories. + +185 +00:08:19,065 --> 00:08:23,737 +And her goal is to make sure +that some of the best values + +186 +00:08:23,737 --> 00:08:25,138 +are instilled in you. + +187 +00:08:25,138 --> 00:08:27,007 +Empowering girls to become + +188 +00:08:27,007 --> 00:08:30,877 +the most confident +generation of girls. + +189 +00:08:30,877 --> 00:08:34,481 +- It's an app that is really +the friend you wish you had + +190 +00:08:34,481 --> 00:08:35,849 +when you lose a loved one. + +191 +00:08:35,849 --> 00:08:37,283 +It's the smart checklist + +192 +00:08:37,283 --> 00:08:39,252 +to telling you all the things +you need to do, + +193 +00:08:39,252 --> 00:08:40,253 +but at the same time, + +194 +00:08:40,253 --> 00:08:42,022 +only the things +you need to do right now + +195 +00:08:42,022 --> 00:08:45,625 +and what can wait, but also +allowing us to do it for you. + +196 +00:08:45,625 --> 00:08:46,726 +- For Behind the Frame, + +197 +00:08:46,726 --> 00:08:49,696 +it's basically +an escape room game. + +198 +00:08:49,696 --> 00:08:54,834 +Has that Ghibli kind of feel +to feel like playable anime. + +199 +00:08:54,834 --> 00:08:58,171 +We look at +the color scheme of Ghibli + +200 +00:08:58,171 --> 00:09:00,440 +and also Renaissance paintings. + +201 +00:09:00,440 --> 00:09:05,545 +- The idea behind Townscaper is +you just build beautiful houses. + +202 +00:09:05,545 --> 00:09:07,213 +That's all there is to it. + +203 +00:09:07,213 --> 00:09:09,115 +- The whole concept +is that you're finally + +204 +00:09:09,115 --> 00:09:11,084 +able to touch the artwork, + +205 +00:09:11,084 --> 00:09:14,020 +something that you're not +able to do in real life. + +206 +00:09:14,020 --> 00:09:16,689 +♪ + +207 +00:09:16,689 --> 00:09:19,192 +- One of the biggest challenges +of designing tools + +208 +00:09:19,192 --> 00:09:21,761 +for creative people is that +a lot of the tasks + +209 +00:09:21,761 --> 00:09:24,197 +that artists want to create, +they're quite complex tasks. + +210 +00:09:24,197 --> 00:09:26,866 +So the challenge is +how do we take all that power + +211 +00:09:26,866 --> 00:09:29,836 +and make it really +usable and simple? + +212 +00:09:29,836 --> 00:09:33,072 +- It has to be easy for someone +who's jumping straight in + +213 +00:09:33,072 --> 00:09:35,041 +and hasn't really done +this kind of work before, + +214 +00:09:35,041 --> 00:09:38,478 +but it also has to be effortless +for professionals as well. + +215 +00:09:38,478 --> 00:09:41,614 +Making something simple +is quite complex. + +216 +00:09:41,614 --> 00:09:45,185 +- There are so many tiny details + +217 +00:09:45,185 --> 00:09:48,688 +that go into creating +an experience. + +218 +00:09:48,688 --> 00:09:51,324 +There was no way for it not +to feel really clunky. + +219 +00:09:51,324 --> 00:09:53,460 +The next four and a half years +of development + +220 +00:09:53,460 --> 00:09:56,062 +was a lot of kind of +stripping those things away. + +221 +00:09:56,062 --> 00:09:59,399 +And how do we get rid of the +things that might otherwise + +222 +00:09:59,399 --> 00:10:01,768 +distract people so that +they can actually focus + +223 +00:10:01,768 --> 00:10:05,171 +on this moment +that we're trying to create? + +224 +00:10:05,171 --> 00:10:06,873 +- It was incredibly hard. + +225 +00:10:06,873 --> 00:10:08,842 +Graphic design, +as we know it, + +226 +00:10:08,842 --> 00:10:11,411 +is kind of coming from +the desktop-world era, + +227 +00:10:11,411 --> 00:10:15,615 +and didn't really have a lot of +innovation in the last decades. + +228 +00:10:15,615 --> 00:10:18,785 +- I'd approached it similarly +to designing a building -- + +229 +00:10:18,785 --> 00:10:21,554 +both you need +to navigate through. + +230 +00:10:21,554 --> 00:10:22,922 +With a website, +or even with an app, + +231 +00:10:22,922 --> 00:10:24,491 +you have different views. + +232 +00:10:24,491 --> 00:10:25,425 +You have an entrance. + +233 +00:10:25,425 --> 00:10:26,860 +At a certain point +you have an exit; + +234 +00:10:26,860 --> 00:10:28,361 +you have different rooms, + +235 +00:10:28,361 --> 00:10:30,430 +and the experience +needs to be good. + +236 +00:10:30,430 --> 00:10:33,900 +- My watercolor skills +are horrible. + +237 +00:10:33,900 --> 00:10:37,770 +My code development +is at the HTML levels. + +238 +00:10:37,770 --> 00:10:40,874 +So I also need to convince other +people, through my journey, + +239 +00:10:40,874 --> 00:10:42,876 +to build these things with me. + +240 +00:10:42,876 --> 00:10:47,113 +When we put it out, there was no +other watercolor game like it + +241 +00:10:47,113 --> 00:10:48,681 +or even watercolor app. + +242 +00:10:48,681 --> 00:10:50,083 +So I really worked heavily + +243 +00:10:50,083 --> 00:10:51,784 +with getting in +all the small details, + +244 +00:10:51,784 --> 00:10:53,286 +getting the water +to flow correctly + +245 +00:10:53,286 --> 00:10:56,289 +and getting it to just feel +really nice. + +246 +00:10:56,289 --> 00:10:58,458 +- Pushing the limits +has never been the problem. + +247 +00:10:58,458 --> 00:11:00,693 +The whole team loves to do that, + +248 +00:11:00,693 --> 00:11:02,695 +loves to see how far +they can take a character + +249 +00:11:02,695 --> 00:11:05,798 +or how much story +we can get into this scene. + +250 +00:11:05,798 --> 00:11:07,233 +The challenge +is to keep limits on that + +251 +00:11:07,233 --> 00:11:08,835 +so we can actually get it done. + +252 +00:11:08,835 --> 00:11:11,638 +- If you can create something +that people cannot see + +253 +00:11:11,638 --> 00:11:14,807 +in the real life, then sometimes +they will feel "wow" + +254 +00:11:14,807 --> 00:11:18,478 +at that moment +and we will feel so satisfied. + +255 +00:11:18,478 --> 00:11:19,779 +[LAUGHS] + +256 +00:11:19,779 --> 00:11:23,550 +Yeah, so this is why +we choose optical illusions. + +257 +00:11:23,550 --> 00:11:26,319 +For most people, when you see +some optical illusion, + +258 +00:11:26,319 --> 00:11:27,787 +you are just wowed. + +259 +00:11:27,787 --> 00:11:30,390 +- It was quite +a tricky problem to solve + +260 +00:11:30,390 --> 00:11:32,659 +of just making sure that +you could be playing on a bus, + +261 +00:11:32,659 --> 00:11:34,794 +but you don't notice +that you're on the bus anymore. + +262 +00:11:34,794 --> 00:11:37,597 +It's all of the little things +that build up the immersion, + +263 +00:11:37,597 --> 00:11:39,065 +not the big, obvious ones. + +264 +00:11:39,065 --> 00:11:42,669 +So the scuttling noises, +steam vents, things like that. + +265 +00:11:42,669 --> 00:11:45,371 +- We had huge geometries +and landscapes + +266 +00:11:45,371 --> 00:11:48,007 +and things that we wanted +to make really quick and custom. + +267 +00:11:48,007 --> 00:11:50,510 +We would just use our tool +that we designed, + +268 +00:11:50,510 --> 00:11:53,446 +and it would automatically +Lego-ify it. + +269 +00:11:53,446 --> 00:11:57,116 +- Our goal was to design +a very focused experience + +270 +00:11:57,116 --> 00:11:59,252 +for touch devices. + +271 +00:11:59,252 --> 00:12:00,920 +The player can interact +with this world + +272 +00:12:00,920 --> 00:12:03,223 +without having to think about. + +273 +00:12:03,223 --> 00:12:05,058 +That might sound simple, + +274 +00:12:05,058 --> 00:12:07,927 +but it's actually one of +the hardest things to pull off. + +275 +00:12:07,927 --> 00:12:12,899 +- Marvel Future Revolution +is an open-world, superhero, + +276 +00:12:12,899 --> 00:12:16,135 +action MMO RPG on mobile. + +277 +00:12:16,135 --> 00:12:19,239 +So it's got all your favorite +Marvel superheroes + +278 +00:12:19,239 --> 00:12:22,976 +saving the world with all +your buddies and friends. + +279 +00:12:22,976 --> 00:12:24,477 +The fact that +you could actually have + +280 +00:12:24,477 --> 00:12:26,879 +the true superhero experience + +281 +00:12:26,879 --> 00:12:29,449 +and you could actually take it +everywhere with you. + +282 +00:12:29,449 --> 00:12:31,851 +- I think that from +the very beginning we knew + +283 +00:12:31,851 --> 00:12:33,653 +we wanted to make a game + +284 +00:12:33,653 --> 00:12:37,457 +that was a mix +between music and story. + +285 +00:12:37,457 --> 00:12:40,727 +I mean, everything is in +the name of the game, you know. + +286 +00:12:40,727 --> 00:12:44,263 +So there's this character, +Gabrielle, who's in a coma. + +287 +00:12:44,263 --> 00:12:47,934 +You have to play music +to unlock memories. + +288 +00:12:47,934 --> 00:12:51,371 +To make this whole game, +it was a gigantic process, + +289 +00:12:51,371 --> 00:12:54,907 +but I mean, +it was a fantastic one. + +290 +00:12:54,907 --> 00:12:56,542 +- I thought, +"Cameras are so delightful; + +291 +00:12:56,542 --> 00:13:00,146 +maybe we can bring, like, +a semblance of that delight + +292 +00:13:00,146 --> 00:13:02,015 +to an iPhone camera." + +293 +00:13:02,015 --> 00:13:04,517 +I get emails every month -- +every week, sometimes -- + +294 +00:13:04,517 --> 00:13:06,386 +of people saying, like, +"I really enjoyed that. + +295 +00:13:06,386 --> 00:13:07,553 +It really helped a lot." + +296 +00:13:07,553 --> 00:13:09,922 +And that's so cool. +[LAUGHS] + +297 +00:13:09,922 --> 00:13:12,992 +- This app that we had created +actually convinced people + +298 +00:13:12,992 --> 00:13:15,228 +to get rid of their vehicle + +299 +00:13:15,228 --> 00:13:17,030 +and made riding +public transit fun. + +300 +00:13:17,030 --> 00:13:18,097 +And from that point on, + +301 +00:13:18,097 --> 00:13:20,333 +I think the motivation +became that transit + +302 +00:13:20,333 --> 00:13:23,236 +can have an impact on +people's transportation choices. + +303 +00:13:23,236 --> 00:13:26,739 +- Mental health was one +of those glowing beacons of need + +304 +00:13:26,739 --> 00:13:31,210 +where the skillset of a designer +and a product thinker + +305 +00:13:31,210 --> 00:13:34,881 +are deeply needed, +directing a lot of our attention + +306 +00:13:34,881 --> 00:13:39,018 +toward traditionally +underrepresented populations + +307 +00:13:39,018 --> 00:13:41,921 +and showing up in a way that, +we like to say, + +308 +00:13:41,921 --> 00:13:46,092 +opens a front door to care. + +309 +00:13:46,092 --> 00:13:49,362 +- Apps and games +have to tackle social change. + +310 +00:13:49,362 --> 00:13:51,764 +- An inclusive game +or app is something + +311 +00:13:51,764 --> 00:13:54,033 +that everyone feels at home. + +312 +00:13:54,033 --> 00:13:55,535 +Everyone can... + +313 +00:13:55,535 --> 00:13:58,337 +- ...have fun, +yet still connect with people. + +314 +00:13:58,337 --> 00:14:01,107 +- That authenticity allows us +not to just have + +315 +00:14:01,107 --> 00:14:04,644 +kind of symbolic diversity, +but actual diversity. + +316 +00:14:04,644 --> 00:14:07,647 +- I think if we want +to see social change, + +317 +00:14:07,647 --> 00:14:10,683 +then we need to exhibit it, +especially in entertainment + +318 +00:14:10,683 --> 00:14:14,287 +because it shows people +where we can go. + +319 +00:14:14,287 --> 00:14:16,122 +- I think I really +started to think, + +320 +00:14:16,122 --> 00:14:18,524 +"Hey, I can now make things +that help people + +321 +00:14:18,524 --> 00:14:21,160 +and solve the world +a little bit." + +322 +00:14:21,160 --> 00:14:23,863 +- I think over the last +two years, especially, + +323 +00:14:23,863 --> 00:14:25,998 +like, we're seeing +where we're going, + +324 +00:14:25,998 --> 00:14:28,267 +where we're going to spend more +and more of our lives digitally + +325 +00:14:28,267 --> 00:14:29,769 +in this world. + +326 +00:14:29,769 --> 00:14:37,810 +And how do we, as people, shape +those everyday experiences? + +327 +00:14:37,810 --> 00:14:40,513 +- A tip I could give +to the young developers + +328 +00:14:40,513 --> 00:14:44,083 +is stick to your original idea. + +329 +00:14:44,083 --> 00:14:45,685 +- If you're only +getting inspiration + +330 +00:14:45,685 --> 00:14:48,187 +from all the other games +that you love, + +331 +00:14:48,187 --> 00:14:50,223 +it's just going to be +another game. + +332 +00:14:50,223 --> 00:14:54,093 +- Try, fail, and try again. +And fail. And try again. + +333 +00:14:54,093 --> 00:14:56,863 +- Make as much as you can. + +334 +00:14:56,863 --> 00:14:58,164 +They don't have to be huge. + +335 +00:14:58,164 --> 00:15:00,566 +They don't have to be exciting. + +336 +00:15:00,566 --> 00:15:02,702 +But the more you make, +the more you learn how to make. + +337 +00:15:02,702 --> 00:15:06,072 +- When to leave it alone +and stop tinkering on it. + +338 +00:15:06,072 --> 00:15:07,673 +- Really figure out +what the game is + +339 +00:15:07,673 --> 00:15:09,842 +before bringing people on. + +340 +00:15:09,842 --> 00:15:11,077 +- Don't get frustrated. + +341 +00:15:11,077 --> 00:15:13,112 +- Let go of some +of your assumptions. + +342 +00:15:13,112 --> 00:15:14,580 +- Trying to decide what +the market wants + +343 +00:15:14,580 --> 00:15:15,915 +doesn't really make sense. + +344 +00:15:15,915 --> 00:15:17,917 +It's like, do something that +you're going to be motivated + +345 +00:15:17,917 --> 00:15:18,785 +to keep working on, + +346 +00:15:18,785 --> 00:15:21,154 +that you actually want +to exist in the world. + +347 +00:15:21,154 --> 00:15:24,290 +- You can steal, +but steal from the best + +348 +00:15:24,290 --> 00:15:28,594 +and always add your own +kind of magic fairy dust. + +349 +00:15:28,594 --> 00:15:29,495 +- Get inspired. + +350 +00:15:29,495 --> 00:15:31,831 +Get inspired by what's out there +and what's adjacent. + +351 +00:15:31,831 --> 00:15:33,299 +And look at what techniques +are in there + +352 +00:15:33,299 --> 00:15:35,802 +and how you can approach it +from a totally fresh start + +353 +00:15:35,802 --> 00:15:40,606 +with the things +and the biases that you have. + +354 +00:15:40,606 --> 00:15:47,480 +- The Apple Design Award, +it is like the Oscar. [LAUGHS] + +355 +00:15:47,480 --> 00:15:51,250 +- I couldn't believe it. +I was like, in tears, basically. + +356 +00:15:51,250 --> 00:15:54,520 +- I didn't know who I can tell. +It's like, oh my God. + +357 +00:15:54,520 --> 00:15:57,990 +I think something -- +the most amazing thing happened + +358 +00:15:57,990 --> 00:16:01,494 +and there was no one in the room +I could share it with. + +359 +00:16:01,494 --> 00:16:04,964 +- I remember seeing +some tears in his eyes. + +360 +00:16:04,964 --> 00:16:07,567 +It means a lot to me as well, + +361 +00:16:07,567 --> 00:16:11,003 +because I was the one +that asked him to quit his job. + +362 +00:16:11,003 --> 00:16:13,539 +- Are they still asleep? +I mean, come on. + +363 +00:16:13,539 --> 00:16:17,844 +It's the Apple Design Awards +happening over here. + +364 +00:16:17,844 --> 00:16:21,447 +- Very first reaction +when I received the email. + +365 +00:16:21,447 --> 00:16:23,616 +OK, it's probably a spam. + +366 +00:16:23,616 --> 00:16:27,420 +Then I was like, hmm, +it's a good spam. + +367 +00:16:27,420 --> 00:16:30,022 +And then I started +talking about it with the team, + +368 +00:16:30,022 --> 00:16:34,493 +and, like, everyone was like, +"What?!" [LAUGHS] + +369 +00:16:34,493 --> 00:16:37,363 +- Well, what would I want to say +to my fellow finalists? + +370 +00:16:37,363 --> 00:16:38,731 +Best of luck. + +371 +00:16:38,731 --> 00:16:41,067 +You've built something +that's amazing, + +372 +00:16:41,067 --> 00:16:43,202 +whether it gets an award or not. + +373 +00:16:43,202 --> 00:16:48,608 +- And now we become the +Apple Design Awards finalists. + +374 +00:16:48,608 --> 00:16:53,045 +It's like every day I'm in +the highest point in my life. + +375 +00:16:53,045 --> 00:16:55,348 +- We made it! +[LAUGHS] + +376 +00:16:55,348 --> 00:16:57,250 +- I remember the moment +when I first heard it, + +377 +00:16:57,250 --> 00:16:58,784 +it was quite emotional for me. + +378 +00:16:58,784 --> 00:17:02,488 +So I hope they're also looking +back at their progress + +379 +00:17:02,488 --> 00:17:05,258 +and their work +over the last year or more. + +380 +00:17:05,258 --> 00:17:07,026 +- Great job and congratulations. + +381 +00:17:07,026 --> 00:17:08,227 +- Congratulations to everybody. + +382 +00:17:08,227 --> 00:17:09,362 +- Congratulations. + +383 +00:17:09,362 --> 00:17:10,529 +- Congratulations! + +384 +00:17:10,529 --> 00:17:12,331 +- It's an honor +to be amongst you. + +385 +00:17:12,331 --> 00:17:13,766 +- Great job. + +386 +00:17:13,766 --> 00:17:15,635 +And let's keep on building it. + +387 +00:17:15,635 --> 00:17:20,773 +- Your continuous work +and creation and your effort + +388 +00:17:20,773 --> 00:17:23,276 +will make a difference. + +389 +00:17:23,276 --> 00:17:27,446 +♪ Upbeat electronic music ♪ + +390 +00:17:27,446 --> 00:18:36,182 +♪ +