24 Apr

Creating clickable URLs in a UITextField that open in your app

Creating clickable links that do something in your app.

The DatesInSwift app (which you can download from Git by clicking the link) includes a number of “magic dates” to demonstrate using tuples and a Swift switch statement for complex pattern matching.

The magic dates are listed on the screen, but I wanted a way for users to select them without having to enter them manually.

The app now displays an attributed string listing the magic dates, and those dates are links. If you double-tap or long-press on one of the dates, the app selects that date in the picker and chooses that date.

Here are the steps involved in making that work:

Teach the app to respond a custom URL scheme


I defined a new URL scheme, WTDateLink://. After setting up the DatesInSwift app correctly and installing it on a device, tapping a link that contains a WTDateLink URL in any app causes the system to offer to open the URL in DatesInSwift. Here’s what you do to make an app support a custom URL scheme:

Add a CFBundleURLTypes key to your app’s info.plist file. The value for that key is an array of the URL types your app supports. For the DatesInSwift app, we only add one entry, an dictionary with a key CFBundleURLSchemes that contains an array with a single entry, the string WTDateLink. The whole thing looks like this in XML format:

 

Sigh. Having formatting problems with that. Here’s what it looks like in the Xcode plist editor:

Custom URL scheme info.plist entry
The next step is to implement the application:openURL:sourceApplication:annotation: UIApplicationDelegate method. The system calls that method when the user triggers an event to open an URL of a type you’ve regestered for in your info.plist file.

In DatesForSwift, the URL format it supports is quite simple. A URL looks like this:

or

Which is a special case to pick the current date.

The application:openURL:sourceApplication:annotation: method looks like this:

Setting up a UITextView to support tapping on URL links:


The next step is to set up a UITextView so that it can contain links and respond to taps on those links.

  • Create a UITextView in IB (Interface Builder).
  • In the Attributes inspector, do the following:
    • Set the text type to Attributed
    • Make the text Selectable but not Editable
    • Under Detection, check “Links.”
  • Enter your text into the text field. If you want to style your text the easiest way to do it is ot edit the text in TextEdit then copy it and paste it into IB. Unfortuantely, the input box for styled text in IB does not honor links with a user-readable title and a URL that’s different than the text that’s displayed. The only type of links you can use here are URL strings like http://www.apple.com/store or `WTDateLink://?date=12_31_2000″. You can’t create a link that looks like this: The Apple Store. Getting clickable links where the text that’s shown is different from the URL requires special tricks.

Installing clickable links in your UITextView

There are 2 different ways to create a link where the title of the link is different from the URL:

  1. Construct an attributed string in code
  2. Load an attributed string from a file.

In OSX, the NSAttributedString method has an intializer initWithRTF:documentAttributes:. You can use it to load an attributed string directly from an RTF file. Unfortunately the iOS version of NSAttributedString does not support this initializer.

The good news is that NSAttributedString does conform to the NSCoding protocol, which makes it possible to convert an NSAttributedString back and forth to NSData.

The solution I’ve come up with is to write an OSX utility that attributed strings from RTF files using initWithRTF:documentAttributes:, then converts them to NSData and saves the data to a file with the suffix “.data”. I then load the contents of the file as an attributed string with code like this:

I have created an OSX command line tool that takes a path to an RTF file and an output path as parameters and creates the .data files for use in iOS. I then build that command-line tool into the build process of projects that need to load lots of attributed strings from files. The details of the command line tool are beyond the scope of this document. For this project, I just manually converted my RTF file to a .data file and included the .data file in the project.