Transcript
[ 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.