iOS File Association, Preview, and Open In… with Xamarin

May 29, 2014

tags: , ,
one comment

Many mobile apps need the ability to preview files — email attachments, web links, cloud photos, and other assets. Some apps even need the ability to open and handle files themselves. Although file sharing between iOS applications hasn’t always been available and easy, basic file sharing scenarios are now entirely accessible and easily available to any iOS app. In this post we’ll take a look at how iOS apps can register as a file type handler for a specific file type, how apps can preview files, and how apps can trigger an “Open in…” dialog so that another app can get its chance to handle the file.

As in previous posts, I’ll be using Xamarin but the Objective C APIs are almost identical, so it should be quite easy to port the examples below to a native iOS app if necessary. Let’s get started with file association.

File Association

An iOS app declares itself as a file type handler in its main .plist file, which contains a section for the document types the app supports. The association is not based only on the file extension. Instead, you have to use the UTI (Uniform Type Identifier) for the file type you’re interested in, unless you’re making up your own file format — in which case you’ll have to register a custom UTI as well.

Although you can edit the .plist file directly, Xamarin’s project options offers an “Advanced” tab where you can add the document type quite easily. In Xcode, the same property pane is under the Target properties if you select your project in the navigator.

iOS File Type Association Dialog in Xamarin Studio

After adding the document type in the .plist, your app is all set to handle the specified file type — in the example above, we’re all set to handle PDF files. Now it’s just a matter of finding some other app that wants to open a PDF file, such as Safari. Click the PDF file’s background and wait for the “Open in…” menu to appear on the top. Your app should be one of the candidates now.

Open In... Menu from Safari PDF File

But wait, we haven’t done anything yet on the app side to actually handle the incoming file. What iOS does when sharing a file from one app to another is copy the file to the target application’s Documents/Inbox directory, where you can find it yourself. The system also calls the OpenUrl ([UIApplicationDelegate application:openUrl:sourceApplication:annotation:] in Objective C) method on your application delegate, which can work directly with the url parameter to display or process the incoming file.

Because the application delegate is often decoupled from the view controller that will be handling the file, you might consider using a pub/sub solution, such as NSNotificationCenter, to post a message from the application delegate to the view controller responsible for displaying the file. Something along the following lines:

// In your AppDelegate class:
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
	Console.WriteLine ("Invoked with OpenUrl: {0}", url.AbsoluteString);
	NSNotificationCenter.DefaultCenter.PostNotificationName ("OpenUrl", url);
	return true;
}

Preview

OK, at this time we’re registered to handle PDF files, but we don’t actually do anything about them. The next step is to display a preview of the provided PDF file. Instead of integrating a third party PDF library, we’re going to use QuickLook, a powerful built-in framework that can display a preview for a large number of file formats, including Microsoft Office documents, PDF files, and images.

QuickLook is built around QLPreviewController, which is a view controller that you can present with the item(s) to preview. It requires an implementation of QLPreviewControllerDataSource, which feeds it with preview items (QLPreviewItem) that have a title and a URL. The following view controller handles the details of receiving the notification from NSNotificationCenter posted in the previous step, and offers the user a button which will display the document using QuickLook’s preview controller.

public class MainViewController : UIViewController
{
	private NSUrl _documentUrl;

	public override void ViewDidLoad ()
	{
		base.ViewDidLoad ();

		View.BackgroundColor = UIColor.LightGray;

		UIButton previewButton = new UIButton (new RectangleF (100, 200, 150, 30));
		previewButton.SetTitle ("Preview", UIControlState.Normal);
		previewButton.TouchUpInside += OnPreviewTapped;
		View.AddSubview (previewButton);

		NSNotificationCenter.DefaultCenter.AddObserver ("OpenUrl", OnOpenUrl);
	}

	private class PreviewItem : QLPreviewItem
	{
		public string Title { get; set; }
		public NSUrl Url { get; set; }

		public override NSUrl ItemUrl { get { return Url; } }
		public override string ItemTitle { get { return Title; } }
	}

	private class PreviewDataSource : QLPreviewControllerDataSource
	{
		private NSUrl _url;

		public PreviewDataSource(NSUrl url)
		{
			_url = url;
		}

		public override int PreviewItemCount (QLPreviewController controller)
		{
			return 1;
		}

		public override QLPreviewItem GetPreviewItem (QLPreviewController controller, int index)
		{
			return new PreviewItem { Title = "PDF Document", Url = _url };
		}
	}

	private void OnPreviewTapped(object sender, EventArgs args)
	{
		QLPreviewController qlPreview = new QLPreviewController();
		qlPreview.DataSource = new PreviewDataSource(_documentUrl);
		PresentViewController (qlPreview, true, null);
	}

	private void OnOpenUrl (NSNotification notification)
	{
		_documentUrl = (NSUrl) notification.Object;
	}
}

The result, when the button is tapped, looks like this — the additional sharing capabilities are provided directly by QLPreviewController:

iOS QuickLook's Preview Controller Displaying PDF Document

Open In…

Next, it would be nice to integrate our app back with the rest of the system. Specifically, the “Open in…” feature we used in Safari to test our app is something users might look for when viewing documents or working with data. If our app can handle PDF documents, it might be nice enough to offer other apps a go at that document. Even if the app doesn’t handle any files, it might still have some data (such as links, text, tables, or images) to share.

Modern versions of iOS have a very simple facility for sharing data from your application — UIActivityViewController. This view controller accepts an array of data items such as links, images, URLs, or files, and displays a list of apps and actions appropriate for your data source. Again, all you need to do is provide the data:

// In the following code, _documentUrl is the NSUrl for the document we're working on
UIActivityViewController activityVC = new UIActivityViewController (
	new NSObject[] { new NSString("PDF Document"), _documentUrl }, null);
PresentViewController (activityVC, true, null);

The result is a menu with the actions suitable for a PDF document, such as sending it by email:

iOS Open In... Menu Presented from Our App iOS Mail Composer with Attached PDF Document

Summary

In this post, we looked at some ways an iOS app can integrate with the result of the system by sharing files and receiving files from other applications. Associating your app with a file type makes sense if you can display, edit, or process specific files. Sharing data/files or previewing files makes sense even if you’re not a content provider or the user’s first pick at editing a certain type of file.


I am posting short links and updates on Twitter as well as on this blog. You can follow me: @goldshtn

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

one comment

  1. Pingback: Intents, Contracts, and App Extensions: App-to-App Communication