Document-Based Apps Tutorial: Getting Started
In this document-based app tutorial, you will explore how you can save and open custom documents and interact with the Files app. By Warren Burton.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Document-Based Apps Tutorial: Getting Started
35 mins
- Introduction
- Getting Started
- Archiving and De-archiving Data
- Saving and Loading Your Composition
- Working With the Document Browser
- Creating a UIDocument Subclass
- Encoding the Document
- Decoding the Document
- Installing UIDocumentBrowserViewController
- Presenting a Container View Controller
- Configuring UIDocumentBrowserViewController
- Configuring Info.plist
- Responding to UIDocumentBrowserViewController Delegate Actions
- Creating Documents
- Importing Documents
- Opening Documents
- Transitioning to the Markup Editor
- Opening a MarkupDocument From a URL
- Supplying DocumentBrowserDelegate With a Presentation Closure
- Allowing Other Apps to Open Documents
- Setting Up the App Delegate
- Updating DocumentBrowserViewController
- Updating RootViewController
- Updating AppDelegate
- Providing a Custom Document Icon
- Adding a ThumbnailProvider Extension Target
- Configuring a QLThumbnailProvider Subclass
- Configuring the Info.plist
- Linking the Framework
- Where to Go From Here?
Introduction
It used to be the case that, if your app used documents, you needed to create your own document browser UI and logic. This was a lot of work. With iOS 11, that all changed. It’s no longer impossible to share documents from your app’s own sandbox with other apps on the same device. iOS 11 introduced both the Files app and a new public API called UIDocumentBrowserViewController
that provides most of the functions that document-based apps use.
UIDocumentBrowserViewController
provides developers with several features:
- A system UI that all users will be able to recognize and use.
- No need to write your own UI and associated logic to deal with file management locally or on iCloud.
- Simple sharing of documents globally across the user’s account.
- Fewer bugs because you are writing less code.
And via UIDocument
:
- File locking and unlocking.
- Conflict resolution.
In this tutorial, you will cover creating a simple UIDocument
subclass implementation, using UIDocumentBrowserViewController
in your document-based app. You will also use a Thumbnail Provider extension to create custom icons for your documents.
To do this tutorial, you will need:
- Xcode 10 or higher.
- Intermediate Swift skills.
- A basic understanding of delegation patterns and protocol-oriented programming.
- A basic understanding of do/try/catch and Error types.
Getting Started
The starter app, called Markup, can be found using the Download Materials link at the top or the bottom of this tutorial. The app is a simple tool that allows you to add text over the top of an image. It uses a Model-View-Controller pattern to decouple the data from the UI.
Open the Markup.xcodeproj file in the Markup-Starter folder. Select the Markup project in the Project navigator. You will see that there are two targets. The app Markup and a framework target MarkupFramework:
You’re using a framework here because later on you’ll be adding an app extension. The framework allows you to share code between the app and the extension.
You don’t need to have an in-depth understanding of this app’s workings in order to do this tutorial; it’s bolted together with stock UIKit parts and modeling glue. Since there’s a lot of material to cover, the starter app contains a lot of stub files to help you get going — even if you don’t fully understand everything that’s there, you’ll still be learning a lot about the topic. Feel free to poke around the code later to see how it works.
Next, ensure that Markup is selected in the target selector. Choose the iPad Pro (10.5-inch) simulator:
The app is universal and will work on any device if you want to try it later.
Build and run. You will see the following UI:
Choose any available image and add some random words to the title and description fields. They should render in the bottom half of the screen. You can export a JPEG image using the share button on the right of the screen above the rendering:
Archiving and De-archiving Data
Go to the Project navigator and open the folder Markup Framework/Model. Inside you will find two files:
- MarkupDescription.swift provides a protocol for the data structure that describes the page: title, long description, image, color and rendering style.
-
ContentDescription.swift is a class that adopts the
MarkupDescription
protocol. It provides a concrete implementation that can be instantiated.
ContentDescription
conforms to NSCoding
. This means that you can use an NSKeyedArchiver
to turn an instance into data, or you can use an NSKeyedUnarchiver
to recover an instance from data. Why this is useful will become clear later in the tutorial.
In this app, you use NSCoding
instead of Codable
because UIColor
and UIImage
don’t conform to Codable
. The important thing, here, is that you can encode and decode Data
.
Saving and Loading Your Composition
Build and run. Next, create something with an image, title and description.
Put the app into the background with the Hardware > Home menu item (or Command-Shift-H). You should see a message like this in the Xcode console (the path will be a little different, that’s fine):
save OK to file:///Users/yourname/.../Documents/Default.rwmarkup
If you want to see the code behind this, have a look at observeAppBackground()
in MarkupViewController
.
Stop the app. Build and run again. Your previous composition should appear in front of you, ready for editing.
Working With the Document Browser
At this stage, a user can save and edit exactly one file. If you want an App Store success, you’re going to need to do better.
In the section that follows, you’ll install and use a UIDocumentBrowserViewController
to allow your customers the ability to work with any number of documents.
Creating a UIDocument Subclass
UIDocumentBrowserViewController
works together with instances of UIDocument
. UIDocument
is what’s known as an abstract base class. This means that it can’t be instantiated by itself; you must subclass it and implement some functionality.
In this section, you’ll create that subclass and add the needed functionality.
Open the Markup/UIDocument Mechanics folder in the Project navigator. Open MarkupDocument.swift.
DocumentError
defines some Error
types for potential failure events. MarkupDocument
is a subclass of UIDocument
that contains stubs for the two methods that must be implemented in a valid UIDocument
.
When you save or close the document, the UIDocument
internals will call contents(forType:)
to get the data that represents your document in order to save the data to the file system. When you open a document, UIDocument
will call load(fromContents:ofType:)
to supply you with the encoded data in the content
parameter.
The contents passed into the method can be one of two things:
-
Data
for when your data is a binary blob. You’ll be using this format in this tutorial. - A
FileWrapper
for when your document is a package. Packaged — a.k.a. bundled — documents are not in the scope of this tutorial, but it’s helpful to know about them.
It’s your job to decode the data object and provide it to your app.
You’ll add code for these two methods, now.