[ Music ]
Welcome to What's New in File Management and Quick Look.
We have a lot of great new API for managing documents and file previews to share with you today.
Here are the areas that we're going to cover in this session.
First, we'll show how your app can use the document picker to access a directory and its content.
We'll also show how your app can properly support files stored on external USB drives or SMB servers.
Then also, see how your app can fetch and display rich thumbnails.
And you'll see how your app can provide quick editing support for images, PDFs, and videos via Quick Look.
We've made it easy to port your iOS app to macOS as the Quick Look Extension APIs are now cross platform.
And finally, we'll talk about supporting iPad apps on Mac.
So who is this session for?
If you are writing a new app or already have an app that presents documents or performs file operations, you're in the right session.
You want to learn the best practices to support files stored on removable drives, such as USB and SMB.
You may also need your app to recursively access multiple documents within a folder and perform batch file operations.
Stick around if you want to learn more about displaying file thumbnails or if you want to provide simple image and video editing in your app.
By the way, if you haven't checked it out yet, you should definitely take a look at Managing Documents in Your iOS Apps session from WWDC 2018 in which we detailed step-by-step how to use the UIDocumentBrowserViewController and the UIDocumentPickerViewController.
There's also a session, Quick Look Previews from the Ground Up from WWDC 2018, which we highly encourage you to watch if you're interested in presenting file previews on iOS and now macOS.
One of the most requested features has been to access an entire directory and its contents.
We're really excited that an iOS 13 you now can.
Features of your app may need to access content of a directory for reading, writing, or creating documents.
For instance, an image editor that needs to apply the same change on a batch of images would read every image, apply modifications, and save a new image right next to the original.
Prior to iOS 13, an app could access files outside of its application container via the UIDocumentPickerViewController but the user had to pick one file at a time.
New in iOS 13, your app can present a UIDocumentPickerViewController to let the user pick a folder.
After validation, your app will be granted recursive access to the directory as well as all of its contents.
Additionally, you can now set the default directory that is shown on the document picker when presented.
Now let's take a look at how you can do that now using a few examples of how to configure the UIDocumentPickerViewController.
To allow folder selection, creating instance initialized with an array containing only the kUTTypeFolder type and then present it.
To set the default directory of UIDocumentPickerViewController instance, set its directoryURL property and then present.
Now let's take a look at this example.
We show you how to enumerate the contents of a folder, but you can use the same example to write a file here using standard File Manager API.
Also, make sure to use URL start and stopAccessing SecurityScopedResource calls and use NSFileCoordinator API for reading and writing operations as you can see in the example.
In iOS 13, users are in control of which applications have access to folders.
This access is listed and can be revoked in the privacy pane under the Files and Folder section.
Be aware that because users can revoke access at any time, you need to make sure that your code gracefully handles errors when attempting to open documents or when enumerating folders.
In this example, you can see how you can maintain persistent access to a folder across application launches by storing bookmark data and then recreating the URL using the URL resolvingBookmarkData options call.
New in iOS 13, we've added support for external USB drives, thumb drives, and SMB servers.
Drives formatted as APFS, HFS+, FAT and ExFAT are supported.
Drives can be plugged in either on the USBC connector on the iPad Pro or via the SD card reader attachment.
They are automatically listed in the sidebar of the files app or in your app with the UIDocumentPickerViewController or document browser.
Users can also connect to SMB servers using the Connect to Server action using the same keyboard shortcut as finder or using the sidebar menu.
Any client of UIDocumentBrowserViewController API or UIDocumentPickerViewController API get the same support for free as long as the app was built using the iOS 13 SDK or later.
However, there are few things you need to check to make sure your app has great support for USB and SMB.
There are few assumptions that are no longer true.
File access is no longer on a single volume because they can now be stored on external volumes, such as USB drives or SMB servers.
Volumes can disappear if the USB drive is unplugged or if the network connection to an SMB server volume is lost.
Disk access can be slower than what you expected in the past.
An operation that you would assume is instant can now take several seconds or even minutes depending on the network conditions or the speed of the USB device.
Also, don't assume the underlying file system is always APFS or HFS+.
So how does this affect your app?
If you need to move a file from one location to another, use the FileManager.moveItem at API and this will take care of moving files across volumes for you.
In the past, move and clone operations were always fast.
When moving across volumes, the operation can become slower copy and delete.
If you use NSTemporaryDirectory to save a temporary version of a file, you should update your code to choose the location of the temporary folder based on the ultimate destination of the files written in it.
You can do this with the following File Manager call, File Manager URL for itemReplacementDirectory in userDomainMask appropriateFor URL.
This will always give you the right temporary directory to write files, to.
In the past, your app may have assumed that a file may never disappear while it is open and this is no longer true because USB drives can be unplugged and SMB server connections can be interrupted.
This may cause your app to fail while reading or writing file contents.
Your app should check for errors and fail gracefully.
One suggestion is if your app fails to write a file, it could present a UIDocumentPickerViewController to let the user choose an alternate location to save the file.
Reading or writing a file stored on a USB drive or on a distant SMB server can be slow so you need to make sure your app doesn't hang while performing a file operation.
Always perform file system operations on a background queue.
We also recommend that you add UI like an activity indicator to let the user know something is happening, and offering a way for your user to cancel the operation is a great idea.
Otherwise the user might feel like the application is unresponsive.
When a document is stored on a USB drive or remote SMB server, LIFS is reported instead of the file system the media is formatted as.
It's likely that your app should not worry about the underlying file system.
Instead you should check the file system capabilities to know which file system operations are supported.
LIFS is a file system abstraction and it do not look for its presence directly.
To know more about LIFS, we recommend that you look at the What's New in File Systems session.
New in iOS 13, there's additional customization support in UIDocumentBrowserViewController.
Let's take a look at this now.
Now, you can choose to always show file extensions by setting the shouldShowFileExtensions property to true.
The Create Document button can now also be customized to adjust the aspect ratio of the button icon by setting the defaultDocumentAspectRatio property.
And the button titled Text can now be changed by setting the localizedCreate DocumentActionTitle property.
And now Lyn will talk about What's New in Quick Look.
Fetching File Thumbnails in Your App.
This is for those of you who want to enhance the appearance of your application by displaying rich file thumbnails.
So instead of something like this, you could have something like this.
Quick Look thumbnailing is a new cross platform framework for retrieving thumbnail images for file URLs.
This is new functionality on iOS and replaces the C API for QLThumbnail on macOS.
It also replaces NSURLThumbnailDictionaryKey.
iOS and macOS provide built-in support for several file types, such as images, PDFs, text files, videos, et cetera.
You can also provide support for your own file types with thumbnail extensions.
Quick Look thumbnailing is non-UI framework.
We don't import UIKit or AppKit by default.
We can get CGImages of your thumbnail without either but you can also obtain UIImages on iOS or NSImages on macOS by explicitly linking UIKit or AppKit respectively.
It is asynchronous so it won't block while retrieving the thumbnail and it supports cancellation in case you decide you don't want that thumbnail after all.
Let's start with a big picture overview.
To get a thumbnail, you start by creating a QLThumbnailGenerator.Request and then pass it to a QLThumbnailGenerator specifying if you would like to be notified every time a new representation of the thumbnail is available or only when the best representation you requested is complete.
You can request different representations of thumbnails which will have different quality and performance costs.
Icon is the generic image associated with that file type.
This will be the same for all files of a given type.
Low quality thumbnail is a faster thumbnail that we were able to provide that may not be exactly the size you specified.
This typically comes from a cached version of the thumbnail or possibly thumbnails embedded in the file itself.
The last representation type thumbnail is a full quality version of the thumbnail, matching the request exactly.
This will take the longest to generate.
For convenience, you can also specify all to get any type of available thumbnail.
When you create a QLThumbnail generation request, you specify size, scale and representation types you would like.
You may only care about the full quality thumbnail or you may be OK with an icon or lower quality version.
It's best to specify all representation types that will work for you as not all three types may be available for a given thumbnail.
You will get a QLThumbnailRepresentation back which will specify the type of thumbnail it is, icon, low quality or thumbnail and an image which can either be accessed as a CGImage, or if you've linked UIKit on iOS or AppKit on macOS, you can get a UIImage or NS Image respectively.
To get that QLThumbnailRepresentation, you pass your thumbnail request to a QLThumbnailGenerator.
If you just want the best quality thumbnail available, use generateBestRepresentation.
This will call your completion handler with the best version of your thumbnail request that could be created.
Or you may prefer to get incremental updates by providing an updateHandler to generate representations that will be called as each representation becomes available.
You can use this, for example, to update your UI quickly and then improve the quality of what is shown as better thumbnails arrive.
So that's enough about thumbnail retrieval.
Let's talk about using Quick Look to edit images, PDFs and videos.
Quick Look provides API to preview documents of various types of files on iOS.
It also allows you to provide custom previews for your own documents via extensions.
Now in iOS 13, we've also added support to quickly edit images, PDFs and videos with the QLPreviewController.
The QLPreviewController is what you use to preview a file.
To make use if it simply allocate an instance of QLPreviewController and set a data source and delegate before presenting it.
To learn more about Quick Look and the QLPreviewController, we highly encourage you to check out our WWDC presentation from 2018 Quick Look Previews from the Ground Up.
New to iOS 13, the QLPreviewController also provides access to Markup, the editing tools available when you preview a document and files or edit an attachment in mail.
This is available for images and PDFs.
QLPreviewController also provides support for trimming and rotating videos.
To use these features, you just have to enable editing mode.
All you have to do is implement an optional delegate method editing mode for preview item.
By default, editing is disabled but it is possible to enable it on a per item basis.
This delegate method allows you to specify both if you want editing capabilities and how you want to handle the resulting file.
You can have the QLPreviewController overwrite the original file with the user's edited version.
To do so, return updateContents from editing mode for preview item.
If you need to react to edits being successfully saved, you can also implement the optional didUpdateContentsof previewItem method.
Or you may prefer to use QLPreviewController to create edited copies of the files being previewed and manage them yourself.
To do so return createCopy from editing mode for preview item and implement the delegate method that saved edited copy of preview item at modified contents URL.
Quick Look Extension APIs on macOS.
So we've just talked about getting and using thumbnails and previews for files.
Now let's talk about how to provide thumbnails and previews for your own file types.
Already on iOS, an app can implement a Quick Look thumbnail and preview extension to provide systemwide thumbnail and preview support for its own documents.
Now the same Quick Look extension API is available on macOS.
Thumbnails are used throughout macOS and iOS to allow users to identify files quickly.
For instance, you can see on the screenshot how macOS makes use of them in Finder.
MacOS can generate thumbnails for many common file types, but implementing a thumbnail extension allows you to also provide thumbnails for your own file types so that they can have a rich representation in Finder and elsewhere a thumbnail might show.
The extension API replaces the old CF plug-in based generator system.
If you have a Quick Look generator, consider migrating soon.
I say Quick Look extension in a general sense here as the thumbnail extension point comes from Quick Look thumbnailing.
This was previously vended from the Quick Look framework on iOS but you might want to consider importing it from the Quick Look thumbnailing instead if you do not need UIKit as this will make your extension leaner.
To create a thumbnail extension, add a new target in your Xcode project using the thumbnail extension template.
After setting up your extension target, you will be able to start implementing your QLThumbnailProvider subclass.
You can draw your thumbnail using either a CG context, an AppKit context, or you can provide a file URL to an image.
Here's a quick example where we overwrite the provide thumbnail for request handler method.
We extensively cover this part of the Quick Look API in our session from WWDC 2017 "Building Great Document-based Apps in iOS 11".
We highly encourage you to check it out.
In a nutshell, you will get a QLFileThumbnailRequest which will have the URL of the file, the maximum and minimum sizes of the thumbnail, and its scale.
Use these to create a thumbnail matching those specifications.
For each thumbnail request, the API expects you to create a QLThumbnailReply which will be used to generate the thumbnail.
Pass the QLThumbnailReply to the completion handler above.
To debug your thumbnail extension on macOS, you can use QLManage.
If you have written Quick Look generators in the past, you may recognize this tool.
Build and run your extension target in Xcode to register your extension with the system.
Then use QLManage to generate a thumbnail of your file type and attach to your extension in Xcode.
Preview Extensions for Files.
New this year, Quick Look on macOS also allows you to provide previews for your own file types by implementing a preview extension.
Quick Look preview extensions already existed to support Core Spotlight previews.
See WWDC 2017's What's New in Core Spotlight for iOS and macOS.
This year we've also added support for file previews.
With the extension, you can now directly provide a view to display your preview.
This is different from the old Quick Look generators in which you provided data or a URL for the system to present.
The new extension API allows you greater control over the appearance of your preview.
We recommend you migrate off the old Quick Look generators as they will be deprecated in a future release.
To create a Preview Extension, add a new target in your project and select the Preview Extension template.
The API is identical to iOS Preview extensions.
See WWDC 2018's Quick Look Previews from the Ground Up.
As a quick summary, you will want to declare the list of supported UTIs in QLSupportedContentTypes in your Info.plist.
This must exactly match the UTIs of the files you want previewed.
Matching by parent UTI is not supported.
The template will provide a view controller which will be what you prepare your preview in.
Here's a quick example of the view controller template.
Implement, preparePreviewofFile at URL to prepare your view controller.
And once you're ready, call the completion handler.
The QLPreviewView presenting you preview will show a spinning wheel until this completion handler is called.
So get your preview ready as quickly as possible.
Keep in mind, your preview can be shown in any QLPreviewView which may be the Preview panel, the Column View sidebar, Spotlight or any client of the QLPreviewView at all.
To debug, launch your extension target with Xcode.
Note that Xcode will lunch the Quick Look simulator by default.
This is to aid in debugging Core Spotlight previews which are extension can also provide support for.
To debug regular file previews, you can ignore the simulator and invoke your preview with finder or QLManage.
As long as your preview extension is launched through Xcode, Xcode will automatically attach to it.
Let's talk about supporting iPad apps on Mac.
Our document handling APIs are supported in iPad apps on Mac so you can bring your document-based iOS apps to macOS.
For UIDocumentPickerViewController use it as you would on iOS and users will automatically get and experience that feels native to macOS.
Specifically, the .import and .open modes show an NSOpenPanel and the .exportToService and .moveToService modes show an NSSavePanel.
Similarly, for UIDocumentBrowserViewController, users will get an experience that feels native for a document-based app on macOS.
The user will get an NSOpenPanel in a separate window just like they do in TextEdit.
There are a few minor runtime differences due to differences between the interfaces on iOS and macOS.
A good example of this is the UIDocumentBrowserViewControllers navigation bar which doesn't exist on macOS.
Check the documentation for details on what to do for these particular cases.
In some cases, you can use new APIs, such as the menu bar API, to help you address these UI differences.
Quick Look is also available on iPad apps on Mac.
Presenting a QLPreviewController will launch a QLPreviewPanel on macOS to provide a more native Mac experience.
Since the QLPreviewPanel is a separate window, you're presenting view controllers content will be visible but it will be great out and non-interactive.
Test your iPad apps on Mac to make sure it looks great.
One limitation to note of QLPreviewController in iPad apps on Mac is that embedding the QLPreviewController's view is not fully supported and we will provide a thumbnail instead of a live preview in this case.
So what have we learned today?
First, we've shown you how to leverage new features of iOS 13 in your app, such as accessing multiple files in a directory, supporting USB drives and SMB servers.
Then we've taken a look at using Quick Look to display the same rich thumbnails as in the files app, provide Quick Look editing support for images and videos, and provide previews and thumbnails with modern extension API on the Mac.
Thank you for watching.