Topics

#321: Opening Files from the Files App πŸ“‚

Topics

Apple's Files.app is a great way to interact with files on iOS.

Today, we'll begin taking a look at how our app can better integrate with it. First up, we're going to learn how to allow users to open files in our app, from the Files.app. Let's get started.

We'll begin by creating a new project with the Single View App template in Xcode. In the future, we'll look at how Document Picker apps work, but for simplicity, we'll use just a plain single view app this time.

Then, we'll head into our AppDelegate.swift file, and add a new function:

func application(
  _ app: UIApplication, 
  open inputURL: URL, 
  options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
  // TODO
}

This application(_:open:options:) function will be called when the user selects a file inside the Files app and presses the Share (  ) button on it, and selects our app in the UIActivityViewController that appears.

Pro Tip: We can also access this by long-pressing a file in the Files app and selecting Share from the menu:

Next we'll need to tell Xcode that our app supports the type of files we want to our app to be able to open. For this, we'll choose Text Files.

We'll click on our project at the top of the project navigator and click Info at the top. We'll find this screen:

We want that area labeled Document Types (0). We'll click the + button and enter in Text for the Name and public.text for Types.

(This public.text thing is part of a system called Uniform Type Identifiers, Apple uses these to identify specific kinds of file types. Read more here).

Then, we'll add a new key to our Info.plist to make our app work more smoothly. We'll right-click on the keys at the top and select Add Row, then add the key LSSupportsOpeningDocumentsInPlace. We'll make it a Boolean and set its value to YES:

This allows our app to work with the files users open, without needing to first copy them into its own sandbox directory.

Finally, we'll head back to our AppDelegate.swift file and fill out that function we added earlier:

func application(
  _ app: UIApplication, 
  open url: URL, 
  options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
  let needTo = url.startAccessingSecurityScopedResource()

  do {
    let data = try Data(contentsOf: url)
    let contents = String(
      data: data,
      encoding: String.Encoding.utf8
    )

    print(contents)
  } catch(let error) { print(error) }

  if needTo {
    url.stopAccessingSecurityScopedResource()
  }

  return true
}

First we setup a do/catch block so we can catch and print any errors that might happen during all of this. Then we call a special function on our inputURL.

Since, we're opening in place, iOS has gives us what Apple calls a "security scoped" URL. (Read more about them here).

First we have to tell the system when we begin using a URL, saving the Bool value that url.startAccessingSecurityScopedResource() returns. If that Bool is true, then we'll also need to call stopAccessingSecurityScopedResource on our URL once we're finished with it.

After that we simply read the Data at the URL, then put it into a String so we can print it out.

Now we can Build & Run the app on our device, open the Files.app, choose an .txt file, share it, and our app appears in the Share sheet:

Tapping our app's icon opens it, and runs our code to print out the contents of the file, neat!

That's all for today. Shout out to friend of the show Casey for inspiring the idea for this Bite!

Have an idea for a Bite? Send it in!