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.
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
Universal Type Identifiers Tutorial for iOS: Importing and Exporting App Data
25 mins
- Getting Started
- Exploring the App
- Creating a Custom File Type
- Saving the New File Type
- Understanding the Uniform Type Identifier
- Registering Your New UTI
- Defining Import Types
- Importing Files to your App
- Using Scenes
- Working Without Scenes
- Defining Export Types
- Setting Up the Export Logic
- Switching from Import to Export
- Exporting Files with Activity View Controller
- Excluding Activity Types
- Exporting Files via Email
- Adding an Attachment
- Understanding the MIME Type
- Where to Go From Here?
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.
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:
- 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. - You check for at least one valid UIOpenURLContext. If there isn’t one, you return immediately.
- 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.
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).
Find and replace the text UTImportedTypeDeclarations
with UTExportedTypeDeclarations
.
To make sure everything works as before, delete the app from your Simulator. Build and run and drop PrioritizedTasks.rwtl onto the Simulator again.
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.
If you’re running this in the simulator you won’t see as many apps installed as you would on a physical device.