Universal Type Identifiers Tutorial for iOS: Importing and Exporting App Data

In this tutorial, you’ll learn how to export and import app data to and from your iOS app, as well as create custom file types and extensions. By Ehab Amer.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Importing Files to your App

Although your app launched, its contents didn’t change. If you think about it, that makes sense. There is no implementation yet in the project to load the contents of the file. You’ll fix that next.

Note: It’s worth pointing out that there is a way for your app to preview the contents of the file without opening it directly. You can learn how to do this from this article: Document-Based Apps Tutorial: Getting Started.

Using Scenes

iOS 13 introduced a new concept called Scenes, which allows you to have more than one window open in your application. It’s very handy on an iPad, and you actually get this by default when you create a new project from Xcode 11. You can learn how to make the best use of scenes in our Adopting Scenes in iPadOS tutorial. Scenes are also important because iOS uses them to inform your app the user is trying to open one of your files.

The starter project is already using scenes, so you’re ready to learn how to import app data.

Open SceneDelegate.swift and add the following code at the end of the class, before the final closing brace:

// 1
func scene(
  _ scene: UIScene,
  openURLContexts URLContexts: Set<UIOpenURLContext>
) {
  // 2
  guard let urlContext = URLContexts.first else {
    return
  }
  // 3
  TaskStore.shared.importPrioritizedTasks(from: urlContext.url)
}

Here’s what you’re doing in the code above:

  1. iOS passes information about the source that launched your app with openURLContexts. If the user launched the app by tapping on a file, the context will contain a URL to that file.
  2. You check for at least one valid UIOpenURLContext. If there isn’t one, you return immediately.
  3. You then provide the URL of the file to TaskStore for your app to load. As soon as you update the store object, the view reloads with the new data.

Build and run, and drop the the same PrioritizedTasks.rwtl file again on your simulator while the app is running.

Import app data into TasksList main screen

Note: At the time of writing, macOS Catalina has some issues copying dragged and dropped files to the Simulator, especially when dragging from your Downloads or Documents folders. If you move the file to your home directory (/Users/your-user-name) and drag from there, it should work.

Whoa, the new tasks appeared right in front of your eyes! How cool is that! Except that whoever made this list needs to get their priorities right. :]

Working Without Scenes

What if you want to apply this to an app that isn’t scene-based? You can but the method for importing data is different. You’ll add that logic now even though it won’t work in this project. (It won’t break anything either and it’ll serve as a reference if you decide to build a non-scene-based app later.)

Add the following to AppDelegate.swift:

func application(
  _ app: UIApplication, 
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
  TaskStore.shared.importPrioritizedTasks(from: url)
  return true
}

Remember the method you implemented in the scene delegate to grab the URL of the file that opened the app? This method does the same, but in the app delegate. In this case, you’ll receive the path to the file that you’re importing directly so you can start using it right away.

However, iOS won’t call this method unless you implement either application(_:didFinishLaunchingWithOptions:) or applicationDidFinishLaunching(_:). Add the following method to the class:

func applicationDidFinishLaunching(_ application: UIApplication) { 
}

Even though the method doesn’t do anything, simply implementing it will allow iOS to call application(_:open:options:).

Defining Export Types

Now that you have the import logic for TaskList, you’re ready for the next step: setting up the export logic.

Setting Up the Export Logic

The setup you did for the import process makes the setup for exporting much easier. The information for exporting is already in the file you saved to the document directory. And the rwtl file type you created let’s you use that file without any kind of conversion.

But hold on… What is the difference between Imported UTIs and Exported UTIs under the Info tab? Does the first have all the definitions for file types you can import and the second for those you can export? Not exactly.

Though you haven’t added anything yet to Exported UTIs, you already have what you need to export files. If you try to export an rwtl file, you might think that you’d get an error telling you that you didn’t define this type in Exported UTIs. You won’t. The export will work. So what’s the point of having this section?

Think of those configurations as saying: I want to import a file that another app created. And I want to import a file that this app created.

Exported UTIs is used for this second situation. Your app created the .rwtl file so it doesn’t make sense to classify it as imported. So rather than registering it there, you register it in the Exported UTIs section.

Switching from Import to Export

To make this switch in your project, open Info.plist as Source Code (in the Supporting Files folder).

Open Info.plist as source code

Find and replace the text UTImportedTypeDeclarations with UTExportedTypeDeclarations.

Note: The project Info tab will not reflect the changes to Info.plist that you made in the source code. You need to reopen the project to see those.

To make sure everything works as before, delete the app from your Simulator. Build and run and drop PrioritizedTasks.rwtl onto the Simulator again.

Import app data into TaskList main screen

Everything still works, but now you have the export process configured properly.

Exporting Files with Activity View Controller

The Share Sheet, also known as UIActivityViewController, is the most convenient way to share information on iOS. It offers plenty of channels to do so, and it knows which apps are available on the device that can handle the information the user wants to share.

In ContentView.swift, add the following code right after the existing add button inside the HStack.

// Share Sheet
Button(action: { self.shareSheetIsPresented = true }) {
  Image(systemName: "square.and.arrow.up")
}
.frame(width: 44, height: 44, alignment: .center)
.sheet(isPresented: $shareSheetIsPresented) {
  ShareSheet(
    activityItems: [TaskStore.shared.tasksDocURL],
    excludedActivityTypes: [])
}

This creates a Share activity and provides it with the URL of your saved tasks list file. UIActivityViewController recognizes that the URL points to a file, rather than a webpage, and treats it as such.

ShareSheet is a SwiftUI view from the starter project that wraps around a standard UIKit UIActivityViewController.

Build and run and tap the new button to see it in action.

TaskList share sheet

If you’re running this in the simulator you won’t see as many apps installed as you would on a physical device.